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