2 * Expansion - quoting, separation, substitution, globbing
13 * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution.
14 * second pass: alternation ({,}), filename expansion (*?[]).
17 /* expansion generator state */
18 typedef struct Expand {
19 /* int type; */ /* see expand() */
20 const char *str; /* string */
22 const char **strv;/* string[] */
23 struct shf *shf;/* file */
25 struct tbl *var; /* variable in ${var..} */
26 short split; /* split "$@" / call waitlast $() */
29 #define XBASE 0 /* scanning original */
30 #define XSUB 1 /* expanding ${} string */
31 #define XARGSEP 2 /* ifs0 between "$*" */
32 #define XARG 3 /* expanding $*, $@ */
33 #define XCOM 4 /* expanding $() */
34 #define XNULLSUB 5 /* "$@" when $# is 0 (don't generate word) */
36 /* States used for field splitting */
37 #define IFS_WORD 0 /* word has chars (or quotes) */
38 #define IFS_WS 1 /* have seen IFS white-space */
39 #define IFS_NWS 2 /* have seen IFS non-white-space */
41 static int varsub ARGS((Expand *xp, char *sp, char *word, int *stypep, int *slenp));
42 static int comsub ARGS((Expand *xp, char *cp));
43 static char *trimsub ARGS((char *str, char *pat, int how));
44 static void glob ARGS((char *cp, XPtrV *wp, int markdirs));
45 static void globit ARGS((XString *xs, char **xpp, char *sp, XPtrV *wp,
47 static char *maybe_expand_tilde ARGS((char *p, XString *dsp, char **dpp,
49 static char *tilde ARGS((char *acp));
50 static char *homedir ARGS((char *name));
52 static void alt_expand ARGS((XPtrV *wp, char *start, char *exp_start,
56 /* compile and expand word */
62 struct source *s, *sold;
65 s = pushs(SWSTR, ATEMP);
66 s->start = s->str = cp;
68 if (yylex(ONEWORD) != LWORD)
69 internal_errorf(1, "substitute");
72 return evalstr(yylval.cp, f);
88 XPput(w, NULL); /* space for shell name */
90 XPput(w, NULL); /* and space for one arg */
96 return (char **) XPclose(w) + 2;
98 return (char **) XPclose(w) + 1;
114 cp = (XPsize(w) == 0) ? null : (char*) *XPptrv(w);
120 * expand string - return only one component
121 * used from iosetup to expand redirection files
137 cp = (char*) *XPptrv(w);
140 cp = evalstr(cp, f&~DOGLOB);
147 /* for nested substitution: ${var:=$var2} */
148 typedef struct SubType {
149 short stype; /* [=+-?%#] action after expanded word */
150 short base; /* begin position of expanded word */
151 short f; /* saved value of f (DOPAT, etc) */
152 struct tbl *var; /* variable for ${var..} */
153 short quote; /* saved value of quote (for ${..[%#]..}) */
154 struct SubType *prev; /* old type */
155 struct SubType *next; /* poped type (to avoid re-allocating) */
160 char *cp; /* input word */
161 register XPtrV *wp; /* output words */
162 int f; /* DO* flags */
164 register int UNINITIALIZED(c);
165 register int type; /* expansion type */
166 register int quote = 0; /* quoted */
167 XString ds; /* destination string */
168 register char *dp, *sp; /* dest., source */
169 int fdo, word; /* second pass flags; have word */
170 int doblank; /* field spliting of parameter/command subst */
171 Expand x; /* expansion variables */
172 SubType st_head, *st;
173 int UNINITIALIZED(newlines); /* For trailing newlines in COMSUB */
174 int saw_eq, tilde_ok;
178 internal_errorf(1, "expand(NULL)");
179 /* for alias, readonly, set, typeset commands */
180 if ((f & DOVACHECK) && is_wdvarassign(cp)) {
181 f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE);
189 if (Flag(FBRACEEXPAND) && (f & DOGLOB))
191 #endif /* BRACE_EXPAND */
193 Xinit(ds, dp, 128, ATEMP); /* init dest. string */
198 tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; /* must be 1/0 */
201 word = (f&DOBLANK) ? IFS_WS : IFS_WORD;
202 st_head.next = (SubType *) 0;
209 case XBASE: /* original prefixed string */
219 quote |= 2; /* temporary quote */
232 if (f & DONTRUNCOMMAND) {
234 *dp++ = '$'; *dp++ = '(';
235 while (*sp != '\0') {
241 type = comsub(&x, sp);
242 if (type == XCOM && (f&DOBLANK))
244 sp = strchr(sp, 0) + 1;
251 if (f & DONTRUNCOMMAND) {
252 *dp++ = '$'; *dp++ = '('; *dp++ = '(';
253 while (*sp != '\0') {
257 *dp++ = ')'; *dp++ = ')';
262 v.flag = DEFINED|ISSET|INTEGER;
263 v.type = 10; /* not default */
265 v_evaluate(&v, substitute(sp, 0),
267 sp = strchr(sp, 0) + 1;
268 for (p = str_val(&v); *p; ) {
274 case OSUBST: /* ${{#}var{:}[=+-?#%]word} */
276 * OSUBST [{x] plain-variable-part \0
277 * compiled-word-part CSUBST [}x]
278 * This is were all syntax checking gets done...
281 char *varname = ++sp; /* skip the { or x (}) */
285 sp = strchr(sp, '\0') + 1; /* skip variable */
286 type = varsub(&x, varname, sp, &stype, &slen);
291 end = (char *) wdscan(sp, CSUBST);
292 /* ({) the } or x is already skipped */
295 str = snptreef((char *) 0, 64, "%S",
298 errorf("%s: bad substitution", str);
303 if (type == XBASE) { /* expand? */
307 newst = (SubType *) alloc(
308 sizeof(SubType), ATEMP);
309 newst->next = (SubType *) 0;
315 st->base = Xsavepos(ds, dp);
319 /* skip qualifier(s) */
322 switch (stype & 0x7f) {
325 /* ! DOBLANK,DOBRACE_,DOTILDE */
326 f = DOPAT | (f&DONTRUNCOMMAND)
329 /* Prepend open pattern (so |
330 * in a trim will work as
337 /* Enabling tilde expansion
339 * non-standard ksh, but is
340 * consistent with rules for
341 * other assignments. Not
342 * sure what POSIX thinks of
344 * Not doing tilde expansion
345 * for integer variables is a
346 * non-POSIX thing - makes
347 * sense though, since ~ is
348 * a arithmetic operator.
350 if (!(x.var->flag & INTEGER))
351 f |= DOASNTILDE|DOTILDE;
353 /* These will be done after the
354 * value has been assigned.
356 f &= ~(DOBLANK|DOGLOB|DOBRACE_);
364 /* Enable tilde expansion */
370 sp = (char *) wdscan(sp, CSUBST);
373 case CSUBST: /* only get here if expanding word */
374 sp++; /* ({) skip the } or x */
375 tilde_ok = 0; /* in case of ${unset:-} */
381 switch (st->stype&0x7f) {
384 /* Append end-pattern */
385 *dp++ = MAGIC; *dp++ = ')'; *dp = '\0';
386 dp = Xrestpos(ds, dp, st->base);
387 /* Must use st->var since calling
388 * global would break things
391 x.str = trimsub(str_val(st->var),
399 /* Restore our position and substitute
400 * the value of st->var (may not be
401 * the assigned value in the presence
402 * of integer/right-adj/etc attributes).
404 dp = Xrestpos(ds, dp, st->base);
405 /* Must use st->var since calling
406 * global would cause with things
407 * like x[i+=1] to be evaluated twice.
409 /* Note: not exported by FEXPORT
412 /* XXX POSIX says readonly is only
413 * fatal for special builtins (setstr
414 * does readonly check).
416 setstr(st->var, debunk(
417 (char *) alloc(strlen(dp) + 1,
420 x.str = str_val(st->var);
428 char *s = Xrestpos(ds, dp, st->base);
430 errorf("%s: %s", st->var->name,
432 "parameter null or not set"
433 : (debunk(s, s), s));
440 case OPAT: /* open pattern: *(foo|bar) */
441 /* Next char is the type of pattern */
446 case SPAT: /* pattern seperator (|) */
451 case CPAT: /* close pattern */
459 /* Special case for "$@" (and "${foo[@]}") - no
460 * word is generated if $# is 0 (unless there is
461 * other stuff inside the quotes).
466 /* not really correct: x=; "$x$@" should
467 * generate a null argument and
468 * set A; "${@:+}" shouldn't.
470 if (dp == Xstring(ds, dp))
476 if ((c = *x.str++) == 0) {
488 if ((c = *x.str++) == '\0') {
489 /* force null words to be created so
490 * set -- '' 2 ''; foo "$@" will do
493 if (quote && x.split)
495 if ((x.str = *x.u.strv++) == NULL) {
503 if (quote && !x.split)
507 if (quote && x.split) {
508 /* terminate word for "$@" */
516 if (newlines) { /* Spit out saved nl's */
520 while ((c = shf_getc(x.u.shf)) == 0 || c == '\n')
522 newlines++; /* Save newlines */
523 if (newlines && c != EOF) {
524 shf_ungetc(c, x.u.shf);
533 subst_exstat = waitlast();
542 /* check for end of word or IFS separation */
543 if (c == 0 || (!quote && (f & DOBLANK) && doblank && !make_magic
546 /* How words are broken up:
549 * -----------------------------------
550 * IFS_WORD w/WS w/NWS w
551 * IFS_WS -/WS w/NWS -
552 * IFS_NWS -/NWS w/NWS w
553 * (w means generate a word)
554 * Note that IFS_NWS/0 generates a word (at&t ksh
555 * doesn't do this, but POSIX does).
558 || (!ctype(c, C_IFSWS) && (c || word == IFS_NWS)))
566 /* also does globbing */
568 p + Xlength(ds, (dp - 1)),
569 fdo | (f & DOMARKDIRS));
571 #endif /* BRACE_EXPAND */
573 glob(p, wp, f & DOMARKDIRS);
574 else if ((f & DOPAT) || !(fdo & DOMAGIC_))
577 XPput(*wp, debunk(p, p));
580 tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
582 Xinit(ds, dp, 128, ATEMP);
587 word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
589 /* age tilde_ok info - ~ code tests second bit */
591 /* mark any special second pass chars */
598 /* For character classes - doesn't hurt
599 * to have magic !,-,]'s outside of
602 if (f & (DOPAT | DOGLOB)) {
611 if (f & (DOPAT | DOGLOB)) {
612 fdo |= DOMAGIC_ | (f & DOGLOB);
620 if ((f & DOBRACE_) && (c == OBRACE
621 || (fdo & DOBRACE_)))
623 fdo |= DOBRACE_|DOMAGIC_;
627 #endif /* BRACE_EXPAND */
629 /* Note first unquoted = for ~ */
630 if (!(f & DOTEMP_) && !saw_eq) {
635 case PATHSEP: /* : */
636 /* Note unquoted : for ~ */
637 if (!(f & DOTEMP_) && (f & DOASNTILDE))
641 /* tilde_ok is reset whenever
642 * any of ' " $( $(( ${ } are seen.
643 * Note that tilde_ok must be preserved
644 * through the sequence ${A=a=}~
647 && (f & (DOTILDE|DOASNTILDE))
653 p = maybe_expand_tilde(sp,
667 quote &= ~2; /* undo temporary */
671 fdo |= DOMAGIC_ | (f & DOGLOB);
673 } else if (ISMAGIC(c)) {
677 *dp++ = c; /* save output char */
684 * Prepare to generate the string returned by ${} substitution.
687 varsub(xp, sp, word, stypep, slenp)
691 int *stypep; /* becomes qualifier type */
692 int *slenp; /* " " len (=, :=, etc.) valid iff *stypep != 0 */
695 int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */
696 int stype; /* substitution type */
701 if (sp[0] == '\0') /* Bad variable name */
704 xp->var = (struct tbl *) 0;
706 /* ${#var}, string length or array size */
707 if (sp[0] == '#' && (c = sp[1]) != '\0') {
710 /* Can't have any modifiers for ${#...} */
714 /* Check for size of array */
715 if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') {
718 vp = global(arrayname(sp));
719 if (vp->flag & (ISSET|ARRAY))
721 for (; vp; vp = vp->u.array)
722 if (vp->flag & ISSET) {
726 c = n; /* ksh88/ksh93 go for number, not max index */
727 } else if (c == '*' || c == '@')
730 p = str_val(global(sp));
734 if (Flag(FNOUNSET) && c == 0 && !zero_ok)
735 errorf("%s: parameter not set", sp);
736 *stypep = 0; /* unqualified variable/string substitution */
737 xp->str = str_save(ulton((unsigned long)c, 10), ATEMP);
741 /* Check for qualifiers in word part */
743 c = word[slen = 0] == CHAR ? word[1] : 0;
747 c = word[slen + 0] == CHAR ? word[slen + 1] : 0;
749 if (ctype(c, C_SUBOP1)) {
752 } else if (ctype(c, C_SUBOP2)) { /* Note: ksh88 allows :%, :%%, etc */
755 if (word[slen + 0] == CHAR && c == word[slen + 1]) {
759 } else if (stype) /* : is not ok */
761 if (!stype && *word != CSUBST)
767 if (c == '*' || c == '@') {
768 switch (stype & 0x7f) {
769 case '=': /* can't assign to a vector */
770 case '%': /* can't trim a vector (yet) */
774 if (e->loc->argc == 0) {
776 state = c == '@' ? XNULLSUB : XSUB;
778 xp->u.strv = (const char **) e->loc->argv + 1;
779 xp->str = *xp->u.strv++;
780 xp->split = c == '@'; /* $@ */
784 if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') {
787 switch (stype & 0x7f) {
788 case '=': /* can't assign to a vector */
789 case '%': /* can't trim a vector (yet) */
794 vp = global(arrayname(sp));
795 for (; vp; vp = vp->u.array) {
796 if (!(vp->flag&ISSET))
798 XPput(wv, str_val(vp));
800 if (XPsize(wv) == 0) {
802 state = p[1] == '@' ? XNULLSUB : XSUB;
806 xp->u.strv = (const char **) XPptrv(wv);
807 xp->str = *xp->u.strv++;
808 xp->split = p[1] == '@'; /* ${foo[@]} */
812 /* Can't assign things like $! or $1 */
813 if ((stype & 0x7f) == '='
814 && (ctype(*sp, C_VAR1) || digit(*sp)))
816 xp->var = global(sp);
817 xp->str = str_val(xp->var);
823 /* test the compiler's code generator */
824 if (ctype(c, C_SUBOP2) ||
825 (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
826 c == '=' || c == '-' || c == '?' : c == '+'))
827 state = XBASE; /* expand word instead of variable value */
828 if (Flag(FNOUNSET) && xp->str == null
829 && (ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))
830 errorf("%s: parameter not set", sp);
835 * Run the command in $(...) and read its output.
843 register struct op *t;
846 s = pushs(SSTRING, ATEMP);
847 s->start = s->str = cp;
855 if (t != NULL && t->type == TCOM && /* $(<file) */
856 *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
857 register struct ioword *io = *t->ioact;
860 if ((io->flag&IOTYPE) != IOREAD)
861 errorf("funny $() command: %s",
862 snptreef((char *) 0, 32, "%R", io));
863 shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0,
864 SHF_MAPHI|SHF_CLEXEC);
866 errorf("%s: cannot open $() input", name);
867 xp->split = 0; /* no waitlast() */
871 shf = shf_fdopen(pv[0], SHF_RD, (struct shf *) 0);
872 ofd1 = savefd(1, 0); /* fd 1 may be closed... */
873 ksh_dup2(pv[1], 1, FALSE);
875 execute(t, XFORK|XXCOM|XPIPEO);
878 xp->split = 1; /* waitlast() */
886 * perform #pattern and %pattern substitution in ${}
890 trimsub(str, pat, how)
895 register char *end = strchr(str, 0);
898 switch (how&0xff) { /* UCHAR_MAX maybe? */
899 case '#': /* shortest at begining */
900 for (p = str; p <= end; p++) {
902 if (gmatch(str, pat, FALSE)) {
909 case '#'|0x80: /* longest match at begining */
910 for (p = end; p >= str; p--) {
912 if (gmatch(str, pat, FALSE)) {
919 case '%': /* shortest match at end */
920 for (p = end; p >= str; p--) {
921 if (gmatch(p, pat, FALSE))
922 return str_nsave(str, p - str, ATEMP);
925 case '%'|0x80: /* longest match at end */
926 for (p = str; p <= end; p++) {
927 if (gmatch(p, pat, FALSE))
928 return str_nsave(str, p - str, ATEMP);
933 return str; /* no match, return string */
938 * Name derived from V6's /etc/glob, the program that expanded filenames.
941 /* XXX cp not const 'cause slashes are temporarily replaced with nulls... */
943 glob(cp, wp, markdirs)
948 int oldsize = XPsize(*wp);
950 if (glob_str(cp, wp, markdirs) == 0)
951 XPput(*wp, debunk(cp, cp));
953 qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize),
958 #define GF_EXCHECK BIT(0) /* do existance check on file */
959 #define GF_GLOBBED BIT(1) /* some globbing has been done */
960 #define GF_MARKDIR BIT(2) /* add trailing / to directories */
962 /* Apply file globbing to cp and store the matching files in wp. Returns
963 * the number of matches found.
966 glob_str(cp, wp, markdirs)
971 int oldsize = XPsize(*wp);
975 Xinit(xs, xp, 256, ATEMP);
976 globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE);
979 return XPsize(*wp) - oldsize;
983 globit(xs, xpp, sp, wp, check)
984 XString *xs; /* dest string */
985 char **xpp; /* ptr to dest end */
986 char *sp; /* source path */
987 register XPtrV *wp; /* output list */
988 int check; /* GF_* flags */
990 register char *np; /* next source component */
995 /* This to allow long expansions to be interrupted */
998 if (sp == NULL) { /* end of source path */
999 /* We only need to check if the file exists if a pattern
1000 * is followed by a non-pattern (eg, foo*x/bar; no check
1001 * is needed for foo* since the match must exist) or if
1002 * any patterns were expanded and the markdirs option is set.
1003 * Symlinks make things a bit tricky...
1005 if ((check & GF_EXCHECK)
1006 || ((check & GF_MARKDIR) && (check & GF_GLOBBED)))
1008 #define stat_check() (stat_done ? stat_done : \
1009 (stat_done = stat(Xstring(*xs, xp), &statb) < 0 \
1011 struct stat lstatb, statb;
1012 int stat_done = 0; /* -1: failed, 1 ok */
1014 if (lstat(Xstring(*xs, xp), &lstatb) < 0)
1016 /* special case for systems which strip trailing
1017 * slashes from regular files (eg, /etc/passwd/).
1018 * SunOS 4.1.3 does this...
1020 if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp)
1021 && ISDIRSEP(xp[-1]) && !S_ISDIR(lstatb.st_mode)
1023 && (!S_ISLNK(lstatb.st_mode)
1025 || !S_ISDIR(statb.st_mode))
1026 #endif /* S_ISLNK */
1029 /* Possibly tack on a trailing / if there isn't already
1030 * one and if the file is a directory or a symlink to a
1033 if (((check & GF_MARKDIR) && (check & GF_GLOBBED))
1034 && xp > Xstring(*xs, xp) && !ISDIRSEP(xp[-1])
1035 && (S_ISDIR(lstatb.st_mode)
1037 || (S_ISLNK(lstatb.st_mode)
1039 && S_ISDIR(statb.st_mode))
1040 #endif /* S_ISLNK */
1047 #ifdef OS2 /* Done this way to avoid bug in gcc 2.7.2... */
1048 /* Ugly kludge required for command
1049 * completion - see how search_access()
1050 * is implemented for OS/2...
1052 # define KLUDGE_VAL 4
1054 # define KLUDGE_VAL 0
1056 XPput(*wp, str_nsave(Xstring(*xs, xp), Xlength(*xs, xp)
1057 + KLUDGE_VAL, ATEMP));
1061 if (xp > Xstring(*xs, xp))
1063 while (ISDIRSEP(*sp)) {
1067 np = ksh_strchr_dirsep(sp);
1070 odirsep = *np; /* don't assume DIRSEP, can be multiple kinds */
1073 odirsep = '\0'; /* keep gcc quiet */
1074 se = sp + strlen(sp);
1078 /* Check if sp needs globbing - done to avoid pattern checks for strings
1079 * containing MAGIC characters, open ['s without the matching close ],
1080 * etc. (otherwise opendir() will be called which may fail because the
1081 * directory isn't readable - if no globbing is needed, only execute
1082 * permission should be required (as per POSIX)).
1084 if (!has_globbing(sp, se)) {
1085 XcheckN(*xs, xp, se - sp + 1);
1089 globit(xs, xpp, np, wp, check);
1097 /* xp = *xpp; copy_non_glob() may have re-alloc'd xs */
1099 prefix_len = Xlength(*xs, xp);
1100 dirp = ksh_opendir(prefix_len ? Xstring(*xs, xp) : ".");
1103 while ((d = readdir(dirp)) != NULL) {
1105 if (name[0] == '.' &&
1106 (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
1107 continue; /* always ignore . and .. */
1108 if ((*name == '.' && *sp != '.')
1109 || !gmatch(name, sp, TRUE))
1112 len = NLENGTH(d) + 1;
1113 XcheckN(*xs, xp, len);
1114 memcpy(xp, name, len);
1115 *xpp = xp + len - 1;
1116 globit(xs, xpp, np, wp,
1117 (check & GF_MARKDIR) | GF_GLOBBED
1118 | (np ? GF_EXCHECK : GF_NONE));
1119 xp = Xstring(*xs, xp) + prefix_len;
1130 /* Check if p contains something that needs globbing; if it does, 0 is
1131 * returned; if not, p is copied into xs/xp after stripping any MAGICs
1133 static int copy_non_glob ARGS((XString *xs, char **xpp, char *p));
1135 copy_non_glob(xs, xpp, p)
1141 int len = strlen(p);
1143 XcheckN(*xs, *xpp, len);
1149 if (c == '*' || c == '?')
1154 if (ISMAGIC(*q) && q[1] == NOT)
1156 if (ISMAGIC(*q) && q[1] == ']')
1159 if (ISMAGIC(*q) && *++q == ']')
1161 /* pass a literal [ through */
1163 /* must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, etc. */
1173 /* remove MAGIC from string */
1181 if ((s = strchr(sp, MAGIC))) {
1182 memcpy(dp, sp, s - sp);
1183 for (d = dp + (s - sp); *s; s++)
1184 if (!ISMAGIC(*s) || !(*++s & 0x80)
1185 || !strchr("*+?@! ", *s & 0x7f))
1188 /* extended pattern operators: *+?@! */
1189 if ((*s & 0x7f) != ' ')
1194 } else if (dp != sp)
1199 /* Check if p is an unquoted name, possibly followed by a / or :. If so
1200 * puts the expanded version in *dcp,dp and returns a pointer in p just
1201 * past the name, otherwise returns 0.
1204 maybe_expand_tilde(p, dsp, dpp, isassign)
1214 Xinit(ts, tp, 16, ATEMP);
1215 /* : only for DOASNTILDE form */
1216 while (p[0] == CHAR && !ISDIRSEP(p[1])
1217 && (!isassign || p[1] != PATHSEP))
1224 r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ? tilde(Xstring(ts, tp)) : (char *) 0;
1242 * based on a version by Arnold Robbins
1252 dp = str_val(global("HOME"));
1253 else if (cp[0] == '+' && cp[1] == '\0')
1254 dp = str_val(global("PWD"));
1255 else if (cp[0] == '-' && cp[1] == '\0')
1256 dp = str_val(global("OLDPWD"));
1259 /* If HOME, PWD or OLDPWD are not set, don't expand ~ */
1266 * map userid to user's home directory.
1267 * note that 4.3's getpw adds more than 6K to the shell,
1268 * and the YP version probably adds much more.
1269 * we might consider our own version of getpwnam() to keep the size down.
1276 register struct tbl *ap;
1278 ap = tenter(&homedirs, name, hash(name));
1279 if (!(ap->flag & ISSET)) {
1281 /* No usernames in OS2 - punt */
1286 pw = getpwnam(name);
1289 ap->val.s = str_save(pw->pw_dir, APERM);
1290 ap->flag |= DEFINED|ISSET|ALLOC;
1298 alt_expand(wp, start, exp_start, end, fdo)
1300 char *start, *exp_start;
1304 int UNINITIALIZED(count);
1305 char *brace_start, *brace_end, *UNINITIALIZED(comma);
1309 /* search for open brace */
1310 for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != OBRACE; p += 2)
1314 /* find matching close brace, if any */
1318 for (p += 2; *p && count; p++) {
1322 else if (*p == CBRACE)
1324 else if (*p == ',' && count == 1)
1329 /* no valid expansions... */
1330 if (!p || count != 0) {
1331 /* Note that given a{{b,c} we do not expand anything (this is
1332 * what at&t ksh does. This may be changed to do the {b,c}
1336 glob(start, wp, fdo & DOMARKDIRS);
1338 XPput(*wp, debunk(start, start));
1343 alt_expand(wp, start, brace_end, end, fdo);
1347 /* expand expression */
1348 field_start = brace_start + 2;
1350 for (p = brace_start + 2; p != brace_end; p++) {
1354 else if ((*p == CBRACE && --count == 0)
1355 || (*p == ',' && count == 1))
1360 l1 = brace_start - start;
1361 l2 = (p - 1) - field_start;
1362 l3 = end - brace_end;
1363 new = (char *) alloc(l1 + l2 + l3 + 1, ATEMP);
1364 memcpy(new, start, l1);
1365 memcpy(new + l1, field_start, l2);
1366 memcpy(new + l1 + l2, brace_end, l3);
1367 new[l1 + l2 + l3] = '\0';
1368 alt_expand(wp, new, new + l1,
1369 new + l1 + l2 + l3, fdo);
1370 field_start = p + 1;
1376 #endif /* BRACE_EXPAND */