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