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