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