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