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.5 2015/04/12 22:32:24 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
utflen(const char * s)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
utfincptr(const char * s,mksh_ari_t * lp)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 *
substitute(const char * cp,int f)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 **
eval(const char ** ap,int f)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 *
evalstr(const char * cp,int f)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 *
evalonestr(const char * cp,int f)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
expand(const char * ccp,XPtrV * wp,int f)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
varsub(Expand * xp,const char * sp,const char * word,int * stypep,int * slenp)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
comsub(Expand * xp,const char * cp,int fn MKSH_A_UNUSED)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->ioflag & 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 *
trimsub(char * str,char * pat,int how)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
glob(char * cp,XPtrV * wp,bool markdirs)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
glob_str(char * cp,XPtrV * wp,bool markdirs)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
globit(XString * xs,char ** xpp,char * sp,XPtrV * wp,int check)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 *
debunk(char * dp,const char * sp,size_t dlen)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 *
maybe_expand_tilde(const char * p,XString * dsp,char ** dpp,bool isassign)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 *
do_tilde(char * cp)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 *
homedir(char * name)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
alt_expand(XPtrV * wp,char * start,char * exp_start,char * end,int fdo)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 *
valsub(struct op * t,Area * ap)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 }
1874