xref: /titanic_50/usr/src/lib/libshell/common/bltins/shiocmd_solaris.c (revision 3db3491215579980a91e230cf21b20608fbb8259)
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@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@research.att.com>]"
429 "[-author?Roland Mainz <roland.mainz@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@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@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@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@research.att.com>]"
792 "[-author?Roland Mainz <roland.mainz@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@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 	struct pollfd pollfd[bpoll_max];
1116 
1117 	for(i=0 ; i < bpoll_max ; i++)
1118 	{
1119 		np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL|NV_NOADD, "%s[%d].fd", varname, i);
1120 		if (!np)
1121 			break;
1122 		fd = (int)nv_getnum(np);
1123 		if (fd < 0 || fd > OPEN_MAX)
1124 			errormsg(SH_DICT, ERROR_system(1), "invalid pollfd fd");
1125 		nv_close(np);
1126 		pollfd[i].fd = fd;
1127 
1128 		np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL|NV_NOADD, "%s[%d].events", varname, i);
1129 		if (!np)
1130 			errormsg(SH_DICT, ERROR_system(1), "missing pollfd events");
1131 
1132 		s = nv_getval(np);
1133 		if (!s)
1134 			errormsg(SH_DICT, ERROR_system(1), "missing pollfd events value");
1135 		pollfd[i].events  = poll_strtoevents(s);
1136 		nv_close(np);
1137 
1138 		pollfd[i].revents = 0;
1139 
1140 		numpollfd++;
1141         }
1142 
1143 	if (i == bpoll_max)
1144 		errormsg(SH_DICT, ERROR_system(1), "cannot handle more than %d entries.", bpoll_max);
1145 
1146 	n = poll(pollfd, numpollfd, timeout);
1147 	/* FixMe: EGAIN and EINTR may require extra handling */
1148 	if (n < 0)
1149 		errormsg(SH_DICT, ERROR_system(1), "failure");
1150 
1151 	if (pollfdcountvarname)
1152 	{
1153 		int32_t v = n;
1154 
1155 		np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL, "%s", pollfdcountvarname);
1156 		if (!np)
1157 			errormsg(SH_DICT, ERROR_system(1), "couldn't create poll count variable %s", pollfdcountvarname);
1158 		nv_putval(np, (char *)&v, NV_INTEGER);
1159 		nv_close(np);
1160 	}
1161 
1162 	for(i=0 ; i < numpollfd ; i++)
1163 	{
1164 		np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL, "%s[%d].revents", varname, i);
1165 		if (!np)
1166 			errormsg(SH_DICT, ERROR_system(1), "couldn't create pollfd %s[%d].revents", varname, i);
1167 
1168 		poll_eventstostr(buff, pollfd[i].revents);
1169 
1170 		nv_putval(np, buff, 0);
1171 		nv_close(np);
1172         }
1173 
1174 	return(0);
1175 }
1176 
1177 static const char sh_optrewind[] =
1178 "[-?\n@(#)$Id: rewind (AT&T Labs Research) 2007-05-07 $\n]"
1179 "[-author?Roland Mainz <roland.mainz@nrubsig.org>]"
1180 "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
1181 "[+NAME? rewind - reset file position indicator in a stream]"
1182 "[+DESCRIPTION?The \brewind\b command will move the file pointer of fd to position 0.]"
1183 "\n"
1184 "\nfd\n"
1185 "\n"
1186 "[+EXIT STATUS?]{"
1187         "[+0?Success.]"
1188         "[+>0?An error occurred.]"
1189 "}"
1190 "[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bstat\b(1),\bstat\b(2)]"
1191 ;
1192 
1193 
1194 extern int b_rewind(int argc, char *argv[], void *extra)
1195 {
1196 	Shell_t *shp = sh_contexttoshell(extra);
1197 	int fd = -1;
1198 	register int n;
1199 	while (n = optget(argv, sh_optrewind)) switch (n)
1200 	{
1201 	    case ':':
1202 		errormsg(SH_DICT, 2, "%s", opt_info.arg);
1203 		break;
1204 	    case '?':
1205 		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
1206 		break;
1207 	}
1208 	argc -= opt_info.index;
1209 	argv += opt_info.index;
1210 	if(argc!=1)
1211 		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
1212 
1213 	errno = 0;
1214 	fd = strtol(argv[0], (char **)NULL, 0);
1215 	if (errno != 0 || fd < 0)
1216 		errormsg(SH_DICT, ERROR_system(1), "%s: invalid fd", argv[0]);
1217 
1218 	if (sh_seek(fd, 0, SEEK_SET) == (off_t)-1)
1219 		errormsg(SH_DICT, ERROR_system(1), "seek error");
1220 
1221 	return(0);
1222 }
1223