14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*8462SApril.Chin@Sun.COM *          Copyright (c) 1982-2007 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
7*8462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                  David Korn <dgk@research.att.com>                   *
184887Schin *                                                                      *
194887Schin ***********************************************************************/
204887Schin #pragma prototyped
214887Schin 
224887Schin #include	<shell.h>
234887Schin #include	<stdio.h>
24*8462SApril.Chin@Sun.COM #include	<stdbool.h>
254887Schin #include	<option.h>
264887Schin #include	<stk.h>
274887Schin #include	<tm.h>
284887Schin #include	"name.h"
294887Schin #undef nv_isnull
304887Schin #ifndef SH_DICT
314887Schin #   define SH_DICT     "libshell"
324887Schin #endif
334887Schin #include	<poll.h>
344887Schin 
35*8462SApril.Chin@Sun.COM #define sh_contexttoshb(context)	((Shbltin_t*)(context))
36*8462SApril.Chin@Sun.COM #define sh_contexttoshell(context)	((context)?(sh_contexttoshb(context)->shp):(NULL))
37*8462SApril.Chin@Sun.COM 
384887Schin /*
394887Schin  * time formatting related
404887Schin */
414887Schin struct dctime
424887Schin {
434887Schin 	Namfun_t	fun;
444887Schin 	Namval_t 	*format;
454887Schin 	char		buff[256]; /* Must be large enougth for |tmfmt()| */
464887Schin };
474887Schin 
484887Schin static char *get_time(Namval_t* np, Namfun_t* nfp)
494887Schin {
504887Schin 	struct dctime *dp = (struct dctime*)nfp;
514887Schin 	time_t t = nv_getn(np,nfp);
524887Schin 	char *format = nv_getval(dp->format);
534887Schin 	tmfmt(dp->buff,sizeof(dp->buff),format,(time_t*)0);
544887Schin 	return(dp->buff);
554887Schin }
564887Schin 
574887Schin static void put_time(Namval_t* np, const char* val, int flag, Namfun_t* nfp)
584887Schin {
594887Schin 	struct dctime *dp = (struct dctime*)nfp;
604887Schin 	char *last;
614887Schin 	if(val)
624887Schin 	{
634887Schin 		int32_t t;
644887Schin 		if(flag&NV_INTEGER)
654887Schin 		{
664887Schin 			if(flag&NV_LONG)
674887Schin 				t = *(Sfdouble_t*)val;
684887Schin 			else
694887Schin 				t = *(double*)val;
704887Schin 		}
714887Schin 		else
724887Schin 		{
734887Schin 			t = tmdate(val, &last, (time_t*)0);
744887Schin 			if(*last)
754887Schin 				errormsg(SH_DICT, ERROR_exit(1),"%s: invalid date/time string", val);
764887Schin 		}
774887Schin 		nv_putv(np, (char*)&t,NV_INTEGER, nfp);
784887Schin 	}
794887Schin 	else
804887Schin 	{
814887Schin 		nv_unset(dp->format);
824887Schin 		free((void*)dp->format);
834887Schin 		nv_putv(np, val, flag, nfp);
844887Schin 	}
854887Schin }
864887Schin 
874887Schin static Namval_t *create_time(Namval_t *np, const char *name, int flags, Namfun_t *nfp)
884887Schin {
894887Schin 	struct dctime *dp = (struct dctime*)nfp;
904887Schin 	if(strcmp(name, "format"))
914887Schin 		return((Namval_t*)0);
924887Schin 	return(dp->format);
934887Schin }
944887Schin 
954887Schin static const Namdisc_t timedisc =
964887Schin {
974887Schin         sizeof(struct dctime),
984887Schin         put_time,
994887Schin         get_time,
1004887Schin         0,
1014887Schin         0,
1024887Schin         create_time,
1034887Schin };
1044887Schin 
1054887Schin 
1064887Schin static Namval_t *make_time(Namval_t* np)
1074887Schin {
1084887Schin 	int offset = stktell(stkstd);
1094887Schin 	char *name = nv_name(np);
1104887Schin 	struct dctime *dp = newof(NULL,struct dctime,1,0);
1114887Schin 	if(!dp)
1124887Schin 		return((Namval_t*)0);
1134887Schin 	sfprintf(stkstd,"%s.format\0",name);
1144887Schin 	sfputc(stkstd,0);
1154887Schin 	dp->format = nv_search(stkptr(stkstd,offset),sh.var_tree,NV_ADD);
1164887Schin 	dp->fun.disc = &timedisc;
1174887Schin 	nv_stack(np,&dp->fun);
1184887Schin 	return(np);
1194887Schin }
1204887Schin 
1214887Schin /*
1224887Schin  * mode formatting related
1234887Schin */
1244887Schin static char *get_mode(Namval_t* np, Namfun_t* nfp)
1254887Schin {
1264887Schin 	mode_t mode = nv_getn(np,nfp);
1274887Schin 	return(fmtperm(mode));
1284887Schin }
1294887Schin 
1304887Schin static void put_mode(Namval_t* np, const char* val, int flag, Namfun_t* nfp)
1314887Schin {
1324887Schin 	if(val)
1334887Schin 	{
1344887Schin 		int32_t mode;
1354887Schin 		char *last;
1364887Schin 		if(flag&NV_INTEGER)
1374887Schin 		{
1384887Schin 			if(flag&NV_LONG)
1394887Schin 				mode = *(Sfdouble_t*)val;
1404887Schin 			else
1414887Schin 				mode = *(double*)val;
1424887Schin 		}
1434887Schin 		else
1444887Schin 		{
1454887Schin 			mode = strperm(val, &last,0);
1464887Schin 			if(*last)
1474887Schin 				errormsg(SH_DICT, ERROR_exit(1),"%s: invalid mode string", val);
1484887Schin 		}
1494887Schin 		nv_putv(np,(char*)&mode,NV_INTEGER,nfp);
1504887Schin 	}
1514887Schin 	else
1524887Schin 		nv_putv(np,val,flag,nfp);
1534887Schin }
1544887Schin 
1554887Schin static const Namdisc_t modedisc =
1564887Schin {
1574887Schin 	0,
1584887Schin         put_mode,
1594887Schin         get_mode,
1604887Schin };
1614887Schin 
1624887Schin static Namval_t *make_mode(Namval_t* np)
1634887Schin {
1644887Schin 	char *name = nv_name(np);
1654887Schin 	Namfun_t *nfp = newof(NULL,Namfun_t,1,0);
1664887Schin 	if(!nfp)
1674887Schin 		return((Namval_t*)0);
1684887Schin 	nfp->disc = &modedisc;
1694887Schin 	nv_stack(np,nfp);
1704887Schin 	return(np);
1714887Schin }
1724887Schin 
1734887Schin /*
1744887Schin  *  field related typese and functions
1754887Schin  */
1764887Schin typedef struct _field_
1774887Schin {
1784887Schin 	char		*name;		/* field name */
1794887Schin 	int		flags;		/* flags */
1804887Schin 	short		offset;		/* offset of field into data */
1814887Schin 	short		size;		/* size of field */
1824887Schin 	Namval_t	*(*make)(Namval_t*);	/* discipline constructor */
1834887Schin } Shfield_t;
1844887Schin 
1854887Schin /*
1864887Schin  * lookup field in field table
1874887Schin  */
1884887Schin static Shfield_t *sh_findfield(Shfield_t *ftable, int nelem, const char *name)
1894887Schin {
1904887Schin 	Shfield_t *fp = ftable;
1914887Schin 	register int i,n;
1924887Schin 	register const char *cp;
1934887Schin 	for(cp=name; *cp; cp++)
1944887Schin 	{
1954887Schin 		if(*cp=='.')
1964887Schin 			break;
1974887Schin 	}
1984887Schin 	n = cp-name;
1994887Schin 	for(i=0; i < nelem; i++,fp++)
2004887Schin 	{
2014887Schin 		if(memcmp(fp->name,name,n)==0 && fp->name[n]==0)
2024887Schin 			return(fp);
2034887Schin 	}
2044887Schin 	return(0);
2054887Schin }
2064887Schin 
2074887Schin /*
2084887Schin  * class types and functions
2094887Schin  */
2104887Schin 
2114887Schin typedef struct _class_
2124887Schin {
2134887Schin 	int		nelem;		/* number of elements */
2144887Schin 	int		dsize;		/* size for data structure */
2154887Schin 	Shfield_t 	*fields;	/* field description table */
2164887Schin } Shclass_t;
2174887Schin 
2184887Schin struct dcclass
2194887Schin {
2204887Schin 	Namfun_t	fun;
2214887Schin 	Shclass_t	sclass;
2224887Schin };
2234887Schin 
2244887Schin static Namval_t *sh_newnode(register Shfield_t *fp, Namval_t *np)
2254887Schin {
2264887Schin 	char *val = np->nvalue + fp->offset;
2274887Schin 	char *name = nv_name(np);
2284887Schin 	register Namval_t *nq;
2294887Schin 	int offset = stktell(stkstd);
2304887Schin 	sfprintf(stkstd,"%s.%s\0",name,fp->name);
2314887Schin 	sfputc(stkstd,0);
2324887Schin 	nq = nv_search(stkptr(stkstd,offset),sh.var_tree,NV_ADD);
2334887Schin 	if(fp->size<0)
2344887Schin 		val = *(char**)val;
2354887Schin 	nv_putval(nq,val,fp->flags|NV_NOFREE);
2364887Schin 	if(fp->make)
2374887Schin 		(*fp->make)(nq);
2384887Schin 	return(nq);
2394887Schin }
2404887Schin 
2414887Schin static Namval_t *fieldcreate(Namval_t *np, const char *name, int flags, Namfun_t *nfp)
2424887Schin {
2434887Schin 	struct dcclass *dcp = (struct dcclass*)nfp;
2444887Schin 	Shclass_t *sp = &dcp->sclass;
2454887Schin 	Shfield_t *fp = sh_findfield(sp->fields,sp->nelem,name);
2464887Schin 	Namval_t *nq,**nodes = (Namval_t**)(dcp+1);
2474887Schin 	int n = fp-sp->fields;
2484887Schin 	int len =  strlen(fp->name);
2494887Schin 	void *data = (void*)np->nvalue;
2504887Schin 	if(!(nq=nodes[n]))
2514887Schin 	{
2524887Schin 		nodes[n] = nq = sh_newnode(fp,np);
2534887Schin 		nfp->last = "";
2544887Schin 	}
2554887Schin 	if(name[len]==0)
2564887Schin 		return(nq);
2574887Schin 	return(nq);
2584887Schin }
2594887Schin 
2604887Schin static void genvalue(Sfio_t *out, Shclass_t *sp, int indent, Namval_t *npar)
2614887Schin {
2624887Schin 	Shfield_t *fp = sp->fields;
2634887Schin 	Namval_t *np, **nodes= (Namval_t**)(sp+1);
2644887Schin 	register int i,isarray;
2654887Schin 	if(out)
2664887Schin 	{
2674887Schin 		sfwrite(out,"(\n",2);
2684887Schin 		indent++;
2694887Schin 	}
2704887Schin 	for(i=0; i < sp->nelem; i++,fp++)
2714887Schin 	{
2724887Schin #if 0
2734887Schin 		/* handle recursive case */
2744887Schin #endif
2754887Schin 		if(!(np=nodes[i]) && out)
2764887Schin 			np = sh_newnode(fp,npar);
2774887Schin 		if(np)
2784887Schin 		{
2794887Schin 			isarray=0;
2804887Schin 			if(nv_isattr(np,NV_ARRAY))
2814887Schin 			{
2824887Schin 				isarray=1;
2834887Schin 				if(array_elem(nv_arrayptr(np))==0)
2844887Schin 					isarray=2;
2854887Schin 				else
2864887Schin 					nv_putsub(np,(char*)0,ARRAY_SCAN);
2874887Schin 			}
2884887Schin 			sfnputc(out,'\t',indent);
2894887Schin 			sfputr(out,fp->name,(isarray==2?'\n':'='));
2904887Schin 			if(isarray)
2914887Schin 			{
2924887Schin 				if(isarray==2)
2934887Schin 					continue;
2944887Schin 				sfwrite(out,"(\n",2);
2954887Schin 				sfnputc(out,'\t',++indent);
2964887Schin 			}
2974887Schin 			while(1)
2984887Schin 			{
2994887Schin 				char *fmtq;
3004887Schin 				if(isarray)
3014887Schin 				{
3024887Schin 					sfprintf(out,"[%s]",sh_fmtq(nv_getsub(np)));
3034887Schin 					sfputc(out,'=');
3044887Schin 				}
3054887Schin 				if(!(fmtq=nv_getval(np)) || !(fmtq=sh_fmtq(fmtq)))
3064887Schin 					fmtq = "";
3074887Schin 				sfputr(out,fmtq,'\n');
3084887Schin 				if(!nv_nextsub(np))
3094887Schin 					break;
3104887Schin 				sfnputc(out,'\t',indent);
3114887Schin 			}
3124887Schin 			if(isarray)
3134887Schin 			{
3144887Schin 				sfnputc(out,'\t',--indent);
3154887Schin 				sfwrite(out,")\n",2);
3164887Schin 			}
3174887Schin 		}
3184887Schin 	}
3194887Schin 	if(out)
3204887Schin 	{
3214887Schin 		if(indent>1)
3224887Schin 			sfnputc(out,'\t',indent-1);
3234887Schin 		sfputc(out,')');
3244887Schin 	}
3254887Schin }
3264887Schin 
3274887Schin static char *walk_class(register Namval_t *np, int dlete, struct dcclass *dcp)
3284887Schin {
3294887Schin 	static Sfio_t *out;
3304887Schin 	Sfio_t *outfile;
3314887Schin 	int savtop = stktell(stkstd);
3324887Schin 	char *savptr =  stkfreeze(stkstd,0);
3334887Schin 	if(dlete)
3344887Schin 		outfile = 0;
3354887Schin 	else if(!(outfile=out))
3364887Schin                 outfile = out =  sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
3374887Schin 	else
3384887Schin 		sfseek(outfile,0L,SEEK_SET);
3394887Schin 	genvalue(outfile,&dcp->sclass,0,np);
3404887Schin 	stkset(stkstd,savptr,savtop);
3414887Schin 	if(!outfile)
3424887Schin 		return((char*)0);
3434887Schin 	sfputc(out,0);
3444887Schin 	return((char*)out->_data);
3454887Schin }
3464887Schin 
3474887Schin static char *get_classval(Namval_t* np, Namfun_t* nfp)
3484887Schin {
3494887Schin 	return(walk_class(np,0,(struct dcclass *)nfp));
3504887Schin }
3514887Schin 
3524887Schin static void put_classval(Namval_t* np, const char* val, int flag, Namfun_t* nfp)
3534887Schin {
3544887Schin 	walk_class(np,1,(struct dcclass *)nfp);
3554887Schin 	if(nfp = nv_stack(np,(Namfun_t*)0))
3564887Schin 	{
3574887Schin 		free((void*)nfp);
3584887Schin 		if(np->nvalue && !nv_isattr(np,NV_NOFREE))
3594887Schin 			free((void*)np->nvalue);
3604887Schin 	}
3614887Schin 	if(val)
3624887Schin 		nv_putval(np,val,flag);
3634887Schin }
3644887Schin 
3654887Schin static const Namdisc_t classdisc =
3664887Schin {
3674887Schin         sizeof(struct dcclass),
3684887Schin         put_classval,
3694887Schin         get_classval,
3704887Schin         0,
3714887Schin         0,
3724887Schin 	fieldcreate
3734887Schin };
3744887Schin 
3754887Schin static int mkclass(Namval_t *np, Shclass_t *sp)
3764887Schin {
3774887Schin 	struct dcclass *tcp = newof(NULL,struct dcclass,1,sp->nelem*sizeof(Namval_t*));
3784887Schin 	if(!tcp)
3794887Schin 		return(0);
3804887Schin 	memset((void*)(tcp+1),0,sp->nelem*sizeof(Namval_t*));
3814887Schin 	tcp->fun.disc = &classdisc;
3824887Schin 	tcp->sclass = *sp;
3834887Schin 	np->nvalue = (char*)calloc(sp->dsize,1);
3844887Schin 	nv_stack(np,&tcp->fun);
3854887Schin 	return(1);
3864887Schin }
3874887Schin 
3884887Schin /*
3894887Schin  * ====================from here down is file class specific
3904887Schin  */
3914887Schin static struct stat *Sp;
3924887Schin 
3934887Schin struct filedata
3944887Schin {
3954887Schin 	struct stat	statb;
3964887Schin 	int		fd;
3974887Schin 	char		*name;
3984887Schin };
3994887Schin 
4004887Schin static Shfield_t filefield[] =
4014887Schin {
4024887Schin 	{ "atime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_atime), sizeof(Sp->st_atime), make_time},
4034887Schin 	{ "ctime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_ctime), sizeof(Sp->st_ctime), make_time},
4044887Schin 	{ "dev",   NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_dev),sizeof(Sp->st_dev)},
4054887Schin 	{ "fd",    NV_INTEGER|NV_RDONLY, offsetof(struct filedata,fd), 		sizeof(int)},
4064887Schin 	{ "gid",   NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_gid), sizeof(Sp->st_gid)},
4074887Schin 	{ "ino",   NV_LONG|NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_ino), sizeof(Sp->st_ino)},
4084887Schin 	{ "mode",  NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_mode), sizeof(Sp->st_mode), make_mode},
4094887Schin 	{ "mtime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_mtime), sizeof(Sp->st_mtime), make_time},
4104887Schin 	{ "name",   NV_RDONLY, offsetof(struct filedata,name), 	-1 },
4114887Schin 	{ "nlink", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_nlink), sizeof(Sp->st_nlink)},
4124887Schin 	{ "size",  NV_LONG|NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_size), sizeof(Sp->st_size)},
4134887Schin 	{ "uid",   NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_uid), sizeof(Sp->st_uid)}
4144887Schin };
4154887Schin 
4164887Schin static Shclass_t Fileclass =
4174887Schin {
4184887Schin 	sizeof(filefield)/sizeof(*filefield),
4194887Schin 	sizeof(struct filedata),
4204887Schin 	filefield
4214887Schin };
4224887Schin 
4234887Schin 
4244887Schin #define letterbit(bit)	(1<<((bit)-'a'))
4254887Schin 
4264887Schin static const char sh_optopen[] =
4274887Schin "[-?\n@(#)$Id: open (AT&T Labs Research) 2007-05-07 $\n]"
4284887Schin "[-author?David Korn <dgk@research.att.com>]"
4294887Schin "[-author?Roland Mainz <roland.mainz@nrubsig.org>]"
4304887Schin "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
4314887Schin "[+NAME? open - create a shell variable correspnding to a file]"
4324887Schin "[+DESCRIPTION?\bopen\b creates the compound variable \avar\a correspinding "
4334887Schin 	"to the file given by the pathname \afile\a.  The elements of \avar\a "
4344887Schin 	"are the names of elements in the \astat\a structure with the \bst_\b "
4354887Schin 	"prefix removed.]"
4364887Schin "[+?\afile\a is opened (based on \b-r\b and/or \b-w\b) and the variable "
4374887Schin 	"\avar\a\b.fd\b is the file descriptor.]"
4384887Schin "[a:append?Open for append.]"
4394887Schin "[b:binary?Open in binary mode"
4404887Schin #ifndef O_BINARY
4414887Schin 	" (not supported/ignored on this platform)"
4424887Schin #endif
4434887Schin 	".]"
4444887Schin "[t:text?Open in text mode"
4454887Schin #ifndef O_TEXT
4464887Schin 	" (not supported/ignored on this platform)"
4474887Schin #endif
4484887Schin 	".]"
4494887Schin "[c:create?Open for create.]"
4504887Schin "[i:inherit?Open without the close-on-exec bit set.]"
4514887Schin "[I:noinherit?Open with the close-on-exec bit set.]"
4524887Schin "[r:read?Open with read access.]"
4534887Schin "[w:write?Open with write access.]"
4544887Schin "[m:mode]:[mode:=rwrwrw?Open with access mode \amode\a.]"
4554887Schin "[x:exclusive?Open exclusive.]"
4564887Schin 
4574887Schin "[N:nofollow?If the path names a symbolic link, open fails with ELOOP "
4584887Schin #ifndef O_NOFOLLOW
4594887Schin 	" (not supported/ignored on this platform)"
4604887Schin #endif
4614887Schin 	".]"
4624887Schin "[S:sync?Write I/O operations on the file descriptor complete as "
4634887Schin 	"defined by synchronized I/O file integrity completion"
4644887Schin #ifndef O_SYNC
4654887Schin 	" (not supported/ignored on this platform)"
4664887Schin #endif
4674887Schin 	".]"
4684887Schin "[T:trunc?If the file exists and is a regular file, and  the  file "
4694887Schin         "is successfully opened read/write or write-only, its length is "
4704887Schin         "truncated to 0 and the mode and owner are unchanged.  It "
4714887Schin         "has  no  effect on FIFO special files or terminal device "
4724887Schin         "files.   Its   effect   on   other   file    types    is "
4734887Schin         "implementation-dependent.  The  result  of using -T "
4744887Schin         "with read-only files is undefined"
4754887Schin #ifndef O_TRUNC
4764887Schin 	" (not supported/ignored on this platform)"
4774887Schin #endif
4784887Schin 	".]"
4794887Schin "\n"
4804887Schin "\nvar file\n"
4814887Schin "\n"
4824887Schin "[+EXIT STATUS?]{"
4834887Schin         "[+0?Success.]"
4844887Schin         "[+>0?An error occurred.]"
4854887Schin "}"
4864887Schin "[+SEE ALSO?\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bstat\b(1),\bpoll\b(1),\bstat\b(2)]"
4874887Schin ;
4884887Schin 
4894887Schin 
4904887Schin extern int b_open(int argc, char *argv[], void *extra)
4914887Schin {
4924887Schin 	register Namval_t *np;
4934887Schin 	register int n,oflag=0;
494*8462SApril.Chin@Sun.COM 	Shell_t *shp = sh_contexttoshell(extra);
4954887Schin 	struct filedata *fdp;
4964887Schin 	mode_t mode = 0666;
4974887Schin 	long flags = 0;
4984887Schin 	int fd = -1;
4994887Schin 	char *arg;
5004887Schin 
5014887Schin 	while (n = optget(argv, sh_optopen)) switch (n)
5024887Schin 	{
5034887Schin 	    case 'r':
5044887Schin 	    case 'w':
5054887Schin 	    case 'i':
5064887Schin 		flags |= letterbit(n);
5074887Schin 		break;
5084887Schin 	    case 'I':
5094887Schin 		flags &= ~(letterbit('i'));
5104887Schin 		break;
5114887Schin 	    case 'b':
5124887Schin #ifdef O_BINARY
5134887Schin 		oflag |= O_BINARY;
5144887Schin #endif
5154887Schin 		break;
5164887Schin 	    case 't':
5174887Schin #ifdef O_TEXT
5184887Schin 		oflag |= O_TEXT;
5194887Schin #endif
5204887Schin 		break;
5214887Schin 	    case 'N':
5224887Schin #ifdef O_NOFOLLOW
5234887Schin 		oflag |= O_NOFOLLOW;
5244887Schin #endif
5254887Schin 		break;
5264887Schin 	    case 'T':
5274887Schin #ifdef O_TRUNC
5284887Schin 		oflag |= O_TRUNC;
5294887Schin #endif
5304887Schin 		break;
5314887Schin 	    case 'x':
5324887Schin 		oflag |= O_EXCL;
5334887Schin 		break;
5344887Schin 	    case 'c':
5354887Schin 		oflag |= O_CREAT;
5364887Schin 		break;
5374887Schin 	    case 'a':
5384887Schin 		oflag |= O_APPEND;
5394887Schin 		break;
5404887Schin 	    case 'S':
5414887Schin #ifdef O_SYNC
5424887Schin 		oflag |= O_SYNC;
5434887Schin #endif
5444887Schin 		break;
5454887Schin 	    case 'm':
5464887Schin 		mode = strperm(arg = opt_info.arg, &opt_info.arg, mode);
5474887Schin 		if (*opt_info.arg)
5484887Schin 			errormsg(SH_DICT, ERROR_system(1), "%s: invalid mode", arg);
5494887Schin 	    	break;
5504887Schin 	    case ':':
5514887Schin 		errormsg(SH_DICT, 2, "%s", opt_info.arg);
5524887Schin 		break;
5534887Schin 	    case '?':
5544887Schin 		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
5554887Schin 		break;
5564887Schin 	}
5574887Schin 	argc -= opt_info.index;
5584887Schin 	argv += opt_info.index;
5594887Schin 	if(argc!=2 || !(flags&(letterbit('r')|letterbit('w'))))
5604887Schin 		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
5614887Schin 
5624887Schin 	if(flags&letterbit('r'))
5634887Schin 	{
5644887Schin 		if(flags&letterbit('w'))
5654887Schin 			oflag |= O_RDWR;
5664887Schin 		else
5674887Schin 			oflag |= O_RDONLY;
5684887Schin 	}
5694887Schin 	else if(flags&letterbit('w'))
5704887Schin 		oflag |= O_WRONLY;
5714887Schin 
5724887Schin 	fd = sh_open(argv[1], oflag, mode);
5734887Schin 	if(fd<0)
5744887Schin 		errormsg(SH_DICT, ERROR_system(1), "%s: open failed", argv[1]);
5754887Schin 
5764887Schin 	if(!(flags&letterbit('i')))
5774887Schin 		fcntl(fd, F_SETFL, 0);
5784887Schin 
5794887Schin 	np = nv_open(argv[0], shp->var_tree, NV_ARRAY|NV_VARNAME|NV_NOASSIGN);
5804887Schin 	if(!nv_isnull(np))
5814887Schin 		nv_unset(np);
5824887Schin 	mkclass(np, &Fileclass);
5834887Schin 	fdp = (struct filedata*)np->nvalue;
5844887Schin 	fstat(fd, &fdp->statb);
5854887Schin 	fdp->fd = fd;
5864887Schin 	fdp->name = strdup(argv[1]);
5874887Schin 	return(0);
5884887Schin }
5894887Schin 
5904887Schin static const char sh_optclose[] =
5914887Schin "[-?\n@(#)$Id: close (AT&T Labs Research) 2007-04-21 $\n]"
5924887Schin "[-author?Roland Mainz <roland.mainz@nrubsig.org>]"
5934887Schin "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
5944887Schin "[+NAME? close - close a file descriptor]"
5954887Schin "[+DESCRIPTION?\bclose\b closes the file descriptor specified by fd.]"
5964887Schin "\n"
5974887Schin "\nfd\n"
5984887Schin "\n"
5994887Schin "[+EXIT STATUS?]{"
6004887Schin         "[+0?Success.]"
6014887Schin         "[+>0?An error occurred.]"
6024887Schin "}"
6034887Schin "[+SEE ALSO?\bopen\b(1),\bdup\b(1),\btmpfile\b(1),\bpoll\b(1),\bstat\b(1)]"
6044887Schin ;
6054887Schin 
6064887Schin extern int b_close(int argc, char *argv[], void *extra)
6074887Schin {
6084887Schin 	register int n=0;
6094887Schin 	int fd = -1;
6104887Schin 
6114887Schin 	while (n = optget(argv, sh_optclose)) switch (n)
6124887Schin 	{
6134887Schin 	    case ':':
6144887Schin 		errormsg(SH_DICT, 2, "%s", opt_info.arg);
6154887Schin 		break;
6164887Schin 	    case '?':
6174887Schin 		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
6184887Schin 		break;
6194887Schin 	}
6204887Schin 	argc -= opt_info.index;
6214887Schin 	argv += opt_info.index;
6224887Schin 	if(argc!=1)
6234887Schin 		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
6244887Schin 
6254887Schin 	errno = 0;
6264887Schin 	fd = strtol(argv[0], (char **)NULL, 0);
6274887Schin 	if (errno != 0 || fd < 0)
6284887Schin 		errormsg(SH_DICT, ERROR_system(1), "%s: invalid descriptor", argv[0]);
6294887Schin 
6304887Schin         n = sh_close(fd);
6314887Schin 
6324887Schin 	if (n < 0)
6334887Schin 		errormsg(SH_DICT, ERROR_system(1), "%s: close error", argv[0]);
6344887Schin 
6354887Schin 	return(n==0?0:1);
6364887Schin }
6374887Schin 
6384887Schin 
6394887Schin static const char sh_opttmpfile[] =
6404887Schin "[-?\n@(#)$Id: tmpfile (AT&T Labs Research) 2007-05-07 $\n]"
6414887Schin "[-author?Roland Mainz <roland.mainz@nrubsig.org>]"
6424887Schin "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
6434887Schin "[+NAME? tmpfile - create a shell variable correspnding to a temporary file]"
6444887Schin "[+DESCRIPTION?\btmpfile\b creates the compound variable \avar\a correspinding "
6454887Schin 	"to a temporary file.  The elements of \avar\a "
6464887Schin 	"are the names of elements in the \astat\a structure with the \bst_\b "
6474887Schin 	"prefix removed.]"
6484887Schin "[i:inherit?Open without the close-on-exec bit set.]"
6494887Schin "[I:noinherit?Open with the close-on-exec bit set.]"
6504887Schin "\n"
6514887Schin "\nvar\n"
6524887Schin "\n"
6534887Schin "[+EXIT STATUS?]{"
6544887Schin         "[+0?Success.]"
6554887Schin         "[+>0?An error occurred.]"
6564887Schin "}"
6574887Schin "[+SEE ALSO?\bopen\b(1),\bdup\b(1),\bclose\b(1),\bstat\b(1),\bstat\b(2)]"
6584887Schin ;
6594887Schin 
6604887Schin 
6614887Schin extern int b_tmpfile(int argc, char *argv[], void *extra)
6624887Schin {
6634887Schin 	register Namval_t *np;
6644887Schin 	register int n;
665*8462SApril.Chin@Sun.COM 	Shell_t *shp = sh_contexttoshell(extra);
6664887Schin 	struct filedata *fdp;
667*8462SApril.Chin@Sun.COM 	bool inherit = false;
6684887Schin 	FILE *file = NULL;
6694887Schin 	int ffd, fd = -1;
6704887Schin 	while (n = optget(argv, sh_opttmpfile)) switch (n)
6714887Schin 	{
6724887Schin 	    case 'i':
673*8462SApril.Chin@Sun.COM 		inherit = true;
6744887Schin 		break;
6754887Schin 	    case 'I':
676*8462SApril.Chin@Sun.COM 		inherit = false;
6774887Schin 		break;
6784887Schin 	    case ':':
6794887Schin 		errormsg(SH_DICT, 2, "%s", opt_info.arg);
6804887Schin 		break;
6814887Schin 	    case '?':
6824887Schin 		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
6834887Schin 		break;
6844887Schin 	}
6854887Schin 	argc -= opt_info.index;
6864887Schin 	argv += opt_info.index;
6874887Schin 	if(argc!=1)
6884887Schin 		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
6894887Schin 
6904887Schin 	file = tmpfile();
6914887Schin 	if(!file)
6924887Schin 		errormsg(SH_DICT, ERROR_system(1), "%s: tmpfile failed", argv[1]);
6934887Schin 	ffd = fileno(file);
6944887Schin 	fd = sh_dup(ffd);
6954887Schin 	if(fd<0)
6964887Schin 		errormsg(SH_DICT, ERROR_system(1), "%s: tmpfile failed", argv[1]);
6974887Schin 	fclose(file);
6984887Schin 
6994887Schin 	if(!inherit)
7004887Schin 		fcntl(fd, F_SETFL, 0);
7014887Schin 
7024887Schin 	np = nv_open(argv[0], shp->var_tree, NV_ARRAY|NV_VARNAME|NV_NOASSIGN);
7034887Schin 	if(!nv_isnull(np))
7044887Schin 		nv_unset(np);
7054887Schin 	mkclass(np,&Fileclass);
7064887Schin 	fdp = (struct filedata*)np->nvalue;
7074887Schin 
7084887Schin 	fstat(fd, &fdp->statb);
7094887Schin 	fdp->fd = fd;
7104887Schin 	fdp->name = NULL;
7114887Schin 	return(0);
7124887Schin }
7134887Schin 
7144887Schin static const char sh_optdup[] =
7154887Schin "[-?\n@(#)$Id: dup (AT&T Labs Research) 2007-05-07 $\n]"
7164887Schin "[-author?Roland Mainz <roland.mainz@nrubsig.org>]"
7174887Schin "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
7184887Schin "[+NAME? dup - duplicate an open file descriptor]"
7194887Schin "[+DESCRIPTION?The \bdup\b commands returns a new file descriptor having the "
7204887Schin      "following in common with the original open file descriptor "
7214887Schin      "fd: same open file (or pipe), same file pointer (that is, both  file descriptors "
7224887Schin      "share one file pointer) same access mode (read, write or read/write). "
7234887Schin      "The file descriptor returned is the lowest one available.]"
7244887Schin "[i:inherit?Open without the close-on-exec bit set.]"
7254887Schin "[I:noinherit?Open with the close-on-exec bit set.]"
7264887Schin "\n"
7274887Schin "\nvar fd\n"
7284887Schin "\n"
7294887Schin "[+EXIT STATUS?]{"
7304887Schin         "[+0?Success.]"
7314887Schin         "[+>0?An error occurred.]"
7324887Schin "}"
7334887Schin "[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bclose\b(1),\bpoll\b(1),\bstat\b(1)]"
7344887Schin ;
7354887Schin 
7364887Schin 
7374887Schin extern int b_dup(int argc, char *argv[], void *extra)
7384887Schin {
7394887Schin 	register Namval_t *np;
7404887Schin 	register int n;
741*8462SApril.Chin@Sun.COM 	Shell_t *shp = sh_contexttoshell(extra);
7424887Schin 	struct filedata *fdp;
743*8462SApril.Chin@Sun.COM 	bool inherit = false;
7444887Schin 	int ffd, fd = -1;
7454887Schin 	while (n = optget(argv, sh_optdup)) switch (n)
7464887Schin 	{
7474887Schin 	    case 'i':
748*8462SApril.Chin@Sun.COM 		inherit = true;
7494887Schin 		break;
7504887Schin 	    case 'I':
751*8462SApril.Chin@Sun.COM 		inherit = false;
7524887Schin 		break;
7534887Schin 	    case ':':
7544887Schin 		errormsg(SH_DICT, 2, "%s", opt_info.arg);
7554887Schin 		break;
7564887Schin 	    case '?':
7574887Schin 		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
7584887Schin 		break;
7594887Schin 	}
7604887Schin 	argc -= opt_info.index;
7614887Schin 	argv += opt_info.index;
7624887Schin 	if(argc!=2)
7634887Schin 		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
7644887Schin 
7654887Schin 	errno = 0;
7664887Schin 	ffd = strtol(argv[1], (char **)NULL, 0);
7674887Schin 	if (errno != 0 || ffd < 0)
7684887Schin 		errormsg(SH_DICT, ERROR_system(1), "%s: invalid fd", argv[1]);
7694887Schin 
7704887Schin 	fd = sh_dup(ffd);
7714887Schin 	if(fd<0)
7724887Schin 		errormsg(SH_DICT, ERROR_system(1), "%s: dup failed", argv[1]);
7734887Schin 
7744887Schin 	if(!inherit)
7754887Schin 		fcntl(fd,F_SETFL,0);
7764887Schin 
7774887Schin 	np = nv_open(argv[0],shp->var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN);
7784887Schin 	if(!nv_isnull(np))
7794887Schin 		nv_unset(np);
7804887Schin 	mkclass(np, &Fileclass);
7814887Schin 	fdp = (struct filedata*)np->nvalue;
7824887Schin 
7834887Schin 	fstat(fd, &fdp->statb);
7844887Schin 	fdp->fd = fd;
7854887Schin 	fdp->name = NULL;
7864887Schin 	return(0);
7874887Schin }
7884887Schin 
7894887Schin static const char sh_optstat[] =
7904887Schin "[-?\n@(#)$Id: stat (AT&T Labs Research) 2007-05-07 $\n]"
7914887Schin "[-author?David Korn <dgk@research.att.com>]"
7924887Schin "[-author?Roland Mainz <roland.mainz@nrubsig.org>]"
7934887Schin "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
7944887Schin "[+NAME? stat - get file status]"
7954887Schin "[+DESCRIPTION?\bstat\b creates the compound variable \avar\a correspinding "
7964887Schin 	"to the file given by the pathname \afile\a.  The elements of \avar\a "
7974887Schin 	"are the names of elements in the \astat\a structure with the \bst_\b "
7984887Schin 	"prefix removed.]"
7994887Schin "[l:lstat?If the the named file is a symbolic link returns information about "
8004887Schin 	"the link itself.]"
8014887Schin "\n"
8024887Schin "\nvar file\n"
8034887Schin "\n"
8044887Schin "[+EXIT STATUS?]{"
8054887Schin         "[+0?Success.]"
8064887Schin         "[+>0?An error occurred.]"
8074887Schin "}"
8084887Schin "[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bpoll\b(1),\bstat\b(2),\blstat\b(2)]"
8094887Schin ;
8104887Schin 
8114887Schin 
8124887Schin extern int b_stat(int argc, char *argv[], void *extra)
8134887Schin {
8144887Schin 	register Namval_t *np;
8154887Schin 	register int n;
816*8462SApril.Chin@Sun.COM 	Shell_t *shp = sh_contexttoshell(extra);
8174887Schin 	struct filedata *fdp;
8184887Schin 	long flags = 0;
8194887Schin 	struct stat statb;
8204887Schin 	while (n = optget(argv, sh_optstat)) switch (n)
8214887Schin 	{
8224887Schin 	    case 'l':
8234887Schin 		flags |= letterbit(n);
8244887Schin 		break;
8254887Schin 	    case ':':
8264887Schin 		errormsg(SH_DICT, 2, "%s", opt_info.arg);
8274887Schin 		break;
8284887Schin 	    case '?':
8294887Schin 		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
8304887Schin 		break;
8314887Schin 	}
8324887Schin 	argc -= opt_info.index;
8334887Schin 	argv += opt_info.index;
8344887Schin 	if(argc!=2)
8354887Schin 		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
8364887Schin 
8374887Schin 	if(flags&letterbit('l'))
8384887Schin 	{
8394887Schin 		if(lstat(argv[1], &statb) < 0)
8404887Schin 			errormsg(SH_DICT, ERROR_system(1), "%s: stat failed", argv[1]);
8414887Schin 	}
8424887Schin 	else
8434887Schin 	{
8444887Schin 		if(stat(argv[1], &statb) < 0)
8454887Schin 			errormsg(SH_DICT, ERROR_system(1), "%s: stat failed", argv[1]);
8464887Schin 
8474887Schin 	}
8484887Schin 
8494887Schin 	np = nv_open(argv[0],shp->var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN);
8504887Schin 	if(!nv_isnull(np))
8514887Schin 		nv_unset(np);
8524887Schin 	mkclass(np,&Fileclass);
8534887Schin 	fdp = (struct filedata*)np->nvalue;
8544887Schin 	fdp->statb = statb;
8554887Schin 	fdp->fd = -1;
8564887Schin 	fdp->name = strdup(argv[1]);
8574887Schin 	return(0);
8584887Schin }
8594887Schin 
8604887Schin static const char sh_optpoll[] =
861*8462SApril.Chin@Sun.COM "[-?\n@(#)$Id: poll (AT&T Labs Research) 2007-12-20 $\n]"
8624887Schin "[-author?Roland Mainz <roland.mainz@nrubsig.org]"
8634887Schin "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
8644887Schin "[+NAME? poll - input/output multiplexing]"
8654887Schin "[+DESCRIPTION?The poll command provides applications with a mechanism "
8664887Schin 	"for multiplexing input/output over a set of file descriptors. "
8674887Schin 	"For each member of the array variable \bvar\b, "
8684887Schin 	"poll examines the given file descriptor in the subscript \b.fd\b "
8694887Schin 	"for the event(s) specified in the subscript \b.events\b."
8704887Schin 	"The poll command identifies those file descriptors on which an "
8714887Schin 	"application can read or write data, or on which certain events have "
8724887Schin 	"occurred.]"
8734887Schin "[+?The \bvar\b argument specifies the file descriptors to be examined "
8744887Schin 	"and the events of interest for each file descriptor. "
8754887Schin 	"It is a array of structured variables with one member for each open "
8764887Schin 	"file descriptor of interest. The array's members contain the following "
8774887Schin 	"subscripts:]{"
8784887Schin 		"[+?\b.fd\b       # file descriptor]"
8794887Schin 		"[+?\b.events\b   # requested events]"
8804887Schin 		"[+?\b.revents\b  # returned event]"
8814887Schin 	"}"
8824887Schin "[+?The \bfd\b variable specifies an open file descriptor and the "
8834887Schin 	"\bevents\b and \brevents\b members are strings constructed from "
8844887Schin 	"a concaternation of the following event flags, seperated by '|':]"
8854887Schin 	"{ "
8864887Schin 	"[+POLLIN?Data other than high priority data may be "
8874887Schin 		"read without blocking. For STREAMS, this "
8884887Schin 		"flag is set in revents even if the message "
8894887Schin 		"is of zero length.]"
8904887Schin 	"[+POLLRDNORM?Normal data (priority band equals 0) may be "
8914887Schin 		"read without blocking. For STREAMS, this "
8924887Schin 		"flag is set in revents even if the message "
8934887Schin 		"is of zero length.]"
8944887Schin 	"[+POLLRDBAND?Data from a non-zero priority band may be "
8954887Schin 		"read without blocking. For STREAMS, this "
8964887Schin 		"flag is set in revents even if the message "
8974887Schin 		"is of zero length.]"
8984887Schin 	"[+POLLPRI?High priority data may be received without "
8994887Schin 		"blocking. For STREAMS, this flag is set in "
9004887Schin 		"revents even if the message is of zero "
9014887Schin 		"length.]"
9024887Schin 	"[+POLLOUT?Normal data (priority band equals 0) may be "
9034887Schin 		"written without blocking.]"
9044887Schin 	"[+POLLWRNORM?The same as POLLOUT.]"
9054887Schin 	"[+POLLWRBAND?Priority data (priority band > 0) may be "
9064887Schin 		"written.  This event only examines bands "
9074887Schin 		"that have been written to at least once.]"
9084887Schin 	"[+POLLERR?An error has occurred on the device or "
9094887Schin 		"stream.  This flag is only valid in the "
9104887Schin 		"revents bitmask; it is not used in the "
9114887Schin 		"events member.]"
9124887Schin 	"[+POLLHUP?A hangup has occurred on the stream. This "
9134887Schin 		"event and POLLOUT are mutually exclusive; a "
9144887Schin 		"stream can never be writable if a hangup has "
9154887Schin 		"occurred. However, this event and POLLIN, "
9164887Schin 		", POLLRDBAND, or POLLPRI are not "
9174887Schin 		"mutually exclusive. This flag is only valid "
9184887Schin 		"in the revents bitmask; it is not used in "
9194887Schin 		"the events member.]"
9204887Schin 	"[+POLLNVAL?The specified fd value does not belong to an "
9214887Schin 		"open file. This flag is only valid in the "
9224887Schin 		"revents member; it is not used in the events "
9234887Schin 		"member.]"
9244887Schin    "}"
9254887Schin "]"
9264887Schin 
9274887Schin "[+?If the value fd is less than 0, events is ignored and "
9284887Schin 	"revents is set to 0 in that entry on return from poll.]"
9294887Schin 
9304887Schin "[+?The results of the poll query are stored in the revents "
9314887Schin 	"member in the \bvar\b structure. POLL*-strings are set in the \brevents\b "
9324887Schin 	"variable to indicate which of the requested events are true. "
9334887Schin 	"If none are true, the \brevents\b will be an empty string when "
9344887Schin 	"the poll command returns. The event flags "
9354887Schin 	"POLLHUP, POLLERR, and POLLNVAL are always set in \brevents\b "
9364887Schin 	"if the conditions they indicate are true; this occurs even "
9374887Schin 	"though these flags were not present in events.]"
9384887Schin 
9394887Schin "[+?If none of the defined events have occurred on any selected "
9404887Schin 	"file descriptor, poll waits at least timeout milliseconds "
9414887Schin 	"for an event to occur on any of the selected file descriptors. "
9424887Schin 	"On a computer where millisecond timing accuracy is not "
9434887Schin 	"available, timeout is rounded up to the nearest legal value "
9444887Schin 	"available on that system. If the value timeout is 0, poll "
9454887Schin 	"returns immediately. If the value of timeout is -1, poll "
9464887Schin 	"blocks until a requested event occurs or until the call is "
9474887Schin 	"interrupted.]"
9484887Schin 
9494887Schin "[+?The poll function supports regular files, terminal and "
9504887Schin 	"pseudo-terminal devices, STREAMS-based files, FIFOs and "
9514887Schin 	"pipes. The behavior of poll on elements of fds that refer "
9524887Schin 	"to other types of file is unspecified.]"
9534887Schin 
9544887Schin "[+?The poll function supports sockets.]"
9554887Schin 
9564887Schin "[+?A file descriptor for a socket that is listening for connections "
9574887Schin 	"will indicate that it is ready for reading, once connections "
9584887Schin 	"are available. A file descriptor for a socket that "
9594887Schin 	"is connecting asynchronously will indicate that it is ready "
9604887Schin 	"for writing, once a connection has been established.]"
9614887Schin 
9624887Schin "[+?Regular files always poll TRUE for reading and writing.]"
9634887Schin 
964*8462SApril.Chin@Sun.COM "[c:fdcount]:[fdcount?Upon successful completion, a non-negative value is "
965*8462SApril.Chin@Sun.COM 	"returned. A positive value indicates the total number of "
966*8462SApril.Chin@Sun.COM 	"file descriptors that has been selected (that is, file "
967*8462SApril.Chin@Sun.COM 	"descriptors for which the revents member is non-zero). A "
968*8462SApril.Chin@Sun.COM 	"value of 0 indicates that the call timed out and no file "
969*8462SApril.Chin@Sun.COM 	"descriptors have been selected. Upon failure, -1 is returned.]"
970*8462SApril.Chin@Sun.COM "[t:timeout]:[seconds?Timeout in seconds. If the value timeout is 0, "
971*8462SApril.Chin@Sun.COM 	"poll returns immediately. If the value of timeout is -1, poll "
972*8462SApril.Chin@Sun.COM 	"blocks until a requested event occurs or until the call is "
973*8462SApril.Chin@Sun.COM 	"interrupted.]"
974*8462SApril.Chin@Sun.COM "[T:mtimeout]:[milliseconds?Timeout in milliseconds. If the value timeout is 0, "
9754887Schin 	"poll returns immediately. If the value of timeout is -1, poll "
9764887Schin 	"blocks until a requested event occurs or until the call is "
9774887Schin 	"interrupted.]"
9784887Schin "\n"
9794887Schin "\nvar\n"
9804887Schin "\n"
9814887Schin "[+EXIT STATUS?]{"
9824887Schin         "[+0?Success.]"
9834887Schin         "[+>0?An error occurred.]"
9844887Schin "}"
9854887Schin "[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bpoll\b(2)]"
9864887Schin ;
9874887Schin 
9884887Schin /*
9894887Schin  * |mystpcpy| - like |strcpy()| but returns the end of the buffer
9904887Schin  *
9914887Schin  * Copy string s2 to s1.  s1 must be large enough.
9924887Schin  * return s1-1 (position of string terminator ('\0') in destnation buffer).
9934887Schin  */
9944887Schin static
9954887Schin char *mystpcpy(char *s1, const char *s2)
9964887Schin {
9974887Schin         while (*s1++ = *s2++)
9984887Schin                 ;
9994887Schin         return (s1-1);
10004887Schin }
10014887Schin 
10024887Schin static
10034887Schin Namval_t *nv_open_fmt(Dt_t *dict, int flags, const char *namefmt, ...)
10044887Schin {
10054887Schin 	char 	varnamebuff[PATH_MAX];
10064887Schin 	va_list	ap;
10074887Schin 
10084887Schin 	va_start(ap, namefmt);
10094887Schin 	vsnprintf(varnamebuff, sizeof(varnamebuff), namefmt, ap);
10104887Schin 	va_end(ap);
10114887Schin 
10124887Schin 	return nv_open(varnamebuff, dict, flags);
10134887Schin }
10144887Schin 
10154887Schin static
10164887Schin int poll_strtoevents(const char *str)
10174887Schin {
10184887Schin 	int events = 0;
10194887Schin 
10204887Schin 	if (strstr(str, "POLLIN"))     events |= POLLIN;
10214887Schin 	if (strstr(str, "POLLRDNORM")) events |= POLLRDNORM;
10224887Schin 	if (strstr(str, "POLLRDBAND")) events |= POLLRDBAND;
10234887Schin 	if (strstr(str, "POLLPRI"))    events |= POLLPRI;
10244887Schin 	if (strstr(str, "POLLOUT"))    events |= POLLOUT;
10254887Schin 	if (strstr(str, "POLLWRNORM")) events |= POLLWRNORM;
10264887Schin 	if (strstr(str, "POLLWRBAND")) events |= POLLWRBAND;
10274887Schin 	if (strstr(str, "POLLERR"))    events |= POLLERR;
10284887Schin 	if (strstr(str, "POLLHUP"))    events |= POLLHUP;
10294887Schin 	if (strstr(str, "POLLNVAL"))   events |= POLLNVAL;
10304887Schin 
10314887Schin 	return events;
10324887Schin }
10334887Schin 
10344887Schin 
10354887Schin static
10364887Schin void poll_eventstostr(char *s, int events)
10374887Schin {
10384887Schin 	*s='\0';
10394887Schin 	if (!events)
10404887Schin 		return;
10414887Schin 
10424887Schin 	if (events & POLLIN)		s=mystpcpy(s, "POLLIN|");
10434887Schin 	if (events & POLLRDNORM)	s=mystpcpy(s, "POLLRDNORM|");
10444887Schin 	if (events & POLLRDBAND)	s=mystpcpy(s, "POLLRDBAND|");
10454887Schin 	if (events & POLLPRI)		s=mystpcpy(s, "POLLPRI|");
10464887Schin 	if (events & POLLOUT)		s=mystpcpy(s, "POLLOUT|");
10474887Schin 	if (events & POLLWRNORM)	s=mystpcpy(s, "POLLWRNORM|");
10484887Schin 	if (events & POLLWRBAND)	s=mystpcpy(s, "POLLWRBAND|");
10494887Schin 	if (events & POLLERR)		s=mystpcpy(s, "POLLERR|");
10504887Schin 	if (events & POLLHUP)		s=mystpcpy(s, "POLLHUP|");
10514887Schin 	if (events & POLLNVAL)		s=mystpcpy(s, "POLLNVAL|");
10524887Schin 
10534887Schin 	/* Remove trailling '|' */
10544887Schin 	s--;
10554887Schin 	if(*s=='|')
10564887Schin 		*s='\0';
10574887Schin }
1058*8462SApril.Chin@Sun.COM 
1059*8462SApril.Chin@Sun.COM #undef  getconf
1060*8462SApril.Chin@Sun.COM #define getconf(x)      strtol(astconf(x,NiL,NiL),NiL,0)
10614887Schin 
10624887Schin extern int b_poll(int argc, char *argv[], void *extra)
10634887Schin {
10644887Schin 	register Namval_t *np;
10654887Schin 	register int n;
1066*8462SApril.Chin@Sun.COM 	Shell_t *shp = sh_contexttoshell(extra);
10674887Schin 	char *varname;
10684887Schin 	int fd;
10694887Schin 	unsigned int numpollfd = 0;
10704887Schin 	int i;
10714887Schin 	char *s;
1072*8462SApril.Chin@Sun.COM 	double timeout = -1.;
10734887Schin 	char buff[256];
1074*8462SApril.Chin@Sun.COM 	char *pollfdcountvarname = NULL;
1075*8462SApril.Chin@Sun.COM 	long open_max,
1076*8462SApril.Chin@Sun.COM 	     bpoll_max;
1077*8462SApril.Chin@Sun.COM 
1078*8462SApril.Chin@Sun.COM 	if ((open_max = getconf("OPEN_MAX")) <= 0)
1079*8462SApril.Chin@Sun.COM 		open_max = OPEN_MAX;
1080*8462SApril.Chin@Sun.COM 	/* |bpoll_max| needs to be larger than |OPEN_MAX| to make sure we
1081*8462SApril.Chin@Sun.COM 	 * can listen to different sets of events per fd.
1082*8462SApril.Chin@Sun.COM 	 */
1083*8462SApril.Chin@Sun.COM 	bpoll_max = open_max*2L;
1084*8462SApril.Chin@Sun.COM 
10854887Schin 	while (n = optget(argv, sh_optpoll)) switch (n)
10864887Schin 	{
10874887Schin 	    case 't':
1088*8462SApril.Chin@Sun.COM 	    case 'T':
10894887Schin 		errno = 0;
1090*8462SApril.Chin@Sun.COM 		timeout = strtod(opt_info.arg, (char **)NULL);
10914887Schin 		if (errno != 0)
10924887Schin 			errormsg(SH_DICT, ERROR_system(1), "%s: invalid timeout", opt_info.arg);
1093*8462SApril.Chin@Sun.COM 
1094*8462SApril.Chin@Sun.COM 		/* -t uses seconds, -T milliseconds */
1095*8462SApril.Chin@Sun.COM 		if (n == 't')
1096*8462SApril.Chin@Sun.COM 			timeout *= 1000.;
1097*8462SApril.Chin@Sun.COM 		break;
1098*8462SApril.Chin@Sun.COM 	    case 'c':
1099*8462SApril.Chin@Sun.COM 	    	pollfdcountvarname = opt_info.arg;
11004887Schin 		break;
11014887Schin 	    case ':':
11024887Schin 		errormsg(SH_DICT, 2, "%s", opt_info.arg);
11034887Schin 		break;
11044887Schin 	    case '?':
11054887Schin 		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
11064887Schin 		break;
11074887Schin 	}
11084887Schin 	argc -= opt_info.index;
11094887Schin 	argv += opt_info.index;
11104887Schin 	if(argc!=1)
11114887Schin 		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
11124887Schin 
11134887Schin         varname = argv[0];
1114*8462SApril.Chin@Sun.COM 
1115*8462SApril.Chin@Sun.COM 	struct pollfd pollfd[bpoll_max];
11164887Schin 
1117*8462SApril.Chin@Sun.COM 	for(i=0 ; i < bpoll_max ; i++)
11184887Schin 	{
1119*8462SApril.Chin@Sun.COM 		np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL|NV_NOADD, "%s[%d].fd", varname, i);
11204887Schin 		if (!np)
11214887Schin 			break;
11224887Schin 		fd = (int)nv_getnum(np);
11234887Schin 		if (fd < 0 || fd > OPEN_MAX)
1124*8462SApril.Chin@Sun.COM 			errormsg(SH_DICT, ERROR_system(1), "invalid pollfd fd");
11254887Schin 		nv_close(np);
11264887Schin 		pollfd[i].fd = fd;
11274887Schin 
1128*8462SApril.Chin@Sun.COM 		np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL|NV_NOADD, "%s[%d].events", varname, i);
1129*8462SApril.Chin@Sun.COM 		if (!np)
1130*8462SApril.Chin@Sun.COM 			errormsg(SH_DICT, ERROR_system(1), "missing pollfd events");
11314887Schin 
11324887Schin 		s = nv_getval(np);
11334887Schin 		if (!s)
1134*8462SApril.Chin@Sun.COM 			errormsg(SH_DICT, ERROR_system(1), "missing pollfd events value");
11354887Schin 		pollfd[i].events  = poll_strtoevents(s);
11364887Schin 		nv_close(np);
11374887Schin 
11384887Schin 		pollfd[i].revents = 0;
11394887Schin 
11404887Schin 		numpollfd++;
11414887Schin         }
11424887Schin 
1143*8462SApril.Chin@Sun.COM 	if (i == bpoll_max)
1144*8462SApril.Chin@Sun.COM 		errormsg(SH_DICT, ERROR_system(1), "cannot handle more than %d entries.", bpoll_max);
11454887Schin 
11464887Schin 	n = poll(pollfd, numpollfd, timeout);
11474887Schin 	/* FixMe: EGAIN and EINTR may require extra handling */
11484887Schin 	if (n < 0)
1149*8462SApril.Chin@Sun.COM 		errormsg(SH_DICT, ERROR_system(1), "failure");
1150*8462SApril.Chin@Sun.COM 
1151*8462SApril.Chin@Sun.COM 	if (pollfdcountvarname)
1152*8462SApril.Chin@Sun.COM 	{
1153*8462SApril.Chin@Sun.COM 		int32_t v = n;
1154*8462SApril.Chin@Sun.COM 
1155*8462SApril.Chin@Sun.COM 		np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL, "%s", pollfdcountvarname);
1156*8462SApril.Chin@Sun.COM 		if (!np)
1157*8462SApril.Chin@Sun.COM 			errormsg(SH_DICT, ERROR_system(1), "couldn't create poll count variable %s", pollfdcountvarname);
1158*8462SApril.Chin@Sun.COM 		nv_putval(np, (char *)&v, NV_INTEGER);
1159*8462SApril.Chin@Sun.COM 		nv_close(np);
1160*8462SApril.Chin@Sun.COM 	}
11614887Schin 
11624887Schin 	for(i=0 ; i < numpollfd ; i++)
11634887Schin 	{
1164*8462SApril.Chin@Sun.COM 		np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL, "%s[%d].revents", varname, i);
11654887Schin 		if (!np)
1166*8462SApril.Chin@Sun.COM 			errormsg(SH_DICT, ERROR_system(1), "couldn't create pollfd %s[%d].revents", varname, i);
11674887Schin 
11684887Schin 		poll_eventstostr(buff, pollfd[i].revents);
11694887Schin 
11704887Schin 		nv_putval(np, buff, 0);
11714887Schin 		nv_close(np);
11724887Schin         }
11734887Schin 
11744887Schin 	return(0);
11754887Schin }
11764887Schin 
11774887Schin static const char sh_optrewind[] =
11784887Schin "[-?\n@(#)$Id: rewind (AT&T Labs Research) 2007-05-07 $\n]"
11794887Schin "[-author?Roland Mainz <roland.mainz@nrubsig.org>]"
11804887Schin "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
11814887Schin "[+NAME? rewind - reset file position indicator in a stream]"
11824887Schin "[+DESCRIPTION?The \brewind\b command will move the file pointer of fd to position 0.]"
11834887Schin "\n"
11844887Schin "\nfd\n"
11854887Schin "\n"
11864887Schin "[+EXIT STATUS?]{"
11874887Schin         "[+0?Success.]"
11884887Schin         "[+>0?An error occurred.]"
11894887Schin "}"
11904887Schin "[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bstat\b(1),\bstat\b(2)]"
11914887Schin ;
11924887Schin 
11934887Schin 
11944887Schin extern int b_rewind(int argc, char *argv[], void *extra)
11954887Schin {
1196*8462SApril.Chin@Sun.COM 	Shell_t *shp = sh_contexttoshell(extra);
11974887Schin 	int fd = -1;
11984887Schin 	register int n;
11994887Schin 	while (n = optget(argv, sh_optrewind)) switch (n)
12004887Schin 	{
12014887Schin 	    case ':':
12024887Schin 		errormsg(SH_DICT, 2, "%s", opt_info.arg);
12034887Schin 		break;
12044887Schin 	    case '?':
12054887Schin 		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
12064887Schin 		break;
12074887Schin 	}
12084887Schin 	argc -= opt_info.index;
12094887Schin 	argv += opt_info.index;
12104887Schin 	if(argc!=1)
12114887Schin 		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
12124887Schin 
12134887Schin 	errno = 0;
12144887Schin 	fd = strtol(argv[0], (char **)NULL, 0);
12154887Schin 	if (errno != 0 || fd < 0)
12164887Schin 		errormsg(SH_DICT, ERROR_system(1), "%s: invalid fd", argv[0]);
12174887Schin 
12184887Schin 	if (sh_seek(fd, 0, SEEK_SET) == (off_t)-1)
12194887Schin 		errormsg(SH_DICT, ERROR_system(1), "seek error");
12204887Schin 
12214887Schin 	return(0);
12224887Schin }
1223