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