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