xref: /titanic_51/usr/src/lib/libshell/common/sh/args.c (revision 4df55fde49134f9735f84011f23a767c75e393c7)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1982-2009 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  * UNIX shell
23  *
24  * S. R. Bourne
25  * Rewritten by David Korn
26  * AT&T Labs
27  *
28  */
29 
30 #include	"defs.h"
31 #include	"path.h"
32 #include	"builtins.h"
33 #include	"terminal.h"
34 #include	"edit.h"
35 #include	"FEATURE/poll"
36 #if SHOPT_KIA
37 #   include	"shlex.h"
38 #   include	"io.h"
39 #endif /* SHOPT_KIA */
40 #if SHOPT_PFSH
41 #   define PFSHOPT	"P"
42 #else
43 #   define PFSHOPT
44 #endif
45 #if SHOPT_BASH
46 #   define BASHOPT	"\374"
47 #else
48 #   define BASHOPT
49 #endif
50 #if SHOPT_HISTEXPAND
51 #   define HFLAG        "H"
52 #else
53 #   define HFLAG        ""
54 #endif
55 
56 #define SORT		1
57 #define PRINT		2
58 
59 static	char		*null;
60 
61 /* The following order is determined by sh_optset */
62 static  const char optksh[] =  PFSHOPT BASHOPT "DircabefhkmnpstuvxBCGEl" HFLAG;
63 static const int flagval[]  =
64 {
65 #if SHOPT_PFSH
66 	SH_PFSH,
67 #endif
68 #if SHOPT_BASH
69 	SH_POSIX,
70 #endif
71 	SH_DICTIONARY, SH_INTERACTIVE, SH_RESTRICTED, SH_CFLAG,
72 	SH_ALLEXPORT, SH_NOTIFY, SH_ERREXIT, SH_NOGLOB, SH_TRACKALL,
73 	SH_KEYWORD, SH_MONITOR, SH_NOEXEC, SH_PRIVILEGED, SH_SFLAG, SH_TFLAG,
74 	SH_NOUNSET, SH_VERBOSE,  SH_XTRACE, SH_BRACEEXPAND, SH_NOCLOBBER,
75 	SH_GLOBSTARS, SH_RC, SH_LOGIN_SHELL,
76 #if SHOPT_HISTEXPAND
77         SH_HISTEXPAND,
78 #endif
79 	0
80 };
81 
82 #define NUM_OPTS	(sizeof(flagval)/sizeof(*flagval))
83 
84 typedef struct _arg_
85 {
86 	Shell_t		*sh;
87 	struct dolnod	*argfor; /* linked list of blocks to be cleaned up */
88 	struct dolnod	*dolh;
89 	char flagadr[NUM_OPTS+1];
90 #if SHOPT_KIA
91 	char	*kiafile;
92 #endif /* SHOPT_KIA */
93 } Arg_t;
94 
95 static int 		arg_expand(Shell_t*,struct argnod*,struct argnod**,int);
96 static void 		sh_argset(Arg_t*, char *[]);
97 
98 
99 /* ======== option handling	======== */
100 
101 void *sh_argopen(Shell_t *shp)
102 {
103 	void *addr = newof(0,Arg_t,1,0);
104 	Arg_t *ap = (Arg_t*)addr;
105 	ap->sh = shp;
106 	return(addr);
107 }
108 
109 static int infof(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp)
110 {
111 #if SHOPT_BASH
112 	extern const char sh_bash1[], sh_bash2[];
113 	if(strcmp(s,"bash1")==0)
114 	{
115 		if(sh_isoption(SH_BASH))
116 			sfputr(sp,sh_bash1,-1);
117 	}
118 	else if(strcmp(s,"bash2")==0)
119 	{
120 		if(sh_isoption(SH_BASH))
121 			sfputr(sp,sh_bash2,-1);
122 	}
123 	else if(*s==':' && sh_isoption(SH_BASH))
124 		sfputr(sp,s,-1);
125 	else
126 #endif
127 	if(*s!=':')
128 		sfputr(sp,sh_set,-1);
129 	return(1);
130 }
131 
132 /*
133  *  This routine turns options on and off
134  *  The options "PDicr" are illegal from set command.
135  *  The -o option is used to set option by name
136  *  This routine returns the number of non-option arguments
137  */
138 int sh_argopts(int argc,register char *argv[], void *context)
139 {
140 	Shell_t		*shp = (Shell_t*)context;
141 	register int	n,o;
142 	register Arg_t	*ap = (Arg_t*)(shp->arg_context);
143 	Lex_t		*lp = (Lex_t*)(shp->lex_context);
144 	Shopt_t		newflags;
145 	int setflag=0, action=0, trace=(int)sh_isoption(SH_XTRACE);
146 	Namval_t *np = NIL(Namval_t*);
147 	const char *cp;
148 	int verbose,f;
149 	Optdisc_t disc;
150 	newflags=ap->sh->options;
151 	memset(&disc, 0, sizeof(disc));
152 	disc.version = OPT_VERSION;
153 	disc.infof = infof;
154 	opt_info.disc = &disc;
155 
156 	if(argc>0)
157 		setflag = 4;
158 	else
159 		argc = -argc;
160 	while((n = optget(argv,setflag?sh_optset:sh_optksh)))
161 	{
162 		o=0;
163 		f=*opt_info.option=='-' && (opt_info.num || opt_info.arg);
164 		switch(n)
165 		{
166 	 	    case 'A':
167 			np = nv_open(opt_info.arg,ap->sh->var_tree,NV_NOASSIGN|NV_ARRAY|NV_VARNAME);
168 			if(f)
169 				nv_unset(np);
170 			continue;
171 #if SHOPT_BASH
172 		    case 'O':	/* shopt options, only in bash mode */
173 			if(!sh_isoption(SH_BASH))
174 				errormsg(SH_DICT,ERROR_exit(1), e_option, opt_info.name);
175 #endif
176 		    case 'o':	/* set options */
177 		    byname:
178 			if(!opt_info.arg||!*opt_info.arg||*opt_info.arg=='-')
179 			{
180 				action = PRINT;
181 				/* print style: -O => shopt options
182 				 * bash => print unset options also, no heading
183 				 */
184 				verbose = (f?PRINT_VERBOSE:PRINT_NO_HEADER)|
185 					  (n=='O'?PRINT_SHOPT:0)|
186 					  (sh_isoption(SH_BASH)?PRINT_ALL|PRINT_NO_HEADER:0)|
187 					  ((opt_info.arg&&(!*opt_info.arg||*opt_info.arg=='-'))?(PRINT_TABLE|PRINT_NO_HEADER):0);
188 				continue;
189 			}
190 			o = sh_lookopt(opt_info.arg,&f);
191 			if(o<=0
192 				|| (!sh_isoption(SH_BASH) && (o&SH_BASHEXTRA))
193 				|| ((!sh_isoption(SH_BASH) || n=='o') && (o&SH_BASHOPT))
194 
195 				|| (setflag && (o&SH_COMMANDLINE)))
196 			{
197 				errormsg(SH_DICT,2, e_option, opt_info.arg);
198 				error_info.errors++;
199 			}
200 			o &= 0xff;
201 			if(sh_isoption(SH_RESTRICTED) && !f && o==SH_RESTRICTED)
202 				errormsg(SH_DICT,ERROR_exit(1), e_restricted, opt_info.arg);
203 			break;
204 #if SHOPT_BASH
205 		    case -1:	/* --rcfile */
206 			ap->sh->rcfile = opt_info.arg;
207 			continue;
208 		    case -2:	/* --noediting */
209 			if (!f)
210 			{
211 				off_option(&newflags,SH_VI);
212 				off_option(&newflags,SH_EMACS);
213 				off_option(&newflags,SH_GMACS);
214 			}
215 			continue;
216 		    case -3:	/* --profile */
217 			n = 'l';
218 			goto skip;
219 		    case -4:	/* --posix */
220 			/* mask lower 8 bits to find char in optksh string */
221 			n&=0xff;
222 			goto skip;
223 		    case -5:	/* --version */
224 			sfputr(sfstdout, "ksh bash emulation, version ",-1);
225 			np = nv_open("BASH_VERSION",ap->sh->var_tree,0);
226 			sfputr(sfstdout, nv_getval(np),-1);
227 			np = nv_open("MACHTYPE",ap->sh->var_tree,0);
228 			sfprintf(sfstdout, " (%s)\n", nv_getval(np));
229 			sh_exit(0);
230 #endif
231 		    case -6:	/* --default */
232 			{
233 				register const Shtable_t *tp;
234 				for(tp=shtab_options; o = tp->sh_number; tp++)
235 					if(!(o&SH_COMMANDLINE) && is_option(&newflags,o&0xff))
236 						off_option(&newflags,o&0xff);
237 			}
238 		    	continue;
239 	 	    case -7:
240 			f = 0;
241 		    	goto byname;
242 	 	    case 'D':
243 			on_option(&newflags,SH_NOEXEC);
244 			goto skip;
245 		    case 'T':
246 			if (opt_info.num)
247 				ap->sh->test |= opt_info.num;
248 			else
249 				ap->sh->test = 0;
250 		    	continue;
251 		    case 's':
252 			if(setflag)
253 			{
254 				action = SORT;
255 				continue;
256 			}
257 #if SHOPT_KIA
258 			goto skip;
259 		    case 'R':
260 			if(setflag)
261 				n = ':';
262 			else
263 			{
264 				ap->kiafile = opt_info.arg;
265 				n = 'n';
266 			}
267 			/*FALLTHROUGH*/
268 #endif /* SHOPT_KIA */
269 #if SHOPT_REGRESS
270 			goto skip;
271 		    case 'I':
272 			continue;
273 #endif /* SHOPT_REGRESS */
274 		    skip:
275 		    default:
276 			if(cp=strchr(optksh,n))
277 				o = flagval[cp-optksh];
278 			break;
279 		    case ':':
280 			if(opt_info.name[0]=='-'&&opt_info.name[1]=='-')
281 			{
282 				opt_info.arg = argv[opt_info.index-1] + 2;
283 				f = 1;
284 				goto byname;
285 			}
286 			errormsg(SH_DICT,2, "%s", opt_info.arg);
287 			continue;
288 		    case '?':
289 			errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg);
290 			return(-1);
291 		}
292 		if(f)
293 		{
294 			if(o==SH_VI || o==SH_EMACS || o==SH_GMACS)
295 			{
296 				off_option(&newflags,SH_VI);
297 				off_option(&newflags,SH_EMACS);
298 				off_option(&newflags,SH_GMACS);
299 			}
300 			on_option(&newflags,o);
301 			off_option(&ap->sh->offoptions,o);
302 		}
303 		else
304 		{
305 			if(o==SH_XTRACE)
306 				trace = 0;
307 			off_option(&newflags,o);
308 			if(setflag==0)
309 				on_option(&ap->sh->offoptions,o);
310 		}
311 	}
312 	if(error_info.errors)
313 		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*)));
314 	/* check for '-' or '+' argument */
315 	if((cp=argv[opt_info.index]) && cp[1]==0 && (*cp=='+' || *cp=='-') &&
316 		strcmp(argv[opt_info.index-1],"--"))
317 	{
318 		opt_info.index++;
319 		off_option(&newflags,SH_XTRACE);
320 		off_option(&newflags,SH_VERBOSE);
321 		trace = 0;
322 	}
323 	if(trace)
324 		sh_trace(argv,1);
325 	argc -= opt_info.index;
326 	argv += opt_info.index;
327 	if(action==PRINT)
328 		sh_printopts(newflags,verbose,0);
329 	if(setflag)
330 	{
331 		if(action==SORT)
332 		{
333 			if(argc>0)
334 				strsort(argv,argc,strcoll);
335 			else
336 				strsort(ap->sh->st.dolv+1,ap->sh->st.dolc,strcoll);
337 		}
338 		if(np)
339 		{
340 			nv_setvec(np,0,argc,argv);
341 			nv_close(np);
342 		}
343 		else if(argc>0 || ((cp=argv[-1]) && strcmp(cp,"--")==0))
344 			sh_argset(ap,argv-1);
345 	}
346 	else if(is_option(&newflags,SH_CFLAG))
347 	{
348 		if(!(ap->sh->comdiv = *argv++))
349 		{
350 			errormsg(SH_DICT,2,e_cneedsarg);
351 			errormsg(SH_DICT,ERROR_usage(2),optusage(NIL(char*)));
352 		}
353 		argc--;
354 	}
355 	/* handling SH_INTERACTIVE and SH_PRIVILEGED has been moved to
356 	 * sh_applyopts(), so that the code can be reused from b_shopt(), too
357 	 */
358 	sh_applyopts(ap->sh,newflags);
359 #if SHOPT_KIA
360 	if(ap->kiafile)
361 	{
362 		if(!argv[0])
363 			errormsg(SH_DICT,ERROR_usage(2),"-R requires scriptname");
364 		if(!(lp->kiafile=sfopen(NIL(Sfio_t*),ap->kiafile,"w+")))
365 			errormsg(SH_DICT,ERROR_system(3),e_create,ap->kiafile);
366 		if(!(lp->kiatmp=sftmp(2*SF_BUFSIZE)))
367 			errormsg(SH_DICT,ERROR_system(3),e_tmpcreate);
368 		sfputr(lp->kiafile,";vdb;CIAO/ksh",'\n');
369 		lp->kiabegin = sftell(lp->kiafile);
370 		lp->entity_tree = dtopen(&_Nvdisc,Dtbag);
371 		lp->scriptname = strdup(sh_fmtq(argv[0]));
372 		lp->script=kiaentity(lp,lp->scriptname,-1,'p',-1,0,0,'s',0,"");
373 		lp->fscript=kiaentity(lp,lp->scriptname,-1,'f',-1,0,0,'s',0,"");
374 		lp->unknown=kiaentity(lp,"<unknown>",-1,'p',-1,0,0,'0',0,"");
375 		kiaentity(lp,"<unknown>",-1,'p',0,0,lp->unknown,'0',0,"");
376 		lp->current = lp->script;
377 		ap->kiafile = 0;
378 	}
379 #endif /* SHOPT_KIA */
380 	return(argc);
381 }
382 
383 /* apply new options */
384 
385 void sh_applyopts(Shell_t* shp,Shopt_t newflags)
386 {
387 	/* cannot set -n for interactive shells since there is no way out */
388 	if(sh_isoption(SH_INTERACTIVE))
389 		off_option(&newflags,SH_NOEXEC);
390 	if(is_option(&newflags,SH_PRIVILEGED))
391 		on_option(&newflags,SH_NOUSRPROFILE);
392 	if(!sh_isstate(SH_INIT) && is_option(&newflags,SH_PRIVILEGED) != sh_isoption(SH_PRIVILEGED) || sh_isstate(SH_INIT) && is_option(&((Arg_t*)shp->arg_context)->sh->offoptions,SH_PRIVILEGED) && shp->userid!=shp->euserid)
393 	{
394 		if(!is_option(&newflags,SH_PRIVILEGED))
395 		{
396 			setuid(shp->userid);
397 			setgid(shp->groupid);
398 			if(shp->euserid==0)
399 			{
400 				shp->euserid = shp->userid;
401 				shp->egroupid = shp->groupid;
402 			}
403 		}
404 		else if((shp->userid!=shp->euserid && setuid(shp->euserid)<0) ||
405 			(shp->groupid!=shp->egroupid && setgid(shp->egroupid)<0) ||
406 			(shp->userid==shp->euserid && shp->groupid==shp->egroupid))
407 				off_option(&newflags,SH_PRIVILEGED);
408 	}
409 #if SHOPT_BASH
410 	on_option(&newflags,SH_CMDHIST);
411 	on_option(&newflags,SH_CHECKHASH);
412 	on_option(&newflags,SH_EXECFAIL);
413 	on_option(&newflags,SH_EXPAND_ALIASES);
414 	on_option(&newflags,SH_HISTAPPEND);
415 	on_option(&newflags,SH_INTERACTIVE_COMM);
416 	on_option(&newflags,SH_LITHIST);
417 	on_option(&newflags,SH_NOEMPTYCMDCOMPL);
418 
419 	if(!is_option(&newflags,SH_XPG_ECHO) && sh_isoption(SH_XPG_ECHO))
420 		astconf("UNIVERSE", 0, "ucb");
421 	if(is_option(&newflags,SH_XPG_ECHO) && !sh_isoption(SH_XPG_ECHO))
422 		astconf("UNIVERSE", 0, "att");
423 	if(!is_option(&newflags,SH_PHYSICAL) && sh_isoption(SH_PHYSICAL))
424 		astconf("PATH_RESOLVE", 0, "metaphysical");
425 	if(is_option(&newflags,SH_PHYSICAL) && !sh_isoption(SH_PHYSICAL))
426 		astconf("PATH_RESOLVE", 0, "physical");
427 	if(is_option(&newflags,SH_HISTORY2) && !sh_isoption(SH_HISTORY2))
428 	{
429 		sh_onstate(SH_HISTORY);
430                 sh_onoption(SH_HISTORY);
431 	}
432 	if(!is_option(&newflags,SH_HISTORY2) && sh_isoption(SH_HISTORY2))
433 	{
434 		sh_offstate(SH_HISTORY);
435 		sh_offoption(SH_HISTORY);
436 	}
437 #endif
438 	shp->options = newflags;
439 }
440 
441 /*
442  * returns the value of $-
443  */
444 char *sh_argdolminus(void* context)
445 {
446 	register Arg_t *ap = (Arg_t*)context;
447 	register const char *cp=optksh;
448 	register char *flagp=ap->flagadr;
449 	while(cp< &optksh[NUM_OPTS])
450 	{
451 		int n = flagval[cp-optksh];
452 		if(sh_isoption(n))
453 			*flagp++ = *cp;
454 		cp++;
455 	}
456 	*flagp = 0;
457 	return(ap->flagadr);
458 }
459 
460 /*
461  * set up positional parameters
462  */
463 static void sh_argset(Arg_t *ap,char *argv[])
464 {
465 	sh_argfree(ap->sh,ap->dolh,0);
466 	ap->dolh = sh_argcreate(argv);
467 	/* link into chain */
468 	ap->dolh->dolnxt = ap->argfor;
469 	ap->argfor = ap->dolh;
470 	ap->sh->st.dolc = ap->dolh->dolnum-1;
471 	ap->sh->st.dolv = ap->dolh->dolval;
472 }
473 
474 /*
475  * free the argument list if the use count is 1
476  * If count is greater than 1 decrement count and return same blk
477  * Free the argument list if the use count is 1 and return next blk
478  * Delete the blk from the argfor chain
479  * If flag is set, then the block dolh is not freed
480  */
481 struct dolnod *sh_argfree(Shell_t *shp, struct dolnod *blk,int flag)
482 {
483 	register struct dolnod*	argr=blk;
484 	register struct dolnod*	argblk;
485 	register Arg_t *ap = (Arg_t*)shp->arg_context;
486 	if(argblk=argr)
487 	{
488 		if((--argblk->dolrefcnt)==0)
489 		{
490 			argr = argblk->dolnxt;
491 			if(flag && argblk==ap->dolh)
492 				ap->dolh->dolrefcnt = 1;
493 			else
494 			{
495 				/* delete from chain */
496 				if(ap->argfor == argblk)
497 					ap->argfor = argblk->dolnxt;
498 				else
499 				{
500 					for(argr=ap->argfor;argr;argr=argr->dolnxt)
501 						if(argr->dolnxt==argblk)
502 							break;
503 					if(!argr)
504 						return(NIL(struct dolnod*));
505 					argr->dolnxt = argblk->dolnxt;
506 					argr = argblk->dolnxt;
507 				}
508 				free((void*)argblk);
509 			}
510 		}
511 	}
512 	return(argr);
513 }
514 
515 /*
516  * grab space for arglist and copy args
517  * The strings are copied after the argment vector
518  */
519 struct dolnod *sh_argcreate(register char *argv[])
520 {
521 	register struct dolnod *dp;
522 	register char **pp=argv, *sp;
523 	register int 	size=0,n;
524 	/* count args and number of bytes of arglist */
525 	while(sp= *pp++)
526 		size += strlen(sp);
527 	n = (pp - argv)-1;
528 	dp=new_of(struct dolnod,n*sizeof(char*)+size+n);
529 	dp->dolrefcnt=1;	/* use count */
530 	dp->dolnum = n;
531 	dp->dolnxt = 0;
532 	pp = dp->dolval;
533 	sp = (char*)dp + sizeof(struct dolnod) + n*sizeof(char*);
534 	while(n--)
535 	{
536 		*pp++ = sp;
537 		sp = strcopy(sp, *argv++) + 1;
538 	}
539 	*pp = NIL(char*);
540 	return(dp);
541 }
542 
543 /*
544  *  used to set new arguments for functions
545  */
546 struct dolnod *sh_argnew(Shell_t *shp,char *argi[], struct dolnod **savargfor)
547 {
548 	register Arg_t *ap = (Arg_t*)shp->arg_context;
549 	register struct dolnod *olddolh = ap->dolh;
550 	*savargfor = ap->argfor;
551 	ap->dolh = 0;
552 	ap->argfor = 0;
553 	sh_argset(ap,argi);
554 	return(olddolh);
555 }
556 
557 /*
558  * reset arguments as they were before function
559  */
560 void sh_argreset(Shell_t *shp,struct dolnod *blk, struct dolnod *afor)
561 {
562 	register Arg_t *ap = (Arg_t*)shp->arg_context;
563 	while(ap->argfor=sh_argfree(shp,ap->argfor,0));
564 	ap->argfor = afor;
565 	if(ap->dolh = blk)
566 	{
567 		shp->st.dolc = ap->dolh->dolnum-1;
568 		shp->st.dolv = ap->dolh->dolval;
569 	}
570 }
571 
572 /*
573  * increase the use count so that an sh_argset will not make it go away
574  */
575 struct dolnod *sh_arguse(Shell_t* shp)
576 {
577 	register struct dolnod *dh;
578 	register Arg_t *ap = (Arg_t*)shp->arg_context;
579 	if(dh=ap->dolh)
580 		dh->dolrefcnt++;
581 	return(dh);
582 }
583 
584 /*
585  *  Print option settings on standard output
586  *  if mode is inclusive or of PRINT_*
587  *  if <mask> is set, only options with this mask value are displayed
588  */
589 void sh_printopts(Shopt_t oflags,register int mode, Shopt_t *mask)
590 {
591 	register const Shtable_t *tp;
592 	const char *name;
593 	int on;
594 	int value;
595 	if(!(mode&PRINT_NO_HEADER))
596 		sfputr(sfstdout,sh_translate(e_heading),'\n');
597 	if(mode&PRINT_TABLE)
598 	{
599 		int	w;
600 		int	c;
601 		int	r;
602 		int	i;
603 
604 		c = 0;
605 		for(tp=shtab_options; value=tp->sh_number; tp++)
606 		{
607 			if(mask && !is_option(mask,value&0xff))
608 				continue;
609 			name = tp->sh_name;
610 			if(name[0] == 'n' && name[1] == 'o' && name[2] != 't')
611 				name += 2;
612 			if(c<(w=strlen(name)))
613 				c = w;
614 		}
615 		c += 4;
616 		if((w = ed_window()) < (2*c))
617 			w = 2*c;
618 		r = w / c;
619 		i = 0;
620 		for(tp=shtab_options; value=tp->sh_number; tp++)
621 		{
622 			if(mask && !is_option(mask,value&0xff))
623 				continue;
624 			on = !!is_option(&oflags,value);
625 			value &= 0xff;
626 			name = tp->sh_name;
627 			if(name[0] == 'n' && name[1] == 'o' && name[2] != 't')
628 			{
629 				name += 2;
630 				on = !on;
631 			}
632 			if(++i>=r)
633 			{
634 				i = 0;
635 				sfprintf(sfstdout, "%s%s\n", on ? "" : "no", name);
636 			}
637 			else
638 				sfprintf(sfstdout, "%s%-*s", on ? "" : "no", on ? c : (c-2), name);
639 		}
640 		if(i)
641 			sfputc(sfstdout,'\n');
642 		return;
643 	}
644 #if SHOPT_RAWONLY
645 	on_option(&oflags,SH_VIRAW);
646 #endif
647 	if(!(mode&(PRINT_ALL|PRINT_VERBOSE))) /* only print set options */
648 	{
649 		if(mode&PRINT_SHOPT)
650 			sfwrite(sfstdout,"shopt -s",3);
651 		else
652 			sfwrite(sfstdout,"set --default",13);
653 	}
654 	for(tp=shtab_options; value=tp->sh_number; tp++)
655 	{
656 		if(mask && !is_option(mask,value&0xff))
657 			continue;
658 		if(sh_isoption(SH_BASH))
659 		{
660 			if (!(mode&PRINT_SHOPT) != !(value&SH_BASHOPT))
661 				continue;
662 		}
663 		else if (value&(SH_BASHEXTRA|SH_BASHOPT))
664 			continue;
665 		on = !!is_option(&oflags,value);
666 		name = tp->sh_name;
667 		if(name[0] == 'n' && name[1] == 'o' && name[2] != 't')
668 		{
669 			name += 2;
670 			on = !on;
671 		}
672 		if(mode&PRINT_VERBOSE)
673 		{
674 			sfputr(sfstdout,name,' ');
675 			sfnputc(sfstdout,' ',24-strlen(name));
676 			sfputr(sfstdout,on ? sh_translate(e_on) : sh_translate(e_off),'\n');
677 		}
678 		else if(mode&PRINT_ALL) /* print unset options also */
679 		{
680 			if(mode&PRINT_SHOPT)
681 				sfprintf(sfstdout, "shopt -%c %s\n",
682 					on?'s':'u',
683 					name);
684 			else
685 				sfprintf(sfstdout, "set %co %s\n",
686 					on?'-':'+',
687 					name);
688 		}
689 		else if(!(value&SH_COMMANDLINE) && is_option(&oflags,value&0xff))
690 			sfprintf(sfstdout," %s%s%s",(mode&PRINT_SHOPT)?"":"--",on?"":"no",name);
691 	}
692 	if(!(mode&(PRINT_VERBOSE|PRINT_ALL)))
693 		sfputc(sfstdout,'\n');
694 }
695 
696 /*
697  * build an argument list
698  */
699 char **sh_argbuild(Shell_t *shp,int *nargs, const struct comnod *comptr,int flag)
700 {
701 	register struct argnod	*argp;
702 	struct argnod *arghead=0;
703 	shp->xargmin = 0;
704 	{
705 		register const struct comnod	*ac = comptr;
706 		register int n;
707 		/* see if the arguments have already been expanded */
708 		if(!ac->comarg)
709 		{
710 			*nargs = 0;
711 			return(&null);
712 		}
713 		else if(!(ac->comtyp&COMSCAN))
714 		{
715 			register struct dolnod *ap = (struct dolnod*)ac->comarg;
716 			*nargs = ap->dolnum;
717 			return(ap->dolval+ap->dolbot);
718 		}
719 		shp->lastpath = 0;
720 		*nargs = 0;
721 		if(ac)
722 		{
723 			if(ac->comnamp == SYSLET)
724 				flag |= ARG_LET;
725 			argp = ac->comarg;
726 			while(argp)
727 			{
728 				n = arg_expand(shp,argp,&arghead,flag);
729 				if(n>1)
730 				{
731 					if(shp->xargmin==0)
732 						shp->xargmin = *nargs;
733 					shp->xargmax = *nargs+n;
734 				}
735 				*nargs += n;
736 				argp = argp->argnxt.ap;
737 			}
738 			argp = arghead;
739 		}
740 	}
741 	{
742 		register char	**comargn;
743 		register int	argn;
744 		register char	**comargm;
745 		argn = *nargs;
746 		/* allow room to prepend args */
747 		argn += 1;
748 
749 		comargn=(char**)stkalloc(shp->stk,(unsigned)(argn+1)*sizeof(char*));
750 		comargm = comargn += argn;
751 		*comargn = NIL(char*);
752 		if(!argp)
753 		{
754 			/* reserve an extra null pointer */
755 			*--comargn = 0;
756 			return(comargn);
757 		}
758 		while(argp)
759 		{
760 			struct argnod *nextarg = argp->argchn.ap;
761 			argp->argchn.ap = 0;
762 			*--comargn = argp->argval;
763 			if(!(argp->argflag&ARG_RAW))
764 				sh_trim(*comargn);
765 			if(!(argp=nextarg) || (argp->argflag&ARG_MAKE))
766 			{
767 				if((argn=comargm-comargn)>1)
768 					strsort(comargn,argn,strcoll);
769 				comargm = comargn;
770 			}
771 		}
772 		shp->last_table = 0;
773 		return(comargn);
774 	}
775 }
776 
777 #if _pipe_socketpair && !_socketpair_devfd
778 #   define sh_pipe	arg_pipe
779 /*
780  * create a real pipe (not a socket) and print message on failure
781  */
782 static int	arg_pipe(register int pv[])
783 {
784 	int fd[2];
785 	if(pipe(fd)<0 || (pv[0]=fd[0])<0 || (pv[1]=fd[1])<0)
786 		errormsg(SH_DICT,ERROR_system(1),e_pipe);
787 	pv[0] = sh_iomovefd(pv[0]);
788 	pv[1] = sh_iomovefd(pv[1]);
789 	sh.fdstatus[pv[0]] = IONOSEEK|IOREAD;
790 	sh.fdstatus[pv[1]] = IONOSEEK|IOWRITE;
791 	sh_subsavefd(pv[0]);
792 	sh_subsavefd(pv[1]);
793 	return(0);
794 }
795 #endif
796 
797 struct argnod *sh_argprocsub(Shell_t *shp,struct argnod *argp)
798 {
799 	/* argument of the form <(cmd) or >(cmd) */
800 	register struct argnod *ap;
801 	int monitor, fd, pv[2];
802 	int subshell = shp->subshell;
803 	ap = (struct argnod*)stkseek(shp->stk,ARGVAL);
804 	ap->argflag |= ARG_MAKE;
805 	ap->argflag &= ~ARG_RAW;
806 	sfwrite(shp->stk,e_devfdNN,8);
807 	sh_pipe(pv);
808 	fd = argp->argflag&ARG_RAW;
809 	sfputr(shp->stk,fmtbase((long)pv[fd],10,0),0);
810 	ap = (struct argnod*)stkfreeze(shp->stk,0);
811 	shp->inpipe = shp->outpipe = 0;
812 	if(monitor = (sh_isstate(SH_MONITOR)!=0))
813 		sh_offstate(SH_MONITOR);
814 	shp->subshell = 0;
815 	if(fd)
816 	{
817 		shp->inpipe = pv;
818 		sh_exec((Shnode_t*)argp->argchn.ap,(int)sh_isstate(SH_ERREXIT));
819 	}
820 	else
821 	{
822 		shp->outpipe = pv;
823 		sh_exec((Shnode_t*)argp->argchn.ap,(int)sh_isstate(SH_ERREXIT));
824 	}
825 	shp->subshell = subshell;
826 	if(monitor)
827 		sh_onstate(SH_MONITOR);
828 	close(pv[1-fd]);
829 	sh_iosave(shp,-pv[fd], shp->topfd, (char*)0);
830 	return(ap);
831 }
832 
833 /* Argument expansion */
834 static int arg_expand(Shell_t *shp,register struct argnod *argp, struct argnod **argchain,int flag)
835 {
836 	register int count = 0;
837 	argp->argflag &= ~ARG_MAKE;
838 #if SHOPT_DEVFD
839 	if(*argp->argval==0 && (argp->argflag&ARG_EXP))
840 	{
841 		struct argnod *ap;
842 		ap = sh_argprocsub(shp,argp);
843 		ap->argchn.ap = *argchain;
844 		*argchain = ap;
845 		count++;
846 	}
847 	else
848 #endif	/* SHOPT_DEVFD */
849 	if(!(argp->argflag&ARG_RAW))
850 	{
851 #if SHOPT_OPTIMIZE
852 		struct argnod *ap;
853 		sh_stats(STAT_ARGEXPAND);
854 		if(flag&ARG_OPTIMIZE)
855 			argp->argchn.ap=0;
856 		if(ap=argp->argchn.ap)
857 		{
858 			sh_stats(STAT_ARGHITS);
859 			count = 1;
860 			ap->argchn.ap = *argchain;
861 			ap->argflag |= ARG_RAW;
862 			ap->argflag &= ~ARG_EXP;
863 			*argchain = ap;
864 		}
865 		else
866 #endif /* SHOPT_OPTIMIZE */
867 		count = sh_macexpand(shp,argp,argchain,flag);
868 	}
869 	else
870 	{
871 		argp->argchn.ap = *argchain;
872 		*argchain = argp;
873 		argp->argflag |= ARG_MAKE;
874 		count++;
875 	}
876 	return(count);
877 }
878 
879