]> git.lizzy.rs Git - plan9front.git/blob - sys/src/ape/cmd/pdksh/eval.c
ape: initialize _tos and use _tos->pid for getpid()
[plan9front.git] / sys / src / ape / cmd / pdksh / eval.c
1 /*
2  * Expansion - quoting, separation, substitution, globbing
3  */
4
5 #include "sh.h"
6 #include <pwd.h>
7 #include "ksh_dir.h"
8 #include "ksh_stat.h"
9
10 /*
11  * string expansion
12  *
13  * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution.
14  * second pass: alternation ({,}), filename expansion (*?[]).
15  */
16
17 /* expansion generator state */
18 typedef struct Expand {
19         /* int  type; */        /* see expand() */
20         const char *str;        /* string */
21         union {
22                 const char **strv;/* string[] */
23                 struct shf *shf;/* file */
24         } u;                    /* source */
25         struct tbl *var;        /* variable in ${var..} */
26         short   split;          /* split "$@" / call waitlast $() */
27 } Expand;
28
29 #define XBASE           0       /* scanning original */
30 #define XSUB            1       /* expanding ${} string */
31 #define XARGSEP         2       /* ifs0 between "$*" */
32 #define XARG            3       /* expanding $*, $@ */
33 #define XCOM            4       /* expanding $() */
34 #define XNULLSUB        5       /* "$@" when $# is 0 (don't generate word) */
35
36 /* States used for field splitting */
37 #define IFS_WORD        0       /* word has chars (or quotes) */
38 #define IFS_WS          1       /* have seen IFS white-space */
39 #define IFS_NWS         2       /* have seen IFS non-white-space */
40
41 static  int     varsub ARGS((Expand *xp, char *sp, char *word, int *stypep, int *slenp));
42 static  int     comsub ARGS((Expand *xp, char *cp));
43 static  char   *trimsub ARGS((char *str, char *pat, int how));
44 static  void    glob ARGS((char *cp, XPtrV *wp, int markdirs));
45 static  void    globit ARGS((XString *xs, char **xpp, char *sp, XPtrV *wp,
46                              int check));
47 static char     *maybe_expand_tilde ARGS((char *p, XString *dsp, char **dpp,
48                                           int isassign));
49 static  char   *tilde ARGS((char *acp));
50 static  char   *homedir ARGS((char *name));
51 #ifdef BRACE_EXPAND
52 static void     alt_expand ARGS((XPtrV *wp, char *start, char *exp_start,
53                                  char *end, int fdo));
54 #endif
55
56 /* compile and expand word */
57 char *
58 substitute(cp, f)
59         const char *cp;
60         int f;
61 {
62         struct source *s, *sold;
63
64         sold = source;
65         s = pushs(SWSTR, ATEMP);
66         s->start = s->str = cp;
67         source = s;
68         if (yylex(ONEWORD) != LWORD)
69                 internal_errorf(1, "substitute");
70         source = sold;
71         afree(s, ATEMP);
72         return evalstr(yylval.cp, f);
73 }
74
75 /*
76  * expand arg-list
77  */
78 char **
79 eval(ap, f)
80         register char **ap;
81         int f;
82 {
83         XPtrV w;
84
85         if (*ap == NULL)
86                 return ap;
87         XPinit(w, 32);
88         XPput(w, NULL);         /* space for shell name */
89 #ifdef  SHARPBANG
90         XPput(w, NULL);         /* and space for one arg */
91 #endif
92         while (*ap != NULL)
93                 expand(*ap++, &w, f);
94         XPput(w, NULL);
95 #ifdef  SHARPBANG
96         return (char **) XPclose(w) + 2;
97 #else
98         return (char **) XPclose(w) + 1;
99 #endif
100 }
101
102 /*
103  * expand string
104  */
105 char *
106 evalstr(cp, f)
107         char *cp;
108         int f;
109 {
110         XPtrV w;
111
112         XPinit(w, 1);
113         expand(cp, &w, f);
114         cp = (XPsize(w) == 0) ? null : (char*) *XPptrv(w);
115         XPfree(w);
116         return cp;
117 }
118
119 /*
120  * expand string - return only one component
121  * used from iosetup to expand redirection files
122  */
123 char *
124 evalonestr(cp, f)
125         register char *cp;
126         int f;
127 {
128         XPtrV w;
129
130         XPinit(w, 1);
131         expand(cp, &w, f);
132         switch (XPsize(w)) {
133         case 0:
134                 cp = null;
135                 break;
136         case 1:
137                 cp = (char*) *XPptrv(w);
138                 break;
139         default:
140                 cp = evalstr(cp, f&~DOGLOB);
141                 break;
142         }
143         XPfree(w);
144         return cp;
145 }
146
147 /* for nested substitution: ${var:=$var2} */
148 typedef struct SubType {
149         short   stype;          /* [=+-?%#] action after expanded word */
150         short   base;           /* begin position of expanded word */
151         short   f;              /* saved value of f (DOPAT, etc) */
152         struct tbl *var;        /* variable for ${var..} */
153         short   quote;          /* saved value of quote (for ${..[%#]..}) */
154         struct SubType *prev;   /* old type */
155         struct SubType *next;   /* poped type (to avoid re-allocating) */
156 } SubType;
157
158 void
159 expand(cp, wp, f)
160         char *cp;               /* input word */
161         register XPtrV *wp;     /* output words */
162         int f;                  /* DO* flags */
163 {
164         register int UNINITIALIZED(c);
165         register int type;      /* expansion type */
166         register int quote = 0; /* quoted */
167         XString ds;             /* destination string */
168         register char *dp, *sp; /* dest., source */
169         int fdo, word;          /* second pass flags; have word */
170         int doblank;            /* field spliting of parameter/command subst */
171         Expand x;               /* expansion variables */
172         SubType st_head, *st;
173         int UNINITIALIZED(newlines); /* For trailing newlines in COMSUB */
174         int saw_eq, tilde_ok;
175         int make_magic;
176
177         if (cp == NULL)
178                 internal_errorf(1, "expand(NULL)");
179         /* for alias, readonly, set, typeset commands */
180         if ((f & DOVACHECK) && is_wdvarassign(cp)) {
181                 f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE);
182                 f |= DOASNTILDE;
183         }
184         if (Flag(FNOGLOB))
185                 f &= ~DOGLOB;
186         if (Flag(FMARKDIRS))
187                 f |= DOMARKDIRS;
188 #ifdef BRACE_EXPAND
189         if (Flag(FBRACEEXPAND) && (f & DOGLOB))
190                 f |= DOBRACE_;
191 #endif /* BRACE_EXPAND */
192
193         Xinit(ds, dp, 128, ATEMP);      /* init dest. string */
194         type = XBASE;
195         sp = cp;
196         fdo = 0;
197         saw_eq = 0;
198         tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; /* must be 1/0 */
199         doblank = 0;
200         make_magic = 0;
201         word = (f&DOBLANK) ? IFS_WS : IFS_WORD;
202         st_head.next = (SubType *) 0;
203         st = &st_head;
204
205         while (1) {
206                 Xcheck(ds, dp);
207
208                 switch (type) {
209                   case XBASE:   /* original prefixed string */
210                         c = *sp++;
211                         switch (c) {
212                           case EOS:
213                                 c = 0;
214                                 break;
215                           case CHAR:
216                                 c = *sp++;
217                                 break;
218                           case QCHAR:
219                                 quote |= 2; /* temporary quote */
220                                 c = *sp++;
221                                 break;
222                           case OQUOTE:
223                                 word = IFS_WORD;
224                                 tilde_ok = 0;
225                                 quote = 1;
226                                 continue;
227                           case CQUOTE:
228                                 quote = 0;
229                                 continue;
230                           case COMSUB:
231                                 tilde_ok = 0;
232                                 if (f & DONTRUNCOMMAND) {
233                                         word = IFS_WORD;
234                                         *dp++ = '$'; *dp++ = '(';
235                                         while (*sp != '\0') {
236                                                 Xcheck(ds, dp);
237                                                 *dp++ = *sp++;
238                                         }
239                                         *dp++ = ')';
240                                 } else {
241                                         type = comsub(&x, sp);
242                                         if (type == XCOM && (f&DOBLANK))
243                                                 doblank++;
244                                         sp = strchr(sp, 0) + 1;
245                                         newlines = 0;
246                                 }
247                                 continue;
248                           case EXPRSUB:
249                                 word = IFS_WORD;
250                                 tilde_ok = 0;
251                                 if (f & DONTRUNCOMMAND) {
252                                         *dp++ = '$'; *dp++ = '('; *dp++ = '(';
253                                         while (*sp != '\0') {
254                                                 Xcheck(ds, dp);
255                                                 *dp++ = *sp++;
256                                         }
257                                         *dp++ = ')'; *dp++ = ')';
258                                 } else {
259                                         struct tbl v;
260                                         char *p;
261
262                                         v.flag = DEFINED|ISSET|INTEGER;
263                                         v.type = 10; /* not default */
264                                         v.name[0] = '\0';
265                                         v_evaluate(&v, substitute(sp, 0),
266                                                 KSH_UNWIND_ERROR);
267                                         sp = strchr(sp, 0) + 1;
268                                         for (p = str_val(&v); *p; ) {
269                                                 Xcheck(ds, dp);
270                                                 *dp++ = *p++;
271                                         }
272                                 }
273                                 continue;
274                           case OSUBST: /* ${{#}var{:}[=+-?#%]word} */
275                           /* format is:
276                            *   OSUBST [{x] plain-variable-part \0
277                            *     compiled-word-part CSUBST [}x]
278                            * This is were all syntax checking gets done...
279                            */
280                           {
281                                 char *varname = ++sp; /* skip the { or x (}) */
282                                 int stype;
283                                 int slen;
284
285                                 sp = strchr(sp, '\0') + 1; /* skip variable */
286                                 type = varsub(&x, varname, sp, &stype, &slen);
287                                 if (type < 0) {
288                                         char endc;
289                                         char *str, *end;
290
291                                         end = (char *) wdscan(sp, CSUBST);
292                                         /* ({) the } or x is already skipped */
293                                         endc = *end;
294                                         *end = EOS;
295                                         str = snptreef((char *) 0, 64, "%S",
296                                                         varname - 1);
297                                         *end = endc;
298                                         errorf("%s: bad substitution", str);
299                                 }
300                                 if (f&DOBLANK)
301                                         doblank++;
302                                 tilde_ok = 0;
303                                 if (type == XBASE) {    /* expand? */
304                                         if (!st->next) {
305                                                 SubType *newst;
306
307                                                 newst = (SubType *) alloc(
308                                                         sizeof(SubType), ATEMP);
309                                                 newst->next = (SubType *) 0;
310                                                 newst->prev = st;
311                                                 st->next = newst;
312                                         }
313                                         st = st->next;
314                                         st->stype = stype;
315                                         st->base = Xsavepos(ds, dp);
316                                         st->f = f;
317                                         st->var = x.var;
318                                         st->quote = quote;
319                                         /* skip qualifier(s) */
320                                         if (stype)
321                                                 sp += slen;
322                                         switch (stype & 0x7f) {
323                                           case '#':
324                                           case '%':
325                                                 /* ! DOBLANK,DOBRACE_,DOTILDE */
326                                                 f = DOPAT | (f&DONTRUNCOMMAND)
327                                                     | DOTEMP_;
328                                                 quote = 0;
329                                                 /* Prepend open pattern (so |
330                                                  * in a trim will work as
331                                                  * expected)
332                                                  */
333                                                 *dp++ = MAGIC;
334                                                 *dp++ = '@' + 0x80;
335                                                 break;
336                                           case '=':
337                                                 /* Enabling tilde expansion
338                                                  * after :'s here is
339                                                  * non-standard ksh, but is
340                                                  * consistent with rules for
341                                                  * other assignments.  Not
342                                                  * sure what POSIX thinks of
343                                                  * this.
344                                                  * Not doing tilde expansion
345                                                  * for integer variables is a
346                                                  * non-POSIX thing - makes
347                                                  * sense though, since ~ is
348                                                  * a arithmetic operator.
349                                                  */
350                                                 if (!(x.var->flag & INTEGER))
351                                                         f |= DOASNTILDE|DOTILDE;
352                                                 f |= DOTEMP_;
353                                                 /* These will be done after the
354                                                  * value has been assigned.
355                                                  */
356                                                 f &= ~(DOBLANK|DOGLOB|DOBRACE_);
357                                                 tilde_ok = 1;
358                                                 break;
359                                           case '?':
360                                                 f &= ~DOBLANK;
361                                                 f |= DOTEMP_;
362                                                 /* fall through */
363                                           default:
364                                                 /* Enable tilde expansion */
365                                                 tilde_ok = 1;
366                                                 f |= DOTILDE;
367                                         }
368                                 } else
369                                         /* skip word */
370                                         sp = (char *) wdscan(sp, CSUBST);
371                                 continue;
372                           }
373                           case CSUBST: /* only get here if expanding word */
374                                 sp++; /* ({) skip the } or x */
375                                 tilde_ok = 0;   /* in case of ${unset:-} */
376                                 *dp = '\0';
377                                 quote = st->quote;
378                                 f = st->f;
379                                 if (f&DOBLANK)
380                                         doblank--;
381                                 switch (st->stype&0x7f) {
382                                   case '#':
383                                   case '%':
384                                         /* Append end-pattern */
385                                         *dp++ = MAGIC; *dp++ = ')'; *dp = '\0';
386                                         dp = Xrestpos(ds, dp, st->base);
387                                         /* Must use st->var since calling
388                                          * global would break things
389                                          * like x[i+=1].
390                                          */
391                                         x.str = trimsub(str_val(st->var),
392                                                 dp, st->stype);
393                                         type = XSUB;
394                                         if (f&DOBLANK)
395                                                 doblank++;
396                                         st = st->prev;
397                                         continue;
398                                   case '=':
399                                         /* Restore our position and substitute
400                                          * the value of st->var (may not be
401                                          * the assigned value in the presence
402                                          * of integer/right-adj/etc attributes).
403                                          */
404                                         dp = Xrestpos(ds, dp, st->base);
405                                         /* Must use st->var since calling
406                                          * global would cause with things
407                                          * like x[i+=1] to be evaluated twice.
408                                          */
409                                         /* Note: not exported by FEXPORT
410                                          * in at&t ksh.
411                                          */
412                                         /* XXX POSIX says readonly is only
413                                          * fatal for special builtins (setstr
414                                          * does readonly check).
415                                          */
416                                         setstr(st->var, debunk(
417                                                 (char *) alloc(strlen(dp) + 1,
418                                                         ATEMP), dp),
419                                                 KSH_UNWIND_ERROR);
420                                         x.str = str_val(st->var);
421                                         type = XSUB;
422                                         if (f&DOBLANK)
423                                                 doblank++;
424                                         st = st->prev;
425                                         continue;
426                                   case '?':
427                                     {
428                                         char *s = Xrestpos(ds, dp, st->base);
429
430                                         errorf("%s: %s", st->var->name,
431                                             dp == s ? 
432                                               "parameter null or not set"
433                                             : (debunk(s, s), s));
434                                     }
435                                 }
436                                 st = st->prev;
437                                 type = XBASE;
438                                 continue;
439
440                           case OPAT: /* open pattern: *(foo|bar) */
441                                 /* Next char is the type of pattern */
442                                 make_magic = 1;
443                                 c = *sp++ + 0x80;
444                                 break;
445
446                           case SPAT: /* pattern seperator (|) */
447                                 make_magic = 1;
448                                 c = '|';
449                                 break;
450
451                           case CPAT: /* close pattern */
452                                 make_magic = 1;
453                                 c = /*(*/ ')';
454                                 break;
455                         }
456                         break;
457
458                   case XNULLSUB:
459                         /* Special case for "$@" (and "${foo[@]}") - no
460                          * word is generated if $# is 0 (unless there is
461                          * other stuff inside the quotes).
462                          */
463                         type = XBASE;
464                         if (f&DOBLANK) {
465                                 doblank--;
466                                 /* not really correct: x=; "$x$@" should
467                                  * generate a null argument and
468                                  * set A; "${@:+}" shouldn't.
469                                  */
470                                 if (dp == Xstring(ds, dp))
471                                         word = IFS_WS;
472                         }
473                         continue;
474
475                   case XSUB:
476                         if ((c = *x.str++) == 0) {
477                                 type = XBASE;
478                                 if (f&DOBLANK)
479                                         doblank--;
480                                 continue;
481                         }
482                         break;
483
484                   case XARGSEP:
485                         type = XARG;
486                         quote = 1;
487                   case XARG:
488                         if ((c = *x.str++) == '\0') {
489                                 /* force null words to be created so
490                                  * set -- '' 2 ''; foo "$@" will do
491                                  * the right thing
492                                  */
493                                 if (quote && x.split)
494                                         word = IFS_WORD;
495                                 if ((x.str = *x.u.strv++) == NULL) {
496                                         type = XBASE;
497                                         if (f&DOBLANK)
498                                                 doblank--;
499                                         continue;
500                                 }
501                                 c = ifs0;
502                                 if (c == 0) {
503                                         if (quote && !x.split)
504                                                 continue;
505                                         c = ' ';
506                                 }
507                                 if (quote && x.split) {
508                                         /* terminate word for "$@" */
509                                         type = XARGSEP;
510                                         quote = 0;
511                                 }
512                         }
513                         break;
514
515                   case XCOM:
516                         if (newlines) {         /* Spit out saved nl's */
517                                 c = '\n';
518                                 --newlines;
519                         } else {
520                                 while ((c = shf_getc(x.u.shf)) == 0 || c == '\n')
521                                     if (c == '\n')
522                                             newlines++; /* Save newlines */
523                                 if (newlines && c != EOF) {
524                                         shf_ungetc(c, x.u.shf);
525                                         c = '\n';
526                                         --newlines;
527                                 }
528                         }
529                         if (c == EOF) {
530                                 newlines = 0;
531                                 shf_close(x.u.shf);
532                                 if (x.split)
533                                         subst_exstat = waitlast();
534                                 type = XBASE;
535                                 if (f&DOBLANK)
536                                         doblank--;
537                                 continue;
538                         }
539                         break;
540                 }
541
542                 /* check for end of word or IFS separation */
543                 if (c == 0 || (!quote && (f & DOBLANK) && doblank && !make_magic
544                                && ctype(c, C_IFS)))
545                 {
546                         /* How words are broken up:
547                          *                 |       value of c
548                          *        word     |    ws      nws     0
549                          *      -----------------------------------
550                          *      IFS_WORD        w/WS    w/NWS   w
551                          *      IFS_WS          -/WS    w/NWS   -
552                          *      IFS_NWS         -/NWS   w/NWS   w
553                          *   (w means generate a word)
554                          * Note that IFS_NWS/0 generates a word (at&t ksh
555                          * doesn't do this, but POSIX does).
556                          */
557                         if (word == IFS_WORD
558                             || (!ctype(c, C_IFSWS) && (c || word == IFS_NWS)))
559                         {
560                                 char *p;
561
562                                 *dp++ = '\0';
563                                 p = Xclose(ds, dp);
564 #ifdef BRACE_EXPAND
565                                 if (fdo & DOBRACE_)
566                                         /* also does globbing */
567                                         alt_expand(wp, p, p,
568                                                    p + Xlength(ds, (dp - 1)),
569                                                    fdo | (f & DOMARKDIRS));
570                                 else
571 #endif /* BRACE_EXPAND */
572                                 if (fdo & DOGLOB)
573                                         glob(p, wp, f & DOMARKDIRS);
574                                 else if ((f & DOPAT) || !(fdo & DOMAGIC_))
575                                         XPput(*wp, p);
576                                 else
577                                         XPput(*wp, debunk(p, p));
578                                 fdo = 0;
579                                 saw_eq = 0;
580                                 tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
581                                 if (c != 0)
582                                         Xinit(ds, dp, 128, ATEMP);
583                         }
584                         if (c == 0)
585                                 return;
586                         if (word != IFS_NWS)
587                                 word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
588                 } else {
589                         /* age tilde_ok info - ~ code tests second bit */
590                         tilde_ok <<= 1;
591                         /* mark any special second pass chars */
592                         if (!quote)
593                                 switch (c) {
594                                   case '[':
595                                   case NOT:
596                                   case '-':
597                                   case ']':
598                                         /* For character classes - doesn't hurt
599                                          * to have magic !,-,]'s outside of
600                                          * [...] expressions.
601                                          */
602                                         if (f & (DOPAT | DOGLOB)) {
603                                                 fdo |= DOMAGIC_;
604                                                 if (c == '[')
605                                                         fdo |= f & DOGLOB;
606                                                 *dp++ = MAGIC;
607                                         }
608                                         break;
609                                   case '*':
610                                   case '?':
611                                         if (f & (DOPAT | DOGLOB)) {
612                                                 fdo |= DOMAGIC_ | (f & DOGLOB);
613                                                 *dp++ = MAGIC;
614                                         }
615                                         break;
616 #ifdef BRACE_EXPAND
617                                   case OBRACE:
618                                   case ',':
619                                   case CBRACE:
620                                         if ((f & DOBRACE_) && (c == OBRACE
621                                                 || (fdo & DOBRACE_)))
622                                         {
623                                                 fdo |= DOBRACE_|DOMAGIC_;
624                                                 *dp++ = MAGIC;
625                                         }
626                                         break;
627 #endif /* BRACE_EXPAND */
628                                   case '=':
629                                         /* Note first unquoted = for ~ */
630                                         if (!(f & DOTEMP_) && !saw_eq) {
631                                                 saw_eq = 1;
632                                                 tilde_ok = 1;
633                                         }
634                                         break;
635                                   case PATHSEP: /* : */
636                                         /* Note unquoted : for ~ */
637                                         if (!(f & DOTEMP_) && (f & DOASNTILDE))
638                                                 tilde_ok = 1;
639                                         break;
640                                   case '~':
641                                         /* tilde_ok is reset whenever
642                                          * any of ' " $( $(( ${ } are seen.
643                                          * Note that tilde_ok must be preserved
644                                          * through the sequence ${A=a=}~
645                                          */
646                                         if (type == XBASE
647                                             && (f & (DOTILDE|DOASNTILDE))
648                                             && (tilde_ok & 2))
649                                         {
650                                                 char *p, *dp_x;
651
652                                                 dp_x = dp;
653                                                 p = maybe_expand_tilde(sp,
654                                                         &ds, &dp_x,
655                                                         f & DOASNTILDE);
656                                                 if (p) {
657                                                         if (dp != dp_x)
658                                                                 word = IFS_WORD;
659                                                         dp = dp_x;
660                                                         sp = p;
661                                                         continue;
662                                                 }
663                                         }
664                                         break;
665                                 }
666                         else
667                                 quote &= ~2; /* undo temporary */
668
669                         if (make_magic) {
670                                 make_magic = 0;
671                                 fdo |= DOMAGIC_ | (f & DOGLOB);
672                                 *dp++ = MAGIC;
673                         } else if (ISMAGIC(c)) {
674                                 fdo |= DOMAGIC_;
675                                 *dp++ = MAGIC;
676                         }
677                         *dp++ = c; /* save output char */
678                         word = IFS_WORD;
679                 }
680         }
681 }
682
683 /*
684  * Prepare to generate the string returned by ${} substitution.
685  */
686 static int
687 varsub(xp, sp, word, stypep, slenp)
688         Expand *xp;
689         char *sp;
690         char *word;
691         int *stypep;    /* becomes qualifier type */
692         int *slenp;     /* " " len (=, :=, etc.) valid iff *stypep != 0 */
693 {
694         int c;
695         int state;      /* next state: XBASE, XARG, XSUB, XNULLSUB */
696         int stype;      /* substitution type */
697         int slen;
698         char *p;
699         struct tbl *vp;
700
701         if (sp[0] == '\0')      /* Bad variable name */
702                 return -1;
703
704         xp->var = (struct tbl *) 0;
705
706         /* ${#var}, string length or array size */
707         if (sp[0] == '#' && (c = sp[1]) != '\0') {
708                 int zero_ok = 0;
709
710                 /* Can't have any modifiers for ${#...} */
711                 if (*word != CSUBST)
712                         return -1;
713                 sp++;
714                 /* Check for size of array */
715                 if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') {
716                         int n = 0;
717                         int max = 0;
718                         vp = global(arrayname(sp));
719                         if (vp->flag & (ISSET|ARRAY))
720                                 zero_ok = 1;
721                         for (; vp; vp = vp->u.array)
722                                 if (vp->flag & ISSET) {
723                                         max = vp->index + 1;
724                                         n++;
725                                 }
726                         c = n; /* ksh88/ksh93 go for number, not max index */
727                 } else if (c == '*' || c == '@')
728                         c = e->loc->argc;
729                 else {
730                         p = str_val(global(sp));
731                         zero_ok = p != null;
732                         c = strlen(p);
733                 }
734                 if (Flag(FNOUNSET) && c == 0 && !zero_ok)
735                         errorf("%s: parameter not set", sp);
736                 *stypep = 0; /* unqualified variable/string substitution */
737                 xp->str = str_save(ulton((unsigned long)c, 10), ATEMP);
738                 return XSUB;
739         }
740
741         /* Check for qualifiers in word part */
742         stype = 0;
743         c = word[slen = 0] == CHAR ? word[1] : 0;
744         if (c == ':') {
745                 slen += 2;
746                 stype = 0x80;
747                 c = word[slen + 0] == CHAR ? word[slen + 1] : 0;
748         }
749         if (ctype(c, C_SUBOP1)) {
750                 slen += 2;
751                 stype |= c;
752         } else if (ctype(c, C_SUBOP2)) { /* Note: ksh88 allows :%, :%%, etc */
753                 slen += 2;
754                 stype = c;
755                 if (word[slen + 0] == CHAR && c == word[slen + 1]) {
756                         stype |= 0x80;
757                         slen += 2;
758                 }
759         } else if (stype)       /* : is not ok */
760                 return -1;
761         if (!stype && *word != CSUBST)
762                 return -1;
763         *stypep = stype;
764         *slenp = slen;
765
766         c = sp[0];
767         if (c == '*' || c == '@') {
768                 switch (stype & 0x7f) {
769                   case '=':     /* can't assign to a vector */
770                   case '%':     /* can't trim a vector (yet) */
771                   case '#':
772                         return -1;
773                 }
774                 if (e->loc->argc == 0) {
775                         xp->str = null;
776                         state = c == '@' ? XNULLSUB : XSUB;
777                 } else {
778                         xp->u.strv = (const char **) e->loc->argv + 1;
779                         xp->str = *xp->u.strv++;
780                         xp->split = c == '@'; /* $@ */
781                         state = XARG;
782                 }
783         } else {
784                 if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') {
785                         XPtrV wv;
786
787                         switch (stype & 0x7f) {
788                           case '=':     /* can't assign to a vector */
789                           case '%':     /* can't trim a vector (yet) */
790                           case '#':
791                                 return -1;
792                         }
793                         XPinit(wv, 32);
794                         vp = global(arrayname(sp));
795                         for (; vp; vp = vp->u.array) {
796                                 if (!(vp->flag&ISSET))
797                                         continue;
798                                 XPput(wv, str_val(vp));
799                         }
800                         if (XPsize(wv) == 0) {
801                                 xp->str = null;
802                                 state = p[1] == '@' ? XNULLSUB : XSUB;
803                                 XPfree(wv);
804                         } else {
805                                 XPput(wv, 0);
806                                 xp->u.strv = (const char **) XPptrv(wv);
807                                 xp->str = *xp->u.strv++;
808                                 xp->split = p[1] == '@'; /* ${foo[@]} */
809                                 state = XARG;
810                         }
811                 } else {
812                         /* Can't assign things like $! or $1 */
813                         if ((stype & 0x7f) == '='
814                             && (ctype(*sp, C_VAR1) || digit(*sp)))
815                                 return -1;
816                         xp->var = global(sp);
817                         xp->str = str_val(xp->var);
818                         state = XSUB;
819                 }
820         }
821
822         c = stype&0x7f;
823         /* test the compiler's code generator */
824         if (ctype(c, C_SUBOP2) ||
825             (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
826              c == '=' || c == '-' || c == '?' : c == '+'))
827                 state = XBASE;  /* expand word instead of variable value */
828         if (Flag(FNOUNSET) && xp->str == null
829             && (ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))
830                 errorf("%s: parameter not set", sp);
831         return state;
832 }
833
834 /*
835  * Run the command in $(...) and read its output.
836  */
837 static int
838 comsub(xp, cp)
839         register Expand *xp;
840         char *cp;
841 {
842         Source *s, *sold;
843         register struct op *t;
844         struct shf *shf;
845
846         s = pushs(SSTRING, ATEMP);
847         s->start = s->str = cp;
848         sold = source;
849         t = compile(s);
850         source = sold;
851
852         if (t == NULL)
853                 return XBASE;
854
855         if (t != NULL && t->type == TCOM && /* $(<file) */
856             *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
857                 register struct ioword *io = *t->ioact;
858                 char *name;
859
860                 if ((io->flag&IOTYPE) != IOREAD)
861                         errorf("funny $() command: %s",
862                                 snptreef((char *) 0, 32, "%R", io));
863                 shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0,
864                         SHF_MAPHI|SHF_CLEXEC);
865                 if (shf == NULL)
866                         errorf("%s: cannot open $() input", name);
867                 xp->split = 0;  /* no waitlast() */
868         } else {
869                 int ofd1, pv[2];
870                 openpipe(pv);
871                 shf = shf_fdopen(pv[0], SHF_RD, (struct shf *) 0);
872                 ofd1 = savefd(1, 0);    /* fd 1 may be closed... */
873                 ksh_dup2(pv[1], 1, FALSE);
874                 close(pv[1]);
875                 execute(t, XFORK|XXCOM|XPIPEO);
876                 restfd(1, ofd1);
877                 startlast();
878                 xp->split = 1;  /* waitlast() */
879         }
880
881         xp->u.shf = shf;
882         return XCOM;
883 }
884
885 /*
886  * perform #pattern and %pattern substitution in ${}
887  */
888
889 static char *
890 trimsub(str, pat, how)
891         register char *str;
892         char *pat;
893         int how;
894 {
895         register char *end = strchr(str, 0);
896         register char *p, c;
897
898         switch (how&0xff) {     /* UCHAR_MAX maybe? */
899           case '#':             /* shortest at begining */
900                 for (p = str; p <= end; p++) {
901                         c = *p; *p = '\0';
902                         if (gmatch(str, pat, FALSE)) {
903                                 *p = c;
904                                 return p;
905                         }
906                         *p = c;
907                 }
908                 break;
909           case '#'|0x80:        /* longest match at begining */
910                 for (p = end; p >= str; p--) {
911                         c = *p; *p = '\0';
912                         if (gmatch(str, pat, FALSE)) {
913                                 *p = c;
914                                 return p;
915                         }
916                         *p = c;
917                 }
918                 break;
919           case '%':             /* shortest match at end */
920                 for (p = end; p >= str; p--) {
921                         if (gmatch(p, pat, FALSE))
922                                 return str_nsave(str, p - str, ATEMP);
923                 }
924                 break;
925           case '%'|0x80:        /* longest match at end */
926                 for (p = str; p <= end; p++) {
927                         if (gmatch(p, pat, FALSE))
928                                 return str_nsave(str, p - str, ATEMP);
929                 }
930                 break;
931         }
932
933         return str;             /* no match, return string */
934 }
935
936 /*
937  * glob
938  * Name derived from V6's /etc/glob, the program that expanded filenames.
939  */
940
941 /* XXX cp not const 'cause slashes are temporarily replaced with nulls... */
942 static void
943 glob(cp, wp, markdirs)
944         char *cp;
945         register XPtrV *wp;
946         int markdirs;
947 {
948         int oldsize = XPsize(*wp);
949
950         if (glob_str(cp, wp, markdirs) == 0)
951                 XPput(*wp, debunk(cp, cp));
952         else
953                 qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize),
954                         xstrcmp);
955 }
956
957 #define GF_NONE         0
958 #define GF_EXCHECK      BIT(0)          /* do existance check on file */
959 #define GF_GLOBBED      BIT(1)          /* some globbing has been done */
960 #define GF_MARKDIR      BIT(2)          /* add trailing / to directories */
961
962 /* Apply file globbing to cp and store the matching files in wp.  Returns
963  * the number of matches found.
964  */
965 int
966 glob_str(cp, wp, markdirs)
967         char *cp;
968         XPtrV *wp;
969         int markdirs;
970 {
971         int oldsize = XPsize(*wp);
972         XString xs;
973         char *xp;
974
975         Xinit(xs, xp, 256, ATEMP);
976         globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE);
977         Xfree(xs, xp);
978
979         return XPsize(*wp) - oldsize;
980 }
981
982 static void
983 globit(xs, xpp, sp, wp, check)
984         XString *xs;            /* dest string */
985         char **xpp;             /* ptr to dest end */
986         char *sp;               /* source path */
987         register XPtrV *wp;     /* output list */
988         int check;              /* GF_* flags */
989 {
990         register char *np;      /* next source component */
991         char *xp = *xpp;
992         char *se;
993         char odirsep;
994
995         /* This to allow long expansions to be interrupted */
996         intrcheck();
997
998         if (sp == NULL) {       /* end of source path */
999                 /* We only need to check if the file exists if a pattern
1000                  * is followed by a non-pattern (eg, foo*x/bar; no check
1001                  * is needed for foo* since the match must exist) or if
1002                  * any patterns were expanded and the markdirs option is set.
1003                  * Symlinks make things a bit tricky...
1004                  */
1005                 if ((check & GF_EXCHECK)
1006                     || ((check & GF_MARKDIR) && (check & GF_GLOBBED)))
1007                 {
1008 #define stat_check()    (stat_done ? stat_done : \
1009                             (stat_done = stat(Xstring(*xs, xp), &statb) < 0 \
1010                                 ? -1 : 1))
1011                         struct stat lstatb, statb;
1012                         int stat_done = 0;       /* -1: failed, 1 ok */
1013
1014                         if (lstat(Xstring(*xs, xp), &lstatb) < 0)
1015                                 return;
1016                         /* special case for systems which strip trailing
1017                          * slashes from regular files (eg, /etc/passwd/).
1018                          * SunOS 4.1.3 does this...
1019                          */
1020                         if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp)
1021                             && ISDIRSEP(xp[-1]) && !S_ISDIR(lstatb.st_mode)
1022 #ifdef S_ISLNK
1023                             && (!S_ISLNK(lstatb.st_mode)
1024                                 || stat_check() < 0
1025                                 || !S_ISDIR(statb.st_mode))
1026 #endif /* S_ISLNK */
1027                                 )
1028                                 return;
1029                         /* Possibly tack on a trailing / if there isn't already
1030                          * one and if the file is a directory or a symlink to a
1031                          * directory
1032                          */
1033                         if (((check & GF_MARKDIR) && (check & GF_GLOBBED))
1034                             && xp > Xstring(*xs, xp) && !ISDIRSEP(xp[-1])
1035                             && (S_ISDIR(lstatb.st_mode)
1036 #ifdef S_ISLNK
1037                                 || (S_ISLNK(lstatb.st_mode)
1038                                     && stat_check() > 0
1039                                     && S_ISDIR(statb.st_mode))
1040 #endif /* S_ISLNK */
1041                                     ))
1042                         {
1043                                 *xp++ = DIRSEP;
1044                                 *xp = '\0';
1045                         }
1046                 }
1047 #ifdef OS2 /* Done this way to avoid bug in gcc 2.7.2... */
1048     /* Ugly kludge required for command
1049      * completion - see how search_access()
1050      * is implemented for OS/2...
1051      */
1052 # define KLUDGE_VAL     4
1053 #else /* OS2 */
1054 # define KLUDGE_VAL     0
1055 #endif /* OS2 */
1056                 XPput(*wp, str_nsave(Xstring(*xs, xp), Xlength(*xs, xp)
1057                         + KLUDGE_VAL, ATEMP));
1058                 return;
1059         }
1060
1061         if (xp > Xstring(*xs, xp))
1062                 *xp++ = DIRSEP;
1063         while (ISDIRSEP(*sp)) {
1064                 Xcheck(*xs, xp);
1065                 *xp++ = *sp++;
1066         }
1067         np = ksh_strchr_dirsep(sp);
1068         if (np != NULL) {
1069                 se = np;
1070                 odirsep = *np;  /* don't assume DIRSEP, can be multiple kinds */
1071                 *np++ = '\0';
1072         } else {
1073                 odirsep = '\0'; /* keep gcc quiet */
1074                 se = sp + strlen(sp);
1075         }
1076
1077
1078         /* Check if sp needs globbing - done to avoid pattern checks for strings
1079          * containing MAGIC characters, open ['s without the matching close ],
1080          * etc. (otherwise opendir() will be called which may fail because the
1081          * directory isn't readable - if no globbing is needed, only execute
1082          * permission should be required (as per POSIX)).
1083          */
1084         if (!has_globbing(sp, se)) {
1085                 XcheckN(*xs, xp, se - sp + 1);
1086                 debunk(xp, sp);
1087                 xp += strlen(xp);
1088                 *xpp = xp;
1089                 globit(xs, xpp, np, wp, check);
1090         } else {
1091                 DIR *dirp;
1092                 struct dirent *d;
1093                 char *name;
1094                 int len;
1095                 int prefix_len;
1096
1097                 /* xp = *xpp;      copy_non_glob() may have re-alloc'd xs */
1098                 *xp = '\0';
1099                 prefix_len = Xlength(*xs, xp);
1100                 dirp = ksh_opendir(prefix_len ? Xstring(*xs, xp) : ".");
1101                 if (dirp == NULL)
1102                         goto Nodir;
1103                 while ((d = readdir(dirp)) != NULL) {
1104                         name = d->d_name;
1105                         if (name[0] == '.' &&
1106                             (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
1107                                 continue; /* always ignore . and .. */
1108                         if ((*name == '.' && *sp != '.')
1109                             || !gmatch(name, sp, TRUE))
1110                                 continue;
1111
1112                         len = NLENGTH(d) + 1;
1113                         XcheckN(*xs, xp, len);
1114                         memcpy(xp, name, len);
1115                         *xpp = xp + len - 1;
1116                         globit(xs, xpp, np, wp,
1117                                 (check & GF_MARKDIR) | GF_GLOBBED
1118                                 | (np ? GF_EXCHECK : GF_NONE));
1119                         xp = Xstring(*xs, xp) + prefix_len;
1120                 }
1121                 closedir(dirp);
1122           Nodir:;
1123         }
1124
1125         if (np != NULL)
1126                 *--np = odirsep;
1127 }
1128
1129 #if 0
1130 /* Check if p contains something that needs globbing; if it does, 0 is
1131  * returned; if not, p is copied into xs/xp after stripping any MAGICs
1132  */
1133 static int      copy_non_glob ARGS((XString *xs, char **xpp, char *p));
1134 static int
1135 copy_non_glob(xs, xpp, p)
1136         XString *xs;
1137         char **xpp;
1138         char *p;
1139 {
1140         char *xp;
1141         int len = strlen(p);
1142
1143         XcheckN(*xs, *xpp, len);
1144         xp = *xpp;
1145         for (; *p; p++) {
1146                 if (ISMAGIC(*p)) {
1147                         int c = *++p;
1148
1149                         if (c == '*' || c == '?')
1150                                 return 0;
1151                         if (*p == '[') {
1152                                 char *q = p + 1;
1153
1154                                 if (ISMAGIC(*q) && q[1] == NOT)
1155                                         q += 2;
1156                                 if (ISMAGIC(*q) && q[1] == ']')
1157                                         q += 2;
1158                                 for (; *q; q++)
1159                                         if (ISMAGIC(*q) && *++q == ']')
1160                                                 return 0;
1161                                 /* pass a literal [ through */
1162                         }
1163                         /* must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, etc. */
1164                 }
1165                 *xp++ = *p;
1166         }
1167         *xp = '\0';
1168         *xpp = xp;
1169         return 1;
1170 }
1171 #endif /* 0 */
1172
1173 /* remove MAGIC from string */
1174 char *
1175 debunk(dp, sp)
1176         char *dp;
1177         const char *sp;
1178 {
1179         char *d, *s;
1180
1181         if ((s = strchr(sp, MAGIC))) {
1182                 memcpy(dp, sp, s - sp);
1183                 for (d = dp + (s - sp); *s; s++)
1184                         if (!ISMAGIC(*s) || !(*++s & 0x80)
1185                             || !strchr("*+?@! ", *s & 0x7f))
1186                                 *d++ = *s;
1187                         else {
1188                                 /* extended pattern operators: *+?@! */
1189                                 if ((*s & 0x7f) != ' ')
1190                                         *d++ = *s & 0x7f;
1191                                 *d++ = '(';
1192                         }
1193                 *d = '\0';
1194         } else if (dp != sp)
1195                 strcpy(dp, sp);
1196         return dp;
1197 }
1198
1199 /* Check if p is an unquoted name, possibly followed by a / or :.  If so
1200  * puts the expanded version in *dcp,dp and returns a pointer in p just
1201  * past the name, otherwise returns 0.
1202  */
1203 static char *
1204 maybe_expand_tilde(p, dsp, dpp, isassign)
1205         char *p;
1206         XString *dsp;
1207         char **dpp;
1208         int isassign;
1209 {
1210         XString ts;
1211         char *dp = *dpp;
1212         char *tp, *r;
1213
1214         Xinit(ts, tp, 16, ATEMP);
1215         /* : only for DOASNTILDE form */
1216         while (p[0] == CHAR && !ISDIRSEP(p[1])
1217                && (!isassign || p[1] != PATHSEP))
1218         {
1219                 Xcheck(ts, tp);
1220                 *tp++ = p[1];
1221                 p += 2;
1222         }
1223         *tp = '\0';
1224         r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ? tilde(Xstring(ts, tp)) : (char *) 0;
1225         Xfree(ts, tp);
1226         if (r) {
1227                 while (*r) {
1228                         Xcheck(*dsp, dp);
1229                         if (ISMAGIC(*r))
1230                                 *dp++ = MAGIC;
1231                         *dp++ = *r++;
1232                 }
1233                 *dpp = dp;
1234                 r = p;
1235         }
1236         return r;
1237 }
1238
1239 /*
1240  * tilde expansion
1241  *
1242  * based on a version by Arnold Robbins
1243  */
1244
1245 static char *
1246 tilde(cp)
1247         char *cp;
1248 {
1249         char *dp;
1250
1251         if (cp[0] == '\0')
1252                 dp = str_val(global("HOME"));
1253         else if (cp[0] == '+' && cp[1] == '\0')
1254                 dp = str_val(global("PWD"));
1255         else if (cp[0] == '-' && cp[1] == '\0')
1256                 dp = str_val(global("OLDPWD"));
1257         else
1258                 dp = homedir(cp);
1259         /* If HOME, PWD or OLDPWD are not set, don't expand ~ */
1260         if (dp == null)
1261                 dp = (char *) 0;
1262         return dp;
1263 }
1264
1265 /*
1266  * map userid to user's home directory.
1267  * note that 4.3's getpw adds more than 6K to the shell,
1268  * and the YP version probably adds much more.
1269  * we might consider our own version of getpwnam() to keep the size down.
1270  */
1271
1272 static char *
1273 homedir(name)
1274         char *name;
1275 {
1276         register struct tbl *ap;
1277
1278         ap = tenter(&homedirs, name, hash(name));
1279         if (!(ap->flag & ISSET)) {
1280 #ifdef OS2
1281                 /* No usernames in OS2 - punt */
1282                 return NULL;
1283 #else /* OS2 */
1284                 struct passwd *pw;
1285
1286                 pw = getpwnam(name);
1287                 if (pw == NULL)
1288                         return NULL;
1289                 ap->val.s = str_save(pw->pw_dir, APERM);
1290                 ap->flag |= DEFINED|ISSET|ALLOC;
1291 #endif /* OS2 */
1292         }
1293         return ap->val.s;
1294 }
1295
1296 #ifdef BRACE_EXPAND
1297 static void
1298 alt_expand(wp, start, exp_start, end, fdo)
1299         XPtrV *wp;
1300         char *start, *exp_start;
1301         char *end;
1302         int fdo;
1303 {
1304         int UNINITIALIZED(count);
1305         char *brace_start, *brace_end, *UNINITIALIZED(comma);
1306         char *field_start;
1307         char *p;
1308
1309         /* search for open brace */
1310         for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != OBRACE; p += 2)
1311                 ;
1312         brace_start = p;
1313
1314         /* find matching close brace, if any */
1315         if (p) {
1316                 comma = (char *) 0;
1317                 count = 1;
1318                 for (p += 2; *p && count; p++) {
1319                         if (ISMAGIC(*p)) {
1320                                 if (*++p == OBRACE)
1321                                         count++;
1322                                 else if (*p == CBRACE)
1323                                         --count;
1324                                 else if (*p == ',' && count == 1)
1325                                         comma = p;
1326                         }
1327                 }
1328         }
1329         /* no valid expansions... */
1330         if (!p || count != 0) {
1331                 /* Note that given a{{b,c} we do not expand anything (this is
1332                  * what at&t ksh does.  This may be changed to do the {b,c}
1333                  * expansion. }
1334                  */
1335                 if (fdo & DOGLOB)
1336                         glob(start, wp, fdo & DOMARKDIRS);
1337                 else
1338                         XPput(*wp, debunk(start, start));
1339                 return;
1340         }
1341         brace_end = p;
1342         if (!comma) {
1343                 alt_expand(wp, start, brace_end, end, fdo);
1344                 return;
1345         }
1346
1347         /* expand expression */
1348         field_start = brace_start + 2;
1349         count = 1;
1350         for (p = brace_start + 2; p != brace_end; p++) {
1351                 if (ISMAGIC(*p)) {
1352                         if (*++p == OBRACE)
1353                                 count++;
1354                         else if ((*p == CBRACE && --count == 0)
1355                                  || (*p == ',' && count == 1))
1356                         {
1357                                 char *new;
1358                                 int l1, l2, l3;
1359
1360                                 l1 = brace_start - start;
1361                                 l2 = (p - 1) - field_start;
1362                                 l3 = end - brace_end;
1363                                 new = (char *) alloc(l1 + l2 + l3 + 1, ATEMP);
1364                                 memcpy(new, start, l1);
1365                                 memcpy(new + l1, field_start, l2);
1366                                 memcpy(new + l1 + l2, brace_end, l3);
1367                                 new[l1 + l2 + l3] = '\0';
1368                                 alt_expand(wp, new, new + l1,
1369                                            new + l1 + l2 + l3, fdo);
1370                                 field_start = p + 1;
1371                         }
1372                 }
1373         }
1374         return;
1375 }
1376 #endif /* BRACE_EXPAND */