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