xref: /netbsd/bin/ksh/exec.c (revision 822c36bb)
1 /*	$NetBSD: exec.c,v 1.28 2018/06/03 12:18:29 kamil Exp $	*/
2 
3 /*
4  * execute command tree
5  */
6 #include <sys/cdefs.h>
7 
8 #ifndef lint
9 __RCSID("$NetBSD: exec.c,v 1.28 2018/06/03 12:18:29 kamil Exp $");
10 #endif
11 
12 #include <sys/stat.h>
13 #include <ctype.h>
14 #include <stdbool.h>
15 
16 #include "sh.h"
17 #include "c_test.h"
18 
19 /* Does ps4 get parameter substitutions done? */
20 #ifdef KSH
21 # define PS4_SUBSTITUTE(s)	substitute((s), 0)
22 #else
23 # define PS4_SUBSTITUTE(s)	(s)
24 #endif /* KSH */
25 
26 static int	comexec	 ARGS((struct op *, struct tbl *volatile, char **,
27 			      int volatile));
28 static void	scriptexec ARGS((struct op *, char **));
29 static int	call_builtin ARGS((struct tbl *, char **));
30 static int	iosetup ARGS((struct ioword *, struct tbl *));
31 static int	herein ARGS((const char *, int));
32 #ifdef KSH
33 static char 	*do_selectargs(char **, bool);
34 #endif /* KSH */
35 #ifdef KSH
36 static int	dbteste_isa ARGS((Test_env *, Test_meta));
37 static const char *dbteste_getopnd ARGS((Test_env *, Test_op, int));
38 static int	dbteste_eval ARGS((Test_env *, Test_op, const char *,
39 				const char *, int));
40 static void	dbteste_error ARGS((Test_env *, int, const char *));
41 #endif /* KSH */
42 
43 /*
44  * handle systems that don't have F_SETFD
45  */
46 #ifndef F_SETFD
47 # ifndef MAXFD
48 #   define  MAXFD 64
49 # endif
50 /* a bit field would be smaller, but this will work */
51 static char clexec_tab[MAXFD+1];
52 #endif
53 
54 /*
55  * we now use this function always.
56  */
57 int
fd_clexec(fd)58 fd_clexec(fd)
59     int fd;
60 {
61 #ifndef F_SETFD
62 	if (fd >= 0 && fd < sizeof(clexec_tab)) {
63 		clexec_tab[fd] = 1;
64 		return 0;
65 	}
66 	return -1;
67 #else
68 	return fcntl(fd, F_SETFD, 1);
69 #endif
70 }
71 
72 
73 /*
74  * execute command tree
75  */
76 int
execute(t,flags)77 execute(t, flags)
78 	struct op * volatile t;
79 	volatile int flags;	/* if XEXEC don't fork */
80 {
81 	int i;
82 	volatile int rv = 0;
83 	int pv[2];
84 	char ** volatile ap;
85 	char *s, *cp;
86 	struct ioword **iowp;
87 	struct tbl *tp = NULL;
88 
89 	if (t == NULL)
90 		return 0;
91 
92 	/* Is this the end of a pipeline?  If so, we want to evaluate the
93 	 * command arguments
94 	bool eval_done = false;
95 	if ((flags&XFORK) && !(flags&XEXEC) && (flags&XPCLOSE)) {
96 		eval_done = true;
97 		tp = eval_execute_args(t, &ap);
98 	}
99 	 */
100 	if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE)
101 		return exchild(t, flags & ~XTIME, -1); /* run in sub-process */
102 
103 	newenv(E_EXEC);
104 	if (trap)
105 		runtraps(0);
106 
107 	if (t->type == TCOM) {
108 		/* Clear subst_exstat before argument expansion.  Used by
109 		 * null commands (see comexec() and c_eval()) and by c_set().
110 		 */
111 		subst_exstat = 0;
112 
113 		current_lineno = t->lineno;	/* for $LINENO */
114 
115 		/* POSIX says expand command words first, then redirections,
116 		 * and assignments last..
117 		 */
118 		ap = eval(t->args, t->u.evalflags | DOBLANK | DOGLOB | DOTILDE);
119 		if (flags & XTIME)
120 			/* Allow option parsing (bizarre, but POSIX) */
121 			timex_hook(t, &ap);
122 		if (Flag(FXTRACE) && ap[0]) {
123 			shf_fprintf(shl_out, "%s",
124 				PS4_SUBSTITUTE(str_val(global("PS4"))));
125 			for (i = 0; ap[i]; i++)
126 				shf_fprintf(shl_out, "%s%s", ap[i],
127 					ap[i + 1] ? space : newline);
128 			shf_flush(shl_out);
129 		}
130 		if (ap[0])
131 			tp = findcom(ap[0], FC_BI|FC_FUNC);
132 	}
133 	flags &= ~XTIME;
134 
135 	if (t->ioact != NULL || t->type == TPIPE || t->type == TCOPROC) {
136 		e->savefd = (short *) alloc(sizeofN(short, NUFILE), ATEMP);
137 		/* initialize to not redirected */
138 		memset(e->savefd, 0, sizeofN(short, NUFILE));
139 	}
140 
141 	/* do redirection, to be restored in quitenv() */
142 	if (t->ioact != NULL)
143 		for (iowp = t->ioact; *iowp != NULL; iowp++) {
144 			if (iosetup(*iowp, tp) < 0) {
145 				exstat = rv = 1;
146 				/* Redirection failures for special commands
147 				 * cause (non-interactive) shell to exit.
148 				 */
149 				if (tp && tp->type == CSHELL
150 				    && (tp->flag & SPEC_BI))
151 					errorf("%s", null);
152 				/* Deal with FERREXIT, quitenv(), etc. */
153 				goto Break;
154 			}
155 		}
156 
157 	switch(t->type) {
158 	  case TCOM:
159 		rv = comexec(t, tp, ap, flags);
160 		break;
161 
162 	  case TPAREN:
163 		rv = execute(t->left, flags|XFORK);
164 		break;
165 
166 	  case TPIPE:
167 		flags |= XFORK;
168 		flags &= ~XEXEC;
169 		e->savefd[0] = savefd(0, 0);
170 		(void) ksh_dup2(e->savefd[0], 0, false); /* stdin of first */
171 		e->savefd[1] = savefd(1, 0);
172 		while (t->type == TPIPE) {
173 			openpipe(pv);
174 			(void) ksh_dup2(pv[1], 1, false); /* stdout of curr */
175 			/* Let exchild() close pv[0] in child
176 			 * (if this isn't done, commands like
177 			 *    (: ; cat /etc/termcap) | sleep 1
178 			 *  will hang forever).
179 			 */
180 			exchild(t->left, flags|XPIPEO|XCCLOSE, pv[0]);
181 			(void) ksh_dup2(pv[0], 0, false); /* stdin of next */
182 			closepipe(pv);
183 			flags |= XPIPEI;
184 			t = t->right;
185 		}
186 		restfd(1, e->savefd[1]); /* stdout of last */
187 		e->savefd[1] = 0; /* no need to re-restore this */
188 		/* Let exchild() close 0 in parent, after fork, before wait */
189 		i = exchild(t, flags|XPCLOSE, 0);
190 		if (!(flags&XBGND) && !(flags&XXCOM))
191 			rv = i;
192 		break;
193 
194 	  case TLIST:
195 		while (t->type == TLIST) {
196 			execute(t->left, flags & XERROK);
197 			t = t->right;
198 		}
199 		rv = execute(t, flags & XERROK);
200 		break;
201 
202 #ifdef KSH
203 	  case TCOPROC:
204 	  {
205 		sigset_t	omask;
206 
207 		/* Block sigchild as we are using things changed in the
208 		 * signal handler
209 		 */
210 		sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
211 		e->type = E_ERRH;
212 		i = ksh_sigsetjmp(e->jbuf, 0);
213 		if (i) {
214 			sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
215 			quitenv();
216 			unwind(i);
217 			/*NOTREACHED*/
218 		}
219 
220 		/* Already have a (live) co-process? */
221 		if (coproc.job && coproc.write >= 0)
222 			errorf("coprocess already exists");
223 
224 		/* Can we re-use the existing co-process pipe? */
225 		coproc_cleanup(true);
226 
227 		/* do this before opening pipes, in case these fail */
228 		e->savefd[0] = savefd(0, 0);
229 		e->savefd[1] = savefd(1, 0);
230 
231 		openpipe(pv);
232 		if (pv[0] != 0) {
233 			ksh_dup2(pv[0], 0, false);
234 			close(pv[0]);
235 		}
236 		coproc.write = pv[1];
237 		coproc.job = (void *) 0;
238 
239 		if (coproc.readw >= 0)
240 			ksh_dup2(coproc.readw, 1, false);
241 		else {
242 			openpipe(pv);
243 			coproc.read = pv[0];
244 			ksh_dup2(pv[1], 1, false);
245 			coproc.readw = pv[1];	 /* closed before first read */
246 			coproc.njobs = 0;
247 			/* create new coprocess id */
248 			++coproc.id;
249 		}
250 
251 		sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
252 		e->type = E_EXEC; /* no more need for error handler */
253 
254 		/* exchild() closes coproc.* in child after fork,
255 		 * will also increment coproc.njobs when the
256 		 * job is actually created.
257 		 */
258 		flags &= ~XEXEC;
259 		exchild(t->left, flags|XBGND|XFORK|XCOPROC|XCCLOSE,
260 			coproc.readw);
261 		break;
262 	  }
263 #endif /* KSH */
264 
265 	  case TASYNC:
266 		/* XXX non-optimal, I think - "(foo &)", forks for (),
267 		 * forks again for async...  parent should optimize
268 		 * this to "foo &"...
269 		 */
270 		rv = execute(t->left, (flags&~XEXEC)|XBGND|XFORK);
271 		break;
272 
273 	  case TOR:
274 	  case TAND:
275 		rv = execute(t->left, XERROK);
276 		if (t->right != NULL && (rv == 0) == (t->type == TAND))
277 			rv = execute(t->right, flags & XERROK);
278 		else
279 			flags |= XERROK;
280 		break;
281 
282 	  case TBANG:
283 		rv = !execute(t->right, XERROK);
284 		break;
285 
286 #ifdef KSH
287 	  case TDBRACKET:
288 	    {
289 		Test_env te;
290 
291 		te.flags = TEF_DBRACKET;
292 		te.pos.wp = t->args;
293 		te.isa = dbteste_isa;
294 		te.getopnd = dbteste_getopnd;
295 		te.eval = dbteste_eval;
296 		te.error = dbteste_error;
297 
298 		rv = test_parse(&te);
299 		break;
300 	    }
301 #endif /* KSH */
302 
303 	  case TFOR:
304 #ifdef KSH
305 	  case TSELECT:
306 	    {
307 		volatile bool is_first = true;
308 #endif /* KSH */
309 		ap = (t->vars != NULL) ?
310 			  eval(t->vars, DOBLANK|DOGLOB|DOTILDE)
311 			: e->loc->argv + 1;
312 		e->type = E_LOOP;
313 		while (1) {
314 			i = ksh_sigsetjmp(e->jbuf, 0);
315 			if (!i)
316 				break;
317 			if ((e->flags&EF_BRKCONT_PASS)
318 			    || (i != LBREAK && i != LCONTIN))
319 			{
320 				quitenv();
321 				unwind(i);
322 			} else if (i == LBREAK) {
323 				rv = 0;
324 				goto Break;
325 			}
326 		}
327 		rv = 0; /* in case of a continue */
328 		if (t->type == TFOR) {
329 			while (*ap != NULL) {
330 				setstr(global(t->str), *ap++, KSH_UNWIND_ERROR);
331 				rv = execute(t->left, flags & XERROK);
332 			}
333 		}
334 #ifdef KSH
335 		else { /* TSELECT */
336 			for (;;) {
337 				if (!(cp = do_selectargs(ap, is_first))) {
338 					rv = 1;
339 					break;
340 				}
341 				is_first = false;
342 				setstr(global(t->str), cp, KSH_UNWIND_ERROR);
343 				rv = execute(t->left, flags & XERROK);
344 			}
345 		}
346 	    }
347 #endif /* KSH */
348 		break;
349 
350 	  case TWHILE:
351 	  case TUNTIL:
352 		e->type = E_LOOP;
353 		while (1) {
354 			i = ksh_sigsetjmp(e->jbuf, 0);
355 			if (!i)
356 				break;
357 			if ((e->flags&EF_BRKCONT_PASS)
358 			    || (i != LBREAK && i != LCONTIN))
359 			{
360 				quitenv();
361 				unwind(i);
362 			} else if (i == LBREAK) {
363 				rv = 0;
364 				goto Break;
365 			}
366 		}
367 		rv = 0; /* in case of a continue */
368 		while ((execute(t->left, XERROK) == 0) == (t->type == TWHILE))
369 			rv = execute(t->right, flags & XERROK);
370 		break;
371 
372 	  case TIF:
373 	  case TELIF:
374 		if (t->right == NULL)
375 			break;	/* should be error */
376 		rv = execute(t->left, XERROK) == 0 ?
377 			execute(t->right->left, flags & XERROK) :
378 			execute(t->right->right, flags & XERROK);
379 		break;
380 
381 	  case TCASE:
382 		cp = evalstr(t->str, DOTILDE);
383 		for (t = t->left; t != NULL && t->type == TPAT; t = t->right)
384 		    for (ap = t->vars; *ap; ap++)
385 			if ((s = evalstr(*ap, DOTILDE|DOPAT))
386 			    && gmatch(cp, s, false))
387 				goto Found;
388 		break;
389 	  Found:
390 		rv = execute(t->left, flags & XERROK);
391 		break;
392 
393 	  case TBRACE:
394 		rv = execute(t->left, flags & XERROK);
395 		break;
396 
397 	  case TFUNCT:
398 		rv = define(t->str, t);
399 		break;
400 
401 	  case TTIME:
402 		/* Clear XEXEC so nested execute() call doesn't exit
403 		 * (allows "ls -l | time grep foo").
404 		 */
405 		rv = timex(t, flags & ~XEXEC);
406 		break;
407 
408 	  case TEXEC:		/* an eval'd TCOM */
409 		s = t->args[0];
410 		ap = makenv();
411 #ifndef F_SETFD
412 		for (i = 0; i < sizeof(clexec_tab); i++)
413 			if (clexec_tab[i]) {
414 				close(i);
415 				clexec_tab[i] = 0;
416 			}
417 #endif
418 		restoresigs();
419 		cleanup_proc_env();
420 		execve(t->str, t->args, ap);
421 		if (errno == ENOEXEC)
422 			scriptexec(t, ap);
423 		else
424 			errorf("%s: %s", s, strerror(errno));
425 	}
426     Break:
427 	exstat = rv;
428 
429 	quitenv();		/* restores IO */
430 	if ((flags&XEXEC))
431 		unwind(LEXIT);	/* exit child */
432 	if (rv != 0 && !(flags & XERROK)) {
433 		if (Flag(FERREXIT))
434 			unwind(LERROR);
435 		trapsig(SIGERR_);
436 	}
437 	return rv;
438 }
439 
440 /*
441  * execute simple command
442  */
443 
444 static int
comexec(t,tp,ap,flags)445 comexec(t, tp, ap, flags)
446 	struct op *t;
447 	struct tbl *volatile tp;
448 	char **ap;
449 	int volatile flags;
450 {
451 	int i;
452 	int leave = LLEAVE;
453 	volatile int rv = 0;
454 	char *cp;
455 	char **lastp;
456 	static struct op texec; /* Must be static (XXX but why?) */
457 	int type_flags;
458 	int keepasn_ok;
459 	int fcflags = FC_BI|FC_FUNC|FC_PATH;
460 	int bourne_function_call = 0;
461 
462 #ifdef KSH
463 	/* snag the last argument for $_ XXX not the same as at&t ksh,
464 	 * which only seems to set $_ after a newline (but not in
465 	 * functions/dot scripts, but in interactive and script) -
466 	 * perhaps save last arg here and set it in shell()?.
467 	 */
468 	if (Flag(FTALKING) && *(lastp = ap)) {
469 		while (*++lastp)
470 			;
471 		/* setstr() can't fail here */
472 		setstr(typeset("_", LOCAL, 0, INTEGER, 0), *--lastp,
473 		       KSH_RETURN_ERROR);
474 	}
475 #endif /* KSH */
476 
477 	/* Deal with the shell builtins builtin, exec and command since
478 	 * they can be followed by other commands.  This must be done before
479 	 * we know if we should create a local block, which must be done
480 	 * before we can do a path search (in case the assignments change
481 	 * PATH).
482 	 * Odd cases:
483 	 *   FOO=bar exec > /dev/null		FOO is kept but not exported
484 	 *   FOO=bar exec foobar		FOO is exported
485 	 *   FOO=bar command exec > /dev/null	FOO is neither kept nor exported
486 	 *   FOO=bar command			FOO is neither kept nor exported
487 	 *   PATH=... foobar			use new PATH in foobar search
488 	 */
489 	keepasn_ok = 1;
490 	while (tp && tp->type == CSHELL) {
491 		fcflags = FC_BI|FC_FUNC|FC_PATH;/* undo effects of command */
492 		if (tp->val.f == c_builtin) {
493 			if ((cp = *++ap) == NULL) {
494 				tp = NULL;
495 				break;
496 			}
497 			tp = findcom(cp, FC_BI);
498 			if (tp == NULL)
499 				errorf("builtin: %s: not a builtin", cp);
500 			continue;
501 		} else if (tp->val.f == c_exec) {
502 			if (ap[1] == NULL)
503 				break;
504 			ap++;
505 			flags |= XEXEC;
506 		} else if (tp->val.f == c_command) {
507 			int optc, saw_p = 0;
508 
509 			/* Ugly dealing with options in two places (here and
510 			 * in c_command(), but such is life)
511 			 */
512 			ksh_getopt_reset(&builtin_opt, 0);
513 			while ((optc = ksh_getopt(ap, &builtin_opt, ":p"))
514 									== 'p')
515 				saw_p = 1;
516 			if (optc != EOF)
517 				break;	/* command -vV or something */
518 			/* don't look for functions */
519 			fcflags = FC_BI|FC_PATH;
520 			if (saw_p) {
521 				if (Flag(FRESTRICTED)) {
522 					warningf(true,
523 						"command -p: restricted");
524 					rv = 1;
525 					goto Leave;
526 				}
527 				fcflags |= FC_DEFPATH;
528 			}
529 			ap += builtin_opt.optind;
530 			/* POSIX says special builtins lose their status
531 			 * if accessed using command.
532 			 */
533 			keepasn_ok = 0;
534 			if (!ap[0]) {
535 				/* ensure command with no args exits with 0 */
536 				subst_exstat = 0;
537 				break;
538 			}
539 		} else
540 			break;
541 		tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC));
542 	}
543 	if (keepasn_ok && (!ap[0] || (tp && (tp->flag & KEEPASN))))
544 		type_flags = 0;
545 	else {
546 		/* create new variable/function block */
547 		newblock();
548 		/* ksh functions don't keep assignments, POSIX functions do. */
549 		if (keepasn_ok && tp && tp->type == CFUNC
550 		    && !(tp->flag & FKSH)) {
551 			bourne_function_call = 1;
552 			type_flags = 0;
553 		} else
554 			type_flags = LOCAL|LOCAL_COPY|EXPORT;
555 	}
556 	if (Flag(FEXPORT))
557 		type_flags |= EXPORT;
558 	for (i = 0; t->vars[i]; i++) {
559 		cp = evalstr(t->vars[i], DOASNTILDE);
560 		if (Flag(FXTRACE)) {
561 			if (i == 0)
562 				shf_fprintf(shl_out, "%s",
563 					PS4_SUBSTITUTE(str_val(global("PS4"))));
564 			shf_fprintf(shl_out, "%s%s", cp,
565 				t->vars[i + 1] ? space : newline);
566 			if (!t->vars[i + 1])
567 				shf_flush(shl_out);
568 		}
569 		typeset(cp, type_flags, 0, 0, 0);
570 		if (bourne_function_call && !(type_flags & EXPORT))
571 			typeset(cp, LOCAL|LOCAL_COPY|EXPORT, 0, 0, 0);
572 	}
573 
574 	if ((cp = *ap) == NULL) {
575 		rv = subst_exstat;
576 		goto Leave;
577 	} else if (!tp) {
578 		if (Flag(FRESTRICTED) && ksh_strchr_dirsep(cp)) {
579 			warningf(true, "%s: restricted", cp);
580 			rv = 1;
581 			goto Leave;
582 		}
583 		tp = findcom(cp, fcflags);
584 	}
585 
586 	switch (tp->type) {
587 	  case CSHELL:			/* shell built-in */
588 		rv = call_builtin(tp, ap);
589 		break;
590 
591 	  case CFUNC:			/* function call */
592 	  {
593 		volatile int old_xflag;
594 		volatile Tflag old_inuse;
595 		const char *volatile old_kshname;
596 
597 		if (!(tp->flag & ISSET)) {
598 			struct tbl *ftp;
599 
600 			if (!tp->u.fpath) {
601 				if (tp->u2.errno_) {
602 					warningf(true,
603 				"%s: can't find function definition file - %s",
604 						cp, strerror(tp->u2.errno_));
605 					rv = 126;
606 				} else {
607 					warningf(true,
608 				"%s: can't find function definition file", cp);
609 					rv = 127;
610 				}
611 				break;
612 			}
613 			if (include(tp->u.fpath, 0, (char **) 0, 0) < 0) {
614 				warningf(true,
615 			    "%s: can't open function definition file %s - %s",
616 					cp, tp->u.fpath, strerror(errno));
617 				rv = 127;
618 				break;
619 			}
620 			if (!(ftp = findfunc(cp, hash(cp), false))
621 			    || !(ftp->flag & ISSET))
622 			{
623 				warningf(true,
624 					"%s: function not defined by %s",
625 					cp, tp->u.fpath);
626 				rv = 127;
627 				break;
628 			}
629 			tp = ftp;
630 		}
631 
632 		/* ksh functions set $0 to function name, POSIX functions leave
633 		 * $0 unchanged.
634 		 */
635 		old_kshname = kshname;
636 		if (tp->flag & FKSH)
637 			kshname = ap[0];
638 		else
639 			ap[0] = (char *) __UNCONST(kshname);
640 		e->loc->argv = ap;
641 		for (i = 0; *ap++ != NULL; i++)
642 			;
643 		e->loc->argc = i - 1;
644 		/* ksh-style functions handle getopts sanely,
645 		 * bourne/posix functions are insane...
646 		 */
647 		if (tp->flag & FKSH) {
648 			e->loc->flags |= BF_DOGETOPTS;
649 			e->loc->getopts_state = user_opt;
650 			getopts_reset(1);
651 		}
652 
653 		old_xflag = Flag(FXTRACE);
654 		Flag(FXTRACE) = tp->flag & TRACE ? true : false;
655 
656 		old_inuse = tp->flag & FINUSE;
657 		tp->flag |= FINUSE;
658 
659 		e->type = E_FUNC;
660 		i = ksh_sigsetjmp(e->jbuf, 0);
661 		if (i == 0) {
662 			/* seems odd to pass XERROK here, but at&t ksh does */
663 			exstat = execute(tp->val.t, flags & XERROK);
664 			i = LRETURN;
665 		}
666 		kshname = old_kshname;
667 		Flag(FXTRACE) = old_xflag;
668 		tp->flag = (tp->flag & ~FINUSE) | old_inuse;
669 		/* Were we deleted while executing?  If so, free the execution
670 		 * tree.  todo: Unfortunately, the table entry is never re-used
671 		 * until the lookup table is expanded.
672 		 */
673 		if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) {
674 			if (tp->flag & ALLOC) {
675 				tp->flag &= ~ALLOC;
676 				tfree(tp->val.t, tp->areap);
677 			}
678 			tp->flag = 0;
679 		}
680 		switch (i) {
681 		  case LRETURN:
682 		  case LERROR:
683 			rv = exstat;
684 			break;
685 		  case LINTR:
686 		  case LEXIT:
687 		  case LLEAVE:
688 		  case LSHELL:
689 			quitenv();
690 			unwind(i);
691 			/*NOTREACHED*/
692 		  default:
693 			quitenv();
694 			internal_errorf(1, "CFUNC %d", i);
695 		}
696 		break;
697 	  }
698 
699 	  case CEXEC:		/* executable command */
700 	  case CTALIAS:		/* tracked alias */
701 		if (!(tp->flag&ISSET)) {
702 			/* errno_ will be set if the named command was found
703 			 * but could not be executed (permissions, no execute
704 			 * bit, directory, etc).  Print out a (hopefully)
705 			 * useful error message and set the exit status to 126.
706 			 */
707 			if (tp->u2.errno_) {
708 				warningf(true, "%s: cannot execute - %s", cp,
709 					strerror(tp->u2.errno_));
710 				rv = 126;	/* POSIX */
711 			} else {
712 				warningf(true, "%s: not found", cp);
713 				rv = 127;
714 			}
715 			break;
716 		}
717 
718 #ifdef KSH
719 		/* set $_ to program's full path */
720 		/* setstr() can't fail here */
721 		setstr(typeset("_", LOCAL|EXPORT, 0, INTEGER, 0),
722 		       tp->val.s, KSH_RETURN_ERROR);
723 #endif /* KSH */
724 
725 		if (flags&XEXEC) {
726 			j_exit();
727 			if (!(flags&XBGND) || Flag(FMONITOR)) {
728 				setexecsig(&sigtraps[SIGINT], SS_RESTORE_ORIG);
729 				setexecsig(&sigtraps[SIGQUIT], SS_RESTORE_ORIG);
730 			}
731 		}
732 
733 		/* to fork we set up a TEXEC node and call execute */
734 		texec.type = TEXEC;
735 		texec.left = t;	/* for tprint */
736 		texec.str = tp->val.s;
737 		texec.args = ap;
738 		rv = exchild(&texec, flags, -1);
739 		break;
740 	}
741 	leave = LEXIT;
742   Leave:
743 	if (flags & XEXEC) {
744 		exstat = rv;
745 		unwind(leave);
746 	}
747 	return rv;
748 }
749 
750 static void
scriptexec(tp,ap)751 scriptexec(tp, ap)
752 	struct op *tp;
753 	char **ap;
754 {
755 	char *shellv;
756 
757 	shellv = str_val(global(EXECSHELL_STR));
758 	if (shellv && *shellv)
759 		shellv = search(shellv, path, X_OK, (int *) 0);
760 	if (!shellv || !*shellv)
761 		shellv = __UNCONST(EXECSHELL);
762 
763 	*tp->args-- = tp->str;
764 	*tp->args = shellv;
765 
766 	execve(tp->args[0], tp->args, ap);
767 
768 	/* report both the program that was run and the bogus shell */
769 	errorf("%s: %s: %s", tp->str, shellv, strerror(errno));
770 }
771 
772 int
shcomexec(wp)773 shcomexec(wp)
774 	char **wp;
775 {
776 	struct tbl *tp;
777 
778 	tp = mytsearch(&builtins, *wp, hash(*wp));
779 	if (tp == NULL)
780 		internal_errorf(1, "shcomexec: %s", *wp);
781 	return call_builtin(tp, wp);
782 }
783 
784 /*
785  * Search function tables for a function.  If create set, a table entry
786  * is created if none is found.
787  */
788 struct tbl *
findfunc(name,h,create)789 findfunc(name, h, create)
790 	const char *name;
791 	unsigned int h;
792 	int create;
793 {
794 	struct block *l;
795 	struct tbl *tp = (struct tbl *) 0;
796 
797 	for (l = e->loc; l; l = l->next) {
798 		tp = mytsearch(&l->funs, name, h);
799 		if (tp)
800 			break;
801 		if (!l->next && create) {
802 			tp = tenter(&l->funs, name, h);
803 			tp->flag = DEFINED;
804 			tp->type = CFUNC;
805 			tp->val.t = (struct op *) 0;
806 			break;
807 		}
808 	}
809 	return tp;
810 }
811 
812 /*
813  * define function.  Returns 1 if function is being undefined (t == 0) and
814  * function did not exist, returns 0 otherwise.
815  */
816 int
define(name,t)817 define(name, t)
818 	const char *name;
819 	struct op *t;
820 {
821 	struct tbl *tp;
822 	int was_set = 0;
823 
824 	while (1) {
825 		tp = findfunc(name, hash(name), true);
826 
827 		if (tp->flag & ISSET)
828 			was_set = 1;
829 		/* If this function is currently being executed, we zap this
830 		 * table entry so findfunc() won't see it
831 		 */
832 		if (tp->flag & FINUSE) {
833 			tp->name[0] = '\0';
834 			tp->flag &= ~DEFINED; /* ensure it won't be found */
835 			tp->flag |= FDELETE;
836 		} else
837 			break;
838 	}
839 
840 	if (tp->flag & ALLOC) {
841 		tp->flag &= ~(ISSET|ALLOC);
842 		tfree(tp->val.t, tp->areap);
843 	}
844 
845 	if (t == NULL) {		/* undefine */
846 		mytdelete(tp);
847 		return was_set ? 0 : 1;
848 	}
849 
850 	tp->val.t = tcopy(t->left, tp->areap);
851 	tp->flag |= (ISSET|ALLOC);
852 	if (t->u.ksh_func)
853 		tp->flag |= FKSH;
854 
855 	return 0;
856 }
857 
858 /*
859  * add builtin
860  */
861 void
builtin(name,func)862 builtin(name, func)
863 	const char *name;
864 	int (*func) ARGS((char **));
865 {
866 	struct tbl *tp;
867 	Tflag flag;
868 
869 	/* see if any flags should be set for this builtin */
870 	for (flag = 0; ; name++) {
871 		if (*name == '=')	/* command does variable assignment */
872 			flag |= KEEPASN;
873 		else if (*name == '*')	/* POSIX special builtin */
874 			flag |= SPEC_BI;
875 		else if (*name == '+')	/* POSIX regular builtin */
876 			flag |= REG_BI;
877 		else
878 			break;
879 	}
880 
881 	tp = tenter(&builtins, name, hash(name));
882 	tp->flag = DEFINED | flag;
883 	tp->type = CSHELL;
884 	tp->val.f = func;
885 }
886 
887 /*
888  * find command
889  * either function, hashed command, or built-in (in that order)
890  */
891 struct tbl *
findcom(name,flags)892 findcom(name, flags)
893 	const char *name;
894 	int	flags;		/* FC_* */
895 {
896 	static struct tbl temp;
897 	unsigned int h = hash(name);
898 	struct tbl *tp = NULL, *tbi;
899 	int insert = Flag(FTRACKALL);	/* insert if not found */
900 	char *fpath;			/* for function autoloading */
901 	char *npath;
902 
903 	if (ksh_strchr_dirsep(name) != NULL) {
904 		insert = 0;
905 		/* prevent FPATH search below */
906 		flags &= ~FC_FUNC;
907 		goto Search;
908 	}
909 	tbi = (flags & FC_BI) ? mytsearch(&builtins, name, h) : NULL;
910 	/* POSIX says special builtins first, then functions, then
911 	 * POSIX regular builtins, then search path...
912 	 */
913 	if ((flags & FC_SPECBI) && tbi && (tbi->flag & SPEC_BI))
914 		tp = tbi;
915 	if (!tp && (flags & FC_FUNC)) {
916 		tp = findfunc(name, h, false);
917 		if (tp && !(tp->flag & ISSET)) {
918 			if ((fpath = str_val(global("FPATH"))) == null) {
919 				tp->u.fpath = (char *) 0;
920 				tp->u2.errno_ = 0;
921 			} else
922 				tp->u.fpath = search(name, fpath, R_OK,
923 					&tp->u2.errno_);
924 		}
925 	}
926 	if (!tp && (flags & FC_REGBI) && tbi && (tbi->flag & REG_BI))
927 		tp = tbi;
928 	/* todo: posix says non-special/non-regular builtins must
929 	 * be triggered by some user-controllable means like a
930 	 * special directory in PATH.  Requires modifications to
931 	 * the search() function.  Tracked aliases should be
932 	 * modified to allow tracking of builtin commands.
933 	 * This should be under control of the FPOSIX flag.
934 	 * If this is changed, also change c_whence...
935 	 */
936 	if (!tp && (flags & FC_UNREGBI) && tbi)
937 		tp = tbi;
938 	if (!tp && (flags & FC_PATH) && !(flags & FC_DEFPATH)) {
939 		tp = mytsearch(&taliases, name, h);
940 		if (tp && (tp->flag & ISSET) && eaccess(tp->val.s, X_OK) != 0) {
941 			if (tp->flag & ALLOC) {
942 				tp->flag &= ~ALLOC;
943 				afree(tp->val.s, APERM);
944 			}
945 			tp->flag &= ~ISSET;
946 		}
947 	}
948 
949   Search:
950 	if ((!tp || (tp->type == CTALIAS && !(tp->flag&ISSET)))
951 	    && (flags & FC_PATH))
952 	{
953 		if (!tp) {
954 			if (insert && !(flags & FC_DEFPATH)) {
955 				tp = tenter(&taliases, name, h);
956 				tp->type = CTALIAS;
957 			} else {
958 				tp = &temp;
959 				tp->type = CEXEC;
960 			}
961 			tp->flag = DEFINED;	/* make ~ISSET */
962 		}
963 		npath = search(name, flags & FC_DEFPATH ? def_path : path,
964 				X_OK, &tp->u2.errno_);
965 		if (npath) {
966 			if (tp == &temp) {
967 			    tp->val.s = npath;
968 			} else {
969 			    tp->val.s = str_save(npath, APERM);
970 			    afree(npath, ATEMP);
971 			}
972 			tp->flag |= ISSET|ALLOC;
973 		} else if ((flags & FC_FUNC)
974 			   && (fpath = str_val(global("FPATH"))) != null
975 			   && (npath = search(name, fpath, R_OK,
976 					      &tp->u2.errno_)) != (char *) 0)
977 		{
978 			/* An undocumented feature of at&t ksh is that it
979 			 * searches FPATH if a command is not found, even
980 			 * if the command hasn't been set up as an autoloaded
981 			 * function (ie, no typeset -uf).
982 			 */
983 			tp = &temp;
984 			tp->type = CFUNC;
985 			tp->flag = DEFINED; /* make ~ISSET */
986 			tp->u.fpath = npath;
987 		}
988 	}
989 	return tp;
990 }
991 
992 /*
993  * flush executable commands with relative paths
994  */
995 void
flushcom(all)996 flushcom(all)
997 	int all;		/* just relative or all */
998 {
999 	struct tbl *tp;
1000 	struct tstate ts;
1001 
1002 	for (ksh_twalk(&ts, &taliases); (tp = tnext(&ts)) != NULL; )
1003 		if ((tp->flag&ISSET) && (all || !ISDIRSEP(tp->val.s[0]))) {
1004 			if (tp->flag&ALLOC) {
1005 				tp->flag &= ~(ALLOC|ISSET);
1006 				afree(tp->val.s, APERM);
1007 			}
1008 			tp->flag &= ~ISSET;
1009 		}
1010 }
1011 
1012 /* Check if path is something we want to find.  Returns -1 for failure. */
1013 int
search_access(pathx,mode,errnop)1014 search_access(pathx, mode, errnop)
1015 	const char *pathx;
1016 	int mode;
1017 	int *errnop;		/* set if candidate found, but not suitable */
1018 {
1019 	int ret, err = 0;
1020 	struct stat statb;
1021 
1022 	if (stat(pathx, &statb) < 0)
1023 		return -1;
1024 	ret = eaccess(pathx, mode);
1025 	if (ret < 0)
1026 		err = errno; /* File exists, but we can't access it */
1027 	else if (mode == X_OK
1028 		 && (!S_ISREG(statb.st_mode)
1029 		     /* This 'cause access() says root can execute everything */
1030 		     || !(statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))))
1031 	{
1032 		ret = -1;
1033 		err = S_ISDIR(statb.st_mode) ? EISDIR : EACCES;
1034 	}
1035 	if (err && errnop && !*errnop)
1036 		*errnop = err;
1037 	return ret;
1038 }
1039 
1040 /*
1041  * search for command with PATH
1042  */
1043 char *
search(name,pathx,mode,errnop)1044 search(name, pathx, mode, errnop)
1045 	const char *name;
1046 	const char *pathx;
1047 	int mode;		/* R_OK or X_OK */
1048 	int *errnop;		/* set if candidate found, but not suitable */
1049 {
1050 	const char *sp, *p;
1051 	char *xp;
1052 	XString xs;
1053 	int namelen;
1054 
1055 	if (errnop)
1056 		*errnop = 0;
1057 
1058 	if (ksh_strchr_dirsep(name)) {
1059 		if (search_access(name, mode, errnop) == 0)
1060 			return (char *)__UNCONST(name);
1061 		return NULL;
1062 	}
1063 
1064 	namelen = strlen(name) + 1;
1065 	Xinit(xs, xp, 128, ATEMP);
1066 
1067 	sp = pathx;
1068 	while (sp != NULL) {
1069 		xp = Xstring(xs, xp);
1070 		if (!(p = strchr(sp, PATHSEP)))
1071 			p = sp + strlen(sp);
1072 		if (p != sp) {
1073 			XcheckN(xs, xp, p - sp);
1074 			memcpy(xp, sp, p - sp);
1075 			xp += p - sp;
1076 			*xp++ = DIRSEP;
1077 		}
1078 		sp = p;
1079 		XcheckN(xs, xp, namelen);
1080 		memcpy(xp, name, namelen);
1081  		if (search_access(Xstring(xs, xp), mode, errnop) == 0)
1082 			return Xclose(xs, xp + namelen);
1083 		if (*sp++ == '\0')
1084 			sp = NULL;
1085 	}
1086 	Xfree(xs, xp);
1087 	return NULL;
1088 }
1089 
1090 static int
call_builtin(tp,wp)1091 call_builtin(tp, wp)
1092 	struct tbl *tp;
1093 	char **wp;
1094 {
1095 	int rv;
1096 
1097 	builtin_argv0 = wp[0];
1098 	builtin_flag = tp->flag;
1099 	shf_reopen(1, SHF_WR, shl_stdout);
1100 	shl_stdout_ok = 1;
1101 	ksh_getopt_reset(&builtin_opt, GF_ERROR);
1102 	rv = (*tp->val.f)(wp);
1103 	shf_flush(shl_stdout);
1104 	shl_stdout_ok = 0;
1105 	builtin_flag = 0;
1106 	builtin_argv0 = (char *) 0;
1107 	return rv;
1108 }
1109 
1110 /*
1111  * set up redirection, saving old fd's in e->savefd
1112  */
1113 static int
iosetup(iop,tp)1114 iosetup(iop, tp)
1115 	struct ioword *iop;
1116 	struct tbl *tp;
1117 {
1118 	int u = -1;
1119 	char *cp = iop->name;
1120 	int iotype = iop->flag & IOTYPE;
1121 	int do_open = 1, do_close = 0, UNINITIALIZED(flags);
1122 	struct ioword iotmp;
1123 	struct stat statb;
1124 
1125 	if (iotype != IOHERE)
1126 		cp = evalonestr(cp, DOTILDE|(Flag(FTALKING_I) ? DOGLOB : 0));
1127 
1128 	/* Used for tracing and error messages to print expanded cp */
1129 	iotmp = *iop;
1130 	iotmp.name = (iotype == IOHERE) ? (char *) 0 : cp;
1131 	iotmp.flag |= IONAMEXP;
1132 
1133 	if (Flag(FXTRACE))
1134 		shellf("%s%s\n",
1135 			PS4_SUBSTITUTE(str_val(global("PS4"))),
1136 			snptreef((char *) 0, 32, "%R", &iotmp));
1137 
1138 	switch (iotype) {
1139 	  case IOREAD:
1140 		flags = O_RDONLY;
1141 		break;
1142 
1143 	  case IOCAT:
1144 		flags = O_WRONLY | O_APPEND | O_CREAT;
1145 		break;
1146 
1147 	  case IOWRITE:
1148 		flags = O_WRONLY | O_CREAT | O_TRUNC;
1149 		/* The stat() is here to allow redirections to
1150 		 * things like /dev/null without error.
1151 		 */
1152 		if (Flag(FNOCLOBBER) && !(iop->flag & IOCLOB)
1153 		    && (stat(cp, &statb) < 0 || S_ISREG(statb.st_mode)))
1154 			flags |= O_EXCL;
1155 		break;
1156 
1157 	  case IORDWR:
1158 		flags = O_RDWR | O_CREAT;
1159 		break;
1160 
1161 	  case IOHERE:
1162 		do_open = 0;
1163 		/* herein() returns -2 if error has been printed */
1164 		u = herein(iop->heredoc, iop->flag & IOEVAL);
1165 		/* cp may have wrong name */
1166 		break;
1167 
1168 	  case IODUP:
1169 	  {
1170 		const char *emsg;
1171 
1172 		do_open = 0;
1173 		if (*cp == '-' && !cp[1]) {
1174 			u = 1009;	 /* prevent error return below */
1175 			do_close = 1;
1176 		} else if ((u = check_fd(cp,
1177 				X_OK | ((iop->flag & IORDUP) ? R_OK : W_OK),
1178 				&emsg)) < 0)
1179 		{
1180 			warningf(true, "%s: %s",
1181 				snptreef((char *) 0, 32, "%R", &iotmp), emsg);
1182 			return -1;
1183 		}
1184 		if (u == iop->unit)
1185 			return 0;		/* "dup from" == "dup to" */
1186 		break;
1187 	  }
1188 	}
1189 	if (do_open) {
1190 		if (Flag(FRESTRICTED) && (flags & O_CREAT)) {
1191 			warningf(true, "%s: restricted", cp);
1192 			return -1;
1193 		}
1194 		u = open(cp, flags, 0666);
1195 	}
1196 	if (u < 0) {
1197 		/* herein() may already have printed message */
1198 		if (u == -1)
1199 			warningf(true, "cannot %s %s: %s",
1200 			       iotype == IODUP ? "dup"
1201 				: (iotype == IOREAD || iotype == IOHERE) ?
1202 				    "open" : "create", cp, strerror(errno));
1203 		return -1;
1204 	}
1205 	/* Do not save if it has already been redirected (i.e. "cat >x >y"). */
1206 	if (e->savefd[iop->unit] == 0) {
1207 		/* If these are the same, it means unit was previously closed */
1208 		if (u == iop->unit)
1209 			e->savefd[iop->unit] = -1;
1210 		else
1211 			/* c_exec() assumes e->savefd[fd] set for any
1212 			 * redirections.  Ask savefd() not to close iop->unit;
1213 			 * this allows error messages to be seen if iop->unit
1214 			 * is 2; also means we can't lose the fd (eg, both
1215 			 * dup2 below and dup2 in restfd() failing).
1216 			 */
1217 			e->savefd[iop->unit] = savefd(iop->unit, 1);
1218 	}
1219 
1220 	if (do_close)
1221 		close(iop->unit);
1222 	else if (u != iop->unit) {
1223 		if (ksh_dup2(u, iop->unit, true) < 0) {
1224 			warningf(true,
1225 				"could not finish (dup) redirection %s: %s",
1226 				snptreef((char *) 0, 32, "%R", &iotmp),
1227 				strerror(errno));
1228 			if (iotype != IODUP)
1229 				close(u);
1230 			return -1;
1231 		}
1232 		if (iotype != IODUP)
1233 			close(u);
1234 #ifdef KSH
1235 		/* Touching any co-process fd in an empty exec
1236 		 * causes the shell to close its copies
1237 		 */
1238 		else if (tp && tp->type == CSHELL && tp->val.f == c_exec) {
1239 			if (iop->flag & IORDUP)	/* possible exec <&p */
1240 				coproc_read_close(u);
1241 			else			/* possible exec >&p */
1242 				coproc_write_close(u);
1243 		}
1244 #endif /* KSH */
1245 	}
1246 	if (u == 2) /* Clear any write errors */
1247 		shf_reopen(2, SHF_WR, shl_out);
1248 	return 0;
1249 }
1250 
1251 /*
1252  * open here document temp file.
1253  * if unquoted here, expand here temp file into second temp file.
1254  */
1255 static int
herein(content,sub)1256 herein(content, sub)
1257 	const char *content;
1258 	int sub;
1259 {
1260 	volatile int fd = -1;
1261 	struct source *s, *volatile osource;
1262 	struct shf *volatile shf;
1263 	struct temp *h;
1264 	int i;
1265 
1266 	/* ksh -c 'cat << EOF' can cause this... */
1267 	if (content == (char *) 0) {
1268 		warningf(true, "here document missing");
1269 		return -2; /* special to iosetup(): don't print error */
1270 	}
1271 
1272 	/* Create temp file to hold content (done before newenv so temp
1273 	 * doesn't get removed too soon).
1274 	 */
1275 	h = maketemp(ATEMP, TT_HEREDOC_EXP, &e->temps);
1276 	if (!(shf = h->shf) || (fd = open(h->name, O_RDONLY, 0)) < 0) {
1277 		warningf(true, "can't %s temporary file %s: %s",
1278 			!shf ? "create" : "open",
1279 			h->name, strerror(errno));
1280 		if (shf)
1281 			shf_close(shf);
1282 		return -2 /* special to iosetup(): don't print error */;
1283 	}
1284 
1285 	osource = source;
1286 	newenv(E_ERRH);
1287 	i = ksh_sigsetjmp(e->jbuf, 0);
1288 	if (i) {
1289 		source = osource;
1290 		quitenv();
1291 		shf_close(shf);	/* after quitenv */
1292 		close(fd);
1293 		return -2; /* special to iosetup(): don't print error */
1294 	}
1295 	if (sub) {
1296 		/* Do substitutions on the content of heredoc */
1297 		s = pushs(SSTRING, ATEMP);
1298 		s->start = s->str = content;
1299 		source = s;
1300 		if (yylex(ONEWORD|HEREDOC) != LWORD)
1301 			internal_errorf(1, "herein: yylex");
1302 		source = osource;
1303 		shf_puts(evalstr(yylval.cp, 0), shf);
1304 	} else
1305 		shf_puts(content, shf);
1306 
1307 	quitenv();
1308 
1309 	if (shf_close(shf) == EOF) {
1310 		close(fd);
1311 		warningf(true, "error writing %s: %s", h->name,
1312 			strerror(errno));
1313 		return -2; /* special to iosetup(): don't print error */
1314 	}
1315 
1316 	return fd;
1317 }
1318 
1319 #ifdef KSH
1320 /*
1321  *	ksh special - the select command processing section
1322  *	print the args in column form - assuming that we can
1323  */
1324 static char *
do_selectargs(char ** ap,bool print_menu)1325 do_selectargs(char **ap, bool print_menu)
1326 {
1327 	static const char *const read_args[] = {
1328 					"read", "-r", "REPLY", (char *) 0
1329 				    };
1330 	char *s;
1331 	int i, argct;
1332 
1333 	for (argct = 0; ap[argct]; argct++)
1334 		;
1335 	while (1) {
1336 		/* Menu is printed if
1337 		 *	- this is the first time around the select loop
1338 		 *	- the user enters a blank line
1339 		 *	- the REPLY parameter is empty
1340 		 */
1341 		if (print_menu || !*str_val(global("REPLY")))
1342 			pr_menu(ap);
1343 		shellf("%s", str_val(global("PS3")));
1344 		if (call_builtin(findcom("read", FC_BI),
1345 		    (char **) __UNCONST(read_args)))
1346 			return (char *) 0;
1347 		s = str_val(global("REPLY"));
1348 		if (*s) {
1349 			i = atoi(s);
1350 			return (i >= 1 && i <= argct) ? ap[i - 1] : null;
1351 		}
1352 		print_menu = 1;
1353 	}
1354 }
1355 
1356 struct select_menu_info {
1357 	char	*const *args;
1358 	int	arg_width;
1359 	int	num_width;
1360 } info;
1361 
1362 static char *select_fmt_entry ARGS((void *arg, int i, char *buf, int buflen));
1363 
1364 /* format a single select menu item */
1365 static char *
select_fmt_entry(arg,i,buf,buflen)1366 select_fmt_entry(arg, i, buf, buflen)
1367 	void *arg;
1368 	int i;
1369 	char *buf;
1370 	int buflen;
1371 {
1372 	struct select_menu_info *smi = (struct select_menu_info *) arg;
1373 
1374 	shf_snprintf(buf, buflen, "%*d) %s",
1375 		smi->num_width, i + 1, smi->args[i]);
1376 	return buf;
1377 }
1378 
1379 /*
1380  *	print a select style menu
1381  */
1382 int
pr_menu(ap)1383 pr_menu(ap)
1384 	char *const *ap;
1385 {
1386 	struct select_menu_info smi;
1387 	char *const *pp;
1388 	int nwidth, dwidth;
1389 	int i, n;
1390 
1391 	/* Width/column calculations were done once and saved, but this
1392 	 * means select can't be used recursively so we re-calculate each
1393 	 * time (could save in a structure that is returned, but its probably
1394 	 * not worth the bother).
1395 	 */
1396 
1397 	/*
1398 	 * get dimensions of the list
1399 	 */
1400 	for (n = 0, nwidth = 0, pp = ap; *pp; n++, pp++) {
1401 		i = strlen(*pp);
1402 		nwidth = (i > nwidth) ? i : nwidth;
1403 	}
1404 	/*
1405 	 * we will print an index of the form
1406 	 *	%d)
1407 	 * in front of each entry
1408 	 * get the max width of this
1409 	 */
1410 	for (i = n, dwidth = 1; i >= 10; i /= 10)
1411 		dwidth++;
1412 
1413 	smi.args = ap;
1414 	smi.arg_width = nwidth;
1415 	smi.num_width = dwidth;
1416 	print_columns(shl_out, n, select_fmt_entry, (void *) &smi,
1417 		dwidth + nwidth + 2, 1);
1418 
1419 	return n;
1420 }
1421 
1422 /* XXX: horrible kludge to fit within the framework */
1423 
1424 static char *plain_fmt_entry ARGS((void *arg, int i, char *buf, int buflen));
1425 
1426 static char *
plain_fmt_entry(arg,i,buf,buflen)1427 plain_fmt_entry(arg, i, buf, buflen)
1428 	void *arg;
1429 	int i;
1430 	char *buf;
1431 	int buflen;
1432 {
1433 	shf_snprintf(buf, buflen, "%s", ((char *const *)arg)[i]);
1434 	return buf;
1435 }
1436 
1437 int
pr_list(ap)1438 pr_list(ap)
1439 	char *const *ap;
1440 {
1441 	char *const *pp;
1442 	int nwidth;
1443 	int i, n;
1444 
1445 	for (n = 0, nwidth = 0, pp = ap; *pp; n++, pp++) {
1446 		i = strlen(*pp);
1447 		nwidth = (i > nwidth) ? i : nwidth;
1448 	}
1449 	print_columns(shl_out, n, plain_fmt_entry, (void *)__UNCONST(ap),
1450 	    nwidth + 1, 0);
1451 
1452 	return n;
1453 }
1454 #endif /* KSH */
1455 #ifdef KSH
1456 
1457 /*
1458  *	[[ ... ]] evaluation routines
1459  */
1460 
1461 extern const char *const dbtest_tokens[];
1462 extern const char db_close[];
1463 
1464 /* Test if the current token is a whatever.  Accepts the current token if
1465  * it is.  Returns 0 if it is not, non-zero if it is (in the case of
1466  * TM_UNOP and TM_BINOP, the returned value is a Test_op).
1467  */
1468 static int
dbteste_isa(te,meta)1469 dbteste_isa(te, meta)
1470 	Test_env *te;
1471 	Test_meta meta;
1472 {
1473 	int ret = 0;
1474 	int uqword;
1475 	char *p;
1476 
1477 	if (!*te->pos.wp)
1478 		return meta == TM_END;
1479 
1480 	/* unquoted word? */
1481 	for (p = *te->pos.wp; *p == CHAR; p += 2)
1482 		;
1483 	uqword = *p == EOS;
1484 
1485 	if (meta == TM_UNOP || meta == TM_BINOP) {
1486 		if (uqword) {
1487 			char buf[8];	/* longer than the longest operator */
1488 			char *q = buf;
1489 			for (p = *te->pos.wp; *p == CHAR
1490 					      && q < &buf[sizeof(buf) - 1];
1491 					      p += 2)
1492 				*q++ = p[1];
1493 			*q = '\0';
1494 			ret = (int) test_isop(te, meta, buf);
1495 		}
1496 	} else if (meta == TM_END)
1497 		ret = 0;
1498 	else
1499 		ret = uqword
1500 			&& strcmp(*te->pos.wp, dbtest_tokens[(int) meta]) == 0;
1501 
1502 	/* Accept the token? */
1503 	if (ret)
1504 		te->pos.wp++;
1505 
1506 	return ret;
1507 }
1508 
1509 static const char *
dbteste_getopnd(te,op,do_eval)1510 dbteste_getopnd(te, op, do_eval)
1511 	Test_env *te;
1512 	Test_op op;
1513 	int do_eval;
1514 {
1515 	char *s = *te->pos.wp;
1516 
1517 	if (!s)
1518 		return (char *) 0;
1519 
1520 	te->pos.wp++;
1521 
1522 	if (!do_eval)
1523 		return null;
1524 
1525 	if (op == TO_STEQL || op == TO_STNEQ)
1526 		s = evalstr(s, DOTILDE | DOPAT);
1527 	else
1528 		s = evalstr(s, DOTILDE);
1529 
1530 	return s;
1531 }
1532 
1533 static int
dbteste_eval(te,op,opnd1,opnd2,do_eval)1534 dbteste_eval(te, op, opnd1, opnd2, do_eval)
1535 	Test_env *te;
1536 	Test_op op;
1537 	const char *opnd1;
1538 	const char *opnd2;
1539 	int do_eval;
1540 {
1541 	return test_eval(te, op, opnd1, opnd2, do_eval);
1542 }
1543 
1544 static void
dbteste_error(te,offset,msg)1545 dbteste_error(te, offset, msg)
1546 	Test_env *te;
1547 	int offset;
1548 	const char *msg;
1549 {
1550 	te->flags |= TEF_ERROR;
1551 	internal_errorf(0, "dbteste_error: %s (offset %d)", msg, offset);
1552 }
1553 #endif /* KSH */
1554