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