OSDN Git Service

f9c189d2782d984debebe32704707f935aacdf5d
[android-x86/external-mksh.git] / src / eval.c
1 /*      $OpenBSD: eval.c,v 1.40 2013/09/14 20:09:30 millert Exp $       */
2
3 /*-
4  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
5  *               2011, 2012, 2013, 2014, 2015
6  *      Thorsten Glaser <tg@mirbsd.org>
7  *
8  * Provided that these terms and disclaimer and all copyright notices
9  * are retained or reproduced in an accompanying document, permission
10  * is granted to deal in this work without restriction, including un-
11  * limited rights to use, publicly perform, distribute, sell, modify,
12  * merge, give away, or sublicence.
13  *
14  * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
15  * the utmost extent permitted by applicable law, neither express nor
16  * implied; without malicious intent or gross negligence. In no event
17  * may a licensor, author or contributor be held liable for indirect,
18  * direct, other damage, loss, or other issues arising in any way out
19  * of dealing in the work, even if advised of the possibility of such
20  * damage or existence of a defect, except proven that it results out
21  * of said person's immediate fault when using the work as intended.
22  */
23
24 #include "sh.h"
25
26 __RCSID("$MirOS: src/bin/mksh/eval.c,v 1.158.2.4 2015/03/01 15:42:58 tg Exp $");
27
28 /*
29  * string expansion
30  *
31  * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution.
32  * second pass: alternation ({,}), filename expansion (*?[]).
33  */
34
35 /* expansion generator state */
36 typedef struct {
37         /* not including an "int type;" member, see expand() */
38         /* string */
39         const char *str;
40         /* source */
41         union {
42                 /* string[] */
43                 const char **strv;
44                 /* file */
45                 struct shf *shf;
46         } u;
47         /* variable in ${var...} */
48         struct tbl *var;
49         /* split "$@" / call waitlast in $() */
50         bool split;
51 } Expand;
52
53 #define XBASE           0       /* scanning original */
54 #define XSUB            1       /* expanding ${} string */
55 #define XARGSEP         2       /* ifs0 between "$*" */
56 #define XARG            3       /* expanding $*, $@ */
57 #define XCOM            4       /* expanding $() */
58 #define XNULLSUB        5       /* "$@" when $# is 0 (don't generate word) */
59 #define XSUBMID         6       /* middle of expanding ${} */
60
61 /* States used for field splitting */
62 #define IFS_WORD        0       /* word has chars (or quotes except "$@") */
63 #define IFS_WS          1       /* have seen IFS white-space */
64 #define IFS_NWS         2       /* have seen IFS non-white-space */
65 #define IFS_IWS         3       /* begin of word, ignore IFS WS */
66 #define IFS_QUOTE       4       /* beg.w/quote, become IFS_WORD unless "$@" */
67
68 static int varsub(Expand *, const char *, const char *, int *, int *);
69 static int comsub(Expand *, const char *, int);
70 static char *valsub(struct op *, Area *);
71 static char *trimsub(char *, char *, int);
72 static void glob(char *, XPtrV *, bool);
73 static void globit(XString *, char **, char *, XPtrV *, int);
74 static const char *maybe_expand_tilde(const char *, XString *, char **, bool);
75 #ifndef MKSH_NOPWNAM
76 static char *homedir(char *);
77 #endif
78 static void alt_expand(XPtrV *, char *, char *, char *, int);
79 static int utflen(const char *) MKSH_A_PURE;
80 static void utfincptr(const char *, mksh_ari_t *);
81
82 /* UTFMODE functions */
83 static int
84 utflen(const char *s)
85 {
86         size_t n;
87
88         if (UTFMODE) {
89                 n = 0;
90                 while (*s) {
91                         s += utf_ptradj(s);
92                         ++n;
93                 }
94         } else
95                 n = strlen(s);
96
97         if (n > 2147483647)
98                 n = 2147483647;
99         return ((int)n);
100 }
101
102 static void
103 utfincptr(const char *s, mksh_ari_t *lp)
104 {
105         const char *cp = s;
106
107         while ((*lp)--)
108                 cp += utf_ptradj(cp);
109         *lp = cp - s;
110 }
111
112 /* compile and expand word */
113 char *
114 substitute(const char *cp, int f)
115 {
116         struct source *s, *sold;
117
118         sold = source;
119         s = pushs(SWSTR, ATEMP);
120         s->start = s->str = cp;
121         source = s;
122         if (yylex(ONEWORD) != LWORD)
123                 internal_errorf("bad substitution");
124         source = sold;
125         afree(s, ATEMP);
126         return (evalstr(yylval.cp, f));
127 }
128
129 /*
130  * expand arg-list
131  */
132 char **
133 eval(const char **ap, int f)
134 {
135         XPtrV w;
136
137         if (*ap == NULL) {
138                 union mksh_ccphack vap;
139
140                 vap.ro = ap;
141                 return (vap.rw);
142         }
143         XPinit(w, 32);
144         /* space for shell name */
145         XPput(w, NULL);
146         while (*ap != NULL)
147                 expand(*ap++, &w, f);
148         XPput(w, NULL);
149         return ((char **)XPclose(w) + 1);
150 }
151
152 /*
153  * expand string
154  */
155 char *
156 evalstr(const char *cp, int f)
157 {
158         XPtrV w;
159         char *dp = null;
160
161         XPinit(w, 1);
162         expand(cp, &w, f);
163         if (XPsize(w))
164                 dp = *XPptrv(w);
165         XPfree(w);
166         return (dp);
167 }
168
169 /*
170  * expand string - return only one component
171  * used from iosetup to expand redirection files
172  */
173 char *
174 evalonestr(const char *cp, int f)
175 {
176         XPtrV w;
177         char *rv;
178
179         XPinit(w, 1);
180         expand(cp, &w, f);
181         switch (XPsize(w)) {
182         case 0:
183                 rv = null;
184                 break;
185         case 1:
186                 rv = (char *) *XPptrv(w);
187                 break;
188         default:
189                 rv = evalstr(cp, f&~DOGLOB);
190                 break;
191         }
192         XPfree(w);
193         return (rv);
194 }
195
196 /* for nested substitution: ${var:=$var2} */
197 typedef struct SubType {
198         struct tbl *var;        /* variable for ${var..} */
199         struct SubType *prev;   /* old type */
200         struct SubType *next;   /* poped type (to avoid re-allocating) */
201         size_t  base;           /* begin position of expanded word */
202         short   stype;          /* [=+-?%#] action after expanded word */
203         short   f;              /* saved value of f (DOPAT, etc) */
204         uint8_t quotep;         /* saved value of quote (for ${..[%#]..}) */
205         uint8_t quotew;         /* saved value of quote (for ${..[+-=]..}) */
206 } SubType;
207
208 void
209 expand(
210     /* input word */
211     const char *ccp,
212     /* output words */
213     XPtrV *wp,
214     /* DO* flags */
215     int f)
216 {
217         int c = 0;
218         /* expansion type */
219         int type;
220         /* quoted */
221         int quote = 0;
222         /* destination string and live pointer */
223         XString ds;
224         char *dp;
225         /* source */
226         const char *sp;
227         /* second pass flags */
228         int fdo;
229         /* have word */
230         int word;
231         /* field splitting of parameter/command substitution */
232         int doblank;
233         /* expansion variables */
234         Expand x = {
235                 NULL, { NULL }, NULL, 0
236         };
237         SubType st_head, *st;
238         /* record number of trailing newlines in COMSUB */
239         int newlines = 0;
240         bool saw_eq, make_magic;
241         unsigned int tilde_ok;
242         size_t len;
243         char *cp;
244
245         if (ccp == NULL)
246                 internal_errorf("expand(NULL)");
247         /* for alias, readonly, set, typeset commands */
248         if ((f & DOVACHECK) && is_wdvarassign(ccp)) {
249                 f &= ~(DOVACHECK | DOBLANK | DOGLOB | DOTILDE);
250                 f |= DOASNTILDE | DOSCALAR;
251         }
252         if (Flag(FNOGLOB))
253                 f &= ~DOGLOB;
254         if (Flag(FMARKDIRS))
255                 f |= DOMARKDIRS;
256         if (Flag(FBRACEEXPAND) && (f & DOGLOB))
257                 f |= DOBRACE;
258
259         /* init destination string */
260         Xinit(ds, dp, 128, ATEMP);
261         type = XBASE;
262         sp = ccp;
263         fdo = 0;
264         saw_eq = false;
265         /* must be 1/0 */
266         tilde_ok = (f & (DOTILDE | DOASNTILDE)) ? 1 : 0;
267         doblank = 0;
268         make_magic = false;
269         word = (f&DOBLANK) ? IFS_WS : IFS_WORD;
270         /* clang doesn't know OSUBST comes before CSUBST */
271         memset(&st_head, 0, sizeof(st_head));
272         st = &st_head;
273
274         while (/* CONSTCOND */ 1) {
275                 Xcheck(ds, dp);
276
277                 switch (type) {
278                 case XBASE:
279                         /* original prefixed string */
280                         c = *sp++;
281                         switch (c) {
282                         case EOS:
283                                 c = 0;
284                                 break;
285                         case CHAR:
286                                 c = *sp++;
287                                 break;
288                         case QCHAR:
289                                 /* temporary quote */
290                                 quote |= 2;
291                                 c = *sp++;
292                                 break;
293                         case OQUOTE:
294                                 switch (word) {
295                                 case IFS_QUOTE:
296                                         /* """something */
297                                         word = IFS_WORD;
298                                         break;
299                                 case IFS_WORD:
300                                         break;
301                                 default:
302                                         word = IFS_QUOTE;
303                                         break;
304                                 }
305                                 tilde_ok = 0;
306                                 quote = 1;
307                                 continue;
308                         case CQUOTE:
309                                 quote = st->quotew;
310                                 continue;
311                         case COMSUB:
312                         case FUNSUB:
313                         case VALSUB:
314                                 tilde_ok = 0;
315                                 if (f & DONTRUNCOMMAND) {
316                                         word = IFS_WORD;
317                                         *dp++ = '$';
318                                         *dp++ = c == COMSUB ? '(' : '{';
319                                         if (c != COMSUB)
320                                                 *dp++ = c == FUNSUB ? ' ' : '|';
321                                         while (*sp != '\0') {
322                                                 Xcheck(ds, dp);
323                                                 *dp++ = *sp++;
324                                         }
325                                         if (c != COMSUB) {
326                                                 *dp++ = ';';
327                                                 *dp++ = '}';
328                                         } else
329                                                 *dp++ = ')';
330                                 } else {
331                                         type = comsub(&x, sp, c);
332                                         if (type != XBASE && (f & DOBLANK))
333                                                 doblank++;
334                                         sp = strnul(sp) + 1;
335                                         newlines = 0;
336                                 }
337                                 continue;
338                         case EXPRSUB:
339                                 tilde_ok = 0;
340                                 if (f & DONTRUNCOMMAND) {
341                                         word = IFS_WORD;
342                                         *dp++ = '$'; *dp++ = '('; *dp++ = '(';
343                                         while (*sp != '\0') {
344                                                 Xcheck(ds, dp);
345                                                 *dp++ = *sp++;
346                                         }
347                                         *dp++ = ')'; *dp++ = ')';
348                                 } else {
349                                         struct tbl v;
350
351                                         v.flag = DEFINED|ISSET|INTEGER;
352                                         /* not default */
353                                         v.type = 10;
354                                         v.name[0] = '\0';
355                                         v_evaluate(&v, substitute(sp, 0),
356                                             KSH_UNWIND_ERROR, true);
357                                         sp = strnul(sp) + 1;
358                                         x.str = str_val(&v);
359                                         type = XSUB;
360                                         if (f & DOBLANK)
361                                                 doblank++;
362                                 }
363                                 continue;
364                         case OSUBST: {
365                                 /* ${{#}var{:}[=+-?#%]word} */
366                         /*-
367                          * format is:
368                          *      OSUBST [{x] plain-variable-part \0
369                          *          compiled-word-part CSUBST [}x]
370                          * This is where all syntax checking gets done...
371                          */
372                                 /* skip the { or x (}) */
373                                 const char *varname = ++sp;
374                                 int stype;
375                                 int slen = 0;
376
377                                 /* skip variable */
378                                 sp = cstrchr(sp, '\0') + 1;
379                                 type = varsub(&x, varname, sp, &stype, &slen);
380                                 if (type < 0) {
381                                         char *beg, *end, *str;
382  unwind_substsyn:
383                                         /* restore sp */
384                                         sp = varname - 2;
385                                         end = (beg = wdcopy(sp, ATEMP)) +
386                                             (wdscan(sp, CSUBST) - sp);
387                                         /* ({) the } or x is already skipped */
388                                         if (end < wdscan(beg, EOS))
389                                                 *end = EOS;
390                                         str = snptreef(NULL, 64, "%S", beg);
391                                         afree(beg, ATEMP);
392                                         errorf("%s: %s", str, "bad substitution");
393                                 }
394                                 if (f & DOBLANK)
395                                         doblank++;
396                                 tilde_ok = 0;
397                                 if (word == IFS_QUOTE && type != XNULLSUB)
398                                         word = IFS_WORD;
399                                 if (type == XBASE) {
400                                         /* expand? */
401                                         if (!st->next) {
402                                                 SubType *newst;
403
404                                                 newst = alloc(sizeof(SubType), ATEMP);
405                                                 newst->next = NULL;
406                                                 newst->prev = st;
407                                                 st->next = newst;
408                                         }
409                                         st = st->next;
410                                         st->stype = stype;
411                                         st->base = Xsavepos(ds, dp);
412                                         st->f = f;
413                                         if (x.var == &vtemp) {
414                                                 st->var = tempvar();
415                                                 st->var->flag &= ~INTEGER;
416                                                 /* can't fail here */
417                                                 setstr(st->var,
418                                                     str_val(x.var),
419                                                     KSH_RETURN_ERROR | 0x4);
420                                         } else
421                                                 st->var = x.var;
422
423                                         st->quotew = st->quotep = quote;
424                                         /* skip qualifier(s) */
425                                         if (stype)
426                                                 sp += slen;
427                                         switch (stype & 0x17F) {
428                                         case 0x100 | '#':
429                                                 x.str = shf_smprintf("%08X",
430                                                     (unsigned int)hash(str_val(st->var)));
431                                                 break;
432                                         case 0x100 | 'Q': {
433                                                 struct shf shf;
434
435                                                 shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
436                                                 print_value_quoted(&shf, str_val(st->var));
437                                                 x.str = shf_sclose(&shf);
438                                                 break;
439                                             }
440                                         case '0': {
441                                                 char *beg, *mid, *end, *stg;
442                                                 mksh_ari_t from = 0, num = -1, flen, finc = 0;
443
444                                                 beg = wdcopy(sp, ATEMP);
445                                                 mid = beg + (wdscan(sp, ADELIM) - sp);
446                                                 stg = beg + (wdscan(sp, CSUBST) - sp);
447                                                 if (mid >= stg)
448                                                         goto unwind_substsyn;
449                                                 mid[-2] = EOS;
450                                                 if (mid[-1] == /*{*/'}') {
451                                                         sp += mid - beg - 1;
452                                                         end = NULL;
453                                                 } else {
454                                                         end = mid +
455                                                             (wdscan(mid, ADELIM) - mid);
456                                                         if (end >= stg ||
457                                                             /* more than max delimiters */
458                                                             end[-1] != /*{*/ '}')
459                                                                 goto unwind_substsyn;
460                                                         end[-2] = EOS;
461                                                         sp += end - beg - 1;
462                                                 }
463                                                 evaluate(substitute(stg = wdstrip(beg, 0), 0),
464                                                     &from, KSH_UNWIND_ERROR, true);
465                                                 afree(stg, ATEMP);
466                                                 if (end) {
467                                                         evaluate(substitute(stg = wdstrip(mid, 0), 0),
468                                                             &num, KSH_UNWIND_ERROR, true);
469                                                         afree(stg, ATEMP);
470                                                 }
471                                                 afree(beg, ATEMP);
472                                                 beg = str_val(st->var);
473                                                 flen = utflen(beg);
474                                                 if (from < 0) {
475                                                         if (-from < flen)
476                                                                 finc = flen + from;
477                                                 } else
478                                                         finc = from < flen ? from : flen;
479                                                 if (UTFMODE)
480                                                         utfincptr(beg, &finc);
481                                                 beg += finc;
482                                                 flen = utflen(beg);
483                                                 if (num < 0 || num > flen)
484                                                         num = flen;
485                                                 if (UTFMODE)
486                                                         utfincptr(beg, &num);
487                                                 strndupx(x.str, beg, num, ATEMP);
488                                                 goto do_CSUBST;
489                                             }
490                                         case '/': {
491                                                 char *s, *p, *d, *sbeg, *end;
492                                                 char *pat, *rrep;
493                                                 char *tpat0, *tpat1, *tpat2;
494
495                                                 s = wdcopy(sp, ATEMP);
496                                                 p = s + (wdscan(sp, ADELIM) - sp);
497                                                 d = s + (wdscan(sp, CSUBST) - sp);
498                                                 if (p >= d)
499                                                         goto unwind_substsyn;
500                                                 p[-2] = EOS;
501                                                 if (p[-1] == /*{*/'}')
502                                                         d = NULL;
503                                                 else
504                                                         d[-2] = EOS;
505                                                 sp += (d ? d : p) - s - 1;
506                                                 tpat0 = wdstrip(s,
507                                                     WDS_KEEPQ | WDS_MAGIC);
508                                                 pat = substitute(tpat0, 0);
509                                                 if (d) {
510                                                         d = wdstrip(p, WDS_KEEPQ);
511                                                         rrep = substitute(d, 0);
512                                                         afree(d, ATEMP);
513                                                 } else
514                                                         rrep = null;
515                                                 afree(s, ATEMP);
516                                                 s = d = pat;
517                                                 while (*s)
518                                                         if (*s != '\\' ||
519                                                             s[1] == '%' ||
520                                                             s[1] == '#' ||
521                                                             s[1] == '\0' ||
522                                 /* XXX really? */           s[1] == '\\' ||
523                                                             s[1] == '/')
524                                                                 *d++ = *s++;
525                                                         else
526                                                                 s++;
527                                                 *d = '\0';
528                                                 afree(tpat0, ATEMP);
529
530                                                 /* check for special cases */
531                                                 d = str_val(st->var);
532                                                 switch (*pat) {
533                                                 case '#':
534                                                         /* anchor at begin */
535                                                         tpat0 = pat + 1;
536                                                         tpat1 = rrep;
537                                                         tpat2 = d;
538                                                         break;
539                                                 case '%':
540                                                         /* anchor at end */
541                                                         tpat0 = pat + 1;
542                                                         tpat1 = d;
543                                                         tpat2 = rrep;
544                                                         break;
545                                                 case '\0':
546                                                         /* empty pattern */
547                                                         goto no_repl;
548                                                 default:
549                                                         tpat0 = pat;
550                                                         /* silence gcc */
551                                                         tpat1 = tpat2 = NULL;
552                                                 }
553                                                 if (gmatchx(null, tpat0, false)) {
554                                                         /*
555                                                          * pattern matches
556                                                          * the empty string
557                                                          */
558                                                         if (tpat0 == pat)
559                                                                 goto no_repl;
560                                                         /* but is anchored */
561                                                         s = shf_smprintf("%s%s",
562                                                             tpat1, tpat2);
563                                                         goto do_repl;
564                                                 }
565
566                                                 /* prepare string on which to work */
567                                                 strdupx(s, d, ATEMP);
568                                                 sbeg = s;
569
570                                                 /* first see if we have any match at all */
571                                                 tpat0 = pat;
572                                                 if (*pat == '#') {
573                                                         /* anchor at the beginning */
574                                                         tpat1 = shf_smprintf("%s%c*", ++tpat0, MAGIC);
575                                                         tpat2 = tpat1;
576                                                 } else if (*pat == '%') {
577                                                         /* anchor at the end */
578                                                         tpat1 = shf_smprintf("%c*%s", MAGIC, ++tpat0);
579                                                         tpat2 = tpat0;
580                                                 } else {
581                                                         /* float */
582                                                         tpat1 = shf_smprintf("%c*%s%c*", MAGIC, pat, MAGIC);
583                                                         tpat2 = tpat1 + 2;
584                                                 }
585  again_repl:
586                                                 /*
587                                                  * this would not be necessary if gmatchx would return
588                                                  * the start and end values of a match found, like re*
589                                                  */
590                                                 if (!gmatchx(sbeg, tpat1, false))
591                                                         goto end_repl;
592                                                 end = strnul(s);
593                                                 /* now anchor the beginning of the match */
594                                                 if (*pat != '#')
595                                                         while (sbeg <= end) {
596                                                                 if (gmatchx(sbeg, tpat2, false))
597                                                                         break;
598                                                                 else
599                                                                         sbeg++;
600                                                         }
601                                                 /* now anchor the end of the match */
602                                                 p = end;
603                                                 if (*pat != '%')
604                                                         while (p >= sbeg) {
605                                                                 bool gotmatch;
606
607                                                                 c = *p;
608                                                                 *p = '\0';
609                                                                 gotmatch = tobool(gmatchx(sbeg, tpat0, false));
610                                                                 *p = c;
611                                                                 if (gotmatch)
612                                                                         break;
613                                                                 p--;
614                                                         }
615                                                 strndupx(end, s, sbeg - s, ATEMP);
616                                                 d = shf_smprintf("%s%s%s", end, rrep, p);
617                                                 afree(end, ATEMP);
618                                                 sbeg = d + (sbeg - s) + strlen(rrep);
619                                                 afree(s, ATEMP);
620                                                 s = d;
621                                                 if (stype & 0x80)
622                                                         goto again_repl;
623  end_repl:
624                                                 afree(tpat1, ATEMP);
625  do_repl:
626                                                 x.str = s;
627  no_repl:
628                                                 afree(pat, ATEMP);
629                                                 if (rrep != null)
630                                                         afree(rrep, ATEMP);
631                                                 goto do_CSUBST;
632                                             }
633                                         case '#':
634                                         case '%':
635                                                 /* ! DOBLANK,DOBRACE,DOTILDE */
636                                                 f = (f & DONTRUNCOMMAND) |
637                                                     DOPAT | DOTEMP | DOSCALAR;
638                                                 st->quotew = quote = 0;
639                                                 /*
640                                                  * Prepend open pattern (so |
641                                                  * in a trim will work as
642                                                  * expected)
643                                                  */
644                                                 if (!Flag(FSH)) {
645                                                         *dp++ = MAGIC;
646                                                         *dp++ = 0x80 | '@';
647                                                 }
648                                                 break;
649                                         case '=':
650                                                 /*
651                                                  * Enabling tilde expansion
652                                                  * after :s here is
653                                                  * non-standard ksh, but is
654                                                  * consistent with rules for
655                                                  * other assignments. Not
656                                                  * sure what POSIX thinks of
657                                                  * this.
658                                                  * Not doing tilde expansion
659                                                  * for integer variables is a
660                                                  * non-POSIX thing - makes
661                                                  * sense though, since ~ is
662                                                  * a arithmetic operator.
663                                                  */
664                                                 if (!(x.var->flag & INTEGER))
665                                                         f |= DOASNTILDE | DOTILDE;
666                                                 f |= DOTEMP;
667                                                 /*
668                                                  * These will be done after the
669                                                  * value has been assigned.
670                                                  */
671                                                 f &= ~(DOBLANK|DOGLOB|DOBRACE);
672                                                 tilde_ok = 1;
673                                                 break;
674                                         case '?':
675                                                 f &= ~DOBLANK;
676                                                 f |= DOTEMP;
677                                                 /* FALLTHROUGH */
678                                         default:
679                                                 /* '-' '+' '?' */
680                                                 if (quote)
681                                                         word = IFS_WORD;
682                                                 else if (dp == Xstring(ds, dp))
683                                                         word = IFS_IWS;
684                                                 /* Enable tilde expansion */
685                                                 tilde_ok = 1;
686                                                 f |= DOTILDE;
687                                         }
688                                 } else
689                                         /* skip word */
690                                         sp += wdscan(sp, CSUBST) - sp;
691                                 continue;
692                             }
693                         case CSUBST:
694                                 /* only get here if expanding word */
695  do_CSUBST:
696                                 /* ({) skip the } or x */
697                                 sp++;
698                                 /* in case of ${unset:-} */
699                                 tilde_ok = 0;
700                                 *dp = '\0';
701                                 quote = st->quotep;
702                                 f = st->f;
703                                 if (f & DOBLANK)
704                                         doblank--;
705                                 switch (st->stype & 0x17F) {
706                                 case '#':
707                                 case '%':
708                                         if (!Flag(FSH)) {
709                                                 /* Append end-pattern */
710                                                 *dp++ = MAGIC;
711                                                 *dp++ = ')';
712                                         }
713                                         *dp = '\0';
714                                         dp = Xrestpos(ds, dp, st->base);
715                                         /*
716                                          * Must use st->var since calling
717                                          * global would break things
718                                          * like x[i+=1].
719                                          */
720                                         x.str = trimsub(str_val(st->var),
721                                                 dp, st->stype);
722                                         if (x.str[0] != '\0') {
723                                                 word = IFS_IWS;
724                                                 type = XSUB;
725                                         } else if (quote) {
726                                                 word = IFS_WORD;
727                                                 type = XSUB;
728                                         } else {
729                                                 if (dp == Xstring(ds, dp))
730                                                         word = IFS_IWS;
731                                                 type = XNULLSUB;
732                                         }
733                                         if (f & DOBLANK)
734                                                 doblank++;
735                                         st = st->prev;
736                                         continue;
737                                 case '=':
738                                         /*
739                                          * Restore our position and substitute
740                                          * the value of st->var (may not be
741                                          * the assigned value in the presence
742                                          * of integer/right-adj/etc attributes).
743                                          */
744                                         dp = Xrestpos(ds, dp, st->base);
745                                         /*
746                                          * Must use st->var since calling
747                                          * global would cause with things
748                                          * like x[i+=1] to be evaluated twice.
749                                          */
750                                         /*
751                                          * Note: not exported by FEXPORT
752                                          * in AT&T ksh.
753                                          */
754                                         /*
755                                          * XXX POSIX says readonly is only
756                                          * fatal for special builtins (setstr
757                                          * does readonly check).
758                                          */
759                                         len = strlen(dp) + 1;
760                                         setstr(st->var,
761                                             debunk(alloc(len, ATEMP),
762                                             dp, len), KSH_UNWIND_ERROR);
763                                         x.str = str_val(st->var);
764                                         type = XSUB;
765                                         if (f & DOBLANK)
766                                                 doblank++;
767                                         st = st->prev;
768                                         word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS;
769                                         continue;
770                                 case '?': {
771                                         char *s = Xrestpos(ds, dp, st->base);
772
773                                         errorf("%s: %s", st->var->name,
774                                             dp == s ?
775                                             "parameter null or not set" :
776                                             (debunk(s, s, strlen(s) + 1), s));
777                                     }
778                                 case '0':
779                                 case '/':
780                                 case 0x100 | '#':
781                                 case 0x100 | 'Q':
782                                         dp = Xrestpos(ds, dp, st->base);
783                                         type = XSUB;
784                                         word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS;
785                                         if (f & DOBLANK)
786                                                 doblank++;
787                                         st = st->prev;
788                                         continue;
789                                 /* default: '-' '+' */
790                                 }
791                                 st = st->prev;
792                                 type = XBASE;
793                                 continue;
794
795                         case OPAT:
796                                 /* open pattern: *(foo|bar) */
797                                 /* Next char is the type of pattern */
798                                 make_magic = true;
799                                 c = *sp++ | 0x80;
800                                 break;
801
802                         case SPAT:
803                                 /* pattern separator (|) */
804                                 make_magic = true;
805                                 c = '|';
806                                 break;
807
808                         case CPAT:
809                                 /* close pattern */
810                                 make_magic = true;
811                                 c = /*(*/ ')';
812                                 break;
813                         }
814                         break;
815
816                 case XNULLSUB:
817                         /*
818                          * Special case for "$@" (and "${foo[@]}") - no
819                          * word is generated if $# is 0 (unless there is
820                          * other stuff inside the quotes).
821                          */
822                         type = XBASE;
823                         if (f & DOBLANK) {
824                                 doblank--;
825                                 if (dp == Xstring(ds, dp) && word != IFS_WORD)
826                                         word = IFS_IWS;
827                         }
828                         continue;
829
830                 case XSUB:
831                 case XSUBMID:
832                         if ((c = *x.str++) == 0) {
833                                 type = XBASE;
834                                 if (f & DOBLANK)
835                                         doblank--;
836                                 continue;
837                         }
838                         break;
839
840                 case XARGSEP:
841                         type = XARG;
842                         quote = 1;
843                         /* FALLTHROUGH */
844                 case XARG:
845                         if ((c = *x.str++) == '\0') {
846                                 /*
847                                  * force null words to be created so
848                                  * set -- "" 2 ""; echo "$@" will do
849                                  * the right thing
850                                  */
851                                 if (quote && x.split)
852                                         word = IFS_WORD;
853                                 if ((x.str = *x.u.strv++) == NULL) {
854                                         type = XBASE;
855                                         if (f & DOBLANK)
856                                                 doblank--;
857                                         continue;
858                                 }
859                                 c = ifs0;
860                                 if ((f & DOHEREDOC)) {
861                                         /* pseudo-field-split reliably */
862                                         if (c == 0)
863                                                 c = ' ';
864                                         break;
865                                 }
866                                 if ((f & DOSCALAR)) {
867                                         /* do not field-split */
868                                         if (x.split) {
869                                                 c = ' ';
870                                                 break;
871                                         }
872                                         if (c == 0)
873                                                 continue;
874                                 }
875                                 if (c == 0) {
876                                         if (quote && !x.split)
877                                                 continue;
878                                         if (!quote && word == IFS_WS)
879                                                 continue;
880                                         /* this is so we don't terminate */
881                                         c = ' ';
882                                         /* now force-emit a word */
883                                         goto emit_word;
884                                 }
885                                 if (quote && x.split) {
886                                         /* terminate word for "$@" */
887                                         type = XARGSEP;
888                                         quote = 0;
889                                 }
890                         }
891                         break;
892
893                 case XCOM:
894                         if (x.u.shf == NULL) {
895                                 /* $(<...) failed */
896                                 subst_exstat = 1;
897                                 /* fake EOF */
898                                 c = -1;
899                         } else if (newlines) {
900                                 /* spit out saved NLs */
901                                 c = '\n';
902                                 --newlines;
903                         } else {
904                                 while ((c = shf_getc(x.u.shf)) == 0 || c == '\n')
905                                         if (c == '\n')
906                                                 /* save newlines */
907                                                 newlines++;
908                                 if (newlines && c != -1) {
909                                         shf_ungetc(c, x.u.shf);
910                                         c = '\n';
911                                         --newlines;
912                                 }
913                         }
914                         if (c == -1) {
915                                 newlines = 0;
916                                 if (x.u.shf)
917                                         shf_close(x.u.shf);
918                                 if (x.split)
919                                         subst_exstat = waitlast();
920                                 type = XBASE;
921                                 if (f & DOBLANK)
922                                         doblank--;
923                                 continue;
924                         }
925                         break;
926                 }
927
928                 /* check for end of word or IFS separation */
929                 if (c == 0 || (!quote && (f & DOBLANK) && doblank &&
930                     !make_magic && ctype(c, C_IFS))) {
931                         /*-
932                          * How words are broken up:
933                          *                      |       value of c
934                          *      word            |       ws      nws     0
935                          *      -----------------------------------
936                          *      IFS_WORD                w/WS    w/NWS   w
937                          *      IFS_WS                  -/WS    -/NWS   -
938                          *      IFS_NWS                 -/NWS   w/NWS   -
939                          *      IFS_IWS                 -/WS    w/NWS   -
940                          * (w means generate a word)
941                          */
942                         if ((word == IFS_WORD) || (word == IFS_QUOTE) || (c &&
943                             (word == IFS_IWS || word == IFS_NWS) &&
944                             !ctype(c, C_IFSWS))) {
945  emit_word:
946                                 *dp++ = '\0';
947                                 cp = Xclose(ds, dp);
948                                 if (fdo & DOBRACE)
949                                         /* also does globbing */
950                                         alt_expand(wp, cp, cp,
951                                             cp + Xlength(ds, (dp - 1)),
952                                             fdo | (f & DOMARKDIRS));
953                                 else if (fdo & DOGLOB)
954                                         glob(cp, wp, tobool(f & DOMARKDIRS));
955                                 else if ((f & DOPAT) || !(fdo & DOMAGIC))
956                                         XPput(*wp, cp);
957                                 else
958                                         XPput(*wp, debunk(cp, cp,
959                                             strlen(cp) + 1));
960                                 fdo = 0;
961                                 saw_eq = false;
962                                 /* must be 1/0 */
963                                 tilde_ok = (f & (DOTILDE | DOASNTILDE)) ? 1 : 0;
964                                 if (c == 0)
965                                         return;
966                                 Xinit(ds, dp, 128, ATEMP);
967                         } else if (c == 0) {
968                                 return;
969                         } else if (type == XSUB && ctype(c, C_IFS) &&
970                             !ctype(c, C_IFSWS) && Xlength(ds, dp) == 0) {
971                                 *(cp = alloc(1, ATEMP)) = '\0';
972                                 XPput(*wp, cp);
973                                 type = XSUBMID;
974                         }
975                         if (word != IFS_NWS)
976                                 word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
977                 } else {
978                         if (type == XSUB) {
979                                 if (word == IFS_NWS &&
980                                     Xlength(ds, dp) == 0) {
981                                         *(cp = alloc(1, ATEMP)) = '\0';
982                                         XPput(*wp, cp);
983                                 }
984                                 type = XSUBMID;
985                         }
986
987                         /* age tilde_ok info - ~ code tests second bit */
988                         tilde_ok <<= 1;
989                         /* mark any special second pass chars */
990                         if (!quote)
991                                 switch (c) {
992                                 case '[':
993                                 case '!':
994                                 case '-':
995                                 case ']':
996                                         /*
997                                          * For character classes - doesn't hurt
998                                          * to have magic !,-,]s outside of
999                                          * [...] expressions.
1000                                          */
1001                                         if (f & (DOPAT | DOGLOB)) {
1002                                                 fdo |= DOMAGIC;
1003                                                 if (c == '[')
1004                                                         fdo |= f & DOGLOB;
1005                                                 *dp++ = MAGIC;
1006                                         }
1007                                         break;
1008                                 case '*':
1009                                 case '?':
1010                                         if (f & (DOPAT | DOGLOB)) {
1011                                                 fdo |= DOMAGIC | (f & DOGLOB);
1012                                                 *dp++ = MAGIC;
1013                                         }
1014                                         break;
1015                                 case '{':
1016                                 case '}':
1017                                 case ',':
1018                                         if ((f & DOBRACE) && (c == '{' /*}*/ ||
1019                                             (fdo & DOBRACE))) {
1020                                                 fdo |= DOBRACE|DOMAGIC;
1021                                                 *dp++ = MAGIC;
1022                                         }
1023                                         break;
1024                                 case '=':
1025                                         /* Note first unquoted = for ~ */
1026                                         if (!(f & DOTEMP) && !saw_eq &&
1027                                             (Flag(FBRACEEXPAND) ||
1028                                             (f & DOASNTILDE))) {
1029                                                 saw_eq = true;
1030                                                 tilde_ok = 1;
1031                                         }
1032                                         break;
1033                                 case ':':
1034                                         /* : */
1035                                         /* Note unquoted : for ~ */
1036                                         if (!(f & DOTEMP) && (f & DOASNTILDE))
1037                                                 tilde_ok = 1;
1038                                         break;
1039                                 case '~':
1040                                         /*
1041                                          * tilde_ok is reset whenever
1042                                          * any of ' " $( $(( ${ } are seen.
1043                                          * Note that tilde_ok must be preserved
1044                                          * through the sequence ${A=a=}~
1045                                          */
1046                                         if (type == XBASE &&
1047                                             (f & (DOTILDE | DOASNTILDE)) &&
1048                                             (tilde_ok & 2)) {
1049                                                 const char *tcp;
1050                                                 char *tdp = dp;
1051
1052                                                 tcp = maybe_expand_tilde(sp,
1053                                                     &ds, &tdp,
1054                                                     tobool(f & DOASNTILDE));
1055                                                 if (tcp) {
1056                                                         if (dp != tdp)
1057                                                                 word = IFS_WORD;
1058                                                         dp = tdp;
1059                                                         sp = tcp;
1060                                                         continue;
1061                                                 }
1062                                         }
1063                                         break;
1064                                 }
1065                         else
1066                                 /* undo temporary */
1067                                 quote &= ~2;
1068
1069                         if (make_magic) {
1070                                 make_magic = false;
1071                                 fdo |= DOMAGIC | (f & DOGLOB);
1072                                 *dp++ = MAGIC;
1073                         } else if (ISMAGIC(c)) {
1074                                 fdo |= DOMAGIC;
1075                                 *dp++ = MAGIC;
1076                         }
1077                         /* save output char */
1078                         *dp++ = c;
1079                         word = IFS_WORD;
1080                 }
1081         }
1082 }
1083
1084 /*
1085  * Prepare to generate the string returned by ${} substitution.
1086  */
1087 static int
1088 varsub(Expand *xp, const char *sp, const char *word,
1089     int *stypep,        /* becomes qualifier type */
1090     int *slenp)         /* " " len (=, :=, etc.) valid iff *stypep != 0 */
1091 {
1092         int c;
1093         int state;      /* next state: XBASE, XARG, XSUB, XNULLSUB */
1094         int stype;      /* substitution type */
1095         int slen;
1096         const char *p;
1097         struct tbl *vp;
1098         bool zero_ok = false;
1099
1100         if ((stype = sp[0]) == '\0')
1101                 /* Bad variable name */
1102                 return (-1);
1103
1104         xp->var = NULL;
1105
1106         /*-
1107          * ${#var}, string length (-U: characters, +U: octets) or array size
1108          * ${%var}, string width (-U: screen columns, +U: octets)
1109          */
1110         c = sp[1];
1111         if (stype == '%' && c == '\0')
1112                 return (-1);
1113         if ((stype == '#' || stype == '%') && c != '\0') {
1114                 /* Can't have any modifiers for ${#...} or ${%...} */
1115                 if (*word != CSUBST)
1116                         return (-1);
1117                 sp++;
1118                 /* Check for size of array */
1119                 if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
1120                     p[2] == ']') {
1121                         int n = 0;
1122
1123                         if (stype != '#')
1124                                 return (-1);
1125                         vp = global(arrayname(sp));
1126                         if (vp->flag & (ISSET|ARRAY))
1127                                 zero_ok = true;
1128                         for (; vp; vp = vp->u.array)
1129                                 if (vp->flag & ISSET)
1130                                         n++;
1131                         c = n;
1132                 } else if (c == '*' || c == '@') {
1133                         if (stype != '#')
1134                                 return (-1);
1135                         c = e->loc->argc;
1136                 } else {
1137                         p = str_val(global(sp));
1138                         zero_ok = p != null;
1139                         if (stype == '#')
1140                                 c = utflen(p);
1141                         else {
1142                                 /* partial utf_mbswidth reimplementation */
1143                                 const char *s = p;
1144                                 unsigned int wc;
1145                                 size_t len;
1146                                 int cw;
1147
1148                                 c = 0;
1149                                 while (*s) {
1150                                         if (!UTFMODE || (len = utf_mbtowc(&wc,
1151                                             s)) == (size_t)-1)
1152                                                 /* not UTFMODE or not UTF-8 */
1153                                                 wc = (unsigned char)(*s++);
1154                                         else
1155                                                 /* UTFMODE and UTF-8 */
1156                                                 s += len;
1157                                         /* wc == char or wchar at s++ */
1158                                         if ((cw = utf_wcwidth(wc)) == -1) {
1159                                                 /* 646, 8859-1, 10646 C0/C1 */
1160                                                 c = -1;
1161                                                 break;
1162                                         }
1163                                         c += cw;
1164                                 }
1165                         }
1166                 }
1167                 if (Flag(FNOUNSET) && c == 0 && !zero_ok)
1168                         errorf("%s: %s", sp, "parameter not set");
1169                 /* unqualified variable/string substitution */
1170                 *stypep = 0;
1171                 xp->str = shf_smprintf("%d", c);
1172                 return (XSUB);
1173         }
1174
1175         /* Check for qualifiers in word part */
1176         stype = 0;
1177         c = word[slen = 0] == CHAR ? word[1] : 0;
1178         if (c == ':') {
1179                 slen += 2;
1180                 stype = 0x80;
1181                 c = word[slen + 0] == CHAR ? word[slen + 1] : 0;
1182         }
1183         if (!stype && c == '/') {
1184                 slen += 2;
1185                 stype = c;
1186                 if (word[slen] == ADELIM) {
1187                         slen += 2;
1188                         stype |= 0x80;
1189                 }
1190         } else if (stype == 0x80 && (c == ' ' || c == '0')) {
1191                 stype |= '0';
1192         } else if (ctype(c, C_SUBOP1)) {
1193                 slen += 2;
1194                 stype |= c;
1195         } else if (ctype(c, C_SUBOP2)) {
1196                 /* Note: ksh88 allows :%, :%%, etc */
1197                 slen += 2;
1198                 stype = c;
1199                 if (word[slen + 0] == CHAR && c == word[slen + 1]) {
1200                         stype |= 0x80;
1201                         slen += 2;
1202                 }
1203         } else if (c == '@') {
1204                 /* @x where x is command char */
1205                 slen += 2;
1206                 stype |= 0x100;
1207                 if (word[slen] == CHAR) {
1208                         stype |= word[slen + 1];
1209                         slen += 2;
1210                 }
1211         } else if (stype)
1212                 /* : is not ok */
1213                 return (-1);
1214         if (!stype && *word != CSUBST)
1215                 return (-1);
1216         *stypep = stype;
1217         *slenp = slen;
1218
1219         c = sp[0];
1220         if (c == '*' || c == '@') {
1221                 switch (stype & 0x17F) {
1222                 /* can't assign to a vector */
1223                 case '=':
1224                 /* can't trim a vector (yet) */
1225                 case '%':
1226                 case '#':
1227                 case '0':
1228                 case '/':
1229                 case 0x100 | '#':
1230                 case 0x100 | 'Q':
1231                         return (-1);
1232                 }
1233                 if (e->loc->argc == 0) {
1234                         xp->str = null;
1235                         xp->var = global(sp);
1236                         state = c == '@' ? XNULLSUB : XSUB;
1237                 } else {
1238                         xp->u.strv = (const char **)e->loc->argv + 1;
1239                         xp->str = *xp->u.strv++;
1240                         /* $@ */
1241                         xp->split = tobool(c == '@');
1242                         state = XARG;
1243                 }
1244                 /* POSIX 2009? */
1245                 zero_ok = true;
1246         } else {
1247                 if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
1248                     p[2] == ']') {
1249                         XPtrV wv;
1250
1251                         switch (stype & 0x17F) {
1252                         /* can't assign to a vector */
1253                         case '=':
1254                         /* can't trim a vector (yet) */
1255                         case '%':
1256                         case '#':
1257                         case '?':
1258                         case '0':
1259                         case '/':
1260                         case 0x100 | '#':
1261                         case 0x100 | 'Q':
1262                                 return (-1);
1263                         }
1264                         XPinit(wv, 32);
1265                         if ((c = sp[0]) == '!')
1266                                 ++sp;
1267                         vp = global(arrayname(sp));
1268                         for (; vp; vp = vp->u.array) {
1269                                 if (!(vp->flag&ISSET))
1270                                         continue;
1271                                 XPput(wv, c == '!' ? shf_smprintf("%lu",
1272                                     arrayindex(vp)) :
1273                                     str_val(vp));
1274                         }
1275                         if (XPsize(wv) == 0) {
1276                                 xp->str = null;
1277                                 state = p[1] == '@' ? XNULLSUB : XSUB;
1278                                 XPfree(wv);
1279                         } else {
1280                                 XPput(wv, 0);
1281                                 xp->u.strv = (const char **)XPptrv(wv);
1282                                 xp->str = *xp->u.strv++;
1283                                 /* ${foo[@]} */
1284                                 xp->split = tobool(p[1] == '@');
1285                                 state = XARG;
1286                         }
1287                 } else {
1288                         /* Can't assign things like $! or $1 */
1289                         if ((stype & 0x17F) == '=' &&
1290                             ctype(*sp, C_VAR1 | C_DIGIT))
1291                                 return (-1);
1292                         if (*sp == '!' && sp[1]) {
1293                                 ++sp;
1294                                 xp->var = global(sp);
1295                                 if (vstrchr(sp, '['))
1296                                         xp->str = shf_smprintf("%s[%lu]",
1297                                             xp->var->name,
1298                                             arrayindex(xp->var));
1299                                 else
1300                                         xp->str = xp->var->name;
1301                         } else {
1302                                 xp->var = global(sp);
1303                                 xp->str = str_val(xp->var);
1304                         }
1305                         state = XSUB;
1306                 }
1307         }
1308
1309         c = stype & 0x7F;
1310         /* test the compiler's code generator */
1311         if (((stype < 0x100) && (ctype(c, C_SUBOP2) || c == '/' ||
1312             (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
1313             c == '=' || c == '-' || c == '?' : c == '+'))) ||
1314             stype == (0x80 | '0') || stype == (0x100 | '#') ||
1315             stype == (0x100 | 'Q'))
1316                 /* expand word instead of variable value */
1317                 state = XBASE;
1318         if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
1319             (ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))
1320                 errorf("%s: %s", sp, "parameter not set");
1321         return (state);
1322 }
1323
1324 /*
1325  * Run the command in $(...) and read its output.
1326  */
1327 static int
1328 comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED)
1329 {
1330         Source *s, *sold;
1331         struct op *t;
1332         struct shf *shf;
1333         uint8_t old_utfmode = UTFMODE;
1334
1335         s = pushs(SSTRING, ATEMP);
1336         s->start = s->str = cp;
1337         sold = source;
1338         t = compile(s, true);
1339         afree(s, ATEMP);
1340         source = sold;
1341
1342         UTFMODE = old_utfmode;
1343
1344         if (t == NULL)
1345                 return (XBASE);
1346
1347         /* no waitlast() unless specifically enabled later */
1348         xp->split = false;
1349
1350         if (t->type == TCOM &&
1351             *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
1352                 /* $(<file) */
1353                 struct ioword *io = *t->ioact;
1354                 char *name;
1355
1356                 if ((io->flag & IOTYPE) != IOREAD)
1357                         errorf("%s: %s", "funny $() command",
1358                             snptreef(NULL, 32, "%R", io));
1359                 shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0,
1360                         SHF_MAPHI|SHF_CLEXEC);
1361                 if (shf == NULL)
1362                         warningf(!Flag(FTALKING), "%s: %s %s: %s", name,
1363                             "can't open", "$(<...) input", cstrerror(errno));
1364         } else if (fn == FUNSUB) {
1365                 int ofd1;
1366                 struct temp *tf = NULL;
1367
1368                 /*
1369                  * create a temporary file, open for reading and writing,
1370                  * with an shf open for reading (buffered) but yet unused
1371                  */
1372                 maketemp(ATEMP, TT_FUNSUB, &tf);
1373                 if (!tf->shf) {
1374                         errorf("can't %s temporary file %s: %s",
1375                             "create", tf->tffn, cstrerror(errno));
1376                 }
1377                 /* extract shf from temporary file, unlink and free it */
1378                 shf = tf->shf;
1379                 unlink(tf->tffn);
1380                 afree(tf, ATEMP);
1381                 /* save stdout and let it point to the tempfile */
1382                 ofd1 = savefd(1);
1383                 ksh_dup2(shf_fileno(shf), 1, false);
1384                 /*
1385                  * run tree, with output thrown into the tempfile,
1386                  * in a new function block
1387                  */
1388                 valsub(t, NULL);
1389                 subst_exstat = exstat & 0xFF;
1390                 /* rewind the tempfile and restore regular stdout */
1391                 lseek(shf_fileno(shf), (off_t)0, SEEK_SET);
1392                 restfd(1, ofd1);
1393         } else if (fn == VALSUB) {
1394                 xp->str = valsub(t, ATEMP);
1395                 subst_exstat = exstat & 0xFF;
1396                 return (XSUB);
1397         } else {
1398                 int ofd1, pv[2];
1399
1400                 openpipe(pv);
1401                 shf = shf_fdopen(pv[0], SHF_RD, NULL);
1402                 ofd1 = savefd(1);
1403                 if (pv[1] != 1) {
1404                         ksh_dup2(pv[1], 1, false);
1405                         close(pv[1]);
1406                 }
1407                 execute(t, XXCOM | XPIPEO | XFORK, NULL);
1408                 restfd(1, ofd1);
1409                 startlast();
1410                 /* waitlast() */
1411                 xp->split = true;
1412         }
1413
1414         xp->u.shf = shf;
1415         return (XCOM);
1416 }
1417
1418 /*
1419  * perform #pattern and %pattern substitution in ${}
1420  */
1421 static char *
1422 trimsub(char *str, char *pat, int how)
1423 {
1424         char *end = strnul(str);
1425         char *p, c;
1426
1427         switch (how & 0xFF) {
1428         case '#':
1429                 /* shortest match at beginning */
1430                 for (p = str; p <= end; p += utf_ptradj(p)) {
1431                         c = *p; *p = '\0';
1432                         if (gmatchx(str, pat, false)) {
1433                                 *p = c;
1434                                 return (p);
1435                         }
1436                         *p = c;
1437                 }
1438                 break;
1439         case '#'|0x80:
1440                 /* longest match at beginning */
1441                 for (p = end; p >= str; p--) {
1442                         c = *p; *p = '\0';
1443                         if (gmatchx(str, pat, false)) {
1444                                 *p = c;
1445                                 return (p);
1446                         }
1447                         *p = c;
1448                 }
1449                 break;
1450         case '%':
1451                 /* shortest match at end */
1452                 p = end;
1453                 while (p >= str) {
1454                         if (gmatchx(p, pat, false))
1455                                 goto trimsub_match;
1456                         if (UTFMODE) {
1457                                 char *op = p;
1458                                 while ((p-- > str) && ((*p & 0xC0) == 0x80))
1459                                         ;
1460                                 if ((p < str) || (p + utf_ptradj(p) != op))
1461                                         p = op - 1;
1462                         } else
1463                                 --p;
1464                 }
1465                 break;
1466         case '%'|0x80:
1467                 /* longest match at end */
1468                 for (p = str; p <= end; p++)
1469                         if (gmatchx(p, pat, false)) {
1470  trimsub_match:
1471                                 strndupx(end, str, p - str, ATEMP);
1472                                 return (end);
1473                         }
1474                 break;
1475         }
1476
1477         /* no match, return string */
1478         return (str);
1479 }
1480
1481 /*
1482  * glob
1483  * Name derived from V6's /etc/glob, the program that expanded filenames.
1484  */
1485
1486 /* XXX cp not const 'cause slashes are temporarily replaced with NULs... */
1487 static void
1488 glob(char *cp, XPtrV *wp, bool markdirs)
1489 {
1490         int oldsize = XPsize(*wp);
1491
1492         if (glob_str(cp, wp, markdirs) == 0)
1493                 XPput(*wp, debunk(cp, cp, strlen(cp) + 1));
1494         else
1495                 qsort(XPptrv(*wp) + oldsize, XPsize(*wp) - oldsize,
1496                     sizeof(void *), xstrcmp);
1497 }
1498
1499 #define GF_NONE         0
1500 #define GF_EXCHECK      BIT(0)          /* do existence check on file */
1501 #define GF_GLOBBED      BIT(1)          /* some globbing has been done */
1502 #define GF_MARKDIR      BIT(2)          /* add trailing / to directories */
1503
1504 /*
1505  * Apply file globbing to cp and store the matching files in wp. Returns
1506  * the number of matches found.
1507  */
1508 int
1509 glob_str(char *cp, XPtrV *wp, bool markdirs)
1510 {
1511         int oldsize = XPsize(*wp);
1512         XString xs;
1513         char *xp;
1514
1515         Xinit(xs, xp, 256, ATEMP);
1516         globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE);
1517         Xfree(xs, xp);
1518
1519         return (XPsize(*wp) - oldsize);
1520 }
1521
1522 static void
1523 globit(XString *xs,     /* dest string */
1524     char **xpp,         /* ptr to dest end */
1525     char *sp,           /* source path */
1526     XPtrV *wp,          /* output list */
1527     int check)          /* GF_* flags */
1528 {
1529         char *np;               /* next source component */
1530         char *xp = *xpp;
1531         char *se;
1532         char odirsep;
1533
1534         /* This to allow long expansions to be interrupted */
1535         intrcheck();
1536
1537         if (sp == NULL) {
1538                 /* end of source path */
1539                 /*
1540                  * We only need to check if the file exists if a pattern
1541                  * is followed by a non-pattern (eg, foo*x/bar; no check
1542                  * is needed for foo* since the match must exist) or if
1543                  * any patterns were expanded and the markdirs option is set.
1544                  * Symlinks make things a bit tricky...
1545                  */
1546                 if ((check & GF_EXCHECK) ||
1547                     ((check & GF_MARKDIR) && (check & GF_GLOBBED))) {
1548 #define stat_check()    (stat_done ? stat_done : (stat_done = \
1549                             stat(Xstring(*xs, xp), &statb) < 0 ? -1 : 1))
1550                         struct stat lstatb, statb;
1551                         /* -1: failed, 1 ok, 0 not yet done */
1552                         int stat_done = 0;
1553
1554                         if (mksh_lstat(Xstring(*xs, xp), &lstatb) < 0)
1555                                 return;
1556                         /*
1557                          * special case for systems which strip trailing
1558                          * slashes from regular files (eg, /etc/passwd/).
1559                          * SunOS 4.1.3 does this...
1560                          */
1561                         if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp) &&
1562                             xp[-1] == '/' && !S_ISDIR(lstatb.st_mode) &&
1563                             (!S_ISLNK(lstatb.st_mode) ||
1564                             stat_check() < 0 || !S_ISDIR(statb.st_mode)))
1565                                 return;
1566                         /*
1567                          * Possibly tack on a trailing / if there isn't already
1568                          * one and if the file is a directory or a symlink to a
1569                          * directory
1570                          */
1571                         if (((check & GF_MARKDIR) && (check & GF_GLOBBED)) &&
1572                             xp > Xstring(*xs, xp) && xp[-1] != '/' &&
1573                             (S_ISDIR(lstatb.st_mode) ||
1574                             (S_ISLNK(lstatb.st_mode) && stat_check() > 0 &&
1575                             S_ISDIR(statb.st_mode)))) {
1576                                 *xp++ = '/';
1577                                 *xp = '\0';
1578                         }
1579                 }
1580                 strndupx(np, Xstring(*xs, xp), Xlength(*xs, xp), ATEMP);
1581                 XPput(*wp, np);
1582                 return;
1583         }
1584
1585         if (xp > Xstring(*xs, xp))
1586                 *xp++ = '/';
1587         while (*sp == '/') {
1588                 Xcheck(*xs, xp);
1589                 *xp++ = *sp++;
1590         }
1591         np = strchr(sp, '/');
1592         if (np != NULL) {
1593                 se = np;
1594                 /* don't assume '/', can be multiple kinds */
1595                 odirsep = *np;
1596                 *np++ = '\0';
1597         } else {
1598                 odirsep = '\0'; /* keep gcc quiet */
1599                 se = sp + strlen(sp);
1600         }
1601
1602
1603         /*
1604          * Check if sp needs globbing - done to avoid pattern checks for strings
1605          * containing MAGIC characters, open [s without the matching close ],
1606          * etc. (otherwise opendir() will be called which may fail because the
1607          * directory isn't readable - if no globbing is needed, only execute
1608          * permission should be required (as per POSIX)).
1609          */
1610         if (!has_globbing(sp, se)) {
1611                 XcheckN(*xs, xp, se - sp + 1);
1612                 debunk(xp, sp, Xnleft(*xs, xp));
1613                 xp += strlen(xp);
1614                 *xpp = xp;
1615                 globit(xs, xpp, np, wp, check);
1616         } else {
1617                 DIR *dirp;
1618                 struct dirent *d;
1619                 char *name;
1620                 size_t len, prefix_len;
1621
1622                 /* xp = *xpp;   copy_non_glob() may have re-alloc'd xs */
1623                 *xp = '\0';
1624                 prefix_len = Xlength(*xs, xp);
1625                 dirp = opendir(prefix_len ? Xstring(*xs, xp) : ".");
1626                 if (dirp == NULL)
1627                         goto Nodir;
1628                 while ((d = readdir(dirp)) != NULL) {
1629                         name = d->d_name;
1630                         if (name[0] == '.' &&
1631                             (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
1632                                 /* always ignore . and .. */
1633                                 continue;
1634                         if ((*name == '.' && *sp != '.') ||
1635                             !gmatchx(name, sp, true))
1636                                 continue;
1637
1638                         len = strlen(d->d_name) + 1;
1639                         XcheckN(*xs, xp, len);
1640                         memcpy(xp, name, len);
1641                         *xpp = xp + len - 1;
1642                         globit(xs, xpp, np, wp,
1643                                 (check & GF_MARKDIR) | GF_GLOBBED
1644                                 | (np ? GF_EXCHECK : GF_NONE));
1645                         xp = Xstring(*xs, xp) + prefix_len;
1646                 }
1647                 closedir(dirp);
1648  Nodir:
1649                 ;
1650         }
1651
1652         if (np != NULL)
1653                 *--np = odirsep;
1654 }
1655
1656 /* remove MAGIC from string */
1657 char *
1658 debunk(char *dp, const char *sp, size_t dlen)
1659 {
1660         char *d;
1661         const char *s;
1662
1663         if ((s = cstrchr(sp, MAGIC))) {
1664                 if (s - sp >= (ssize_t)dlen)
1665                         return (dp);
1666                 memmove(dp, sp, s - sp);
1667                 for (d = dp + (s - sp); *s && (d - dp < (ssize_t)dlen); s++)
1668                         if (!ISMAGIC(*s) || !(*++s & 0x80) ||
1669                             !vstrchr("*+?@! ", *s & 0x7f))
1670                                 *d++ = *s;
1671                         else {
1672                                 /* extended pattern operators: *+?@! */
1673                                 if ((*s & 0x7f) != ' ')
1674                                         *d++ = *s & 0x7f;
1675                                 if (d - dp < (ssize_t)dlen)
1676                                         *d++ = '(';
1677                         }
1678                 *d = '\0';
1679         } else if (dp != sp)
1680                 strlcpy(dp, sp, dlen);
1681         return (dp);
1682 }
1683
1684 /*
1685  * Check if p is an unquoted name, possibly followed by a / or :. If so
1686  * puts the expanded version in *dcp,dp and returns a pointer in p just
1687  * past the name, otherwise returns 0.
1688  */
1689 static const char *
1690 maybe_expand_tilde(const char *p, XString *dsp, char **dpp, bool isassign)
1691 {
1692         XString ts;
1693         char *dp = *dpp;
1694         char *tp;
1695         const char *r;
1696
1697         Xinit(ts, tp, 16, ATEMP);
1698         /* : only for DOASNTILDE form */
1699         while (p[0] == CHAR && p[1] != '/' && (!isassign || p[1] != ':'))
1700         {
1701                 Xcheck(ts, tp);
1702                 *tp++ = p[1];
1703                 p += 2;
1704         }
1705         *tp = '\0';
1706         r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ?
1707             do_tilde(Xstring(ts, tp)) : NULL;
1708         Xfree(ts, tp);
1709         if (r) {
1710                 while (*r) {
1711                         Xcheck(*dsp, dp);
1712                         if (ISMAGIC(*r))
1713                                 *dp++ = MAGIC;
1714                         *dp++ = *r++;
1715                 }
1716                 *dpp = dp;
1717                 r = p;
1718         }
1719         return (r);
1720 }
1721
1722 /*
1723  * tilde expansion
1724  *
1725  * based on a version by Arnold Robbins
1726  */
1727
1728 char *
1729 do_tilde(char *cp)
1730 {
1731         char *dp = null;
1732
1733         if (cp[0] == '\0')
1734                 dp = str_val(global("HOME"));
1735         else if (cp[0] == '+' && cp[1] == '\0')
1736                 dp = str_val(global("PWD"));
1737         else if (cp[0] == '-' && cp[1] == '\0')
1738                 dp = str_val(global("OLDPWD"));
1739 #ifndef MKSH_NOPWNAM
1740         else
1741                 dp = homedir(cp);
1742 #endif
1743         /* If HOME, PWD or OLDPWD are not set, don't expand ~ */
1744         return (dp == null ? NULL : dp);
1745 }
1746
1747 #ifndef MKSH_NOPWNAM
1748 /*
1749  * map userid to user's home directory.
1750  * note that 4.3's getpw adds more than 6K to the shell,
1751  * and the YP version probably adds much more.
1752  * we might consider our own version of getpwnam() to keep the size down.
1753  */
1754 static char *
1755 homedir(char *name)
1756 {
1757         struct tbl *ap;
1758
1759         ap = ktenter(&homedirs, name, hash(name));
1760         if (!(ap->flag & ISSET)) {
1761                 struct passwd *pw;
1762
1763                 pw = getpwnam(name);
1764                 if (pw == NULL)
1765                         return (NULL);
1766                 strdupx(ap->val.s, pw->pw_dir, APERM);
1767                 ap->flag |= DEFINED|ISSET|ALLOC;
1768         }
1769         return (ap->val.s);
1770 }
1771 #endif
1772
1773 static void
1774 alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
1775 {
1776         unsigned int count = 0;
1777         char *brace_start, *brace_end, *comma = NULL;
1778         char *field_start;
1779         char *p = exp_start;
1780
1781         /* search for open brace */
1782         while ((p = strchr(p, MAGIC)) && p[1] != '{' /*}*/)
1783                 p += 2;
1784         brace_start = p;
1785
1786         /* find matching close brace, if any */
1787         if (p) {
1788                 comma = NULL;
1789                 count = 1;
1790                 p += 2;
1791                 while (*p && count) {
1792                         if (ISMAGIC(*p++)) {
1793                                 if (*p == '{' /*}*/)
1794                                         ++count;
1795                                 else if (*p == /*{*/ '}')
1796                                         --count;
1797                                 else if (*p == ',' && count == 1)
1798                                         comma = p;
1799                                 ++p;
1800                         }
1801                 }
1802         }
1803         /* no valid expansions... */
1804         if (!p || count != 0) {
1805                 /*
1806                  * Note that given a{{b,c} we do not expand anything (this is
1807                  * what AT&T ksh does. This may be changed to do the {b,c}
1808                  * expansion. }
1809                  */
1810                 if (fdo & DOGLOB)
1811                         glob(start, wp, tobool(fdo & DOMARKDIRS));
1812                 else
1813                         XPput(*wp, debunk(start, start, end - start));
1814                 return;
1815         }
1816         brace_end = p;
1817         if (!comma) {
1818                 alt_expand(wp, start, brace_end, end, fdo);
1819                 return;
1820         }
1821
1822         /* expand expression */
1823         field_start = brace_start + 2;
1824         count = 1;
1825         for (p = brace_start + 2; p != brace_end; p++) {
1826                 if (ISMAGIC(*p)) {
1827                         if (*++p == '{' /*}*/)
1828                                 ++count;
1829                         else if ((*p == /*{*/ '}' && --count == 0) ||
1830                             (*p == ',' && count == 1)) {
1831                                 char *news;
1832                                 int l1, l2, l3;
1833
1834                                 /*
1835                                  * addition safe since these operate on
1836                                  * one string (separate substrings)
1837                                  */
1838                                 l1 = brace_start - start;
1839                                 l2 = (p - 1) - field_start;
1840                                 l3 = end - brace_end;
1841                                 news = alloc(l1 + l2 + l3 + 1, ATEMP);
1842                                 memcpy(news, start, l1);
1843                                 memcpy(news + l1, field_start, l2);
1844                                 memcpy(news + l1 + l2, brace_end, l3);
1845                                 news[l1 + l2 + l3] = '\0';
1846                                 alt_expand(wp, news, news + l1,
1847                                     news + l1 + l2 + l3, fdo);
1848                                 field_start = p + 1;
1849                         }
1850                 }
1851         }
1852         return;
1853 }
1854
1855 /* helper function due to setjmp/longjmp woes */
1856 static char *
1857 valsub(struct op *t, Area *ap)
1858 {
1859         char * volatile cp = NULL;
1860         struct tbl * volatile vp = NULL;
1861
1862         newenv(E_FUNC);
1863         newblock();
1864         if (ap)
1865                 vp = local("REPLY", false);
1866         if (!kshsetjmp(e->jbuf))
1867                 execute(t, XXCOM | XERROK, NULL);
1868         if (vp)
1869                 strdupx(cp, str_val(vp), ap);
1870         quitenv(NULL);
1871
1872         return (cp);
1873 }