xref: /openbsd/bin/ksh/c_ksh.c (revision 17df1aa7)
1 /*	$OpenBSD: c_ksh.c,v 1.33 2009/02/07 14:03:24 kili Exp $	*/
2 
3 /*
4  * built-in Korn commands: c_*
5  */
6 
7 #include "sh.h"
8 #include <sys/stat.h>
9 #include <ctype.h>
10 
11 
12 int
13 c_cd(char **wp)
14 {
15 	int optc;
16 	int physical = Flag(FPHYSICAL);
17 	int cdnode;			/* was a node from cdpath added in? */
18 	int printpath = 0;		/* print where we cd'd? */
19 	int rval;
20 	struct tbl *pwd_s, *oldpwd_s;
21 	XString xs;
22 	char *xp;
23 	char *dir, *try, *pwd;
24 	int phys_path;
25 	char *cdpath;
26 	char *fdir = NULL;
27 
28 	while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != -1)
29 		switch (optc) {
30 		case 'L':
31 			physical = 0;
32 			break;
33 		case 'P':
34 			physical = 1;
35 			break;
36 		case '?':
37 			return 1;
38 		}
39 	wp += builtin_opt.optind;
40 
41 	if (Flag(FRESTRICTED)) {
42 		bi_errorf("restricted shell - can't cd");
43 		return 1;
44 	}
45 
46 	pwd_s = global("PWD");
47 	oldpwd_s = global("OLDPWD");
48 
49 	if (!wp[0]) {
50 		/* No arguments - go home */
51 		if ((dir = str_val(global("HOME"))) == null) {
52 			bi_errorf("no home directory (HOME not set)");
53 			return 1;
54 		}
55 	} else if (!wp[1]) {
56 		/* One argument: - or dir */
57 		dir = wp[0];
58 		if (strcmp(dir, "-") == 0) {
59 			dir = str_val(oldpwd_s);
60 			if (dir == null) {
61 				bi_errorf("no OLDPWD");
62 				return 1;
63 			}
64 			printpath++;
65 		}
66 	} else if (!wp[2]) {
67 		/* Two arguments - substitute arg1 in PWD for arg2 */
68 		int ilen, olen, nlen, elen;
69 		char *cp;
70 
71 		if (!current_wd[0]) {
72 			bi_errorf("don't know current directory");
73 			return 1;
74 		}
75 		/* substitute arg1 for arg2 in current path.
76 		 * if the first substitution fails because the cd fails
77 		 * we could try to find another substitution. For now
78 		 * we don't
79 		 */
80 		if ((cp = strstr(current_wd, wp[0])) == (char *) 0) {
81 			bi_errorf("bad substitution");
82 			return 1;
83 		}
84 		ilen = cp - current_wd;
85 		olen = strlen(wp[0]);
86 		nlen = strlen(wp[1]);
87 		elen = strlen(current_wd + ilen + olen) + 1;
88 		fdir = dir = alloc(ilen + nlen + elen, ATEMP);
89 		memcpy(dir, current_wd, ilen);
90 		memcpy(dir + ilen, wp[1], nlen);
91 		memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
92 		printpath++;
93 	} else {
94 		bi_errorf("too many arguments");
95 		return 1;
96 	}
97 
98 	Xinit(xs, xp, PATH, ATEMP);
99 	/* xp will have a bogus value after make_path() - set it to 0
100 	 * so that if it's used, it will cause a dump
101 	 */
102 	xp = (char *) 0;
103 
104 	cdpath = str_val(global("CDPATH"));
105 	do {
106 		cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path);
107 		if (physical)
108 			rval = chdir(try = Xstring(xs, xp) + phys_path);
109 		else {
110 			simplify_path(Xstring(xs, xp));
111 			rval = chdir(try = Xstring(xs, xp));
112 		}
113 	} while (rval < 0 && cdpath != (char *) 0);
114 
115 	if (rval < 0) {
116 		if (cdnode)
117 			bi_errorf("%s: bad directory", dir);
118 		else
119 			bi_errorf("%s - %s", try, strerror(errno));
120 		if (fdir)
121 			afree(fdir, ATEMP);
122 		return 1;
123 	}
124 
125 	/* Clear out tracked aliases with relative paths */
126 	flushcom(0);
127 
128 	/* Set OLDPWD (note: unsetting OLDPWD does not disable this
129 	 * setting in at&t ksh)
130 	 */
131 	if (current_wd[0])
132 		/* Ignore failure (happens if readonly or integer) */
133 		setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR);
134 
135 	if (Xstring(xs, xp)[0] != '/') {
136 		pwd = (char *) 0;
137 	} else
138 	if (!physical || !(pwd = get_phys_path(Xstring(xs, xp))))
139 		pwd = Xstring(xs, xp);
140 
141 	/* Set PWD */
142 	if (pwd) {
143 		char *ptmp = pwd;
144 		set_current_wd(ptmp);
145 		/* Ignore failure (happens if readonly or integer) */
146 		setstr(pwd_s, ptmp, KSH_RETURN_ERROR);
147 	} else {
148 		set_current_wd(null);
149 		pwd = Xstring(xs, xp);
150 		/* XXX unset $PWD? */
151 	}
152 	if (printpath || cdnode)
153 		shprintf("%s\n", pwd);
154 
155 	if (fdir)
156 		afree(fdir, ATEMP);
157 
158 	return 0;
159 }
160 
161 int
162 c_pwd(char **wp)
163 {
164 	int optc;
165 	int physical = Flag(FPHYSICAL);
166 	char *p, *freep = NULL;
167 
168 	while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != -1)
169 		switch (optc) {
170 		case 'L':
171 			physical = 0;
172 			break;
173 		case 'P':
174 			physical = 1;
175 			break;
176 		case '?':
177 			return 1;
178 		}
179 	wp += builtin_opt.optind;
180 
181 	if (wp[0]) {
182 		bi_errorf("too many arguments");
183 		return 1;
184 	}
185 	p = current_wd[0] ? (physical ? get_phys_path(current_wd) : current_wd) :
186 	    (char *) 0;
187 	if (p && access(p, R_OK) < 0)
188 		p = (char *) 0;
189 	if (!p) {
190 		freep = p = ksh_get_wd((char *) 0, 0);
191 		if (!p) {
192 			bi_errorf("can't get current directory - %s",
193 			    strerror(errno));
194 			return 1;
195 		}
196 	}
197 	shprintf("%s\n", p);
198 	if (freep)
199 		afree(freep, ATEMP);
200 	return 0;
201 }
202 
203 int
204 c_print(char **wp)
205 {
206 #define PO_NL		BIT(0)	/* print newline */
207 #define PO_EXPAND	BIT(1)	/* expand backslash sequences */
208 #define PO_PMINUSMINUS	BIT(2)	/* print a -- argument */
209 #define PO_HIST		BIT(3)	/* print to history instead of stdout */
210 #define PO_COPROC	BIT(4)	/* printing to coprocess: block SIGPIPE */
211 	int fd = 1;
212 	int flags = PO_EXPAND|PO_NL;
213 	char *s;
214 	const char *emsg;
215 	XString xs;
216 	char *xp;
217 
218 	if (wp[0][0] == 'e') {	/* echo command */
219 		int nflags = flags;
220 
221 		/* A compromise between sysV and BSD echo commands:
222 		 * escape sequences are enabled by default, and
223 		 * -n, -e and -E are recognized if they appear
224 		 * in arguments with no illegal options (ie, echo -nq
225 		 * will print -nq).
226 		 * Different from sysV echo since options are recognized,
227 		 * different from BSD echo since escape sequences are enabled
228 		 * by default.
229 		 */
230 		wp += 1;
231 		if (Flag(FPOSIX)) {
232 			if (*wp && strcmp(*wp, "-n") == 0) {
233 				flags &= ~PO_NL;
234 				wp++;
235 			}
236 		} else {
237 			while ((s = *wp) && *s == '-' && s[1]) {
238 				while (*++s)
239 					if (*s == 'n')
240 						nflags &= ~PO_NL;
241 					else if (*s == 'e')
242 						nflags |= PO_EXPAND;
243 					else if (*s == 'E')
244 						nflags &= ~PO_EXPAND;
245 					else
246 						/* bad option: don't use
247 						 * nflags, print argument
248 						 */
249 						break;
250 				if (*s)
251 					break;
252 				wp++;
253 				flags = nflags;
254 			}
255 		}
256 	} else {
257 		int optc;
258 		const char *options = "Rnprsu,";
259 		while ((optc = ksh_getopt(wp, &builtin_opt, options)) != -1)
260 			switch (optc) {
261 			case 'R': /* fake BSD echo command */
262 				flags |= PO_PMINUSMINUS;
263 				flags &= ~PO_EXPAND;
264 				options = "ne";
265 				break;
266 			case 'e':
267 				flags |= PO_EXPAND;
268 				break;
269 			case 'n':
270 				flags &= ~PO_NL;
271 				break;
272 			case 'p':
273 				if ((fd = coproc_getfd(W_OK, &emsg)) < 0) {
274 					bi_errorf("-p: %s", emsg);
275 					return 1;
276 				}
277 				break;
278 			case 'r':
279 				flags &= ~PO_EXPAND;
280 				break;
281 			case 's':
282 				flags |= PO_HIST;
283 				break;
284 			case 'u':
285 				if (!*(s = builtin_opt.optarg))
286 					fd = 0;
287 				else if ((fd = check_fd(s, W_OK, &emsg)) < 0) {
288 					bi_errorf("-u: %s: %s", s, emsg);
289 					return 1;
290 				}
291 				break;
292 			case '?':
293 				return 1;
294 			}
295 		if (!(builtin_opt.info & GI_MINUSMINUS)) {
296 			/* treat a lone - like -- */
297 			if (wp[builtin_opt.optind] &&
298 			    strcmp(wp[builtin_opt.optind], "-") == 0)
299 				builtin_opt.optind++;
300 		} else if (flags & PO_PMINUSMINUS)
301 			builtin_opt.optind--;
302 		wp += builtin_opt.optind;
303 	}
304 
305 	Xinit(xs, xp, 128, ATEMP);
306 
307 	while (*wp != NULL) {
308 		int c;
309 		s = *wp;
310 		while ((c = *s++) != '\0') {
311 			Xcheck(xs, xp);
312 			if ((flags & PO_EXPAND) && c == '\\') {
313 				int i;
314 
315 				switch ((c = *s++)) {
316 				/* Oddly enough, \007 seems more portable than
317 				 * \a (due to HP-UX cc, Ultrix cc, old pcc's,
318 				 * etc.).
319 				 */
320 				case 'a': c = '\007'; break;
321 				case 'b': c = '\b'; break;
322 				case 'c': flags &= ~PO_NL;
323 					  continue; /* AT&T brain damage */
324 				case 'f': c = '\f'; break;
325 				case 'n': c = '\n'; break;
326 				case 'r': c = '\r'; break;
327 				case 't': c = '\t'; break;
328 				case 'v': c = 0x0B; break;
329 				case '0':
330 					/* Look for an octal number: can have
331 					 * three digits (not counting the
332 					 * leading 0).  Truly burnt.
333 					 */
334 					c = 0;
335 					for (i = 0; i < 3; i++) {
336 						if (*s >= '0' && *s <= '7')
337 							c = c*8 + *s++ - '0';
338 						else
339 							break;
340 					}
341 					break;
342 				case '\0': s--; c = '\\'; break;
343 				case '\\': break;
344 				default:
345 					Xput(xs, xp, '\\');
346 				}
347 			}
348 			Xput(xs, xp, c);
349 		}
350 		if (*++wp != NULL)
351 			Xput(xs, xp, ' ');
352 	}
353 	if (flags & PO_NL)
354 		Xput(xs, xp, '\n');
355 
356 	if (flags & PO_HIST) {
357 		Xput(xs, xp, '\0');
358 		source->line++;
359 		histsave(source->line, Xstring(xs, xp), 1);
360 		Xfree(xs, xp);
361 	} else {
362 		int n, len = Xlength(xs, xp);
363 		int opipe = 0;
364 
365 		/* Ensure we aren't killed by a SIGPIPE while writing to
366 		 * a coprocess.  at&t ksh doesn't seem to do this (seems
367 		 * to just check that the co-process is alive, which is
368 		 * not enough).
369 		 */
370 		if (coproc.write >= 0 && coproc.write == fd) {
371 			flags |= PO_COPROC;
372 			opipe = block_pipe();
373 		}
374 		for (s = Xstring(xs, xp); len > 0; ) {
375 			n = write(fd, s, len);
376 			if (n < 0) {
377 				if (flags & PO_COPROC)
378 					restore_pipe(opipe);
379 				if (errno == EINTR) {
380 					/* allow user to ^C out */
381 					intrcheck();
382 					if (flags & PO_COPROC)
383 						opipe = block_pipe();
384 					continue;
385 				}
386 				/* This doesn't really make sense - could
387 				 * break scripts (print -p generates
388 				 * error message).
389 				*if (errno == EPIPE)
390 				*	coproc_write_close(fd);
391 				 */
392 				return 1;
393 			}
394 			s += n;
395 			len -= n;
396 		}
397 		if (flags & PO_COPROC)
398 			restore_pipe(opipe);
399 	}
400 
401 	return 0;
402 }
403 
404 int
405 c_whence(char **wp)
406 {
407 	struct tbl *tp;
408 	char *id;
409 	int pflag = 0, vflag = 0, Vflag = 0;
410 	int ret = 0;
411 	int optc;
412 	int iam_whence = wp[0][0] == 'w';
413 	int fcflags;
414 	const char *options = iam_whence ? "pv" : "pvV";
415 
416 	while ((optc = ksh_getopt(wp, &builtin_opt, options)) != -1)
417 		switch (optc) {
418 		case 'p':
419 			pflag = 1;
420 			break;
421 		case 'v':
422 			vflag = 1;
423 			break;
424 		case 'V':
425 			Vflag = 1;
426 			break;
427 		case '?':
428 			return 1;
429 		}
430 	wp += builtin_opt.optind;
431 
432 
433 	fcflags = FC_BI | FC_PATH | FC_FUNC;
434 	if (!iam_whence) {
435 		/* Note that -p on its own is deal with in comexec() */
436 		if (pflag)
437 			fcflags |= FC_DEFPATH;
438 		/* Convert command options to whence options - note that
439 		 * command -pV uses a different path search than whence -v
440 		 * or whence -pv.  This should be considered a feature.
441 		 */
442 		vflag = Vflag;
443 	}
444 	if (pflag)
445 		fcflags &= ~(FC_BI | FC_FUNC);
446 
447 	while ((vflag || ret == 0) && (id = *wp++) != NULL) {
448 		tp = NULL;
449 		if ((iam_whence || vflag) && !pflag)
450 			tp = ktsearch(&keywords, id, hash(id));
451 		if (!tp && !pflag) {
452 			tp = ktsearch(&aliases, id, hash(id));
453 			if (tp && !(tp->flag & ISSET))
454 				tp = NULL;
455 		}
456 		if (!tp)
457 			tp = findcom(id, fcflags);
458 		if (vflag || (tp->type != CALIAS && tp->type != CEXEC &&
459 		    tp->type != CTALIAS))
460 			shprintf("%s", id);
461 		switch (tp->type) {
462 		case CKEYWD:
463 			if (vflag)
464 				shprintf(" is a reserved word");
465 			break;
466 		case CALIAS:
467 			if (vflag)
468 				shprintf(" is an %salias for ",
469 				    (tp->flag & EXPORT) ? "exported " :
470 				    null);
471 			if (!iam_whence && !vflag)
472 				shprintf("alias %s=", id);
473 			print_value_quoted(tp->val.s);
474 			break;
475 		case CFUNC:
476 			if (vflag) {
477 				shprintf(" is a");
478 				if (tp->flag & EXPORT)
479 					shprintf("n exported");
480 				if (tp->flag & TRACE)
481 					shprintf(" traced");
482 				if (!(tp->flag & ISSET)) {
483 					shprintf(" undefined");
484 					if (tp->u.fpath)
485 						shprintf(" (autoload from %s)",
486 						    tp->u.fpath);
487 				}
488 				shprintf(" function");
489 			}
490 			break;
491 		case CSHELL:
492 			if (vflag)
493 				shprintf(" is a%s shell builtin",
494 				    (tp->flag & SPEC_BI) ? " special" : null);
495 			break;
496 		case CTALIAS:
497 		case CEXEC:
498 			if (tp->flag & ISSET) {
499 				if (vflag) {
500 					shprintf(" is ");
501 					if (tp->type == CTALIAS)
502 						shprintf("a tracked %salias for ",
503 						    (tp->flag & EXPORT) ?
504 						    "exported " : null);
505 				}
506 				shprintf("%s", tp->val.s);
507 			} else {
508 				if (vflag)
509 					shprintf(" not found");
510 				ret = 1;
511 			}
512 			break;
513 		default:
514 			shprintf("%s is *GOK*", id);
515 			break;
516 		}
517 		if (vflag || !ret)
518 			shprintf(newline);
519 	}
520 	return ret;
521 }
522 
523 /* Deal with command -vV - command -p dealt with in comexec() */
524 int
525 c_command(char **wp)
526 {
527 	/* Let c_whence do the work.  Note that c_command() must be
528 	 * a distinct function from c_whence() (tested in comexec()).
529 	 */
530 	return c_whence(wp);
531 }
532 
533 /* typeset, export, and readonly */
534 int
535 c_typeset(char **wp)
536 {
537 	struct block *l = e->loc;
538 	struct tbl *vp, **p;
539 	Tflag fset = 0, fclr = 0;
540 	int thing = 0, func = 0, local = 0;
541 	const char *options = "L#R#UZ#fi#lprtux";	/* see comment below */
542 	char *fieldstr, *basestr;
543 	int field, base;
544 	int optc;
545 	Tflag flag;
546 	int pflag = 0;
547 
548 	switch (**wp) {
549 	case 'e':		/* export */
550 		fset |= EXPORT;
551 		options = "p";
552 		break;
553 	case 'r':		/* readonly */
554 		fset |= RDONLY;
555 		options = "p";
556 		break;
557 	case 's':		/* set */
558 		/* called with 'typeset -' */
559 		break;
560 	case 't':		/* typeset */
561 		local = 1;
562 		break;
563 	}
564 
565 	fieldstr = basestr = (char *) 0;
566 	builtin_opt.flags |= GF_PLUSOPT;
567 	/* at&t ksh seems to have 0-9 as options, which are multiplied
568 	 * to get a number that is used with -L, -R, -Z or -i (eg, -1R2
569 	 * sets right justify in a field of 12).  This allows options
570 	 * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
571 	 * does not allow the number to be specified as a separate argument
572 	 * Here, the number must follow the RLZi option, but is optional
573 	 * (see the # kludge in ksh_getopt()).
574 	 */
575 	while ((optc = ksh_getopt(wp, &builtin_opt, options)) != -1) {
576 		flag = 0;
577 		switch (optc) {
578 		case 'L':
579 			flag = LJUST;
580 			fieldstr = builtin_opt.optarg;
581 			break;
582 		case 'R':
583 			flag = RJUST;
584 			fieldstr = builtin_opt.optarg;
585 			break;
586 		case 'U':
587 			/* at&t ksh uses u, but this conflicts with
588 			 * upper/lower case.  If this option is changed,
589 			 * need to change the -U below as well
590 			 */
591 			flag = INT_U;
592 			break;
593 		case 'Z':
594 			flag = ZEROFIL;
595 			fieldstr = builtin_opt.optarg;
596 			break;
597 		case 'f':
598 			func = 1;
599 			break;
600 		case 'i':
601 			flag = INTEGER;
602 			basestr = builtin_opt.optarg;
603 			break;
604 		case 'l':
605 			flag = LCASEV;
606 			break;
607 		case 'p':
608 			/* posix export/readonly -p flag.
609 			 * typeset -p is the same as typeset (in pdksh);
610 			 * here for compatibility with ksh93.
611 			 */
612 			pflag = 1;
613 			break;
614 		case 'r':
615 			flag = RDONLY;
616 			break;
617 		case 't':
618 			flag = TRACE;
619 			break;
620 		case 'u':
621 			flag = UCASEV_AL;	/* upper case / autoload */
622 			break;
623 		case 'x':
624 			flag = EXPORT;
625 			break;
626 		case '?':
627 			return 1;
628 		}
629 		if (builtin_opt.info & GI_PLUS) {
630 			fclr |= flag;
631 			fset &= ~flag;
632 			thing = '+';
633 		} else {
634 			fset |= flag;
635 			fclr &= ~flag;
636 			thing = '-';
637 		}
638 	}
639 
640 	field = 0;
641 	if (fieldstr && !bi_getn(fieldstr, &field))
642 		return 1;
643 	base = 0;
644 	if (basestr && !bi_getn(basestr, &base))
645 		return 1;
646 
647 	if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] &&
648 	    (wp[builtin_opt.optind][0] == '-' ||
649 	    wp[builtin_opt.optind][0] == '+') &&
650 	    wp[builtin_opt.optind][1] == '\0') {
651 		thing = wp[builtin_opt.optind][0];
652 		builtin_opt.optind++;
653 	}
654 
655 	if (func && ((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT))) {
656 		bi_errorf("only -t, -u and -x options may be used with -f");
657 		return 1;
658 	}
659 	if (wp[builtin_opt.optind]) {
660 		/* Take care of exclusions.
661 		 * At this point, flags in fset are cleared in fclr and vise
662 		 * versa.  This property should be preserved.
663 		 */
664 		if (fset & LCASEV)	/* LCASEV has priority over UCASEV_AL */
665 			fset &= ~UCASEV_AL;
666 		if (fset & LJUST)	/* LJUST has priority over RJUST */
667 			fset &= ~RJUST;
668 		if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) { /* -Z implies -ZR */
669 			fset |= RJUST;
670 			fclr &= ~RJUST;
671 		}
672 		/* Setting these attributes clears the others, unless they
673 		 * are also set in this command
674 		 */
675 		if (fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
676 		    INTEGER | INT_U | INT_L))
677 			fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL |
678 			    LCASEV | INTEGER | INT_U | INT_L);
679 	}
680 
681 	/* set variables and attributes */
682 	if (wp[builtin_opt.optind]) {
683 		int i;
684 		int rval = 0;
685 		struct tbl *f;
686 
687 		if (local && !func)
688 			fset |= LOCAL;
689 		for (i = builtin_opt.optind; wp[i]; i++) {
690 			if (func) {
691 				f = findfunc(wp[i], hash(wp[i]),
692 				    (fset&UCASEV_AL) ? true : false);
693 				if (!f) {
694 					/* at&t ksh does ++rval: bogus */
695 					rval = 1;
696 					continue;
697 				}
698 				if (fset | fclr) {
699 					f->flag |= fset;
700 					f->flag &= ~fclr;
701 				} else
702 					fptreef(shl_stdout, 0,
703 					    f->flag & FKSH ?
704 					    "function %s %T\n" :
705 					    "%s() %T\n", wp[i], f->val.t);
706 			} else if (!typeset(wp[i], fset, fclr, field, base)) {
707 				bi_errorf("%s: not identifier", wp[i]);
708 				return 1;
709 			}
710 		}
711 		return rval;
712 	}
713 
714 	/* list variables and attributes */
715 	flag = fset | fclr; /* no difference at this point.. */
716 	if (func) {
717 		for (l = e->loc; l; l = l->next) {
718 			for (p = ktsort(&l->funs); (vp = *p++); ) {
719 				if (flag && (vp->flag & flag) == 0)
720 					continue;
721 				if (thing == '-')
722 					fptreef(shl_stdout, 0, vp->flag & FKSH ?
723 					    "function %s %T\n" : "%s() %T\n",
724 					    vp->name, vp->val.t);
725 				else
726 					shprintf("%s\n", vp->name);
727 			}
728 		}
729 	} else {
730 		for (l = e->loc; l; l = l->next) {
731 			for (p = ktsort(&l->vars); (vp = *p++); ) {
732 				struct tbl *tvp;
733 				int any_set = 0;
734 				/*
735 				 * See if the parameter is set (for arrays, if any
736 				 * element is set).
737 				 */
738 				for (tvp = vp; tvp; tvp = tvp->u.array)
739 					if (tvp->flag & ISSET) {
740 						any_set = 1;
741 						break;
742 					}
743 
744 				/*
745 				 * Check attributes - note that all array elements
746 				 * have (should have?) the same attributes, so checking
747 				 * the first is sufficient.
748 				 *
749 				 * Report an unset param only if the user has
750 				 * explicitly given it some attribute (like export);
751 				 * otherwise, after "echo $FOO", we would report FOO...
752 				 */
753 				if (!any_set && !(vp->flag & USERATTRIB))
754 					continue;
755 				if (flag && (vp->flag & flag) == 0)
756 					continue;
757 				for (; vp; vp = vp->u.array) {
758 					/* Ignore array elements that aren't
759 					 * set unless there are no set elements,
760 					 * in which case the first is reported on */
761 					if ((vp->flag&ARRAY) && any_set &&
762 					    !(vp->flag & ISSET))
763 						continue;
764 					/* no arguments */
765 					if (thing == 0 && flag == 0) {
766 						/* at&t ksh prints things
767 						 * like export, integer,
768 						 * leftadj, zerofill, etc.,
769 						 * but POSIX says must
770 						 * be suitable for re-entry...
771 						 */
772 						shprintf("typeset ");
773 						if ((vp->flag&INTEGER))
774 							shprintf("-i ");
775 						if ((vp->flag&EXPORT))
776 							shprintf("-x ");
777 						if ((vp->flag&RDONLY))
778 							shprintf("-r ");
779 						if ((vp->flag&TRACE))
780 							shprintf("-t ");
781 						if ((vp->flag&LJUST))
782 							shprintf("-L%d ", vp->u2.field);
783 						if ((vp->flag&RJUST))
784 							shprintf("-R%d ", vp->u2.field);
785 						if ((vp->flag&ZEROFIL))
786 							shprintf("-Z ");
787 						if ((vp->flag&LCASEV))
788 							shprintf("-l ");
789 						if ((vp->flag&UCASEV_AL))
790 							shprintf("-u ");
791 						if ((vp->flag&INT_U))
792 							shprintf("-U ");
793 						shprintf("%s\n", vp->name);
794 						    if (vp->flag&ARRAY)
795 						break;
796 					} else {
797 						if (pflag)
798 							shprintf("%s ",
799 							    (flag & EXPORT) ?
800 							    "export" : "readonly");
801 						if ((vp->flag&ARRAY) && any_set)
802 							shprintf("%s[%d]",
803 							    vp->name, vp->index);
804 						else
805 							shprintf("%s", vp->name);
806 						if (thing == '-' && (vp->flag&ISSET)) {
807 							char *s = str_val(vp);
808 
809 							shprintf("=");
810 							/* at&t ksh can't have
811 							 * justified integers.. */
812 							if ((vp->flag &
813 							    (INTEGER|LJUST|RJUST)) ==
814 							    INTEGER)
815 								shprintf("%s", s);
816 							else
817 								print_value_quoted(s);
818 						}
819 						shprintf(newline);
820 					}
821 					/* Only report first `element' of an array with
822 					* no set elements.
823 					*/
824 					if (!any_set)
825 						break;
826 				}
827 			}
828 		}
829 	}
830 	return 0;
831 }
832 
833 int
834 c_alias(char **wp)
835 {
836 	struct table *t = &aliases;
837 	int rv = 0, rflag = 0, tflag, Uflag = 0, pflag = 0;
838 	int prefix = 0;
839 	Tflag xflag = 0;
840 	int optc;
841 
842 	builtin_opt.flags |= GF_PLUSOPT;
843 	while ((optc = ksh_getopt(wp, &builtin_opt, "dprtUx")) != -1) {
844 		prefix = builtin_opt.info & GI_PLUS ? '+' : '-';
845 		switch (optc) {
846 		case 'd':
847 			t = &homedirs;
848 			break;
849 		case 'p':
850 			pflag = 1;
851 			break;
852 		case 'r':
853 			rflag = 1;
854 			break;
855 		case 't':
856 			t = &taliases;
857 			break;
858 		case 'U':
859 			/*
860 			 * kludge for tracked alias initialization
861 			 * (don't do a path search, just make an entry)
862 			 */
863 			Uflag = 1;
864 			break;
865 		case 'x':
866 			xflag = EXPORT;
867 			break;
868 		case '?':
869 			return 1;
870 		}
871 	}
872 	wp += builtin_opt.optind;
873 
874 	if (!(builtin_opt.info & GI_MINUSMINUS) && *wp &&
875 	    (wp[0][0] == '-' || wp[0][0] == '+') && wp[0][1] == '\0') {
876 		prefix = wp[0][0];
877 		wp++;
878 	}
879 
880 	tflag = t == &taliases;
881 
882 	/* "hash -r" means reset all the tracked aliases.. */
883 	if (rflag) {
884 		static const char *const args[] = {
885 			"unalias", "-ta", (const char *) 0
886 		};
887 
888 		if (!tflag || *wp) {
889 			shprintf("alias: -r flag can only be used with -t"
890 			    " and without arguments\n");
891 			return 1;
892 		}
893 		ksh_getopt_reset(&builtin_opt, GF_ERROR);
894 		return c_unalias((char **) args);
895 	}
896 
897 	if (*wp == NULL) {
898 		struct tbl *ap, **p;
899 
900 		for (p = ktsort(t); (ap = *p++) != NULL; )
901 			if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) {
902 				if (pflag)
903 					shf_puts("alias ", shl_stdout);
904 				shf_puts(ap->name, shl_stdout);
905 				if (prefix != '+') {
906 					shf_putc('=', shl_stdout);
907 					print_value_quoted(ap->val.s);
908 				}
909 				shprintf(newline);
910 			}
911 	}
912 
913 	for (; *wp != NULL; wp++) {
914 		char *alias = *wp;
915 		char *val = strchr(alias, '=');
916 		char *newval;
917 		struct tbl *ap;
918 		int h;
919 
920 		if (val)
921 			alias = str_nsave(alias, val++ - alias, ATEMP);
922 		h = hash(alias);
923 		if (val == NULL && !tflag && !xflag) {
924 			ap = ktsearch(t, alias, h);
925 			if (ap != NULL && (ap->flag&ISSET)) {
926 				if (pflag)
927 					shf_puts("alias ", shl_stdout);
928 				shf_puts(ap->name, shl_stdout);
929 				if (prefix != '+') {
930 					shf_putc('=', shl_stdout);
931 					print_value_quoted(ap->val.s);
932 				}
933 				shprintf(newline);
934 			} else {
935 				shprintf("%s alias not found\n", alias);
936 				rv = 1;
937 			}
938 			continue;
939 		}
940 		ap = ktenter(t, alias, h);
941 		ap->type = tflag ? CTALIAS : CALIAS;
942 		/* Are we setting the value or just some flags? */
943 		if ((val && !tflag) || (!val && tflag && !Uflag)) {
944 			if (ap->flag&ALLOC) {
945 				ap->flag &= ~(ALLOC|ISSET);
946 				afree((void*)ap->val.s, APERM);
947 			}
948 			/* ignore values for -t (at&t ksh does this) */
949 			newval = tflag ? search(alias, path, X_OK, (int *) 0) :
950 			    val;
951 			if (newval) {
952 				ap->val.s = str_save(newval, APERM);
953 				ap->flag |= ALLOC|ISSET;
954 			} else
955 				ap->flag &= ~ISSET;
956 		}
957 		ap->flag |= DEFINED;
958 		if (prefix == '+')
959 			ap->flag &= ~xflag;
960 		else
961 			ap->flag |= xflag;
962 		if (val)
963 			afree(alias, ATEMP);
964 	}
965 
966 	return rv;
967 }
968 
969 int
970 c_unalias(char **wp)
971 {
972 	struct table *t = &aliases;
973 	struct tbl *ap;
974 	int rv = 0, all = 0;
975 	int optc;
976 
977 	while ((optc = ksh_getopt(wp, &builtin_opt, "adt")) != -1)
978 		switch (optc) {
979 		case 'a':
980 			all = 1;
981 			break;
982 		case 'd':
983 			t = &homedirs;
984 			break;
985 		case 't':
986 			t = &taliases;
987 			break;
988 		case '?':
989 			return 1;
990 		}
991 	wp += builtin_opt.optind;
992 
993 	for (; *wp != NULL; wp++) {
994 		ap = ktsearch(t, *wp, hash(*wp));
995 		if (ap == NULL) {
996 			rv = 1;	/* POSIX */
997 			continue;
998 		}
999 		if (ap->flag&ALLOC) {
1000 			ap->flag &= ~(ALLOC|ISSET);
1001 			afree((void*)ap->val.s, APERM);
1002 		}
1003 		ap->flag &= ~(DEFINED|ISSET|EXPORT);
1004 	}
1005 
1006 	if (all) {
1007 		struct tstate ts;
1008 
1009 		for (ktwalk(&ts, t); (ap = ktnext(&ts)); ) {
1010 			if (ap->flag&ALLOC) {
1011 				ap->flag &= ~(ALLOC|ISSET);
1012 				afree((void*)ap->val.s, APERM);
1013 			}
1014 			ap->flag &= ~(DEFINED|ISSET|EXPORT);
1015 		}
1016 	}
1017 
1018 	return rv;
1019 }
1020 
1021 int
1022 c_let(char **wp)
1023 {
1024 	int rv = 1;
1025 	long val;
1026 
1027 	if (wp[1] == (char *) 0) /* at&t ksh does this */
1028 		bi_errorf("no arguments");
1029 	else
1030 		for (wp++; *wp; wp++)
1031 			if (!evaluate(*wp, &val, KSH_RETURN_ERROR, true)) {
1032 				rv = 2;	/* distinguish error from zero result */
1033 				break;
1034 			} else
1035 				rv = val == 0;
1036 	return rv;
1037 }
1038 
1039 int
1040 c_jobs(char **wp)
1041 {
1042 	int optc;
1043 	int flag = 0;
1044 	int nflag = 0;
1045 	int rv = 0;
1046 
1047 	while ((optc = ksh_getopt(wp, &builtin_opt, "lpnz")) != -1)
1048 		switch (optc) {
1049 		case 'l':
1050 			flag = 1;
1051 			break;
1052 		case 'p':
1053 			flag = 2;
1054 			break;
1055 		case 'n':
1056 			nflag = 1;
1057 			break;
1058 		case 'z':	/* debugging: print zombies */
1059 			nflag = -1;
1060 			break;
1061 		case '?':
1062 			return 1;
1063 		}
1064 	wp += builtin_opt.optind;
1065 	if (!*wp) {
1066 		if (j_jobs((char *) 0, flag, nflag))
1067 			rv = 1;
1068 	} else {
1069 		for (; *wp; wp++)
1070 			if (j_jobs(*wp, flag, nflag))
1071 				rv = 1;
1072 	}
1073 	return rv;
1074 }
1075 
1076 #ifdef JOBS
1077 int
1078 c_fgbg(char **wp)
1079 {
1080 	int bg = strcmp(*wp, "bg") == 0;
1081 	int rv = 0;
1082 
1083 	if (!Flag(FMONITOR)) {
1084 		bi_errorf("job control not enabled");
1085 		return 1;
1086 	}
1087 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
1088 		return 1;
1089 	wp += builtin_opt.optind;
1090 	if (*wp)
1091 		for (; *wp; wp++)
1092 			rv = j_resume(*wp, bg);
1093 	else
1094 		rv = j_resume("%%", bg);
1095 	/* POSIX says fg shall return 0 (unless an error occurs).
1096 	 * at&t ksh returns the exit value of the job...
1097 	 */
1098 	return (bg || Flag(FPOSIX)) ? 0 : rv;
1099 }
1100 #endif
1101 
1102 struct kill_info {
1103 	int num_width;
1104 	int name_width;
1105 };
1106 static char *kill_fmt_entry(void *arg, int i, char *buf, int buflen);
1107 
1108 /* format a single kill item */
1109 static char *
1110 kill_fmt_entry(void *arg, int i, char *buf, int buflen)
1111 {
1112 	struct kill_info *ki = (struct kill_info *) arg;
1113 
1114 	i++;
1115 	if (sigtraps[i].name)
1116 		shf_snprintf(buf, buflen, "%*d %*s %s",
1117 		    ki->num_width, i,
1118 		    ki->name_width, sigtraps[i].name,
1119 		    sigtraps[i].mess);
1120 	else
1121 		shf_snprintf(buf, buflen, "%*d %*d %s",
1122 		    ki->num_width, i,
1123 		    ki->name_width, sigtraps[i].signal,
1124 		    sigtraps[i].mess);
1125 	return buf;
1126 }
1127 
1128 
1129 int
1130 c_kill(char **wp)
1131 {
1132 	Trap *t = (Trap *) 0;
1133 	char *p;
1134 	int lflag = 0;
1135 	int i, n, rv, sig;
1136 
1137 	/* assume old style options if -digits or -UPPERCASE */
1138 	if ((p = wp[1]) && *p == '-' && (digit(p[1]) || isupper(p[1]))) {
1139 		if (!(t = gettrap(p + 1, true))) {
1140 			bi_errorf("bad signal `%s'", p + 1);
1141 			return 1;
1142 		}
1143 		i = (wp[2] && strcmp(wp[2], "--") == 0) ? 3 : 2;
1144 	} else {
1145 		int optc;
1146 
1147 		while ((optc = ksh_getopt(wp, &builtin_opt, "ls:")) != -1)
1148 			switch (optc) {
1149 			case 'l':
1150 				lflag = 1;
1151 				break;
1152 			case 's':
1153 				if (!(t = gettrap(builtin_opt.optarg, true))) {
1154 					bi_errorf("bad signal `%s'",
1155 					    builtin_opt.optarg);
1156 					return 1;
1157 				}
1158 				break;
1159 			case '?':
1160 				return 1;
1161 			}
1162 		i = builtin_opt.optind;
1163 	}
1164 	if ((lflag && t) || (!wp[i] && !lflag)) {
1165 		shf_fprintf(shl_out,
1166 		    "usage: kill [-s signame | -signum | -signame] { job | pid | pgrp } ...\n"
1167 		    "       kill -l [exit_status ...]\n");
1168 		bi_errorf(null);
1169 		return 1;
1170 	}
1171 
1172 	if (lflag) {
1173 		if (wp[i]) {
1174 			for (; wp[i]; i++) {
1175 				if (!bi_getn(wp[i], &n))
1176 					return 1;
1177 				if (n > 128 && n < 128 + NSIG)
1178 					n -= 128;
1179 				if (n > 0 && n < NSIG && sigtraps[n].name)
1180 					shprintf("%s\n", sigtraps[n].name);
1181 				else
1182 					shprintf("%d\n", n);
1183 			}
1184 		} else if (Flag(FPOSIX)) {
1185 			p = null;
1186 			for (i = 1; i < NSIG; i++, p = space)
1187 				if (sigtraps[i].name)
1188 					shprintf("%s%s", p, sigtraps[i].name);
1189 			shprintf(newline);
1190 		} else {
1191 			int w, i;
1192 			int mess_width;
1193 			struct kill_info ki;
1194 
1195 			for (i = NSIG, ki.num_width = 1; i >= 10; i /= 10)
1196 				ki.num_width++;
1197 			ki.name_width = mess_width = 0;
1198 			for (i = 0; i < NSIG; i++) {
1199 				w = sigtraps[i].name ? strlen(sigtraps[i].name) :
1200 				    ki.num_width;
1201 				if (w > ki.name_width)
1202 					ki.name_width = w;
1203 				w = strlen(sigtraps[i].mess);
1204 				if (w > mess_width)
1205 					mess_width = w;
1206 			}
1207 
1208 			print_columns(shl_stdout, NSIG - 1,
1209 			    kill_fmt_entry, (void *) &ki,
1210 			    ki.num_width + ki.name_width + mess_width + 3, 1);
1211 		}
1212 		return 0;
1213 	}
1214 	rv = 0;
1215 	sig = t ? t->signal : SIGTERM;
1216 	for (; (p = wp[i]); i++) {
1217 		if (*p == '%') {
1218 			if (j_kill(p, sig))
1219 				rv = 1;
1220 		} else if (!getn(p, &n)) {
1221 			bi_errorf("%s: arguments must be jobs or process IDs",
1222 			    p);
1223 			rv = 1;
1224 		} else {
1225 			/* use killpg if < -1 since -1 does special things for
1226 			 * some non-killpg-endowed kills
1227 			 */
1228 			if ((n < -1 ? killpg(-n, sig) : kill(n, sig)) < 0) {
1229 				bi_errorf("%s: %s", p, strerror(errno));
1230 				rv = 1;
1231 			}
1232 		}
1233 	}
1234 	return rv;
1235 }
1236 
1237 void
1238 getopts_reset(int val)
1239 {
1240 	if (val >= 1) {
1241 		ksh_getopt_reset(&user_opt,
1242 		    GF_NONAME | (Flag(FPOSIX) ? 0 : GF_PLUSOPT));
1243 		user_opt.optind = user_opt.uoptind = val;
1244 	}
1245 }
1246 
1247 int
1248 c_getopts(char **wp)
1249 {
1250 	int	argc;
1251 	const char *options;
1252 	const char *var;
1253 	int	optc;
1254 	int	ret;
1255 	char	buf[3];
1256 	struct tbl *vq, *voptarg;
1257 
1258 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
1259 		return 1;
1260 	wp += builtin_opt.optind;
1261 
1262 	options = *wp++;
1263 	if (!options) {
1264 		bi_errorf("missing options argument");
1265 		return 1;
1266 	}
1267 
1268 	var = *wp++;
1269 	if (!var) {
1270 		bi_errorf("missing name argument");
1271 		return 1;
1272 	}
1273 	if (!*var || *skip_varname(var, true)) {
1274 		bi_errorf("%s: is not an identifier", var);
1275 		return 1;
1276 	}
1277 
1278 	if (e->loc->next == (struct block *) 0) {
1279 		internal_errorf(0, "c_getopts: no argv");
1280 		return 1;
1281 	}
1282 	/* Which arguments are we parsing... */
1283 	if (*wp == (char *) 0)
1284 		wp = e->loc->next->argv;
1285 	else
1286 		*--wp = e->loc->next->argv[0];
1287 
1288 	/* Check that our saved state won't cause a core dump... */
1289 	for (argc = 0; wp[argc]; argc++)
1290 		;
1291 	if (user_opt.optind > argc ||
1292 	    (user_opt.p != 0 &&
1293 	    user_opt.p > strlen(wp[user_opt.optind - 1]))) {
1294 		bi_errorf("arguments changed since last call");
1295 		return 1;
1296 	}
1297 
1298 	user_opt.optarg = (char *) 0;
1299 	optc = ksh_getopt(wp, &user_opt, options);
1300 
1301 	if (optc >= 0 && optc != '?' && (user_opt.info & GI_PLUS)) {
1302 		buf[0] = '+';
1303 		buf[1] = optc;
1304 		buf[2] = '\0';
1305 	} else {
1306 		/* POSIX says var is set to ? at end-of-options, at&t ksh
1307 		 * sets it to null - we go with POSIX...
1308 		 */
1309 		buf[0] = optc < 0 ? '?' : optc;
1310 		buf[1] = '\0';
1311 	}
1312 
1313 	/* at&t ksh does not change OPTIND if it was an unknown option.
1314 	 * Scripts counting on this are prone to break... (ie, don't count
1315 	 * on this staying).
1316 	 */
1317 	if (optc != '?') {
1318 		user_opt.uoptind = user_opt.optind;
1319 	}
1320 
1321 	voptarg = global("OPTARG");
1322 	voptarg->flag &= ~RDONLY;	/* at&t ksh clears ro and int */
1323 	/* Paranoia: ensure no bizarre results. */
1324 	if (voptarg->flag & INTEGER)
1325 	    typeset("OPTARG", 0, INTEGER, 0, 0);
1326 	if (user_opt.optarg == (char *) 0)
1327 		unset(voptarg, 0);
1328 	else
1329 		/* This can't fail (have cleared readonly/integer) */
1330 		setstr(voptarg, user_opt.optarg, KSH_RETURN_ERROR);
1331 
1332 	ret = 0;
1333 
1334 	vq = global(var);
1335 	/* Error message already printed (integer, readonly) */
1336 	if (!setstr(vq, buf, KSH_RETURN_ERROR))
1337 	    ret = 1;
1338 	if (Flag(FEXPORT))
1339 		typeset(var, EXPORT, 0, 0, 0);
1340 
1341 	return optc < 0 ? 1 : ret;
1342 }
1343 
1344 #ifdef EMACS
1345 int
1346 c_bind(char **wp)
1347 {
1348 	int optc, rv = 0, macro = 0, list = 0;
1349 	char *cp;
1350 
1351 	while ((optc = ksh_getopt(wp, &builtin_opt, "lm")) != -1)
1352 		switch (optc) {
1353 		case 'l':
1354 			list = 1;
1355 			break;
1356 		case 'm':
1357 			macro = 1;
1358 			break;
1359 		case '?':
1360 			return 1;
1361 		}
1362 	wp += builtin_opt.optind;
1363 
1364 	if (*wp == NULL)	/* list all */
1365 		rv = x_bind((char*)NULL, (char*)NULL, 0, list);
1366 
1367 	for (; *wp != NULL; wp++) {
1368 		cp = strchr(*wp, '=');
1369 		if (cp != NULL)
1370 			*cp++ = '\0';
1371 		if (x_bind(*wp, cp, macro, 0))
1372 			rv = 1;
1373 	}
1374 
1375 	return rv;
1376 }
1377 #endif
1378 
1379 /* A leading = means assignments before command are kept;
1380  * a leading * means a POSIX special builtin;
1381  * a leading + means a POSIX regular builtin
1382  * (* and + should not be combined).
1383  */
1384 const struct builtin kshbuiltins [] = {
1385 	{"+alias", c_alias},	/* no =: at&t manual wrong */
1386 	{"+cd", c_cd},
1387 	{"+command", c_command},
1388 	{"echo", c_print},
1389 	{"*=export", c_typeset},
1390 #ifdef HISTORY
1391 	{"+fc", c_fc},
1392 #endif /* HISTORY */
1393 	{"+getopts", c_getopts},
1394 	{"+jobs", c_jobs},
1395 	{"+kill", c_kill},
1396 	{"let", c_let},
1397 	{"print", c_print},
1398 	{"pwd", c_pwd},
1399 	{"*=readonly", c_typeset},
1400 	{"=typeset", c_typeset},
1401 	{"+unalias", c_unalias},
1402 	{"whence", c_whence},
1403 #ifdef JOBS
1404 	{"+bg", c_fgbg},
1405 	{"+fg", c_fgbg},
1406 #endif
1407 #ifdef EMACS
1408 	{"bind", c_bind},
1409 #endif
1410 	{NULL, NULL}
1411 };
1412