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