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