c_ksh.c revision 1.1 1 /*
2 * built-in Korn commands: c_*
3 */
4
5 #include "sh.h"
6 #include "ksh_stat.h"
7 #include <ctype.h>
8
9 int
10 c_cd(wp)
11 char **wp;
12 {
13 int optc;
14 int physical = Flag(FPHYSICAL);
15 int cdnode; /* was a node from cdpath added in? */
16 int printpath = 0; /* print where we cd'd? */
17 int rval;
18 struct tbl *pwd_s, *oldpwd_s;
19 XString xs;
20 char *xp;
21 char *dir, *try, *pwd;
22 int phys_path;
23 char *cdpath;
24
25 while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != EOF)
26 switch (optc) {
27 case 'L':
28 physical = 0;
29 break;
30 case 'P':
31 physical = 1;
32 break;
33 case '?':
34 return 1;
35 }
36 wp += builtin_opt.optind;
37
38 if (Flag(FRESTRICTED)) {
39 bi_errorf("restricted shell - can't cd");
40 return 1;
41 }
42
43 pwd_s = global("PWD");
44 oldpwd_s = global("OLDPWD");
45
46 if (!wp[0]) {
47 /* No arguments - go home */
48 if ((dir = str_val(global("HOME"))) == null) {
49 bi_errorf("no home directory (HOME not set)");
50 return 1;
51 }
52 } else if (!wp[1]) {
53 /* One argument: - or dir */
54 dir = wp[0];
55 if (strcmp(dir, "-") == 0) {
56 dir = str_val(oldpwd_s);
57 if (dir == null) {
58 bi_errorf("no OLDPWD");
59 return 1;
60 }
61 printpath++;
62 }
63 } else if (!wp[2]) {
64 /* Two arguments - substitute arg1 in PWD for arg2 */
65 int ilen, olen, nlen, elen;
66 char *cp;
67
68 if (!current_wd[0]) {
69 bi_errorf("don't know current directory");
70 return 1;
71 }
72 /* substitue arg1 for arg2 in current path.
73 * if the first substitution fails because the cd fails
74 * we could try to find another substitution. For now
75 * we don't
76 */
77 if ((cp = strstr(current_wd, wp[0])) == (char *) 0) {
78 bi_errorf("bad substitution");
79 return 1;
80 }
81 ilen = cp - current_wd;
82 olen = strlen(wp[0]);
83 nlen = strlen(wp[1]);
84 elen = strlen(current_wd + ilen + olen) + 1;
85 dir = alloc(ilen + nlen + elen, ATEMP);
86 memcpy(dir, current_wd, ilen);
87 memcpy(dir + ilen, wp[1], nlen);
88 memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
89 printpath++;
90 } else {
91 bi_errorf("too many arguments");
92 return 1;
93 }
94
95 Xinit(xs, xp, PATH, ATEMP);
96 /* xp will have a bogus value after make_path() - set it to 0
97 * so that if it's used, it will cause a dump
98 */
99 xp = (char *) 0;
100
101 cdpath = str_val(global("CDPATH"));
102 do {
103 cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path);
104 #ifdef S_ISLNK
105 if (physical)
106 rval = chdir(try = Xstring(xs, xp) + phys_path);
107 else
108 #endif /* S_ISLNK */
109 {
110 simplify_path(Xstring(xs, xp));
111 rval = chdir(try = Xstring(xs, xp));
112 }
113 } while (rval < 0 && cdpath != (char *) 0);
114
115 if (rval < 0) {
116 if (cdnode)
117 bi_errorf("%s: bad directory", dir);
118 else
119 bi_errorf("%s - %s", try, strerror(errno));
120 return 1;
121 }
122
123 /* Clear out tracked aliases with relative paths */
124 flushcom(0);
125
126 /* Set OLDPWD */
127 if (current_wd[0])
128 setstr(oldpwd_s, current_wd);
129
130 if (!ISABSPATH(Xstring(xs, xp))) {
131 #ifdef OS2
132 /* simplify_path() doesn't know about os/2's drive contexts,
133 * so it can't set current_wd when changing to a:foo.
134 * Handle this by calling getcwd()...
135 */
136 pwd = ksh_get_wd((char *) 0, 0);
137 #else /* OS2 */
138 pwd = (char *) 0;
139 #endif /* OS2 */
140 } else
141 #ifdef S_ISLNK
142 if (!physical || !(pwd = get_phys_path(Xstring(xs, xp))))
143 #endif /* S_ISLNK */
144 pwd = Xstring(xs, xp);
145
146 /* Set PWD */
147 if (pwd) {
148 set_current_wd(pwd);
149 setstr(pwd_s, pwd);
150 } else {
151 set_current_wd(null);
152 pwd = Xstring(xs, xp);
153 /* XXX unset $PWD? */
154 }
155 if (printpath || cdnode)
156 shprintf("%s\n", pwd);
157
158 return 0;
159 }
160
161 int
162 c_pwd(wp)
163 char **wp;
164 {
165 int optc;
166 int physical = Flag(FPHYSICAL);
167 char *p;
168
169 while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != EOF)
170 switch (optc) {
171 case 'L':
172 physical = 0;
173 break;
174 case 'P':
175 physical = 1;
176 break;
177 case '?':
178 return 1;
179 }
180 wp += builtin_opt.optind;
181
182 if (wp[0]) {
183 bi_errorf("too many arguments");
184 return 1;
185 }
186 #ifdef S_ISLNK
187 p = current_wd[0] ? (physical ? get_phys_path(current_wd) : current_wd)
188 : (char *) 0;
189 #else /* S_ISLNK */
190 p = current_wd[0] ? current_wd : (char *) 0;
191 #endif /* S_ISLNK */
192 if (p && eaccess(p, R_OK) < 0)
193 p = (char *) 0;
194 if (!p) {
195 p = ksh_get_wd((char *) 0, 0);
196 if (!p) {
197 bi_errorf("can't get current directory - %s",
198 strerror(errno));
199 return 1;
200 }
201 }
202 shprintf("%s\n", p);
203 return 0;
204 }
205
206 int
207 c_print(wp)
208 char **wp;
209 {
210 #define PO_NL BIT(0) /* print newline */
211 #define PO_EXPAND BIT(1) /* expand backslash sequences */
212 #define PO_PMINUSMINUS BIT(2) /* print a -- argument */
213 #define PO_HIST BIT(3) /* print to history instead of stdout */
214 #define PO_COPROC BIT(4) /* printing to coprocess: block SIGPIPE */
215 #define PO_FSLASH BIT(5) /* swap slash for backslash (for os2 ) */
216 int fd = 1;
217 int flags = PO_EXPAND|PO_NL;
218 char *s;
219 const char *emsg;
220 XString xs;
221 char *xp;
222
223 if (wp[0][0] == 'e') { /* echo command */
224 int nflags = flags;
225
226 /* A compromise between sysV and BSD echo commands:
227 * escape sequences are enabled by default, and
228 * -n, -e and -E are recognized if they appear
229 * in arguments with no illegal options (ie, echo -nq
230 * will print -nq).
231 * Different from sysV echo since options are recognized,
232 * different from BSD echo since escape sequences are enabled
233 * by default.
234 */
235 wp += 1;
236 while ((s = *wp) && *s == '-' && s[1]) {
237 while (*++s)
238 if (*s == 'n')
239 nflags &= ~PO_NL;
240 else if (*s == 'e')
241 nflags |= PO_EXPAND;
242 else if (*s == 'E')
243 nflags &= ~PO_EXPAND;
244 else
245 /* bad option: don't use nflags, print
246 * argument
247 */
248 break;
249 if (*s)
250 break;
251 wp++;
252 flags = nflags;
253 }
254 } else {
255 int optc;
256 #if OS2
257 const char *options = "Rnpfrsu,"; /* added f flag */
258 #else
259 const char *options = "Rnprsu,";
260 #endif
261 while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF)
262 switch (optc) {
263 case 'R': /* fake BSD echo command */
264 flags |= PO_PMINUSMINUS;
265 flags &= ~PO_EXPAND;
266 options = "ne";
267 break;
268 case 'e':
269 flags |= PO_EXPAND;
270 break;
271 #ifdef OS2
272 case 'f':
273 flags |= PO_FSLASH;
274 break;
275 #endif
276 case 'n':
277 flags &= ~PO_NL;
278 break;
279 #ifdef KSH
280 case 'p':
281 if ((fd = coproc_getfd(W_OK, &emsg)) < 0) {
282 bi_errorf("-p: %s", emsg);
283 return 1;
284 }
285 break;
286 #endif /* KSH */
287 case 'r':
288 flags &= ~PO_EXPAND;
289 break;
290 case 's':
291 flags |= PO_HIST;
292 break;
293 case 'u':
294 if (!*(s = builtin_opt.optarg))
295 fd = 0;
296 else if ((fd = check_fd(s, W_OK, &emsg)) < 0) {
297 bi_errorf("-u: %s: %s", s, emsg);
298 return 1;
299 }
300 break;
301 case '?':
302 return 1;
303 }
304 if (!(builtin_opt.info & GI_MINUSMINUS)) {
305 /* treat a lone - like -- */
306 if (wp[builtin_opt.optind]
307 && strcmp(wp[builtin_opt.optind], "-") == 0)
308 builtin_opt.optind++;
309 } else if (flags & PO_PMINUSMINUS)
310 builtin_opt.optind--;
311 wp += builtin_opt.optind;
312 }
313
314 Xinit(xs, xp, 128, ATEMP);
315
316 while (*wp != NULL) {
317 register int c;
318 s = *wp;
319 while ((c = *s++) != '\0') {
320 Xcheck(xs, xp);
321 #ifdef OS2
322 if ((flags & PO_FSLASH) && c == '\\')
323 if (*s == '\\')
324 *s++;
325 else
326 c = '/';
327 #endif /* OS2 */
328 if ((flags & PO_EXPAND) && c == '\\') {
329 int i;
330
331 switch ((c = *s++)) {
332 /* Oddly enough, \007 seems more portable than
333 * \a (due to HP-UX cc, Ultrix cc, old pcc's,
334 * etc.).
335 */
336 case 'a': c = '\007'; break;
337 case 'b': c = '\b'; break;
338 case 'c': flags &= ~PO_NL;
339 continue; /* AT&T brain damage */
340 case 'f': c = '\f'; break;
341 case 'n': c = '\n'; break;
342 case 'r': c = '\r'; break;
343 case 't': c = '\t'; break;
344 case 'v': c = 0x0B; break;
345 case '0':
346 /* Look for an octal number: can have
347 * three digits (not counting the
348 * leading 0). Truely burnt.
349 */
350 c = 0;
351 for (i = 0; i < 3; i++) {
352 if (*s >= '0' && *s <= '7')
353 c = c*8 + *s++ - '0';
354 else
355 break;
356 }
357 break;
358 case '\0': s--; c = '\\'; break;
359 case '\\': break;
360 default:
361 Xput(xs, xp, '\\');
362 }
363 }
364 Xput(xs, xp, c);
365 }
366 if (*++wp != NULL)
367 Xput(xs, xp, ' ');
368 }
369 if (flags & PO_NL)
370 Xput(xs, xp, '\n');
371
372 if (flags & PO_HIST) {
373 Xput(xs, xp, '\0');
374 source->line++;
375 histsave(source->line, Xstring(xs, xp), 1);
376 Xfree(xs, xp);
377 } else {
378 int n, len = Xlength(xs, xp);
379 #ifdef KSH
380 int UNINITIALIZED(opipe);
381
382 /* Ensure we aren't killed by a SIGPIPE while writing to
383 * a coprocess. at&t ksh doesn't seem to do this (seems
384 * to just check that the co-process is alive, which is
385 * not enough).
386 */
387 if (coproc.write >= 0 && coproc.write == fd) {
388 flags |= PO_COPROC;
389 opipe = block_pipe();
390 }
391 #endif /* KSH */
392 for (s = Xstring(xs, xp); len > 0; ) {
393 n = write(fd, s, len);
394 if (n < 0) {
395 if (flags & PO_COPROC)
396 restore_pipe(opipe);
397 if (errno == EINTR) {
398 /* allow user to ^C out */
399 intrcheck();
400 if (flags & PO_COPROC)
401 opipe = block_pipe();
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
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 = tsearch(&keywords, id, hash(id));
474 if (!tp && !pflag) {
475 tp = tsearch(&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(newline);
544 }
545 return ret;
546 }
547
548 /* Deal with command -vV - command -p dealt with in comexec() */
549 int
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
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, local = 0;
568 const char *options = "L#R#UZ#fi#lrtux"; /* 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 local = 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 seperate 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 pflag = 1;
636 break;
637 case 'r':
638 flag |= RDONLY;
639 break;
640 case 't':
641 flag |= TRACE;
642 break;
643 case 'u':
644 flag |= UCASEV_AL; /* upper case / autoload */
645 break;
646 case 'x':
647 flag |= EXPORT;
648 break;
649 case '?':
650 return 1;
651 }
652 if (builtin_opt.info & GI_PLUS) {
653 fclr |= flag;
654 fset &= ~flag;
655 thing = '+';
656 } else {
657 fset |= flag;
658 fclr &= ~flag;
659 thing = '-';
660 }
661 }
662
663 field = 0;
664 if (fieldstr && !bi_getn(fieldstr, &field))
665 return 1;
666 base = 0;
667 if (basestr && !bi_getn(basestr, &base))
668 return 1;
669
670 if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind]
671 && (wp[builtin_opt.optind][0] == '-'
672 || wp[builtin_opt.optind][0] == '+')
673 && wp[builtin_opt.optind][1] == '\0')
674 {
675 thing = wp[builtin_opt.optind][0];
676 builtin_opt.optind++;
677 }
678
679 if (func && ((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT))) {
680 bi_errorf("only -t, -u and -x options may be used with -f");
681 return 1;
682 }
683 if (wp[builtin_opt.optind]) {
684 /* Take care of exclusions */
685 /* setting these attributes clears the others, unless they
686 * are also set in this command
687 */
688 if (fset & (LJUST|RJUST|ZEROFIL|UCASEV_AL|LCASEV|INTEGER
689 |INT_U|INT_L))
690 fclr |= ~fset &
691 (LJUST|RJUST|ZEROFIL|UCASEV_AL|LCASEV|INTEGER
692 |INT_U|INT_L);
693 fclr &= ~fset; /* set wins */
694 if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) {
695 fset |= RJUST;
696 fclr &= ~RJUST;
697 }
698 if (fset & LCASEV) /* LCASEV has priority */
699 fclr |= UCASEV_AL;
700 else if (fset & UCASEV_AL)
701 fclr |= LCASEV;
702 if (fset & LJUST) /* LJUST has priority */
703 fclr |= RJUST;
704 else if (fset & RJUST)
705 fclr |= LJUST;
706 if ((fset | fclr) & INTEGER) {
707 if (!(fset | fclr) & INT_U)
708 fclr |= INT_U;
709 if (!(fset | fclr) & INT_L)
710 fclr |= INT_L;
711 }
712 fset &= ~fclr; /* in case of something like -LR */
713 }
714
715 /* set variables and attributes */
716 if (wp[builtin_opt.optind]) {
717 int i;
718 int rval = 0;
719 struct tbl *f;
720
721 if (local && !func)
722 fset |= LOCAL;
723 for (i = builtin_opt.optind; wp[i]; i++) {
724 if (func) {
725 f = findfunc(wp[i], hash(wp[i]),
726 (fset&UCASEV_AL) ? TRUE : FALSE);
727 if (!f) {
728 /* at&t ksh does ++rval: bogus */
729 rval = 1;
730 continue;
731 }
732 if (fset | fclr) {
733 f->flag |= fset;
734 f->flag &= ~fclr;
735 } else
736 fptreef(shl_stdout, 0,
737 "function %s %T\n",
738 wp[i], f->val.t);
739 } else if (!typeset(wp[i], fset, fclr, field, base)) {
740 bi_errorf("%s: not identifier", wp[i]);
741 return 1;
742 }
743 }
744 return rval;
745 }
746
747 /* list variables and attributes */
748 flag = fset | fclr; /* no difference at this point.. */
749 if (func) {
750 for (l = e->loc; l; l = l->next) {
751 for (p = tsort(&l->funs); (vp = *p++); ) {
752 if (flag && (vp->flag & flag) == 0)
753 continue;
754 if (thing == '-')
755 fptreef(shl_stdout, 0, "function %s %T\n",
756 vp->name, vp->val.t);
757 else
758 shprintf("%s\n", vp->name);
759 }
760 }
761 } else {
762 for (l = e->loc; l; l = l->next) {
763 for (p = tsort(&l->vars); (vp = *p++); )
764 for (; vp; vp = vp->u.array) {
765 if (flag && (vp->flag & flag) == 0)
766 continue;
767 /* no arguments */
768 if (thing == 0 && flag == 0) {
769 /* at&t ksh prints things like export, integer,
770 * leftadj, zerofill, etc., but POSIX says must
771 * be suitable for re-entry...
772 */
773 shprintf("typeset ");
774 if ((vp->flag&INTEGER))
775 shprintf("-i ");
776 if ((vp->flag&EXPORT))
777 shprintf("-x ");
778 if ((vp->flag&RDONLY))
779 shprintf("-r ");
780 if ((vp->flag&TRACE))
781 shprintf("-t ");
782 if ((vp->flag&LJUST))
783 shprintf("-L%d ", vp->u2.field);
784 if ((vp->flag&RJUST))
785 shprintf("-R%d ", vp->u2.field);
786 if ((vp->flag&ZEROFIL))
787 shprintf("-Z ");
788 if ((vp->flag&LCASEV))
789 shprintf("-l ");
790 if ((vp->flag&UCASEV_AL))
791 shprintf("-u ");
792 if ((vp->flag&INT_U))
793 shprintf("-U ");
794 if (vp->flag&ARRAY)
795 shprintf("%s[%d]\n", vp->name,vp->index);
796 else
797 shprintf("%s\n", vp->name);
798 } else {
799 if (pflag)
800 shprintf("%s ",
801 (flag & EXPORT) ? "export" : "readonly");
802 if (vp->flag&ARRAY)
803 shprintf("%s[%d]", vp->name, vp->index);
804 else
805 shprintf("%s", vp->name);
806 if (thing == '-' && (vp->flag&ISSET)) {
807 char *s = str_val(vp);
808
809 shprintf("=");
810 /* at&t ksh can't have justified integers.. */
811 if ((vp->flag & (INTEGER|LJUST|RJUST))
812 == INTEGER)
813 shprintf("%s", s);
814 else
815 print_value_quoted(s);
816 }
817 shprintf(newline);
818 }
819 }
820 }
821 }
822 return 0;
823 }
824
825 int
826 c_alias(wp)
827 char **wp;
828 {
829 struct table *t = &aliases;
830 int rv = 0, rflag = 0, tflag, Uflag = 0;
831 Tflag xflag = 0;
832 int optc;
833
834 while ((optc = ksh_getopt(wp, &builtin_opt, "drtUx")) != EOF)
835 switch (optc) {
836 case 'd':
837 t = &homedirs;
838 break;
839 case 'r':
840 rflag = 1;
841 break;
842 case 't':
843 t = &taliases;
844 break;
845 case 'U': /* kludge for tracked alias initialization
846 * (don't do a path search, just make an entry)
847 */
848 Uflag = 1;
849 break;
850 case 'x':
851 xflag = EXPORT;
852 break;
853 case '?':
854 return 1;
855 }
856 wp += builtin_opt.optind;
857
858 tflag = t == &taliases;
859
860 /* "hash -r" means reset all the tracked aliases.. */
861 if (rflag) {
862 static const char *const args[] = {
863 "unalias", "-ta", (const char *) 0
864 };
865
866 if (!tflag || *wp) {
867 shprintf(
868 "alias: -r flag can only be used with -t and without arguments\n");
869 return 1;
870 }
871 ksh_getopt_reset(&builtin_opt, GF_ERROR);
872 return c_unalias((char **) args);
873 }
874
875 if (*wp == NULL) {
876 struct tbl *ap, **p;
877
878 for (p = tsort(t); (ap = *p++) != NULL; )
879 if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) {
880 shprintf("%s=", ap->name);
881 print_value_quoted(ap->val.s);
882 shprintf(newline);
883 }
884 }
885
886 for (; *wp != NULL; wp++) {
887 char *alias = *wp;
888 char *val = strchr(alias, '=');
889 char *newval;
890 struct tbl *ap;
891 int h;
892
893 if (val)
894 alias = str_nsave(alias, val++ - alias, ATEMP);
895 h = hash(alias);
896 if (val == NULL && !tflag && !xflag) {
897 ap = tsearch(t, alias, h);
898 if (ap != NULL && (ap->flag&ISSET)) {
899 shprintf("%s=", ap->name);
900 print_value_quoted(ap->val.s);
901 shprintf(newline);
902 } else {
903 shprintf("%s alias not found\n", alias);
904 rv = 1;
905 }
906 continue;
907 }
908 ap = tenter(t, alias, h);
909 ap->type = tflag ? CTALIAS : CALIAS;
910 /* Are we setting the value or just some flags? */
911 if ((val && !tflag) || (!val && tflag && !Uflag)) {
912 if (ap->flag&ALLOC) {
913 ap->flag &= ~(ALLOC|ISSET);
914 afree((void*)ap->val.s, APERM);
915 }
916 /* ignore values for -t (at&t ksh does this) */
917 newval = tflag ? search(alias, path, X_OK, (int *) 0)
918 : val;
919 if (newval) {
920 ap->val.s = str_save(newval, APERM);
921 ap->flag |= ALLOC|ISSET;
922 } else
923 ap->flag &= ~ISSET;
924 }
925 ap->flag |= DEFINED|xflag;
926 if (val)
927 afree(alias, ATEMP);
928 }
929
930 return rv;
931 }
932
933 int
934 c_unalias(wp)
935 char **wp;
936 {
937 register struct table *t = &aliases;
938 register struct tbl *ap;
939 int rv = 0, all = 0;
940 int optc;
941
942 while ((optc = ksh_getopt(wp, &builtin_opt, "adt")) != EOF)
943 switch (optc) {
944 case 'a':
945 all = 1;
946 break;
947 case 'd':
948 t = &homedirs;
949 break;
950 case 't':
951 t = &taliases;
952 break;
953 case '?':
954 return 1;
955 }
956 wp += builtin_opt.optind;
957
958 for (; *wp != NULL; wp++) {
959 ap = tsearch(t, *wp, hash(*wp));
960 if (ap == NULL) {
961 rv = 1; /* POSIX */
962 continue;
963 }
964 if (ap->flag&ALLOC) {
965 ap->flag &= ~(ALLOC|ISSET);
966 afree((void*)ap->val.s, APERM);
967 }
968 ap->flag &= ~(DEFINED|ISSET|EXPORT);
969 }
970
971 if (all) {
972 struct tstate ts;
973
974 for (twalk(&ts, t); (ap = tnext(&ts)); ) {
975 if (ap->flag&ALLOC) {
976 ap->flag &= ~(ALLOC|ISSET);
977 afree((void*)ap->val.s, APERM);
978 }
979 ap->flag &= ~(DEFINED|ISSET|EXPORT);
980 }
981 }
982
983 return rv;
984 }
985
986 int
987 c_let(wp)
988 char **wp;
989 {
990 int rv = 1;
991 long val;
992
993 if (wp[1] == (char *) 0) /* at&t ksh does this */
994 bi_errorf("no arguments");
995 else
996 for (wp++; *wp; wp++)
997 if (!evaluate(*wp, &val, TRUE)) {
998 rv = 2; /* distinguish error from zero result */
999 break;
1000 } else
1001 rv = val == 0;
1002 return rv;
1003 }
1004
1005 int
1006 c_jobs(wp)
1007 char **wp;
1008 {
1009 int optc;
1010 int flag = 0;
1011 int nflag = 0;
1012 int rv = 0;
1013
1014 while ((optc = ksh_getopt(wp, &builtin_opt, "lpnz")) != EOF)
1015 switch (optc) {
1016 case 'l':
1017 flag = 1;
1018 break;
1019 case 'p':
1020 flag = 2;
1021 break;
1022 case 'n':
1023 nflag = 1;
1024 break;
1025 case 'z': /* debugging: print zombies */
1026 nflag = -1;
1027 break;
1028 case '?':
1029 return 1;
1030 }
1031 wp += builtin_opt.optind;
1032 if (!*wp)
1033 if (j_jobs((char *) 0, flag, nflag))
1034 rv = 1;
1035 else
1036 for (; *wp; wp++)
1037 if (j_jobs(*wp, flag, nflag))
1038 rv = 1;
1039 return rv;
1040 }
1041
1042 #ifdef JOBS
1043 int
1044 c_fgbg(wp)
1045 char **wp;
1046 {
1047 int bg = strcmp(*wp, "bg") == 0;
1048 int UNINITIALIZED(rv);
1049
1050 if (!Flag(FMONITOR)) {
1051 bi_errorf("job control not enabled");
1052 return 1;
1053 }
1054 if (ksh_getopt(wp, &builtin_opt, null) == '?')
1055 return 1;
1056 wp += builtin_opt.optind;
1057 if (*wp)
1058 for (; *wp; wp++)
1059 rv = j_resume(*wp, bg);
1060 else
1061 rv = j_resume("%%", bg);
1062 /* POSIX says fg shall return 0 (unless an error occurs).
1063 * at&t ksh returns the exit value of the job...
1064 */
1065 return (bg || Flag(FPOSIX)) ? 0 : rv;
1066 }
1067 #endif
1068
1069 struct kill_info {
1070 int num_width;
1071 int name_width;
1072 };
1073 static char *kill_fmt_entry ARGS((void *arg, int i, char *buf, int buflen));
1074
1075 /* format a single kill item */
1076 static char *
1077 kill_fmt_entry(arg, i, buf, buflen)
1078 void *arg;
1079 int i;
1080 char *buf;
1081 int buflen;
1082 {
1083 struct kill_info *ki = (struct kill_info *) arg;
1084
1085 i++;
1086 if (sigtraps[i].name)
1087 shf_snprintf(buf, buflen, "%*d %*s %s",
1088 ki->num_width, i,
1089 ki->name_width, sigtraps[i].name,
1090 sigtraps[i].mess);
1091 else
1092 shf_snprintf(buf, buflen, "%*d %*d %s",
1093 ki->num_width, i,
1094 ki->name_width, sigtraps[i].signal,
1095 sigtraps[i].mess);
1096 return buf;
1097 }
1098
1099
1100 int
1101 c_kill(wp)
1102 char **wp;
1103 {
1104 Trap *t = (Trap *) 0;
1105 char *p;
1106 int lflag = 0;
1107 int i, n, rv, sig;
1108
1109 /* assume old style options if -digits or -UPPERCASE */
1110 if ((p = wp[1]) && *p == '-' && (digit(p[1]) || isupper(p[1]))) {
1111 if (!(t = gettrap(p + 1))) {
1112 bi_errorf("bad signal `%s'", p + 1);
1113 return 1;
1114 }
1115 i = (wp[2] && strcmp(wp[2], "--") == 0) ? 3 : 2;
1116 } else {
1117 int optc;
1118
1119 while ((optc = ksh_getopt(wp, &builtin_opt, "ls:")) != EOF)
1120 switch (optc) {
1121 case 'l':
1122 lflag = 1;
1123 break;
1124 case 's':
1125 if (!(t = gettrap(builtin_opt.optarg))) {
1126 bi_errorf("bad signal `%s'",
1127 builtin_opt.optarg);
1128 return 1;
1129 }
1130 case '?':
1131 return 1;
1132 }
1133 i = builtin_opt.optind;
1134 }
1135 if ((lflag && t) || (!wp[i] && !lflag)) {
1136 shf_fprintf(shl_out,
1137 "Usage: kill [ -s signame | -signum | -signame ] {pid|job}...\n\
1138 kill -l [exit_status]\n"
1139 );
1140 bi_errorf(null);
1141 return 1;
1142 }
1143
1144 if (lflag) {
1145 if (wp[i]) {
1146 for (; wp[i]; i++) {
1147 if (!bi_getn(wp[i], &n))
1148 return 1;
1149 if (n > 128 && n < 128 + SIGNALS)
1150 n -= 128;
1151 if (n > 0 && n < SIGNALS && sigtraps[n].name)
1152 shprintf("%s\n", sigtraps[n].name);
1153 else
1154 shprintf("%d\n", n);
1155 }
1156 } else if (Flag(FPOSIX)) {
1157 p = null;
1158 for (i = 1; i < SIGNALS; i++, p = space)
1159 if (sigtraps[i].name)
1160 shprintf("%s%s", p, sigtraps[i].name);
1161 shprintf(newline);
1162 } else {
1163 int w, i;
1164 int mess_width;
1165 struct kill_info ki;
1166
1167 for (i = SIGNALS, ki.num_width = 1; i >= 10; i /= 10)
1168 ki.num_width++;
1169 ki.name_width = mess_width = 0;
1170 for (i = 0; i < SIGNALS; i++) {
1171 w = sigtraps[i].name ? strlen(sigtraps[i].name)
1172 : ki.num_width;
1173 if (w > ki.name_width)
1174 ki.name_width = w;
1175 w = strlen(sigtraps[i].mess);
1176 if (w > mess_width)
1177 mess_width = w;
1178 }
1179
1180 print_columns(shl_stdout, SIGNALS - 1,
1181 kill_fmt_entry, (void *) &ki,
1182 ki.num_width + ki.name_width + mess_width + 3);
1183 }
1184 return 0;
1185 }
1186 rv = 0;
1187 sig = t ? t->signal : SIGTERM;
1188 for (; (p = wp[i]); i++) {
1189 if (*p == '%') {
1190 if (j_kill(p, sig))
1191 rv = 1;
1192 } else if (!getn(p, &n)) {
1193 bi_errorf("%s: arguments must be jobs or process ids",
1194 p);
1195 rv = 1;
1196 } else {
1197 /* use killpg if < -1 since -1 does special things for
1198 * some non-killpg-endowed kills
1199 */
1200 if ((n < -1 ? killpg(-n, sig) : kill(n, sig)) < 0) {
1201 bi_errorf("%s: %s", p, strerror(errno));
1202 rv = 1;
1203 }
1204 }
1205 }
1206 return rv;
1207 }
1208
1209 static Getopt user_opt; /* parsing state for getopts builtin command */
1210 static int getopts_noset; /* stop OPTIND assign from resetting state */
1211
1212 void
1213 getopts_reset(val)
1214 int val;
1215 {
1216 if (!getopts_noset && val >= 1) {
1217 ksh_getopt_reset(&user_opt,
1218 GF_NONAME | (Flag(FPOSIX) ? 0 : GF_PLUSOPT));
1219 user_opt.optind = val;
1220 }
1221 }
1222
1223 int
1224 c_getopts(wp)
1225 char **wp;
1226 {
1227 int argc;
1228 const char *options;
1229 const char *var;
1230 int optc;
1231 char buf[3];
1232 struct tbl *vq;
1233
1234 if (ksh_getopt(wp, &builtin_opt, null) == '?')
1235 return 1;
1236 wp += builtin_opt.optind;
1237
1238 options = *wp++;
1239 if (!options) {
1240 bi_errorf("missing options argument");
1241 return 1;
1242 }
1243
1244 var = *wp++;
1245 if (!var) {
1246 bi_errorf("missing name argument");
1247 return 1;
1248 }
1249 if (!*var || *skip_varname(var, TRUE)) {
1250 bi_errorf("%s: is not an identifier", var);
1251 return 1;
1252 }
1253
1254 if (e->loc->next == (struct block *) 0) {
1255 internal_errorf(0, "c_getopts: no argv");
1256 return 1;
1257 }
1258 /* Which arguments are we parsing... */
1259 if (*wp == (char *) 0)
1260 wp = e->loc->next->argv;
1261 else
1262 *--wp = e->loc->next->argv[0];
1263
1264 /* Check that our saved state won't cause a core dump... */
1265 for (argc = 0; wp[argc]; argc++)
1266 ;
1267 if (user_opt.optind > argc
1268 || (user_opt.p != 0
1269 && user_opt.p > strlen(wp[user_opt.optind - 1])))
1270 {
1271 bi_errorf("arguments changed since last call");
1272 return 1;
1273 }
1274
1275 user_opt.optarg = (char *) 0;
1276 optc = ksh_getopt(wp, &user_opt, options);
1277
1278 if (optc >= 0 && optc != '?' && (user_opt.info & GI_PLUS)) {
1279 buf[0] = '+';
1280 buf[1] = optc;
1281 buf[2] = '\0';
1282 } else {
1283 /* POSIX says var is set to ? at end-of-options, at&t ksh
1284 * sets it to null - we go with POSIX...
1285 */
1286 buf[0] = optc < 0 ? '?' : optc;
1287 buf[1] = '\0';
1288 }
1289
1290 /* at&t ksh does not change OPTIND if it was an unknown option.
1291 * Scripts counting on this are prone to break... (ie, don't count
1292 * on this staying).
1293 */
1294 if (optc != '?') {
1295 getopts_noset = 1;
1296 setint(global("OPTIND"), (long) user_opt.optind);
1297 getopts_noset = 0;
1298 }
1299
1300 if (user_opt.optarg == (char *) 0)
1301 unset(global("OPTARG"), 0);
1302 else
1303 setstr(global("OPTARG"), user_opt.optarg);
1304
1305 vq = global(var);
1306 if (vq->flag & RDONLY) {
1307 bi_errorf("%s is readonly", var);
1308 return 1;
1309 }
1310 if (Flag(FEXPORT))
1311 typeset(var, EXPORT, 0, 0, 0);
1312 setstr(vq, buf);
1313
1314 return optc < 0 ? 1 : 0;
1315 }
1316
1317 #ifdef EMACS
1318 int
1319 c_bind(wp)
1320 char **wp;
1321 {
1322 int rv = 0, macro = 0, list = 0;
1323 register char *cp;
1324 int optc;
1325
1326 while ((optc = ksh_getopt(wp, &builtin_opt, "lm")) != EOF)
1327 switch (optc) {
1328 case 'l':
1329 list = 1;
1330 break;
1331 case 'm':
1332 macro = 1;
1333 break;
1334 case '?':
1335 return 1;
1336 }
1337 wp += builtin_opt.optind;
1338
1339 if (*wp == NULL) /* list all */
1340 rv = x_bind((char*)NULL, (char*)NULL, 0, list);
1341
1342 for (; *wp != NULL; wp++) {
1343 cp = strchr(*wp, '=');
1344 if (cp != NULL)
1345 *cp++ = '\0';
1346 if (x_bind(*wp, cp, macro, 0))
1347 rv = 1;
1348 }
1349
1350 return rv;
1351 }
1352 #endif
1353
1354 /* A leading = means assignments before command are kept;
1355 * a leading * means a POSIX special builtin;
1356 * a leading + means a POSIX regular builtin
1357 * (* and + should not be combined).
1358 */
1359 const struct builtin kshbuiltins [] = {
1360 {"+alias", c_alias}, /* no =: at&t manual wrong */
1361 {"+cd", c_cd},
1362 {"+command", c_command},
1363 {"echo", c_print},
1364 {"*=export", c_typeset},
1365 #ifdef HISTORY
1366 {"+fc", c_fc},
1367 #endif /* HISTORY */
1368 {"+getopts", c_getopts},
1369 {"+jobs", c_jobs},
1370 {"+kill", c_kill},
1371 {"let", c_let},
1372 {"print", c_print},
1373 {"pwd", c_pwd},
1374 {"*=readonly", c_typeset},
1375 {"=typeset", c_typeset},
1376 {"+unalias", c_unalias},
1377 {"whence", c_whence},
1378 #ifdef JOBS
1379 {"+bg", c_fgbg},
1380 {"+fg", c_fgbg},
1381 #endif
1382 #ifdef EMACS
1383 {"bind", c_bind},
1384 #endif
1385 {NULL, NULL}
1386 };
1387