1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1982-2007 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                  David Korn <dgk@research.att.com>                   *
18*                                                                      *
19***********************************************************************/
20#pragma prototyped
21
22#include	<shell.h>
23#include	<stdio.h>
24#include	<stdbool.h>
25#include	<option.h>
26#include	<stk.h>
27#include	<tm.h>
28#include	"name.h"
29#undef nv_isnull
30#ifndef SH_DICT
31#   define SH_DICT     "libshell"
32#endif
33#include	<poll.h>
34
35#define sh_contexttoshb(context)	((Shbltin_t*)(context))
36#define sh_contexttoshell(context)	((context)?(sh_contexttoshb(context)->shp):(NULL))
37
38/*
39 * time formatting related
40*/
41struct dctime
42{
43	Namfun_t	fun;
44	Namval_t 	*format;
45	char		buff[256]; /* Must be large enougth for |tmfmt()| */
46};
47
48static char *get_time(Namval_t* np, Namfun_t* nfp)
49{
50	struct dctime *dp = (struct dctime*)nfp;
51	time_t t = nv_getn(np,nfp);
52	char *format = nv_getval(dp->format);
53	tmfmt(dp->buff,sizeof(dp->buff),format,(time_t*)0);
54	return(dp->buff);
55}
56
57static void put_time(Namval_t* np, const char* val, int flag, Namfun_t* nfp)
58{
59	struct dctime *dp = (struct dctime*)nfp;
60	char *last;
61	if(val)
62	{
63		int32_t t;
64		if(flag&NV_INTEGER)
65		{
66			if(flag&NV_LONG)
67				t = *(Sfdouble_t*)val;
68			else
69				t = *(double*)val;
70		}
71		else
72		{
73			t = tmdate(val, &last, (time_t*)0);
74			if(*last)
75				errormsg(SH_DICT, ERROR_exit(1),"%s: invalid date/time string", val);
76		}
77		nv_putv(np, (char*)&t,NV_INTEGER, nfp);
78	}
79	else
80	{
81		nv_unset(dp->format);
82		free((void*)dp->format);
83		nv_putv(np, val, flag, nfp);
84	}
85}
86
87static Namval_t *create_time(Namval_t *np, const char *name, int flags, Namfun_t *nfp)
88{
89	struct dctime *dp = (struct dctime*)nfp;
90	if(strcmp(name, "format"))
91		return((Namval_t*)0);
92	return(dp->format);
93}
94
95static const Namdisc_t timedisc =
96{
97        sizeof(struct dctime),
98        put_time,
99        get_time,
100        0,
101        0,
102        create_time,
103};
104
105
106static Namval_t *make_time(Namval_t* np)
107{
108	int offset = stktell(stkstd);
109	char *name = nv_name(np);
110	struct dctime *dp = newof(NULL,struct dctime,1,0);
111	if(!dp)
112		return((Namval_t*)0);
113	sfprintf(stkstd,"%s.format\0",name);
114	sfputc(stkstd,0);
115	dp->format = nv_search(stkptr(stkstd,offset),sh.var_tree,NV_ADD);
116	dp->fun.disc = &timedisc;
117	nv_stack(np,&dp->fun);
118	return(np);
119}
120
121/*
122 * mode formatting related
123*/
124static char *get_mode(Namval_t* np, Namfun_t* nfp)
125{
126	mode_t mode = nv_getn(np,nfp);
127	return(fmtperm(mode));
128}
129
130static void put_mode(Namval_t* np, const char* val, int flag, Namfun_t* nfp)
131{
132	if(val)
133	{
134		int32_t mode;
135		char *last;
136		if(flag&NV_INTEGER)
137		{
138			if(flag&NV_LONG)
139				mode = *(Sfdouble_t*)val;
140			else
141				mode = *(double*)val;
142		}
143		else
144		{
145			mode = strperm(val, &last,0);
146			if(*last)
147				errormsg(SH_DICT, ERROR_exit(1),"%s: invalid mode string", val);
148		}
149		nv_putv(np,(char*)&mode,NV_INTEGER,nfp);
150	}
151	else
152		nv_putv(np,val,flag,nfp);
153}
154
155static const Namdisc_t modedisc =
156{
157	0,
158        put_mode,
159        get_mode,
160};
161
162static Namval_t *make_mode(Namval_t* np)
163{
164	char *name = nv_name(np);
165	Namfun_t *nfp = newof(NULL,Namfun_t,1,0);
166	if(!nfp)
167		return((Namval_t*)0);
168	nfp->disc = &modedisc;
169	nv_stack(np,nfp);
170	return(np);
171}
172
173/*
174 *  field related typese and functions
175 */
176typedef struct _field_
177{
178	char		*name;		/* field name */
179	int		flags;		/* flags */
180	short		offset;		/* offset of field into data */
181	short		size;		/* size of field */
182	Namval_t	*(*make)(Namval_t*);	/* discipline constructor */
183} Shfield_t;
184
185/*
186 * lookup field in field table
187 */
188static Shfield_t *sh_findfield(Shfield_t *ftable, int nelem, const char *name)
189{
190	Shfield_t *fp = ftable;
191	register int i,n;
192	register const char *cp;
193	for(cp=name; *cp; cp++)
194	{
195		if(*cp=='.')
196			break;
197	}
198	n = cp-name;
199	for(i=0; i < nelem; i++,fp++)
200	{
201		if(memcmp(fp->name,name,n)==0 && fp->name[n]==0)
202			return(fp);
203	}
204	return(0);
205}
206
207/*
208 * class types and functions
209 */
210
211typedef struct _class_
212{
213	int		nelem;		/* number of elements */
214	int		dsize;		/* size for data structure */
215	Shfield_t 	*fields;	/* field description table */
216} Shclass_t;
217
218struct dcclass
219{
220	Namfun_t	fun;
221	Shclass_t	sclass;
222};
223
224static Namval_t *sh_newnode(register Shfield_t *fp, Namval_t *np)
225{
226	char *val = np->nvalue + fp->offset;
227	char *name = nv_name(np);
228	register Namval_t *nq;
229	int offset = stktell(stkstd);
230	sfprintf(stkstd,"%s.%s\0",name,fp->name);
231	sfputc(stkstd,0);
232	nq = nv_search(stkptr(stkstd,offset),sh.var_tree,NV_ADD);
233	if(fp->size<0)
234		val = *(char**)val;
235	nv_putval(nq,val,fp->flags|NV_NOFREE);
236	if(fp->make)
237		(*fp->make)(nq);
238	return(nq);
239}
240
241static Namval_t *fieldcreate(Namval_t *np, const char *name, int flags, Namfun_t *nfp)
242{
243	struct dcclass *dcp = (struct dcclass*)nfp;
244	Shclass_t *sp = &dcp->sclass;
245	Shfield_t *fp = sh_findfield(sp->fields,sp->nelem,name);
246	Namval_t *nq,**nodes = (Namval_t**)(dcp+1);
247	int n = fp-sp->fields;
248	int len =  strlen(fp->name);
249	void *data = (void*)np->nvalue;
250	if(!(nq=nodes[n]))
251	{
252		nodes[n] = nq = sh_newnode(fp,np);
253		nfp->last = "";
254	}
255	if(name[len]==0)
256		return(nq);
257	return(nq);
258}
259
260static void genvalue(Sfio_t *out, Shclass_t *sp, int indent, Namval_t *npar)
261{
262	Shfield_t *fp = sp->fields;
263	Namval_t *np, **nodes= (Namval_t**)(sp+1);
264	register int i,isarray;
265	if(out)
266	{
267		sfwrite(out,"(\n",2);
268		indent++;
269	}
270	for(i=0; i < sp->nelem; i++,fp++)
271	{
272#if 0
273		/* handle recursive case */
274#endif
275		if(!(np=nodes[i]) && out)
276			np = sh_newnode(fp,npar);
277		if(np)
278		{
279			isarray=0;
280			if(nv_isattr(np,NV_ARRAY))
281			{
282				isarray=1;
283				if(array_elem(nv_arrayptr(np))==0)
284					isarray=2;
285				else
286					nv_putsub(np,(char*)0,ARRAY_SCAN);
287			}
288			sfnputc(out,'\t',indent);
289			sfputr(out,fp->name,(isarray==2?'\n':'='));
290			if(isarray)
291			{
292				if(isarray==2)
293					continue;
294				sfwrite(out,"(\n",2);
295				sfnputc(out,'\t',++indent);
296			}
297			while(1)
298			{
299				char *fmtq;
300				if(isarray)
301				{
302					sfprintf(out,"[%s]",sh_fmtq(nv_getsub(np)));
303					sfputc(out,'=');
304				}
305				if(!(fmtq=nv_getval(np)) || !(fmtq=sh_fmtq(fmtq)))
306					fmtq = "";
307				sfputr(out,fmtq,'\n');
308				if(!nv_nextsub(np))
309					break;
310				sfnputc(out,'\t',indent);
311			}
312			if(isarray)
313			{
314				sfnputc(out,'\t',--indent);
315				sfwrite(out,")\n",2);
316			}
317		}
318	}
319	if(out)
320	{
321		if(indent>1)
322			sfnputc(out,'\t',indent-1);
323		sfputc(out,')');
324	}
325}
326
327static char *walk_class(register Namval_t *np, int dlete, struct dcclass *dcp)
328{
329	static Sfio_t *out;
330	Sfio_t *outfile;
331	int savtop = stktell(stkstd);
332	char *savptr =  stkfreeze(stkstd,0);
333	if(dlete)
334		outfile = 0;
335	else if(!(outfile=out))
336                outfile = out =  sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
337	else
338		sfseek(outfile,0L,SEEK_SET);
339	genvalue(outfile,&dcp->sclass,0,np);
340	stkset(stkstd,savptr,savtop);
341	if(!outfile)
342		return((char*)0);
343	sfputc(out,0);
344	return((char*)out->_data);
345}
346
347static char *get_classval(Namval_t* np, Namfun_t* nfp)
348{
349	return(walk_class(np,0,(struct dcclass *)nfp));
350}
351
352static void put_classval(Namval_t* np, const char* val, int flag, Namfun_t* nfp)
353{
354	walk_class(np,1,(struct dcclass *)nfp);
355	if(nfp = nv_stack(np,(Namfun_t*)0))
356	{
357		free((void*)nfp);
358		if(np->nvalue && !nv_isattr(np,NV_NOFREE))
359			free((void*)np->nvalue);
360	}
361	if(val)
362		nv_putval(np,val,flag);
363}
364
365static const Namdisc_t classdisc =
366{
367        sizeof(struct dcclass),
368        put_classval,
369        get_classval,
370        0,
371        0,
372	fieldcreate
373};
374
375static int mkclass(Namval_t *np, Shclass_t *sp)
376{
377	struct dcclass *tcp = newof(NULL,struct dcclass,1,sp->nelem*sizeof(Namval_t*));
378	if(!tcp)
379		return(0);
380	memset((void*)(tcp+1),0,sp->nelem*sizeof(Namval_t*));
381	tcp->fun.disc = &classdisc;
382	tcp->sclass = *sp;
383	np->nvalue = (char*)calloc(sp->dsize,1);
384	nv_stack(np,&tcp->fun);
385	return(1);
386}
387
388/*
389 * ====================from here down is file class specific
390 */
391static struct stat *Sp;
392
393struct filedata
394{
395	struct stat	statb;
396	int		fd;
397	char		*name;
398};
399
400static Shfield_t filefield[] =
401{
402	{ "atime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_atime), sizeof(Sp->st_atime), make_time},
403	{ "ctime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_ctime), sizeof(Sp->st_ctime), make_time},
404	{ "dev",   NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_dev),sizeof(Sp->st_dev)},
405	{ "fd",    NV_INTEGER|NV_RDONLY, offsetof(struct filedata,fd), 		sizeof(int)},
406	{ "gid",   NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_gid), sizeof(Sp->st_gid)},
407	{ "ino",   NV_LONG|NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_ino), sizeof(Sp->st_ino)},
408	{ "mode",  NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_mode), sizeof(Sp->st_mode), make_mode},
409	{ "mtime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_mtime), sizeof(Sp->st_mtime), make_time},
410	{ "name",   NV_RDONLY, offsetof(struct filedata,name), 	-1 },
411	{ "nlink", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_nlink), sizeof(Sp->st_nlink)},
412	{ "size",  NV_LONG|NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_size), sizeof(Sp->st_size)},
413	{ "uid",   NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_uid), sizeof(Sp->st_uid)}
414};
415
416static Shclass_t Fileclass =
417{
418	sizeof(filefield)/sizeof(*filefield),
419	sizeof(struct filedata),
420	filefield
421};
422
423
424#define letterbit(bit)	(1<<((bit)-'a'))
425
426static const char sh_optopen[] =
427"[-?\n@(#)$Id: open (AT&T Labs Research) 2007-05-07 $\n]"
428"[-author?David Korn <dgk@research.att.com>]"
429"[-author?Roland Mainz <roland.mainz@nrubsig.org>]"
430"[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
431"[+NAME? open - create a shell variable correspnding to a file]"
432"[+DESCRIPTION?\bopen\b creates the compound variable \avar\a correspinding "
433	"to the file given by the pathname \afile\a.  The elements of \avar\a "
434	"are the names of elements in the \astat\a structure with the \bst_\b "
435	"prefix removed.]"
436"[+?\afile\a is opened (based on \b-r\b and/or \b-w\b) and the variable "
437	"\avar\a\b.fd\b is the file descriptor.]"
438"[a:append?Open for append.]"
439"[b:binary?Open in binary mode"
440#ifndef O_BINARY
441	" (not supported/ignored on this platform)"
442#endif
443	".]"
444"[t:text?Open in text mode"
445#ifndef O_TEXT
446	" (not supported/ignored on this platform)"
447#endif
448	".]"
449"[c:create?Open for create.]"
450"[i:inherit?Open without the close-on-exec bit set.]"
451"[I:noinherit?Open with the close-on-exec bit set.]"
452"[r:read?Open with read access.]"
453"[w:write?Open with write access.]"
454"[m:mode]:[mode:=rwrwrw?Open with access mode \amode\a.]"
455"[x:exclusive?Open exclusive.]"
456
457"[N:nofollow?If the path names a symbolic link, open fails with ELOOP "
458#ifndef O_NOFOLLOW
459	" (not supported/ignored on this platform)"
460#endif
461	".]"
462"[S:sync?Write I/O operations on the file descriptor complete as "
463	"defined by synchronized I/O file integrity completion"
464#ifndef O_SYNC
465	" (not supported/ignored on this platform)"
466#endif
467	".]"
468"[T:trunc?If the file exists and is a regular file, and  the  file "
469        "is successfully opened read/write or write-only, its length is "
470        "truncated to 0 and the mode and owner are unchanged.  It "
471        "has  no  effect on FIFO special files or terminal device "
472        "files.   Its   effect   on   other   file    types    is "
473        "implementation-dependent.  The  result  of using -T "
474        "with read-only files is undefined"
475#ifndef O_TRUNC
476	" (not supported/ignored on this platform)"
477#endif
478	".]"
479"\n"
480"\nvar file\n"
481"\n"
482"[+EXIT STATUS?]{"
483        "[+0?Success.]"
484        "[+>0?An error occurred.]"
485"}"
486"[+SEE ALSO?\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bstat\b(1),\bpoll\b(1),\bstat\b(2)]"
487;
488
489
490extern int b_open(int argc, char *argv[], void *extra)
491{
492	register Namval_t *np;
493	register int n,oflag=0;
494	Shell_t *shp = sh_contexttoshell(extra);
495	struct filedata *fdp;
496	mode_t mode = 0666;
497	long flags = 0;
498	int fd = -1;
499	char *arg;
500
501	while (n = optget(argv, sh_optopen)) switch (n)
502	{
503	    case 'r':
504	    case 'w':
505	    case 'i':
506		flags |= letterbit(n);
507		break;
508	    case 'I':
509		flags &= ~(letterbit('i'));
510		break;
511	    case 'b':
512#ifdef O_BINARY
513		oflag |= O_BINARY;
514#endif
515		break;
516	    case 't':
517#ifdef O_TEXT
518		oflag |= O_TEXT;
519#endif
520		break;
521	    case 'N':
522#ifdef O_NOFOLLOW
523		oflag |= O_NOFOLLOW;
524#endif
525		break;
526	    case 'T':
527#ifdef O_TRUNC
528		oflag |= O_TRUNC;
529#endif
530		break;
531	    case 'x':
532		oflag |= O_EXCL;
533		break;
534	    case 'c':
535		oflag |= O_CREAT;
536		break;
537	    case 'a':
538		oflag |= O_APPEND;
539		break;
540	    case 'S':
541#ifdef O_SYNC
542		oflag |= O_SYNC;
543#endif
544		break;
545	    case 'm':
546		mode = strperm(arg = opt_info.arg, &opt_info.arg, mode);
547		if (*opt_info.arg)
548			errormsg(SH_DICT, ERROR_system(1), "%s: invalid mode", arg);
549	    	break;
550	    case ':':
551		errormsg(SH_DICT, 2, "%s", opt_info.arg);
552		break;
553	    case '?':
554		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
555		break;
556	}
557	argc -= opt_info.index;
558	argv += opt_info.index;
559	if(argc!=2 || !(flags&(letterbit('r')|letterbit('w'))))
560		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
561
562	if(flags&letterbit('r'))
563	{
564		if(flags&letterbit('w'))
565			oflag |= O_RDWR;
566		else
567			oflag |= O_RDONLY;
568	}
569	else if(flags&letterbit('w'))
570		oflag |= O_WRONLY;
571
572	fd = sh_open(argv[1], oflag, mode);
573	if(fd<0)
574		errormsg(SH_DICT, ERROR_system(1), "%s: open failed", argv[1]);
575
576	if(!(flags&letterbit('i')))
577		fcntl(fd, F_SETFL, 0);
578
579	np = nv_open(argv[0], shp->var_tree, NV_ARRAY|NV_VARNAME|NV_NOASSIGN);
580	if(!nv_isnull(np))
581		nv_unset(np);
582	mkclass(np, &Fileclass);
583	fdp = (struct filedata*)np->nvalue;
584	fstat(fd, &fdp->statb);
585	fdp->fd = fd;
586	fdp->name = strdup(argv[1]);
587	return(0);
588}
589
590static const char sh_optclose[] =
591"[-?\n@(#)$Id: close (AT&T Labs Research) 2007-04-21 $\n]"
592"[-author?Roland Mainz <roland.mainz@nrubsig.org>]"
593"[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
594"[+NAME? close - close a file descriptor]"
595"[+DESCRIPTION?\bclose\b closes the file descriptor specified by fd.]"
596"\n"
597"\nfd\n"
598"\n"
599"[+EXIT STATUS?]{"
600        "[+0?Success.]"
601        "[+>0?An error occurred.]"
602"}"
603"[+SEE ALSO?\bopen\b(1),\bdup\b(1),\btmpfile\b(1),\bpoll\b(1),\bstat\b(1)]"
604;
605
606extern int b_close(int argc, char *argv[], void *extra)
607{
608	register int n=0;
609	int fd = -1;
610
611	while (n = optget(argv, sh_optclose)) switch (n)
612	{
613	    case ':':
614		errormsg(SH_DICT, 2, "%s", opt_info.arg);
615		break;
616	    case '?':
617		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
618		break;
619	}
620	argc -= opt_info.index;
621	argv += opt_info.index;
622	if(argc!=1)
623		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
624
625	errno = 0;
626	fd = strtol(argv[0], (char **)NULL, 0);
627	if (errno != 0 || fd < 0)
628		errormsg(SH_DICT, ERROR_system(1), "%s: invalid descriptor", argv[0]);
629
630        n = sh_close(fd);
631
632	if (n < 0)
633		errormsg(SH_DICT, ERROR_system(1), "%s: close error", argv[0]);
634
635	return(n==0?0:1);
636}
637
638
639static const char sh_opttmpfile[] =
640"[-?\n@(#)$Id: tmpfile (AT&T Labs Research) 2007-05-07 $\n]"
641"[-author?Roland Mainz <roland.mainz@nrubsig.org>]"
642"[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
643"[+NAME? tmpfile - create a shell variable correspnding to a temporary file]"
644"[+DESCRIPTION?\btmpfile\b creates the compound variable \avar\a correspinding "
645	"to a temporary file.  The elements of \avar\a "
646	"are the names of elements in the \astat\a structure with the \bst_\b "
647	"prefix removed.]"
648"[i:inherit?Open without the close-on-exec bit set.]"
649"[I:noinherit?Open with the close-on-exec bit set.]"
650"\n"
651"\nvar\n"
652"\n"
653"[+EXIT STATUS?]{"
654        "[+0?Success.]"
655        "[+>0?An error occurred.]"
656"}"
657"[+SEE ALSO?\bopen\b(1),\bdup\b(1),\bclose\b(1),\bstat\b(1),\bstat\b(2)]"
658;
659
660
661extern int b_tmpfile(int argc, char *argv[], void *extra)
662{
663	register Namval_t *np;
664	register int n;
665	Shell_t *shp = sh_contexttoshell(extra);
666	struct filedata *fdp;
667	bool inherit = false;
668	FILE *file = NULL;
669	int ffd, fd = -1;
670	while (n = optget(argv, sh_opttmpfile)) switch (n)
671	{
672	    case 'i':
673		inherit = true;
674		break;
675	    case 'I':
676		inherit = false;
677		break;
678	    case ':':
679		errormsg(SH_DICT, 2, "%s", opt_info.arg);
680		break;
681	    case '?':
682		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
683		break;
684	}
685	argc -= opt_info.index;
686	argv += opt_info.index;
687	if(argc!=1)
688		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
689
690	file = tmpfile();
691	if(!file)
692		errormsg(SH_DICT, ERROR_system(1), "%s: tmpfile failed", argv[1]);
693	ffd = fileno(file);
694	fd = sh_dup(ffd);
695	if(fd<0)
696		errormsg(SH_DICT, ERROR_system(1), "%s: tmpfile failed", argv[1]);
697	fclose(file);
698
699	if(!inherit)
700		fcntl(fd, F_SETFL, 0);
701
702	np = nv_open(argv[0], shp->var_tree, NV_ARRAY|NV_VARNAME|NV_NOASSIGN);
703	if(!nv_isnull(np))
704		nv_unset(np);
705	mkclass(np,&Fileclass);
706	fdp = (struct filedata*)np->nvalue;
707
708	fstat(fd, &fdp->statb);
709	fdp->fd = fd;
710	fdp->name = NULL;
711	return(0);
712}
713
714static const char sh_optdup[] =
715"[-?\n@(#)$Id: dup (AT&T Labs Research) 2007-05-07 $\n]"
716"[-author?Roland Mainz <roland.mainz@nrubsig.org>]"
717"[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
718"[+NAME? dup - duplicate an open file descriptor]"
719"[+DESCRIPTION?The \bdup\b commands returns a new file descriptor having the "
720     "following in common with the original open file descriptor "
721     "fd: same open file (or pipe), same file pointer (that is, both  file descriptors "
722     "share one file pointer) same access mode (read, write or read/write). "
723     "The file descriptor returned is the lowest one available.]"
724"[i:inherit?Open without the close-on-exec bit set.]"
725"[I:noinherit?Open with the close-on-exec bit set.]"
726"\n"
727"\nvar fd\n"
728"\n"
729"[+EXIT STATUS?]{"
730        "[+0?Success.]"
731        "[+>0?An error occurred.]"
732"}"
733"[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bclose\b(1),\bpoll\b(1),\bstat\b(1)]"
734;
735
736
737extern int b_dup(int argc, char *argv[], void *extra)
738{
739	register Namval_t *np;
740	register int n;
741	Shell_t *shp = sh_contexttoshell(extra);
742	struct filedata *fdp;
743	bool inherit = false;
744	int ffd, fd = -1;
745	while (n = optget(argv, sh_optdup)) switch (n)
746	{
747	    case 'i':
748		inherit = true;
749		break;
750	    case 'I':
751		inherit = false;
752		break;
753	    case ':':
754		errormsg(SH_DICT, 2, "%s", opt_info.arg);
755		break;
756	    case '?':
757		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
758		break;
759	}
760	argc -= opt_info.index;
761	argv += opt_info.index;
762	if(argc!=2)
763		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
764
765	errno = 0;
766	ffd = strtol(argv[1], (char **)NULL, 0);
767	if (errno != 0 || ffd < 0)
768		errormsg(SH_DICT, ERROR_system(1), "%s: invalid fd", argv[1]);
769
770	fd = sh_dup(ffd);
771	if(fd<0)
772		errormsg(SH_DICT, ERROR_system(1), "%s: dup failed", argv[1]);
773
774	if(!inherit)
775		fcntl(fd,F_SETFL,0);
776
777	np = nv_open(argv[0],shp->var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN);
778	if(!nv_isnull(np))
779		nv_unset(np);
780	mkclass(np, &Fileclass);
781	fdp = (struct filedata*)np->nvalue;
782
783	fstat(fd, &fdp->statb);
784	fdp->fd = fd;
785	fdp->name = NULL;
786	return(0);
787}
788
789static const char sh_optstat[] =
790"[-?\n@(#)$Id: stat (AT&T Labs Research) 2007-05-07 $\n]"
791"[-author?David Korn <dgk@research.att.com>]"
792"[-author?Roland Mainz <roland.mainz@nrubsig.org>]"
793"[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
794"[+NAME? stat - get file status]"
795"[+DESCRIPTION?\bstat\b creates the compound variable \avar\a correspinding "
796	"to the file given by the pathname \afile\a.  The elements of \avar\a "
797	"are the names of elements in the \astat\a structure with the \bst_\b "
798	"prefix removed.]"
799"[l:lstat?If the the named file is a symbolic link returns information about "
800	"the link itself.]"
801"\n"
802"\nvar file\n"
803"\n"
804"[+EXIT STATUS?]{"
805        "[+0?Success.]"
806        "[+>0?An error occurred.]"
807"}"
808"[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bpoll\b(1),\bstat\b(2),\blstat\b(2)]"
809;
810
811
812extern int b_stat(int argc, char *argv[], void *extra)
813{
814	register Namval_t *np;
815	register int n;
816	Shell_t *shp = sh_contexttoshell(extra);
817	struct filedata *fdp;
818	long flags = 0;
819	struct stat statb;
820	while (n = optget(argv, sh_optstat)) switch (n)
821	{
822	    case 'l':
823		flags |= letterbit(n);
824		break;
825	    case ':':
826		errormsg(SH_DICT, 2, "%s", opt_info.arg);
827		break;
828	    case '?':
829		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
830		break;
831	}
832	argc -= opt_info.index;
833	argv += opt_info.index;
834	if(argc!=2)
835		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
836
837	if(flags&letterbit('l'))
838	{
839		if(lstat(argv[1], &statb) < 0)
840			errormsg(SH_DICT, ERROR_system(1), "%s: stat failed", argv[1]);
841	}
842	else
843	{
844		if(stat(argv[1], &statb) < 0)
845			errormsg(SH_DICT, ERROR_system(1), "%s: stat failed", argv[1]);
846
847	}
848
849	np = nv_open(argv[0],shp->var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN);
850	if(!nv_isnull(np))
851		nv_unset(np);
852	mkclass(np,&Fileclass);
853	fdp = (struct filedata*)np->nvalue;
854	fdp->statb = statb;
855	fdp->fd = -1;
856	fdp->name = strdup(argv[1]);
857	return(0);
858}
859
860
861static const char sh_optrewind[] =
862"[-?\n@(#)$Id: rewind (AT&T Labs Research) 2007-05-07 $\n]"
863"[-author?Roland Mainz <roland.mainz@nrubsig.org>]"
864"[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
865"[+NAME? rewind - reset file position indicator in a stream]"
866"[+DESCRIPTION?The \brewind\b command will move the file pointer of fd to position 0.]"
867"\n"
868"\nfd\n"
869"\n"
870"[+EXIT STATUS?]{"
871        "[+0?Success.]"
872        "[+>0?An error occurred.]"
873"}"
874"[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bstat\b(1),\bstat\b(2)]"
875;
876
877
878extern int b_rewind(int argc, char *argv[], void *extra)
879{
880	Shell_t *shp = sh_contexttoshell(extra);
881	int fd = -1;
882	register int n;
883	while (n = optget(argv, sh_optrewind)) switch (n)
884	{
885	    case ':':
886		errormsg(SH_DICT, 2, "%s", opt_info.arg);
887		break;
888	    case '?':
889		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
890		break;
891	}
892	argc -= opt_info.index;
893	argv += opt_info.index;
894	if(argc!=1)
895		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
896
897	errno = 0;
898	fd = strtol(argv[0], (char **)NULL, 0);
899	if (errno != 0 || fd < 0)
900		errormsg(SH_DICT, ERROR_system(1), "%s: invalid fd", argv[0]);
901
902	if (sh_seek(fd, 0, SEEK_SET) == (off_t)-1)
903		errormsg(SH_DICT, ERROR_system(1), "seek error");
904
905	return(0);
906}
907