Home | History | Annotate | Download | only in bltins
      1 /***********************************************************************
      2 *                                                                      *
      3 *               This software is part of the ast package               *
      4 *          Copyright (c) 1982-2007 AT&T Intellectual Property          *
      5 *                      and is licensed under the                       *
      6 *                  Common Public License, Version 1.0                  *
      7 *                    by AT&T Intellectual Property                     *
      8 *                                                                      *
      9 *                A copy of the License is available at                 *
     10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
     11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
     12 *                                                                      *
     13 *              Information and Software Systems Research               *
     14 *                            AT&T Research                             *
     15 *                           Florham Park NJ                            *
     16 *                                                                      *
     17 *                  David Korn <dgk (at) research.att.com>                   *
     18 *                                                                      *
     19 ***********************************************************************/
     20 #pragma prototyped
     21 
     22 #include	<shell.h>
     23 #include	<stdio.h>
     24 #include	<stdbool.h>
     25 #include	<option.h>
     26 #include	<stk.h>
     27 #include	<tm.h>
     28 #include	"name.h"
     29 #undef nv_isnull
     30 #ifndef SH_DICT
     31 #   define SH_DICT     "libshell"
     32 #endif
     33 #include	<poll.h>
     34 
     35 #define sh_contexttoshb(context)	((Shbltin_t*)(context))
     36 #define sh_contexttoshell(context)	((context)?(sh_contexttoshb(context)->shp):(NULL))
     37 
     38 /*
     39  * time formatting related
     40 */
     41 struct dctime
     42 {
     43 	Namfun_t	fun;
     44 	Namval_t 	*format;
     45 	char		buff[256]; /* Must be large enougth for |tmfmt()| */
     46 };
     47 
     48 static char *get_time(Namval_t* np, Namfun_t* nfp)
     49 {
     50 	struct dctime *dp = (struct dctime*)nfp;
     51 	time_t t = nv_getn(np,nfp);
     52 	char *format = nv_getval(dp->format);
     53 	tmfmt(dp->buff,sizeof(dp->buff),format,(time_t*)0);
     54 	return(dp->buff);
     55 }
     56 
     57 static void put_time(Namval_t* np, const char* val, int flag, Namfun_t* nfp)
     58 {
     59 	struct dctime *dp = (struct dctime*)nfp;
     60 	char *last;
     61 	if(val)
     62 	{
     63 		int32_t t;
     64 		if(flag&NV_INTEGER)
     65 		{
     66 			if(flag&NV_LONG)
     67 				t = *(Sfdouble_t*)val;
     68 			else
     69 				t = *(double*)val;
     70 		}
     71 		else
     72 		{
     73 			t = tmdate(val, &last, (time_t*)0);
     74 			if(*last)
     75 				errormsg(SH_DICT, ERROR_exit(1),"%s: invalid date/time string", val);
     76 		}
     77 		nv_putv(np, (char*)&t,NV_INTEGER, nfp);
     78 	}
     79 	else
     80 	{
     81 		nv_unset(dp->format);
     82 		free((void*)dp->format);
     83 		nv_putv(np, val, flag, nfp);
     84 	}
     85 }
     86 
     87 static Namval_t *create_time(Namval_t *np, const char *name, int flags, Namfun_t *nfp)
     88 {
     89 	struct dctime *dp = (struct dctime*)nfp;
     90 	if(strcmp(name, "format"))
     91 		return((Namval_t*)0);
     92 	return(dp->format);
     93 }
     94 
     95 static const Namdisc_t timedisc =
     96 {
     97         sizeof(struct dctime),
     98         put_time,
     99         get_time,
    100         0,
    101         0,
    102         create_time,
    103 };
    104 
    105 
    106 static Namval_t *make_time(Namval_t* np)
    107 {
    108 	int offset = stktell(stkstd);
    109 	char *name = nv_name(np);
    110 	struct dctime *dp = newof(NULL,struct dctime,1,0);
    111 	if(!dp)
    112 		return((Namval_t*)0);
    113 	sfprintf(stkstd,"%s.format\0",name);
    114 	sfputc(stkstd,0);
    115 	dp->format = nv_search(stkptr(stkstd,offset),sh.var_tree,NV_ADD);
    116 	dp->fun.disc = &timedisc;
    117 	nv_stack(np,&dp->fun);
    118 	return(np);
    119 }
    120 
    121 /*
    122  * mode formatting related
    123 */
    124 static char *get_mode(Namval_t* np, Namfun_t* nfp)
    125 {
    126 	mode_t mode = nv_getn(np,nfp);
    127 	return(fmtperm(mode));
    128 }
    129 
    130 static void put_mode(Namval_t* np, const char* val, int flag, Namfun_t* nfp)
    131 {
    132 	if(val)
    133 	{
    134 		int32_t mode;
    135 		char *last;
    136 		if(flag&NV_INTEGER)
    137 		{
    138 			if(flag&NV_LONG)
    139 				mode = *(Sfdouble_t*)val;
    140 			else
    141 				mode = *(double*)val;
    142 		}
    143 		else
    144 		{
    145 			mode = strperm(val, &last,0);
    146 			if(*last)
    147 				errormsg(SH_DICT, ERROR_exit(1),"%s: invalid mode string", val);
    148 		}
    149 		nv_putv(np,(char*)&mode,NV_INTEGER,nfp);
    150 	}
    151 	else
    152 		nv_putv(np,val,flag,nfp);
    153 }
    154 
    155 static const Namdisc_t modedisc =
    156 {
    157 	0,
    158         put_mode,
    159         get_mode,
    160 };
    161 
    162 static Namval_t *make_mode(Namval_t* np)
    163 {
    164 	char *name = nv_name(np);
    165 	Namfun_t *nfp = newof(NULL,Namfun_t,1,0);
    166 	if(!nfp)
    167 		return((Namval_t*)0);
    168 	nfp->disc = &modedisc;
    169 	nv_stack(np,nfp);
    170 	return(np);
    171 }
    172 
    173 /*
    174  *  field related typese and functions
    175  */
    176 typedef struct _field_
    177 {
    178 	char		*name;		/* field name */
    179 	int		flags;		/* flags */
    180 	short		offset;		/* offset of field into data */
    181 	short		size;		/* size of field */
    182 	Namval_t	*(*make)(Namval_t*);	/* discipline constructor */
    183 } Shfield_t;
    184 
    185 /*
    186  * lookup field in field table
    187  */
    188 static Shfield_t *sh_findfield(Shfield_t *ftable, int nelem, const char *name)
    189 {
    190 	Shfield_t *fp = ftable;
    191 	register int i,n;
    192 	register const char *cp;
    193 	for(cp=name; *cp; cp++)
    194 	{
    195 		if(*cp=='.')
    196 			break;
    197 	}
    198 	n = cp-name;
    199 	for(i=0; i < nelem; i++,fp++)
    200 	{
    201 		if(memcmp(fp->name,name,n)==0 && fp->name[n]==0)
    202 			return(fp);
    203 	}
    204 	return(0);
    205 }
    206 
    207 /*
    208  * class types and functions
    209  */
    210 
    211 typedef struct _class_
    212 {
    213 	int		nelem;		/* number of elements */
    214 	int		dsize;		/* size for data structure */
    215 	Shfield_t 	*fields;	/* field description table */
    216 } Shclass_t;
    217 
    218 struct dcclass
    219 {
    220 	Namfun_t	fun;
    221 	Shclass_t	sclass;
    222 };
    223 
    224 static Namval_t *sh_newnode(register Shfield_t *fp, Namval_t *np)
    225 {
    226 	char *val = np->nvalue + fp->offset;
    227 	char *name = nv_name(np);
    228 	register Namval_t *nq;
    229 	int offset = stktell(stkstd);
    230 	sfprintf(stkstd,"%s.%s\0",name,fp->name);
    231 	sfputc(stkstd,0);
    232 	nq = nv_search(stkptr(stkstd,offset),sh.var_tree,NV_ADD);
    233 	if(fp->size<0)
    234 		val = *(char**)val;
    235 	nv_putval(nq,val,fp->flags|NV_NOFREE);
    236 	if(fp->make)
    237 		(*fp->make)(nq);
    238 	return(nq);
    239 }
    240 
    241 static Namval_t *fieldcreate(Namval_t *np, const char *name, int flags, Namfun_t *nfp)
    242 {
    243 	struct dcclass *dcp = (struct dcclass*)nfp;
    244 	Shclass_t *sp = &dcp->sclass;
    245 	Shfield_t *fp = sh_findfield(sp->fields,sp->nelem,name);
    246 	Namval_t *nq,**nodes = (Namval_t**)(dcp+1);
    247 	int n = fp-sp->fields;
    248 	int len =  strlen(fp->name);
    249 	void *data = (void*)np->nvalue;
    250 	if(!(nq=nodes[n]))
    251 	{
    252 		nodes[n] = nq = sh_newnode(fp,np);
    253 		nfp->last = "";
    254 	}
    255 	if(name[len]==0)
    256 		return(nq);
    257 	return(nq);
    258 }
    259 
    260 static void genvalue(Sfio_t *out, Shclass_t *sp, int indent, Namval_t *npar)
    261 {
    262 	Shfield_t *fp = sp->fields;
    263 	Namval_t *np, **nodes= (Namval_t**)(sp+1);
    264 	register int i,isarray;
    265 	if(out)
    266 	{
    267 		sfwrite(out,"(\n",2);
    268 		indent++;
    269 	}
    270 	for(i=0; i < sp->nelem; i++,fp++)
    271 	{
    272 #if 0
    273 		/* handle recursive case */
    274 #endif
    275 		if(!(np=nodes[i]) && out)
    276 			np = sh_newnode(fp,npar);
    277 		if(np)
    278 		{
    279 			isarray=0;
    280 			if(nv_isattr(np,NV_ARRAY))
    281 			{
    282 				isarray=1;
    283 				if(array_elem(nv_arrayptr(np))==0)
    284 					isarray=2;
    285 				else
    286 					nv_putsub(np,(char*)0,ARRAY_SCAN);
    287 			}
    288 			sfnputc(out,'\t',indent);
    289 			sfputr(out,fp->name,(isarray==2?'\n':'='));
    290 			if(isarray)
    291 			{
    292 				if(isarray==2)
    293 					continue;
    294 				sfwrite(out,"(\n",2);
    295 				sfnputc(out,'\t',++indent);
    296 			}
    297 			while(1)
    298 			{
    299 				char *fmtq;
    300 				if(isarray)
    301 				{
    302 					sfprintf(out,"[%s]",sh_fmtq(nv_getsub(np)));
    303 					sfputc(out,'=');
    304 				}
    305 				if(!(fmtq=nv_getval(np)) || !(fmtq=sh_fmtq(fmtq)))
    306 					fmtq = "";
    307 				sfputr(out,fmtq,'\n');
    308 				if(!nv_nextsub(np))
    309 					break;
    310 				sfnputc(out,'\t',indent);
    311 			}
    312 			if(isarray)
    313 			{
    314 				sfnputc(out,'\t',--indent);
    315 				sfwrite(out,")\n",2);
    316 			}
    317 		}
    318 	}
    319 	if(out)
    320 	{
    321 		if(indent>1)
    322 			sfnputc(out,'\t',indent-1);
    323 		sfputc(out,')');
    324 	}
    325 }
    326 
    327 static char *walk_class(register Namval_t *np, int dlete, struct dcclass *dcp)
    328 {
    329 	static Sfio_t *out;
    330 	Sfio_t *outfile;
    331 	int savtop = stktell(stkstd);
    332 	char *savptr =  stkfreeze(stkstd,0);
    333 	if(dlete)
    334 		outfile = 0;
    335 	else if(!(outfile=out))
    336                 outfile = out =  sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
    337 	else
    338 		sfseek(outfile,0L,SEEK_SET);
    339 	genvalue(outfile,&dcp->sclass,0,np);
    340 	stkset(stkstd,savptr,savtop);
    341 	if(!outfile)
    342 		return((char*)0);
    343 	sfputc(out,0);
    344 	return((char*)out->_data);
    345 }
    346 
    347 static char *get_classval(Namval_t* np, Namfun_t* nfp)
    348 {
    349 	return(walk_class(np,0,(struct dcclass *)nfp));
    350 }
    351 
    352 static void put_classval(Namval_t* np, const char* val, int flag, Namfun_t* nfp)
    353 {
    354 	walk_class(np,1,(struct dcclass *)nfp);
    355 	if(nfp = nv_stack(np,(Namfun_t*)0))
    356 	{
    357 		free((void*)nfp);
    358 		if(np->nvalue && !nv_isattr(np,NV_NOFREE))
    359 			free((void*)np->nvalue);
    360 	}
    361 	if(val)
    362 		nv_putval(np,val,flag);
    363 }
    364 
    365 static const Namdisc_t classdisc =
    366 {
    367         sizeof(struct dcclass),
    368         put_classval,
    369         get_classval,
    370         0,
    371         0,
    372 	fieldcreate
    373 };
    374 
    375 static int mkclass(Namval_t *np, Shclass_t *sp)
    376 {
    377 	struct dcclass *tcp = newof(NULL,struct dcclass,1,sp->nelem*sizeof(Namval_t*));
    378 	if(!tcp)
    379 		return(0);
    380 	memset((void*)(tcp+1),0,sp->nelem*sizeof(Namval_t*));
    381 	tcp->fun.disc = &classdisc;
    382 	tcp->sclass = *sp;
    383 	np->nvalue = (char*)calloc(sp->dsize,1);
    384 	nv_stack(np,&tcp->fun);
    385 	return(1);
    386 }
    387 
    388 /*
    389  * ====================from here down is file class specific
    390  */
    391 static struct stat *Sp;
    392 
    393 struct filedata
    394 {
    395 	struct stat	statb;
    396 	int		fd;
    397 	char		*name;
    398 };
    399 
    400 static Shfield_t filefield[] =
    401 {
    402 	{ "atime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_atime), sizeof(Sp->st_atime), make_time},
    403 	{ "ctime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_ctime), sizeof(Sp->st_ctime), make_time},
    404 	{ "dev",   NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_dev),sizeof(Sp->st_dev)},
    405 	{ "fd",    NV_INTEGER|NV_RDONLY, offsetof(struct filedata,fd), 		sizeof(int)},
    406 	{ "gid",   NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_gid), sizeof(Sp->st_gid)},
    407 	{ "ino",   NV_LONG|NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_ino), sizeof(Sp->st_ino)},
    408 	{ "mode",  NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_mode), sizeof(Sp->st_mode), make_mode},
    409 	{ "mtime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_mtime), sizeof(Sp->st_mtime), make_time},
    410 	{ "name",   NV_RDONLY, offsetof(struct filedata,name), 	-1 },
    411 	{ "nlink", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_nlink), sizeof(Sp->st_nlink)},
    412 	{ "size",  NV_LONG|NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_size), sizeof(Sp->st_size)},
    413 	{ "uid",   NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_uid), sizeof(Sp->st_uid)}
    414 };
    415 
    416 static Shclass_t Fileclass =
    417 {
    418 	sizeof(filefield)/sizeof(*filefield),
    419 	sizeof(struct filedata),
    420 	filefield
    421 };
    422 
    423 
    424 #define letterbit(bit)	(1<<((bit)-'a'))
    425 
    426 static const char sh_optopen[] =
    427 "[-?\n@(#)$Id: open (AT&T Labs Research) 2007-05-07 $\n]"
    428 "[-author?David Korn <dgk (at) research.att.com>]"
    429 "[-author?Roland Mainz <roland.mainz (at) nrubsig.org>]"
    430 "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
    431 "[+NAME? open - create a shell variable correspnding to a file]"
    432 "[+DESCRIPTION?\bopen\b creates the compound variable \avar\a correspinding "
    433 	"to the file given by the pathname \afile\a.  The elements of \avar\a "
    434 	"are the names of elements in the \astat\a structure with the \bst_\b "
    435 	"prefix removed.]"
    436 "[+?\afile\a is opened (based on \b-r\b and/or \b-w\b) and the variable "
    437 	"\avar\a\b.fd\b is the file descriptor.]"
    438 "[a:append?Open for append.]"
    439 "[b:binary?Open in binary mode"
    440 #ifndef O_BINARY
    441 	" (not supported/ignored on this platform)"
    442 #endif
    443 	".]"
    444 "[t:text?Open in text mode"
    445 #ifndef O_TEXT
    446 	" (not supported/ignored on this platform)"
    447 #endif
    448 	".]"
    449 "[c:create?Open for create.]"
    450 "[i:inherit?Open without the close-on-exec bit set.]"
    451 "[I:noinherit?Open with the close-on-exec bit set.]"
    452 "[r:read?Open with read access.]"
    453 "[w:write?Open with write access.]"
    454 "[m:mode]:[mode:=rwrwrw?Open with access mode \amode\a.]"
    455 "[x:exclusive?Open exclusive.]"
    456 
    457 "[N:nofollow?If the path names a symbolic link, open fails with ELOOP "
    458 #ifndef O_NOFOLLOW
    459 	" (not supported/ignored on this platform)"
    460 #endif
    461 	".]"
    462 "[S:sync?Write I/O operations on the file descriptor complete as "
    463 	"defined by synchronized I/O file integrity completion"
    464 #ifndef O_SYNC
    465 	" (not supported/ignored on this platform)"
    466 #endif
    467 	".]"
    468 "[T:trunc?If the file exists and is a regular file, and  the  file "
    469         "is successfully opened read/write or write-only, its length is "
    470         "truncated to 0 and the mode and owner are unchanged.  It "
    471         "has  no  effect on FIFO special files or terminal device "
    472         "files.   Its   effect   on   other   file    types    is "
    473         "implementation-dependent.  The  result  of using -T "
    474         "with read-only files is undefined"
    475 #ifndef O_TRUNC
    476 	" (not supported/ignored on this platform)"
    477 #endif
    478 	".]"
    479 "\n"
    480 "\nvar file\n"
    481 "\n"
    482 "[+EXIT STATUS?]{"
    483         "[+0?Success.]"
    484         "[+>0?An error occurred.]"
    485 "}"
    486 "[+SEE ALSO?\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bstat\b(1),\bpoll\b(1),\bstat\b(2)]"
    487 ;
    488 
    489 
    490 extern int b_open(int argc, char *argv[], void *extra)
    491 {
    492 	register Namval_t *np;
    493 	register int n,oflag=0;
    494 	Shell_t *shp = sh_contexttoshell(extra);
    495 	struct filedata *fdp;
    496 	mode_t mode = 0666;
    497 	long flags = 0;
    498 	int fd = -1;
    499 	char *arg;
    500 
    501 	while (n = optget(argv, sh_optopen)) switch (n)
    502 	{
    503 	    case 'r':
    504 	    case 'w':
    505 	    case 'i':
    506 		flags |= letterbit(n);
    507 		break;
    508 	    case 'I':
    509 		flags &= ~(letterbit('i'));
    510 		break;
    511 	    case 'b':
    512 #ifdef O_BINARY
    513 		oflag |= O_BINARY;
    514 #endif
    515 		break;
    516 	    case 't':
    517 #ifdef O_TEXT
    518 		oflag |= O_TEXT;
    519 #endif
    520 		break;
    521 	    case 'N':
    522 #ifdef O_NOFOLLOW
    523 		oflag |= O_NOFOLLOW;
    524 #endif
    525 		break;
    526 	    case 'T':
    527 #ifdef O_TRUNC
    528 		oflag |= O_TRUNC;
    529 #endif
    530 		break;
    531 	    case 'x':
    532 		oflag |= O_EXCL;
    533 		break;
    534 	    case 'c':
    535 		oflag |= O_CREAT;
    536 		break;
    537 	    case 'a':
    538 		oflag |= O_APPEND;
    539 		break;
    540 	    case 'S':
    541 #ifdef O_SYNC
    542 		oflag |= O_SYNC;
    543 #endif
    544 		break;
    545 	    case 'm':
    546 		mode = strperm(arg = opt_info.arg, &opt_info.arg, mode);
    547 		if (*opt_info.arg)
    548 			errormsg(SH_DICT, ERROR_system(1), "%s: invalid mode", arg);
    549 	    	break;
    550 	    case ':':
    551 		errormsg(SH_DICT, 2, "%s", opt_info.arg);
    552 		break;
    553 	    case '?':
    554 		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
    555 		break;
    556 	}
    557 	argc -= opt_info.index;
    558 	argv += opt_info.index;
    559 	if(argc!=2 || !(flags&(letterbit('r')|letterbit('w'))))
    560 		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
    561 
    562 	if(flags&letterbit('r'))
    563 	{
    564 		if(flags&letterbit('w'))
    565 			oflag |= O_RDWR;
    566 		else
    567 			oflag |= O_RDONLY;
    568 	}
    569 	else if(flags&letterbit('w'))
    570 		oflag |= O_WRONLY;
    571 
    572 	fd = sh_open(argv[1], oflag, mode);
    573 	if(fd<0)
    574 		errormsg(SH_DICT, ERROR_system(1), "%s: open failed", argv[1]);
    575 
    576 	if(!(flags&letterbit('i')))
    577 		fcntl(fd, F_SETFL, 0);
    578 
    579 	np = nv_open(argv[0], shp->var_tree, NV_ARRAY|NV_VARNAME|NV_NOASSIGN);
    580 	if(!nv_isnull(np))
    581 		nv_unset(np);
    582 	mkclass(np, &Fileclass);
    583 	fdp = (struct filedata*)np->nvalue;
    584 	fstat(fd, &fdp->statb);
    585 	fdp->fd = fd;
    586 	fdp->name = strdup(argv[1]);
    587 	return(0);
    588 }
    589 
    590 static const char sh_optclose[] =
    591 "[-?\n@(#)$Id: close (AT&T Labs Research) 2007-04-21 $\n]"
    592 "[-author?Roland Mainz <roland.mainz (at) nrubsig.org>]"
    593 "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
    594 "[+NAME? close - close a file descriptor]"
    595 "[+DESCRIPTION?\bclose\b closes the file descriptor specified by fd.]"
    596 "\n"
    597 "\nfd\n"
    598 "\n"
    599 "[+EXIT STATUS?]{"
    600         "[+0?Success.]"
    601         "[+>0?An error occurred.]"
    602 "}"
    603 "[+SEE ALSO?\bopen\b(1),\bdup\b(1),\btmpfile\b(1),\bpoll\b(1),\bstat\b(1)]"
    604 ;
    605 
    606 extern int b_close(int argc, char *argv[], void *extra)
    607 {
    608 	register int n=0;
    609 	int fd = -1;
    610 
    611 	while (n = optget(argv, sh_optclose)) switch (n)
    612 	{
    613 	    case ':':
    614 		errormsg(SH_DICT, 2, "%s", opt_info.arg);
    615 		break;
    616 	    case '?':
    617 		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
    618 		break;
    619 	}
    620 	argc -= opt_info.index;
    621 	argv += opt_info.index;
    622 	if(argc!=1)
    623 		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
    624 
    625 	errno = 0;
    626 	fd = strtol(argv[0], (char **)NULL, 0);
    627 	if (errno != 0 || fd < 0)
    628 		errormsg(SH_DICT, ERROR_system(1), "%s: invalid descriptor", argv[0]);
    629 
    630         n = sh_close(fd);
    631 
    632 	if (n < 0)
    633 		errormsg(SH_DICT, ERROR_system(1), "%s: close error", argv[0]);
    634 
    635 	return(n==0?0:1);
    636 }
    637 
    638 
    639 static const char sh_opttmpfile[] =
    640 "[-?\n@(#)$Id: tmpfile (AT&T Labs Research) 2007-05-07 $\n]"
    641 "[-author?Roland Mainz <roland.mainz (at) nrubsig.org>]"
    642 "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
    643 "[+NAME? tmpfile - create a shell variable correspnding to a temporary file]"
    644 "[+DESCRIPTION?\btmpfile\b creates the compound variable \avar\a correspinding "
    645 	"to a temporary file.  The elements of \avar\a "
    646 	"are the names of elements in the \astat\a structure with the \bst_\b "
    647 	"prefix removed.]"
    648 "[i:inherit?Open without the close-on-exec bit set.]"
    649 "[I:noinherit?Open with the close-on-exec bit set.]"
    650 "\n"
    651 "\nvar\n"
    652 "\n"
    653 "[+EXIT STATUS?]{"
    654         "[+0?Success.]"
    655         "[+>0?An error occurred.]"
    656 "}"
    657 "[+SEE ALSO?\bopen\b(1),\bdup\b(1),\bclose\b(1),\bstat\b(1),\bstat\b(2)]"
    658 ;
    659 
    660 
    661 extern int b_tmpfile(int argc, char *argv[], void *extra)
    662 {
    663 	register Namval_t *np;
    664 	register int n;
    665 	Shell_t *shp = sh_contexttoshell(extra);
    666 	struct filedata *fdp;
    667 	bool inherit = false;
    668 	FILE *file = NULL;
    669 	int ffd, fd = -1;
    670 	while (n = optget(argv, sh_opttmpfile)) switch (n)
    671 	{
    672 	    case 'i':
    673 		inherit = true;
    674 		break;
    675 	    case 'I':
    676 		inherit = false;
    677 		break;
    678 	    case ':':
    679 		errormsg(SH_DICT, 2, "%s", opt_info.arg);
    680 		break;
    681 	    case '?':
    682 		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
    683 		break;
    684 	}
    685 	argc -= opt_info.index;
    686 	argv += opt_info.index;
    687 	if(argc!=1)
    688 		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
    689 
    690 	file = tmpfile();
    691 	if(!file)
    692 		errormsg(SH_DICT, ERROR_system(1), "%s: tmpfile failed", argv[1]);
    693 	ffd = fileno(file);
    694 	fd = sh_dup(ffd);
    695 	if(fd<0)
    696 		errormsg(SH_DICT, ERROR_system(1), "%s: tmpfile failed", argv[1]);
    697 	fclose(file);
    698 
    699 	if(!inherit)
    700 		fcntl(fd, F_SETFL, 0);
    701 
    702 	np = nv_open(argv[0], shp->var_tree, NV_ARRAY|NV_VARNAME|NV_NOASSIGN);
    703 	if(!nv_isnull(np))
    704 		nv_unset(np);
    705 	mkclass(np,&Fileclass);
    706 	fdp = (struct filedata*)np->nvalue;
    707 
    708 	fstat(fd, &fdp->statb);
    709 	fdp->fd = fd;
    710 	fdp->name = NULL;
    711 	return(0);
    712 }
    713 
    714 static const char sh_optdup[] =
    715 "[-?\n@(#)$Id: dup (AT&T Labs Research) 2007-05-07 $\n]"
    716 "[-author?Roland Mainz <roland.mainz (at) nrubsig.org>]"
    717 "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
    718 "[+NAME? dup - duplicate an open file descriptor]"
    719 "[+DESCRIPTION?The \bdup\b commands returns a new file descriptor having the "
    720      "following in common with the original open file descriptor "
    721      "fd: same open file (or pipe), same file pointer (that is, both  file descriptors "
    722      "share one file pointer) same access mode (read, write or read/write). "
    723      "The file descriptor returned is the lowest one available.]"
    724 "[i:inherit?Open without the close-on-exec bit set.]"
    725 "[I:noinherit?Open with the close-on-exec bit set.]"
    726 "\n"
    727 "\nvar fd\n"
    728 "\n"
    729 "[+EXIT STATUS?]{"
    730         "[+0?Success.]"
    731         "[+>0?An error occurred.]"
    732 "}"
    733 "[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bclose\b(1),\bpoll\b(1),\bstat\b(1)]"
    734 ;
    735 
    736 
    737 extern int b_dup(int argc, char *argv[], void *extra)
    738 {
    739 	register Namval_t *np;
    740 	register int n;
    741 	Shell_t *shp = sh_contexttoshell(extra);
    742 	struct filedata *fdp;
    743 	bool inherit = false;
    744 	int ffd, fd = -1;
    745 	while (n = optget(argv, sh_optdup)) switch (n)
    746 	{
    747 	    case 'i':
    748 		inherit = true;
    749 		break;
    750 	    case 'I':
    751 		inherit = false;
    752 		break;
    753 	    case ':':
    754 		errormsg(SH_DICT, 2, "%s", opt_info.arg);
    755 		break;
    756 	    case '?':
    757 		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
    758 		break;
    759 	}
    760 	argc -= opt_info.index;
    761 	argv += opt_info.index;
    762 	if(argc!=2)
    763 		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
    764 
    765 	errno = 0;
    766 	ffd = strtol(argv[1], (char **)NULL, 0);
    767 	if (errno != 0 || ffd < 0)
    768 		errormsg(SH_DICT, ERROR_system(1), "%s: invalid fd", argv[1]);
    769 
    770 	fd = sh_dup(ffd);
    771 	if(fd<0)
    772 		errormsg(SH_DICT, ERROR_system(1), "%s: dup failed", argv[1]);
    773 
    774 	if(!inherit)
    775 		fcntl(fd,F_SETFL,0);
    776 
    777 	np = nv_open(argv[0],shp->var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN);
    778 	if(!nv_isnull(np))
    779 		nv_unset(np);
    780 	mkclass(np, &Fileclass);
    781 	fdp = (struct filedata*)np->nvalue;
    782 
    783 	fstat(fd, &fdp->statb);
    784 	fdp->fd = fd;
    785 	fdp->name = NULL;
    786 	return(0);
    787 }
    788 
    789 static const char sh_optstat[] =
    790 "[-?\n@(#)$Id: stat (AT&T Labs Research) 2007-05-07 $\n]"
    791 "[-author?David Korn <dgk (at) research.att.com>]"
    792 "[-author?Roland Mainz <roland.mainz (at) nrubsig.org>]"
    793 "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
    794 "[+NAME? stat - get file status]"
    795 "[+DESCRIPTION?\bstat\b creates the compound variable \avar\a correspinding "
    796 	"to the file given by the pathname \afile\a.  The elements of \avar\a "
    797 	"are the names of elements in the \astat\a structure with the \bst_\b "
    798 	"prefix removed.]"
    799 "[l:lstat?If the the named file is a symbolic link returns information about "
    800 	"the link itself.]"
    801 "\n"
    802 "\nvar file\n"
    803 "\n"
    804 "[+EXIT STATUS?]{"
    805         "[+0?Success.]"
    806         "[+>0?An error occurred.]"
    807 "}"
    808 "[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bpoll\b(1),\bstat\b(2),\blstat\b(2)]"
    809 ;
    810 
    811 
    812 extern int b_stat(int argc, char *argv[], void *extra)
    813 {
    814 	register Namval_t *np;
    815 	register int n;
    816 	Shell_t *shp = sh_contexttoshell(extra);
    817 	struct filedata *fdp;
    818 	long flags = 0;
    819 	struct stat statb;
    820 	while (n = optget(argv, sh_optstat)) switch (n)
    821 	{
    822 	    case 'l':
    823 		flags |= letterbit(n);
    824 		break;
    825 	    case ':':
    826 		errormsg(SH_DICT, 2, "%s", opt_info.arg);
    827 		break;
    828 	    case '?':
    829 		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
    830 		break;
    831 	}
    832 	argc -= opt_info.index;
    833 	argv += opt_info.index;
    834 	if(argc!=2)
    835 		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
    836 
    837 	if(flags&letterbit('l'))
    838 	{
    839 		if(lstat(argv[1], &statb) < 0)
    840 			errormsg(SH_DICT, ERROR_system(1), "%s: stat failed", argv[1]);
    841 	}
    842 	else
    843 	{
    844 		if(stat(argv[1], &statb) < 0)
    845 			errormsg(SH_DICT, ERROR_system(1), "%s: stat failed", argv[1]);
    846 
    847 	}
    848 
    849 	np = nv_open(argv[0],shp->var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN);
    850 	if(!nv_isnull(np))
    851 		nv_unset(np);
    852 	mkclass(np,&Fileclass);
    853 	fdp = (struct filedata*)np->nvalue;
    854 	fdp->statb = statb;
    855 	fdp->fd = -1;
    856 	fdp->name = strdup(argv[1]);
    857 	return(0);
    858 }
    859 
    860 static const char sh_optpoll[] =
    861 "[-?\n@(#)$Id: poll (AT&T Labs Research) 2007-12-20 $\n]"
    862 "[-author?Roland Mainz <roland.mainz (at) nrubsig.org]"
    863 "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
    864 "[+NAME? poll - input/output multiplexing]"
    865 "[+DESCRIPTION?The poll command provides applications with a mechanism "
    866 	"for multiplexing input/output over a set of file descriptors. "
    867 	"For each member of the array variable \bvar\b, "
    868 	"poll examines the given file descriptor in the subscript \b.fd\b "
    869 	"for the event(s) specified in the subscript \b.events\b."
    870 	"The poll command identifies those file descriptors on which an "
    871 	"application can read or write data, or on which certain events have "
    872 	"occurred.]"
    873 "[+?The \bvar\b argument specifies the file descriptors to be examined "
    874 	"and the events of interest for each file descriptor. "
    875 	"It is a array of structured variables with one member for each open "
    876 	"file descriptor of interest. The array's members contain the following "
    877 	"subscripts:]{"
    878 		"[+?\b.fd\b       # file descriptor]"
    879 		"[+?\b.events\b   # requested events]"
    880 		"[+?\b.revents\b  # returned event]"
    881 	"}"
    882 "[+?The \bfd\b variable specifies an open file descriptor and the "
    883 	"\bevents\b and \brevents\b members are strings constructed from "
    884 	"a concaternation of the following event flags, seperated by '|':]"
    885 	"{ "
    886 	"[+POLLIN?Data other than high priority data may be "
    887 		"read without blocking. For STREAMS, this "
    888 		"flag is set in revents even if the message "
    889 		"is of zero length.]"
    890 	"[+POLLRDNORM?Normal data (priority band equals 0) may be "
    891 		"read without blocking. For STREAMS, this "
    892 		"flag is set in revents even if the message "
    893 		"is of zero length.]"
    894 	"[+POLLRDBAND?Data from a non-zero priority band may be "
    895 		"read without blocking. For STREAMS, this "
    896 		"flag is set in revents even if the message "
    897 		"is of zero length.]"
    898 	"[+POLLPRI?High priority data may be received without "
    899 		"blocking. For STREAMS, this flag is set in "
    900 		"revents even if the message is of zero "
    901 		"length.]"
    902 	"[+POLLOUT?Normal data (priority band equals 0) may be "
    903 		"written without blocking.]"
    904 	"[+POLLWRNORM?The same as POLLOUT.]"
    905 	"[+POLLWRBAND?Priority data (priority band > 0) may be "
    906 		"written.  This event only examines bands "
    907 		"that have been written to at least once.]"
    908 	"[+POLLERR?An error has occurred on the device or "
    909 		"stream.  This flag is only valid in the "
    910 		"revents bitmask; it is not used in the "
    911 		"events member.]"
    912 	"[+POLLHUP?A hangup has occurred on the stream. This "
    913 		"event and POLLOUT are mutually exclusive; a "
    914 		"stream can never be writable if a hangup has "
    915 		"occurred. However, this event and POLLIN, "
    916 		", POLLRDBAND, or POLLPRI are not "
    917 		"mutually exclusive. This flag is only valid "
    918 		"in the revents bitmask; it is not used in "
    919 		"the events member.]"
    920 	"[+POLLNVAL?The specified fd value does not belong to an "
    921 		"open file. This flag is only valid in the "
    922 		"revents member; it is not used in the events "
    923 		"member.]"
    924    "}"
    925 "]"
    926 
    927 "[+?If the value fd is less than 0, events is ignored and "
    928 	"revents is set to 0 in that entry on return from poll.]"
    929 
    930 "[+?The results of the poll query are stored in the revents "
    931 	"member in the \bvar\b structure. POLL*-strings are set in the \brevents\b "
    932 	"variable to indicate which of the requested events are true. "
    933 	"If none are true, the \brevents\b will be an empty string when "
    934 	"the poll command returns. The event flags "
    935 	"POLLHUP, POLLERR, and POLLNVAL are always set in \brevents\b "
    936 	"if the conditions they indicate are true; this occurs even "
    937 	"though these flags were not present in events.]"
    938 
    939 "[+?If none of the defined events have occurred on any selected "
    940 	"file descriptor, poll waits at least timeout milliseconds "
    941 	"for an event to occur on any of the selected file descriptors. "
    942 	"On a computer where millisecond timing accuracy is not "
    943 	"available, timeout is rounded up to the nearest legal value "
    944 	"available on that system. If the value timeout is 0, poll "
    945 	"returns immediately. If the value of timeout is -1, poll "
    946 	"blocks until a requested event occurs or until the call is "
    947 	"interrupted.]"
    948 
    949 "[+?The poll function supports regular files, terminal and "
    950 	"pseudo-terminal devices, STREAMS-based files, FIFOs and "
    951 	"pipes. The behavior of poll on elements of fds that refer "
    952 	"to other types of file is unspecified.]"
    953 
    954 "[+?The poll function supports sockets.]"
    955 
    956 "[+?A file descriptor for a socket that is listening for connections "
    957 	"will indicate that it is ready for reading, once connections "
    958 	"are available. A file descriptor for a socket that "
    959 	"is connecting asynchronously will indicate that it is ready "
    960 	"for writing, once a connection has been established.]"
    961 
    962 "[+?Regular files always poll TRUE for reading and writing.]"
    963 
    964 "[c:fdcount]:[fdcount?Upon successful completion, a non-negative value is "
    965 	"returned. A positive value indicates the total number of "
    966 	"file descriptors that has been selected (that is, file "
    967 	"descriptors for which the revents member is non-zero). A "
    968 	"value of 0 indicates that the call timed out and no file "
    969 	"descriptors have been selected. Upon failure, -1 is returned.]"
    970 "[t:timeout]:[seconds?Timeout in seconds. If the value timeout is 0, "
    971 	"poll returns immediately. If the value of timeout is -1, poll "
    972 	"blocks until a requested event occurs or until the call is "
    973 	"interrupted.]"
    974 "[T:mtimeout]:[milliseconds?Timeout in milliseconds. If the value timeout is 0, "
    975 	"poll returns immediately. If the value of timeout is -1, poll "
    976 	"blocks until a requested event occurs or until the call is "
    977 	"interrupted.]"
    978 "\n"
    979 "\nvar\n"
    980 "\n"
    981 "[+EXIT STATUS?]{"
    982         "[+0?Success.]"
    983         "[+>0?An error occurred.]"
    984 "}"
    985 "[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bpoll\b(2)]"
    986 ;
    987 
    988 /*
    989  * |mystpcpy| - like |strcpy()| but returns the end of the buffer
    990  *
    991  * Copy string s2 to s1.  s1 must be large enough.
    992  * return s1-1 (position of string terminator ('\0') in destnation buffer).
    993  */
    994 static
    995 char *mystpcpy(char *s1, const char *s2)
    996 {
    997         while (*s1++ = *s2++)
    998                 ;
    999         return (s1-1);
   1000 }
   1001 
   1002 static
   1003 Namval_t *nv_open_fmt(Dt_t *dict, int flags, const char *namefmt, ...)
   1004 {
   1005 	char 	varnamebuff[PATH_MAX];
   1006 	va_list	ap;
   1007 
   1008 	va_start(ap, namefmt);
   1009 	vsnprintf(varnamebuff, sizeof(varnamebuff), namefmt, ap);
   1010 	va_end(ap);
   1011 
   1012 	return nv_open(varnamebuff, dict, flags);
   1013 }
   1014 
   1015 static
   1016 int poll_strtoevents(const char *str)
   1017 {
   1018 	int events = 0;
   1019 
   1020 	if (strstr(str, "POLLIN"))     events |= POLLIN;
   1021 	if (strstr(str, "POLLRDNORM")) events |= POLLRDNORM;
   1022 	if (strstr(str, "POLLRDBAND")) events |= POLLRDBAND;
   1023 	if (strstr(str, "POLLPRI"))    events |= POLLPRI;
   1024 	if (strstr(str, "POLLOUT"))    events |= POLLOUT;
   1025 	if (strstr(str, "POLLWRNORM")) events |= POLLWRNORM;
   1026 	if (strstr(str, "POLLWRBAND")) events |= POLLWRBAND;
   1027 	if (strstr(str, "POLLERR"))    events |= POLLERR;
   1028 	if (strstr(str, "POLLHUP"))    events |= POLLHUP;
   1029 	if (strstr(str, "POLLNVAL"))   events |= POLLNVAL;
   1030 
   1031 	return events;
   1032 }
   1033 
   1034 
   1035 static
   1036 void poll_eventstostr(char *s, int events)
   1037 {
   1038 	*s='\0';
   1039 	if (!events)
   1040 		return;
   1041 
   1042 	if (events & POLLIN)		s=mystpcpy(s, "POLLIN|");
   1043 	if (events & POLLRDNORM)	s=mystpcpy(s, "POLLRDNORM|");
   1044 	if (events & POLLRDBAND)	s=mystpcpy(s, "POLLRDBAND|");
   1045 	if (events & POLLPRI)		s=mystpcpy(s, "POLLPRI|");
   1046 	if (events & POLLOUT)		s=mystpcpy(s, "POLLOUT|");
   1047 	if (events & POLLWRNORM)	s=mystpcpy(s, "POLLWRNORM|");
   1048 	if (events & POLLWRBAND)	s=mystpcpy(s, "POLLWRBAND|");
   1049 	if (events & POLLERR)		s=mystpcpy(s, "POLLERR|");
   1050 	if (events & POLLHUP)		s=mystpcpy(s, "POLLHUP|");
   1051 	if (events & POLLNVAL)		s=mystpcpy(s, "POLLNVAL|");
   1052 
   1053 	/* Remove trailling '|' */
   1054 	s--;
   1055 	if(*s=='|')
   1056 		*s='\0';
   1057 }
   1058 
   1059 #undef  getconf
   1060 #define getconf(x)      strtol(astconf(x,NiL,NiL),NiL,0)
   1061 
   1062 extern int b_poll(int argc, char *argv[], void *extra)
   1063 {
   1064 	register Namval_t *np;
   1065 	register int n;
   1066 	Shell_t *shp = sh_contexttoshell(extra);
   1067 	char *varname;
   1068 	int fd;
   1069 	unsigned int numpollfd = 0;
   1070 	int i;
   1071 	char *s;
   1072 	double timeout = -1.;
   1073 	char buff[256];
   1074 	char *pollfdcountvarname = NULL;
   1075 	long open_max,
   1076 	     bpoll_max;
   1077 
   1078 	if ((open_max = getconf("OPEN_MAX")) <= 0)
   1079 		open_max = OPEN_MAX;
   1080 	/* |bpoll_max| needs to be larger than |OPEN_MAX| to make sure we
   1081 	 * can listen to different sets of events per fd.
   1082 	 */
   1083 	bpoll_max = open_max*2L;
   1084 
   1085 	while (n = optget(argv, sh_optpoll)) switch (n)
   1086 	{
   1087 	    case 't':
   1088 	    case 'T':
   1089 		errno = 0;
   1090 		timeout = strtod(opt_info.arg, (char **)NULL);
   1091 		if (errno != 0)
   1092 			errormsg(SH_DICT, ERROR_system(1), "%s: invalid timeout", opt_info.arg);
   1093 
   1094 		/* -t uses seconds, -T milliseconds */
   1095 		if (n == 't')
   1096 			timeout *= 1000.;
   1097 		break;
   1098 	    case 'c':
   1099 	    	pollfdcountvarname = opt_info.arg;
   1100 		break;
   1101 	    case ':':
   1102 		errormsg(SH_DICT, 2, "%s", opt_info.arg);
   1103 		break;
   1104 	    case '?':
   1105 		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
   1106 		break;
   1107 	}
   1108 	argc -= opt_info.index;
   1109 	argv += opt_info.index;
   1110 	if(argc!=1)
   1111 		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
   1112 
   1113         varname = argv[0];
   1114 
   1115 #ifdef __GNUC__
   1116         /*
   1117          * Allocate one extra array entry to keep ctfconvert+gcc builds
   1118          * happy until CR #6379193 is fixed.
   1119          */
   1120 	struct pollfd pollfd[bpoll_max+1];
   1121 #else
   1122 	struct pollfd pollfd[bpoll_max];
   1123 #endif
   1124 	for(i=0 ; i < bpoll_max ; i++)
   1125 	{
   1126 		np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL|NV_NOADD, "%s[%d].fd", varname, i);
   1127 		if (!np)
   1128 			break;
   1129 		fd = (int)nv_getnum(np);
   1130 		if (fd < 0 || fd > OPEN_MAX)
   1131 			errormsg(SH_DICT, ERROR_system(1), "invalid pollfd fd");
   1132 		nv_close(np);
   1133 		pollfd[i].fd = fd;
   1134 
   1135 		np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL|NV_NOADD, "%s[%d].events", varname, i);
   1136 		if (!np)
   1137 			errormsg(SH_DICT, ERROR_system(1), "missing pollfd events");
   1138 
   1139 		s = nv_getval(np);
   1140 		if (!s)
   1141 			errormsg(SH_DICT, ERROR_system(1), "missing pollfd events value");
   1142 		pollfd[i].events  = poll_strtoevents(s);
   1143 		nv_close(np);
   1144 
   1145 		pollfd[i].revents = 0;
   1146 
   1147 		numpollfd++;
   1148         }
   1149 
   1150 	if (i == bpoll_max)
   1151 		errormsg(SH_DICT, ERROR_system(1), "cannot handle more than %d entries.", bpoll_max);
   1152 
   1153 	n = poll(pollfd, numpollfd, timeout);
   1154 	/* FixMe: EGAIN and EINTR may require extra handling */
   1155 	if (n < 0)
   1156 		errormsg(SH_DICT, ERROR_system(1), "failure");
   1157 
   1158 	if (pollfdcountvarname)
   1159 	{
   1160 		int32_t v = n;
   1161 
   1162 		np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL, "%s", pollfdcountvarname);
   1163 		if (!np)
   1164 			errormsg(SH_DICT, ERROR_system(1), "couldn't create poll count variable %s", pollfdcountvarname);
   1165 		nv_putval(np, (char *)&v, NV_INTEGER);
   1166 		nv_close(np);
   1167 	}
   1168 
   1169 	for(i=0 ; i < numpollfd ; i++)
   1170 	{
   1171 		np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL, "%s[%d].revents", varname, i);
   1172 		if (!np)
   1173 			errormsg(SH_DICT, ERROR_system(1), "couldn't create pollfd %s[%d].revents", varname, i);
   1174 
   1175 		poll_eventstostr(buff, pollfd[i].revents);
   1176 
   1177 		nv_putval(np, buff, 0);
   1178 		nv_close(np);
   1179         }
   1180 
   1181 	return(0);
   1182 }
   1183 
   1184 static const char sh_optrewind[] =
   1185 "[-?\n@(#)$Id: rewind (AT&T Labs Research) 2007-05-07 $\n]"
   1186 "[-author?Roland Mainz <roland.mainz (at) nrubsig.org>]"
   1187 "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
   1188 "[+NAME? rewind - reset file position indicator in a stream]"
   1189 "[+DESCRIPTION?The \brewind\b command will move the file pointer of fd to position 0.]"
   1190 "\n"
   1191 "\nfd\n"
   1192 "\n"
   1193 "[+EXIT STATUS?]{"
   1194         "[+0?Success.]"
   1195         "[+>0?An error occurred.]"
   1196 "}"
   1197 "[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bstat\b(1),\bstat\b(2)]"
   1198 ;
   1199 
   1200 
   1201 extern int b_rewind(int argc, char *argv[], void *extra)
   1202 {
   1203 	Shell_t *shp = sh_contexttoshell(extra);
   1204 	int fd = -1;
   1205 	register int n;
   1206 	while (n = optget(argv, sh_optrewind)) switch (n)
   1207 	{
   1208 	    case ':':
   1209 		errormsg(SH_DICT, 2, "%s", opt_info.arg);
   1210 		break;
   1211 	    case '?':
   1212 		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
   1213 		break;
   1214 	}
   1215 	argc -= opt_info.index;
   1216 	argv += opt_info.index;
   1217 	if(argc!=1)
   1218 		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
   1219 
   1220 	errno = 0;
   1221 	fd = strtol(argv[0], (char **)NULL, 0);
   1222 	if (errno != 0 || fd < 0)
   1223 		errormsg(SH_DICT, ERROR_system(1), "%s: invalid fd", argv[0]);
   1224 
   1225 	if (sh_seek(fd, 0, SEEK_SET) == (off_t)-1)
   1226 		errormsg(SH_DICT, ERROR_system(1), "seek error");
   1227 
   1228 	return(0);
   1229 }
   1230