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