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