1*84d9c625SLionel Sambuc /* $NetBSD: eval.c,v 1.15 2013/10/18 19:53:34 christos Exp $ */
22718b568SThomas Cort
32718b568SThomas Cort /*
42718b568SThomas Cort * Expansion - quoting, separation, substitution, globbing
52718b568SThomas Cort */
62718b568SThomas Cort #include <sys/cdefs.h>
72718b568SThomas Cort
82718b568SThomas Cort #ifndef lint
9*84d9c625SLionel Sambuc __RCSID("$NetBSD: eval.c,v 1.15 2013/10/18 19:53:34 christos Exp $");
102718b568SThomas Cort #endif
112718b568SThomas Cort
122718b568SThomas Cort #include <stdint.h>
132718b568SThomas Cort #include <pwd.h>
142718b568SThomas Cort
152718b568SThomas Cort #include "sh.h"
162718b568SThomas Cort #include "ksh_dir.h"
172718b568SThomas Cort #include "ksh_stat.h"
182718b568SThomas Cort
192718b568SThomas Cort /*
202718b568SThomas Cort * string expansion
212718b568SThomas Cort *
222718b568SThomas Cort * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution.
232718b568SThomas Cort * second pass: alternation ({,}), filename expansion (*?[]).
242718b568SThomas Cort */
252718b568SThomas Cort
262718b568SThomas Cort /* expansion generator state */
272718b568SThomas Cort typedef struct Expand {
282718b568SThomas Cort /* int type; */ /* see expand() */
292718b568SThomas Cort const char *str; /* string */
302718b568SThomas Cort union {
312718b568SThomas Cort const char **strv;/* string[] */
322718b568SThomas Cort struct shf *shf;/* file */
332718b568SThomas Cort } u; /* source */
342718b568SThomas Cort struct tbl *var; /* variable in ${var..} */
352718b568SThomas Cort short split; /* split "$@" / call waitlast $() */
362718b568SThomas Cort } Expand;
372718b568SThomas Cort
382718b568SThomas Cort #define XBASE 0 /* scanning original */
392718b568SThomas Cort #define XSUB 1 /* expanding ${} string */
402718b568SThomas Cort #define XARGSEP 2 /* ifs0 between "$*" */
412718b568SThomas Cort #define XARG 3 /* expanding $*, $@ */
422718b568SThomas Cort #define XCOM 4 /* expanding $() */
432718b568SThomas Cort #define XNULLSUB 5 /* "$@" when $# is 0 (don't generate word) */
442718b568SThomas Cort
452718b568SThomas Cort /* States used for field splitting */
462718b568SThomas Cort #define IFS_WORD 0 /* word has chars (or quotes) */
472718b568SThomas Cort #define IFS_WS 1 /* have seen IFS white-space */
482718b568SThomas Cort #define IFS_NWS 2 /* have seen IFS non-white-space */
492718b568SThomas Cort
502718b568SThomas Cort static int varsub ARGS((Expand *xp, char *sp, char *word, int *stypep, int *slenp));
512718b568SThomas Cort static int comsub ARGS((Expand *xp, char *cp));
522718b568SThomas Cort static char *trimsub ARGS((char *str, char *pat, int how));
532718b568SThomas Cort static void glob ARGS((char *cp, XPtrV *wp, int markdirs));
542718b568SThomas Cort static void globit ARGS((XString *xs, char **xpp, char *sp, XPtrV *wp,
552718b568SThomas Cort int check));
562718b568SThomas Cort static char *maybe_expand_tilde ARGS((char *p, XString *dsp, char **dpp,
572718b568SThomas Cort int isassign));
582718b568SThomas Cort static char *tilde ARGS((char *acp));
592718b568SThomas Cort static char *homedir ARGS((char *name));
602718b568SThomas Cort #ifdef BRACE_EXPAND
612718b568SThomas Cort static void alt_expand ARGS((XPtrV *wp, char *start, char *exp_start,
622718b568SThomas Cort char *end, int fdo));
632718b568SThomas Cort #endif
642718b568SThomas Cort
652718b568SThomas Cort /* compile and expand word */
662718b568SThomas Cort char *
substitute(cp,f)672718b568SThomas Cort substitute(cp, f)
682718b568SThomas Cort const char *cp;
692718b568SThomas Cort int f;
702718b568SThomas Cort {
712718b568SThomas Cort struct source *s, *sold;
722718b568SThomas Cort
732718b568SThomas Cort sold = source;
742718b568SThomas Cort s = pushs(SWSTR, ATEMP);
752718b568SThomas Cort s->start = s->str = cp;
762718b568SThomas Cort source = s;
772718b568SThomas Cort if (yylex(ONEWORD) != LWORD)
782718b568SThomas Cort internal_errorf(1, "substitute");
792718b568SThomas Cort source = sold;
802718b568SThomas Cort afree(s, ATEMP);
812718b568SThomas Cort return evalstr(yylval.cp, f);
822718b568SThomas Cort }
832718b568SThomas Cort
842718b568SThomas Cort /*
852718b568SThomas Cort * expand arg-list
862718b568SThomas Cort */
872718b568SThomas Cort char **
eval(ap,f)882718b568SThomas Cort eval(ap, f)
892718b568SThomas Cort register char **ap;
902718b568SThomas Cort int f;
912718b568SThomas Cort {
922718b568SThomas Cort XPtrV w;
932718b568SThomas Cort
942718b568SThomas Cort if (*ap == NULL)
952718b568SThomas Cort return ap;
962718b568SThomas Cort XPinit(w, 32);
972718b568SThomas Cort XPput(w, NULL); /* space for shell name */
982718b568SThomas Cort #ifdef SHARPBANG
992718b568SThomas Cort XPput(w, NULL); /* and space for one arg */
1002718b568SThomas Cort #endif
1012718b568SThomas Cort while (*ap != NULL)
1022718b568SThomas Cort expand(*ap++, &w, f);
1032718b568SThomas Cort XPput(w, NULL);
1042718b568SThomas Cort #ifdef SHARPBANG
1052718b568SThomas Cort return (char **) XPclose(w) + 2;
1062718b568SThomas Cort #else
1072718b568SThomas Cort return (char **) XPclose(w) + 1;
1082718b568SThomas Cort #endif
1092718b568SThomas Cort }
1102718b568SThomas Cort
1112718b568SThomas Cort /*
1122718b568SThomas Cort * expand string
1132718b568SThomas Cort */
1142718b568SThomas Cort char *
evalstr(cp,f)1152718b568SThomas Cort evalstr(cp, f)
1162718b568SThomas Cort char *cp;
1172718b568SThomas Cort int f;
1182718b568SThomas Cort {
1192718b568SThomas Cort XPtrV w;
1202718b568SThomas Cort
1212718b568SThomas Cort XPinit(w, 1);
1222718b568SThomas Cort expand(cp, &w, f);
1232718b568SThomas Cort cp = (XPsize(w) == 0) ? null : (char*) *XPptrv(w);
1242718b568SThomas Cort XPfree(w);
1252718b568SThomas Cort return cp;
1262718b568SThomas Cort }
1272718b568SThomas Cort
1282718b568SThomas Cort /*
1292718b568SThomas Cort * expand string - return only one component
1302718b568SThomas Cort * used from iosetup to expand redirection files
1312718b568SThomas Cort */
1322718b568SThomas Cort char *
evalonestr(cp,f)1332718b568SThomas Cort evalonestr(cp, f)
1342718b568SThomas Cort register char *cp;
1352718b568SThomas Cort int f;
1362718b568SThomas Cort {
1372718b568SThomas Cort XPtrV w;
1382718b568SThomas Cort
1392718b568SThomas Cort XPinit(w, 1);
1402718b568SThomas Cort expand(cp, &w, f);
1412718b568SThomas Cort switch (XPsize(w)) {
1422718b568SThomas Cort case 0:
1432718b568SThomas Cort cp = null;
1442718b568SThomas Cort break;
1452718b568SThomas Cort case 1:
1462718b568SThomas Cort cp = (char*) *XPptrv(w);
1472718b568SThomas Cort break;
1482718b568SThomas Cort default:
1492718b568SThomas Cort cp = evalstr(cp, f&~DOGLOB);
1502718b568SThomas Cort break;
1512718b568SThomas Cort }
1522718b568SThomas Cort XPfree(w);
1532718b568SThomas Cort return cp;
1542718b568SThomas Cort }
1552718b568SThomas Cort
1562718b568SThomas Cort /* for nested substitution: ${var:=$var2} */
1572718b568SThomas Cort typedef struct SubType {
1582718b568SThomas Cort short stype; /* [=+-?%#] action after expanded word */
1592718b568SThomas Cort short base; /* begin position of expanded word */
1602718b568SThomas Cort short f; /* saved value of f (DOPAT, etc) */
1612718b568SThomas Cort struct tbl *var; /* variable for ${var..} */
1622718b568SThomas Cort short quote; /* saved value of quote (for ${..[%#]..}) */
1632718b568SThomas Cort struct SubType *prev; /* old type */
1642718b568SThomas Cort struct SubType *next; /* poped type (to avoid re-allocating) */
1652718b568SThomas Cort } SubType;
1662718b568SThomas Cort
1672718b568SThomas Cort void
expand(cp,wp,f)1682718b568SThomas Cort expand(cp, wp, f)
1692718b568SThomas Cort char *cp; /* input word */
1702718b568SThomas Cort register XPtrV *wp; /* output words */
1712718b568SThomas Cort int f; /* DO* flags */
1722718b568SThomas Cort {
1732718b568SThomas Cort register int UNINITIALIZED(c);
1742718b568SThomas Cort register int type; /* expansion type */
1752718b568SThomas Cort register int quote = 0; /* quoted */
1762718b568SThomas Cort XString ds; /* destination string */
1772718b568SThomas Cort register char *dp, *sp; /* dest., source */
1782718b568SThomas Cort int fdo, word; /* second pass flags; have word */
1792718b568SThomas Cort int doblank; /* field splitting of parameter/command subst */
1802718b568SThomas Cort Expand x; /* expansion variables */
1812718b568SThomas Cort SubType st_head, *st;
1822718b568SThomas Cort int UNINITIALIZED(newlines); /* For trailing newlines in COMSUB */
1832718b568SThomas Cort int saw_eq, tilde_ok;
1842718b568SThomas Cort int make_magic;
1852718b568SThomas Cort size_t len;
1862718b568SThomas Cort
1872718b568SThomas Cort x.split = 0; /* XXX gcc */
1882718b568SThomas Cort x.str = NULL; /* XXX gcc */
1892718b568SThomas Cort x.u.strv = NULL;/* XXX gcc */
1902718b568SThomas Cort if (cp == NULL)
1912718b568SThomas Cort internal_errorf(1, "expand(NULL)");
1922718b568SThomas Cort /* for alias, readonly, set, typeset commands */
1932718b568SThomas Cort if ((f & DOVACHECK) && is_wdvarassign(cp)) {
1942718b568SThomas Cort f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE);
1952718b568SThomas Cort f |= DOASNTILDE;
1962718b568SThomas Cort }
1972718b568SThomas Cort if (Flag(FNOGLOB))
1982718b568SThomas Cort f &= ~DOGLOB;
1992718b568SThomas Cort if (Flag(FMARKDIRS))
2002718b568SThomas Cort f |= DOMARKDIRS;
2012718b568SThomas Cort #ifdef BRACE_EXPAND
2022718b568SThomas Cort if (Flag(FBRACEEXPAND) && (f & DOGLOB))
2032718b568SThomas Cort f |= DOBRACE_;
2042718b568SThomas Cort #endif /* BRACE_EXPAND */
2052718b568SThomas Cort
2062718b568SThomas Cort Xinit(ds, dp, 128, ATEMP); /* init dest. string */
2072718b568SThomas Cort type = XBASE;
2082718b568SThomas Cort sp = cp;
2092718b568SThomas Cort fdo = 0;
2102718b568SThomas Cort saw_eq = 0;
2112718b568SThomas Cort tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; /* must be 1/0 */
2122718b568SThomas Cort doblank = 0;
2132718b568SThomas Cort make_magic = 0;
2142718b568SThomas Cort word = (f&DOBLANK) ? IFS_WS : IFS_WORD;
2152718b568SThomas Cort st_head.next = (SubType *) 0;
2162718b568SThomas Cort st = &st_head;
2172718b568SThomas Cort
2182718b568SThomas Cort while (1) {
2192718b568SThomas Cort Xcheck(ds, dp);
2202718b568SThomas Cort
2212718b568SThomas Cort switch (type) {
2222718b568SThomas Cort case XBASE: /* original prefixed string */
2232718b568SThomas Cort c = *sp++;
2242718b568SThomas Cort switch (c) {
2252718b568SThomas Cort case EOS:
2262718b568SThomas Cort c = 0;
2272718b568SThomas Cort break;
2282718b568SThomas Cort case CHAR:
2292718b568SThomas Cort c = *sp++;
2302718b568SThomas Cort break;
2312718b568SThomas Cort case QCHAR:
2322718b568SThomas Cort quote |= 2; /* temporary quote */
2332718b568SThomas Cort c = *sp++;
2342718b568SThomas Cort break;
2352718b568SThomas Cort case OQUOTE:
2362718b568SThomas Cort word = IFS_WORD;
2372718b568SThomas Cort tilde_ok = 0;
2382718b568SThomas Cort quote = 1;
2392718b568SThomas Cort continue;
2402718b568SThomas Cort case CQUOTE:
2412718b568SThomas Cort quote = 0;
2422718b568SThomas Cort continue;
2432718b568SThomas Cort case COMSUB:
2442718b568SThomas Cort tilde_ok = 0;
2452718b568SThomas Cort if (f & DONTRUNCOMMAND) {
2462718b568SThomas Cort word = IFS_WORD;
2472718b568SThomas Cort *dp++ = '$'; *dp++ = '(';
2482718b568SThomas Cort while (*sp != '\0') {
2492718b568SThomas Cort Xcheck(ds, dp);
2502718b568SThomas Cort *dp++ = *sp++;
2512718b568SThomas Cort }
2522718b568SThomas Cort *dp++ = ')';
2532718b568SThomas Cort } else {
2542718b568SThomas Cort type = comsub(&x, sp);
2552718b568SThomas Cort if (type == XCOM && (f&DOBLANK))
2562718b568SThomas Cort doblank++;
2572718b568SThomas Cort sp = strchr(sp, 0) + 1;
2582718b568SThomas Cort newlines = 0;
2592718b568SThomas Cort }
2602718b568SThomas Cort continue;
2612718b568SThomas Cort case EXPRSUB:
2622718b568SThomas Cort word = IFS_WORD;
2632718b568SThomas Cort tilde_ok = 0;
2642718b568SThomas Cort if (f & DONTRUNCOMMAND) {
2652718b568SThomas Cort *dp++ = '$'; *dp++ = '('; *dp++ = '(';
2662718b568SThomas Cort while (*sp != '\0') {
2672718b568SThomas Cort Xcheck(ds, dp);
2682718b568SThomas Cort *dp++ = *sp++;
2692718b568SThomas Cort }
2702718b568SThomas Cort *dp++ = ')'; *dp++ = ')';
2712718b568SThomas Cort } else {
2722718b568SThomas Cort struct tbl v;
2732718b568SThomas Cort char *p;
2742718b568SThomas Cort
2752718b568SThomas Cort v.flag = DEFINED|ISSET|INTEGER;
2762718b568SThomas Cort v.type = 10; /* not default */
2772718b568SThomas Cort v.name[0] = '\0';
2782718b568SThomas Cort v_evaluate(&v, substitute(sp, 0),
2792718b568SThomas Cort KSH_UNWIND_ERROR);
2802718b568SThomas Cort sp = strchr(sp, 0) + 1;
2812718b568SThomas Cort for (p = str_val(&v); *p; ) {
2822718b568SThomas Cort Xcheck(ds, dp);
2832718b568SThomas Cort *dp++ = *p++;
2842718b568SThomas Cort }
2852718b568SThomas Cort }
2862718b568SThomas Cort continue;
2872718b568SThomas Cort case OSUBST: /* ${{#}var{:}[=+-?#%]word} */
2882718b568SThomas Cort /* format is:
2892718b568SThomas Cort * OSUBST [{x] plain-variable-part \0
2902718b568SThomas Cort * compiled-word-part CSUBST [}x]
2912718b568SThomas Cort * This is were all syntax checking gets done...
2922718b568SThomas Cort */
2932718b568SThomas Cort {
2942718b568SThomas Cort char *varname = ++sp; /* skip the { or x (}) */
2952718b568SThomas Cort int stype;
2962718b568SThomas Cort int slen;
2972718b568SThomas Cort
2982718b568SThomas Cort slen = -1; /* XXX gcc */
2992718b568SThomas Cort sp = strchr(sp, '\0') + 1; /* skip variable */
3002718b568SThomas Cort type = varsub(&x, varname, sp, &stype, &slen);
3012718b568SThomas Cort if (type < 0) {
3022718b568SThomas Cort char endc;
3032718b568SThomas Cort char *str, *end;
3042718b568SThomas Cort
3052718b568SThomas Cort end = (char *) wdscan(sp, CSUBST);
3062718b568SThomas Cort /* ({) the } or x is already skipped */
3072718b568SThomas Cort endc = *end;
3082718b568SThomas Cort *end = EOS;
3092718b568SThomas Cort str = snptreef((char *) 0, 64, "%S",
3102718b568SThomas Cort varname - 1);
3112718b568SThomas Cort *end = endc;
3122718b568SThomas Cort errorf("%s: bad substitution", str);
3132718b568SThomas Cort }
3142718b568SThomas Cort if (f&DOBLANK)
3152718b568SThomas Cort doblank++;
3162718b568SThomas Cort tilde_ok = 0;
3172718b568SThomas Cort if (type == XBASE) { /* expand? */
3182718b568SThomas Cort if (!st->next) {
3192718b568SThomas Cort SubType *newst;
3202718b568SThomas Cort
3212718b568SThomas Cort newst = (SubType *) alloc(
3222718b568SThomas Cort sizeof(SubType), ATEMP);
3232718b568SThomas Cort newst->next = (SubType *) 0;
3242718b568SThomas Cort newst->prev = st;
3252718b568SThomas Cort st->next = newst;
3262718b568SThomas Cort }
3272718b568SThomas Cort st = st->next;
3282718b568SThomas Cort st->stype = stype;
3292718b568SThomas Cort st->base = Xsavepos(ds, dp);
3302718b568SThomas Cort st->f = f;
3312718b568SThomas Cort st->var = x.var;
3322718b568SThomas Cort st->quote = quote;
3332718b568SThomas Cort /* skip qualifier(s) */
3342718b568SThomas Cort if (stype)
3352718b568SThomas Cort sp += slen;
3362718b568SThomas Cort switch (stype & 0x7f) {
3372718b568SThomas Cort case '#':
3382718b568SThomas Cort case '%':
3392718b568SThomas Cort /* ! DOBLANK,DOBRACE_,DOTILDE */
3402718b568SThomas Cort f = DOPAT | (f&DONTRUNCOMMAND)
3412718b568SThomas Cort | DOTEMP_;
3422718b568SThomas Cort quote = 0;
3432718b568SThomas Cort /* Prepend open pattern (so |
3442718b568SThomas Cort * in a trim will work as
3452718b568SThomas Cort * expected)
3462718b568SThomas Cort */
3472718b568SThomas Cort *dp++ = MAGIC;
3482718b568SThomas Cort *dp++ = '@' + 0x80;
3492718b568SThomas Cort break;
3502718b568SThomas Cort case '=':
3512718b568SThomas Cort /* Enabling tilde expansion
3522718b568SThomas Cort * after :'s here is
3532718b568SThomas Cort * non-standard ksh, but is
3542718b568SThomas Cort * consistent with rules for
3552718b568SThomas Cort * other assignments. Not
3562718b568SThomas Cort * sure what POSIX thinks of
3572718b568SThomas Cort * this.
3582718b568SThomas Cort * Not doing tilde expansion
3592718b568SThomas Cort * for integer variables is a
3602718b568SThomas Cort * non-POSIX thing - makes
3612718b568SThomas Cort * sense though, since ~ is
3622718b568SThomas Cort * a arithmetic operator.
3632718b568SThomas Cort */
3642718b568SThomas Cort if (!(x.var->flag & INTEGER))
3652718b568SThomas Cort f |= DOASNTILDE|DOTILDE;
3662718b568SThomas Cort f |= DOTEMP_;
3672718b568SThomas Cort /* These will be done after the
3682718b568SThomas Cort * value has been assigned.
3692718b568SThomas Cort */
3702718b568SThomas Cort f &= ~(DOBLANK|DOGLOB|DOBRACE_);
3712718b568SThomas Cort tilde_ok = 1;
3722718b568SThomas Cort break;
3732718b568SThomas Cort case '?':
3742718b568SThomas Cort f &= ~DOBLANK;
3752718b568SThomas Cort f |= DOTEMP_;
3762718b568SThomas Cort /* fall through */
3772718b568SThomas Cort default:
3782718b568SThomas Cort /* Enable tilde expansion */
3792718b568SThomas Cort tilde_ok = 1;
3802718b568SThomas Cort f |= DOTILDE;
3812718b568SThomas Cort }
3822718b568SThomas Cort } else
3832718b568SThomas Cort /* skip word */
3842718b568SThomas Cort sp = (char *) wdscan(sp, CSUBST);
3852718b568SThomas Cort continue;
3862718b568SThomas Cort }
3872718b568SThomas Cort case CSUBST: /* only get here if expanding word */
3882718b568SThomas Cort sp++; /* ({) skip the } or x */
3892718b568SThomas Cort tilde_ok = 0; /* in case of ${unset:-} */
3902718b568SThomas Cort *dp = '\0';
3912718b568SThomas Cort quote = st->quote;
3922718b568SThomas Cort f = st->f;
3932718b568SThomas Cort if (f&DOBLANK)
3942718b568SThomas Cort doblank--;
3952718b568SThomas Cort switch (st->stype&0x7f) {
3962718b568SThomas Cort case '#':
3972718b568SThomas Cort case '%':
3982718b568SThomas Cort /* Append end-pattern */
3992718b568SThomas Cort *dp++ = MAGIC; *dp++ = ')'; *dp = '\0';
4002718b568SThomas Cort dp = Xrestpos(ds, dp, st->base);
4012718b568SThomas Cort /* Must use st->var since calling
4022718b568SThomas Cort * global would break things
4032718b568SThomas Cort * like x[i+=1].
4042718b568SThomas Cort */
4052718b568SThomas Cort x.str = trimsub(str_val(st->var),
4062718b568SThomas Cort dp, st->stype);
4072718b568SThomas Cort type = XSUB;
4082718b568SThomas Cort if (f&DOBLANK)
4092718b568SThomas Cort doblank++;
4102718b568SThomas Cort st = st->prev;
4112718b568SThomas Cort continue;
4122718b568SThomas Cort case '=':
4132718b568SThomas Cort /* Restore our position and substitute
4142718b568SThomas Cort * the value of st->var (may not be
4152718b568SThomas Cort * the assigned value in the presence
4162718b568SThomas Cort * of integer/right-adj/etc attributes).
4172718b568SThomas Cort */
4182718b568SThomas Cort dp = Xrestpos(ds, dp, st->base);
4192718b568SThomas Cort /* Must use st->var since calling
4202718b568SThomas Cort * global would cause with things
4212718b568SThomas Cort * like x[i+=1] to be evaluated twice.
4222718b568SThomas Cort */
4232718b568SThomas Cort /* Note: not exported by FEXPORT
4242718b568SThomas Cort * in at&t ksh.
4252718b568SThomas Cort */
4262718b568SThomas Cort /* XXX POSIX says readonly is only
4272718b568SThomas Cort * fatal for special builtins (setstr
4282718b568SThomas Cort * does readonly check).
4292718b568SThomas Cort */
4302718b568SThomas Cort len = strlen(dp) + 1;
4312718b568SThomas Cort setstr(st->var,
4322718b568SThomas Cort debunk((char *) alloc(len, ATEMP),
4332718b568SThomas Cort dp, len),
4342718b568SThomas Cort KSH_UNWIND_ERROR);
4352718b568SThomas Cort x.str = str_val(st->var);
4362718b568SThomas Cort type = XSUB;
4372718b568SThomas Cort if (f&DOBLANK)
4382718b568SThomas Cort doblank++;
4392718b568SThomas Cort st = st->prev;
4402718b568SThomas Cort continue;
4412718b568SThomas Cort case '?':
4422718b568SThomas Cort {
4432718b568SThomas Cort char *s = Xrestpos(ds, dp, st->base);
4442718b568SThomas Cort
4452718b568SThomas Cort errorf("%s: %s", st->var->name,
4462718b568SThomas Cort dp == s ?
4472718b568SThomas Cort "parameter null or not set"
4482718b568SThomas Cort : (debunk(s, s, strlen(s) + 1), s));
4492718b568SThomas Cort }
4502718b568SThomas Cort }
4512718b568SThomas Cort st = st->prev;
4522718b568SThomas Cort type = XBASE;
4532718b568SThomas Cort continue;
4542718b568SThomas Cort
4552718b568SThomas Cort case OPAT: /* open pattern: *(foo|bar) */
4562718b568SThomas Cort /* Next char is the type of pattern */
4572718b568SThomas Cort make_magic = 1;
4582718b568SThomas Cort c = *sp++ + 0x80;
4592718b568SThomas Cort break;
4602718b568SThomas Cort
4612718b568SThomas Cort case SPAT: /* pattern separator (|) */
4622718b568SThomas Cort make_magic = 1;
4632718b568SThomas Cort c = '|';
4642718b568SThomas Cort break;
4652718b568SThomas Cort
4662718b568SThomas Cort case CPAT: /* close pattern */
4672718b568SThomas Cort make_magic = 1;
4682718b568SThomas Cort c = /*(*/ ')';
4692718b568SThomas Cort break;
4702718b568SThomas Cort }
4712718b568SThomas Cort break;
4722718b568SThomas Cort
4732718b568SThomas Cort case XNULLSUB:
4742718b568SThomas Cort /* Special case for "$@" (and "${foo[@]}") - no
4752718b568SThomas Cort * word is generated if $# is 0 (unless there is
4762718b568SThomas Cort * other stuff inside the quotes).
4772718b568SThomas Cort */
4782718b568SThomas Cort type = XBASE;
4792718b568SThomas Cort if (f&DOBLANK) {
4802718b568SThomas Cort doblank--;
4812718b568SThomas Cort /* not really correct: x=; "$x$@" should
4822718b568SThomas Cort * generate a null argument and
4832718b568SThomas Cort * set A; "${@:+}" shouldn't.
4842718b568SThomas Cort */
4852718b568SThomas Cort if (dp == Xstring(ds, dp))
4862718b568SThomas Cort word = IFS_WS;
4872718b568SThomas Cort }
4882718b568SThomas Cort continue;
4892718b568SThomas Cort
4902718b568SThomas Cort case XSUB:
4912718b568SThomas Cort if ((c = *x.str++) == 0) {
4922718b568SThomas Cort type = XBASE;
4932718b568SThomas Cort if (f&DOBLANK)
4942718b568SThomas Cort doblank--;
4952718b568SThomas Cort continue;
4962718b568SThomas Cort }
4972718b568SThomas Cort break;
4982718b568SThomas Cort
4992718b568SThomas Cort case XARGSEP:
5002718b568SThomas Cort type = XARG;
5012718b568SThomas Cort quote = 1;
5022718b568SThomas Cort case XARG:
5032718b568SThomas Cort if ((c = *x.str++) == '\0') {
5042718b568SThomas Cort /* force null words to be created so
5052718b568SThomas Cort * set -- '' 2 ''; foo "$@" will do
5062718b568SThomas Cort * the right thing
5072718b568SThomas Cort */
5082718b568SThomas Cort if (quote && x.split)
5092718b568SThomas Cort word = IFS_WORD;
5102718b568SThomas Cort if ((x.str = *x.u.strv++) == NULL) {
5112718b568SThomas Cort type = XBASE;
5122718b568SThomas Cort if (f&DOBLANK)
5132718b568SThomas Cort doblank--;
5142718b568SThomas Cort continue;
5152718b568SThomas Cort }
5162718b568SThomas Cort c = ifs0;
5172718b568SThomas Cort if (c == 0) {
5182718b568SThomas Cort if (quote && !x.split)
5192718b568SThomas Cort continue;
5202718b568SThomas Cort c = ' ';
5212718b568SThomas Cort }
5222718b568SThomas Cort if (quote && x.split) {
5232718b568SThomas Cort /* terminate word for "$@" */
5242718b568SThomas Cort type = XARGSEP;
5252718b568SThomas Cort quote = 0;
5262718b568SThomas Cort }
5272718b568SThomas Cort }
5282718b568SThomas Cort break;
5292718b568SThomas Cort
5302718b568SThomas Cort case XCOM:
5312718b568SThomas Cort if (newlines) { /* Spit out saved nl's */
5322718b568SThomas Cort c = '\n';
5332718b568SThomas Cort --newlines;
5342718b568SThomas Cort } else {
5352718b568SThomas Cort while ((c = shf_getc(x.u.shf)) == 0 || c == '\n')
5362718b568SThomas Cort if (c == '\n')
5372718b568SThomas Cort newlines++; /* Save newlines */
5382718b568SThomas Cort if (newlines && c != EOF) {
5392718b568SThomas Cort shf_ungetc(c, x.u.shf);
5402718b568SThomas Cort c = '\n';
5412718b568SThomas Cort --newlines;
5422718b568SThomas Cort }
5432718b568SThomas Cort }
5442718b568SThomas Cort if (c == EOF) {
5452718b568SThomas Cort newlines = 0;
5462718b568SThomas Cort shf_close(x.u.shf);
5472718b568SThomas Cort if (x.split)
5482718b568SThomas Cort subst_exstat = waitlast();
5492718b568SThomas Cort type = XBASE;
5502718b568SThomas Cort if (f&DOBLANK)
5512718b568SThomas Cort doblank--;
5522718b568SThomas Cort continue;
5532718b568SThomas Cort }
5542718b568SThomas Cort break;
5552718b568SThomas Cort }
5562718b568SThomas Cort
5572718b568SThomas Cort /* check for end of word or IFS separation */
5582718b568SThomas Cort if (c == 0 || (!quote && (f & DOBLANK) && doblank && !make_magic
5592718b568SThomas Cort && ctype(c, C_IFS)))
5602718b568SThomas Cort {
5612718b568SThomas Cort /* How words are broken up:
5622718b568SThomas Cort * | value of c
5632718b568SThomas Cort * word | ws nws 0
5642718b568SThomas Cort * -----------------------------------
5652718b568SThomas Cort * IFS_WORD w/WS w/NWS w
5662718b568SThomas Cort * IFS_WS -/WS w/NWS -
5672718b568SThomas Cort * IFS_NWS -/NWS w/NWS w
5682718b568SThomas Cort * (w means generate a word)
5692718b568SThomas Cort * Note that IFS_NWS/0 generates a word (at&t ksh
5702718b568SThomas Cort * doesn't do this, but POSIX does).
5712718b568SThomas Cort */
5722718b568SThomas Cort if (word == IFS_WORD
5732718b568SThomas Cort || (!ctype(c, C_IFSWS) && (c || word == IFS_NWS)))
5742718b568SThomas Cort {
5752718b568SThomas Cort char *p;
5762718b568SThomas Cort
5772718b568SThomas Cort *dp++ = '\0';
5782718b568SThomas Cort p = Xclose(ds, dp);
5792718b568SThomas Cort #ifdef BRACE_EXPAND
5802718b568SThomas Cort if (fdo & DOBRACE_)
5812718b568SThomas Cort /* also does globbing */
5822718b568SThomas Cort alt_expand(wp, p, p,
5832718b568SThomas Cort p + Xlength(ds, (dp - 1)),
5842718b568SThomas Cort fdo | (f & DOMARKDIRS));
5852718b568SThomas Cort else
5862718b568SThomas Cort #endif /* BRACE_EXPAND */
5872718b568SThomas Cort if (fdo & DOGLOB)
5882718b568SThomas Cort glob(p, wp, f & DOMARKDIRS);
5892718b568SThomas Cort else if ((f & DOPAT) || !(fdo & DOMAGIC_))
5902718b568SThomas Cort XPput(*wp, p);
5912718b568SThomas Cort else
5922718b568SThomas Cort XPput(*wp, debunk(p, p, strlen(p) + 1));
5932718b568SThomas Cort fdo = 0;
5942718b568SThomas Cort saw_eq = 0;
5952718b568SThomas Cort tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
5962718b568SThomas Cort if (c != 0)
5972718b568SThomas Cort Xinit(ds, dp, 128, ATEMP);
5982718b568SThomas Cort }
5992718b568SThomas Cort if (c == 0)
6002718b568SThomas Cort return;
6012718b568SThomas Cort if (word != IFS_NWS)
6022718b568SThomas Cort word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
6032718b568SThomas Cort } else {
6042718b568SThomas Cort /* age tilde_ok info - ~ code tests second bit */
6052718b568SThomas Cort tilde_ok <<= 1;
6062718b568SThomas Cort /* mark any special second pass chars */
6072718b568SThomas Cort if (!quote)
6082718b568SThomas Cort switch (c) {
6092718b568SThomas Cort case '[':
6102718b568SThomas Cort case NOT:
6112718b568SThomas Cort case '-':
6122718b568SThomas Cort case ']':
6132718b568SThomas Cort /* For character classes - doesn't hurt
6142718b568SThomas Cort * to have magic !,-,]'s outside of
6152718b568SThomas Cort * [...] expressions.
6162718b568SThomas Cort */
6172718b568SThomas Cort if (f & (DOPAT | DOGLOB)) {
6182718b568SThomas Cort fdo |= DOMAGIC_;
6192718b568SThomas Cort if (c == '[')
6202718b568SThomas Cort fdo |= f & DOGLOB;
6212718b568SThomas Cort *dp++ = MAGIC;
6222718b568SThomas Cort }
6232718b568SThomas Cort break;
6242718b568SThomas Cort case '*':
6252718b568SThomas Cort case '?':
6262718b568SThomas Cort if (f & (DOPAT | DOGLOB)) {
6272718b568SThomas Cort fdo |= DOMAGIC_ | (f & DOGLOB);
6282718b568SThomas Cort *dp++ = MAGIC;
6292718b568SThomas Cort }
6302718b568SThomas Cort break;
6312718b568SThomas Cort #ifdef BRACE_EXPAND
6322718b568SThomas Cort case OBRACE:
6332718b568SThomas Cort case ',':
6342718b568SThomas Cort case CBRACE:
6352718b568SThomas Cort if ((f & DOBRACE_) && (c == OBRACE
6362718b568SThomas Cort || (fdo & DOBRACE_)))
6372718b568SThomas Cort {
6382718b568SThomas Cort fdo |= DOBRACE_|DOMAGIC_;
6392718b568SThomas Cort *dp++ = MAGIC;
6402718b568SThomas Cort }
6412718b568SThomas Cort break;
6422718b568SThomas Cort #endif /* BRACE_EXPAND */
6432718b568SThomas Cort case '=':
6442718b568SThomas Cort /* Note first unquoted = for ~ */
6452718b568SThomas Cort if (!(f & DOTEMP_) && !saw_eq) {
6462718b568SThomas Cort saw_eq = 1;
6472718b568SThomas Cort tilde_ok = 1;
6482718b568SThomas Cort }
6492718b568SThomas Cort break;
6502718b568SThomas Cort case PATHSEP: /* : */
6512718b568SThomas Cort /* Note unquoted : for ~ */
6522718b568SThomas Cort if (!(f & DOTEMP_) && (f & DOASNTILDE))
6532718b568SThomas Cort tilde_ok = 1;
6542718b568SThomas Cort break;
6552718b568SThomas Cort case '~':
6562718b568SThomas Cort /* tilde_ok is reset whenever
6572718b568SThomas Cort * any of ' " $( $(( ${ } are seen.
6582718b568SThomas Cort * Note that tilde_ok must be preserved
6592718b568SThomas Cort * through the sequence ${A=a=}~
6602718b568SThomas Cort */
6612718b568SThomas Cort if (type == XBASE
6622718b568SThomas Cort && (f & (DOTILDE|DOASNTILDE))
6632718b568SThomas Cort && (tilde_ok & 2))
6642718b568SThomas Cort {
6652718b568SThomas Cort char *p, *dp_x;
6662718b568SThomas Cort
6672718b568SThomas Cort dp_x = dp;
6682718b568SThomas Cort p = maybe_expand_tilde(sp,
6692718b568SThomas Cort &ds, &dp_x,
6702718b568SThomas Cort f & DOASNTILDE);
6712718b568SThomas Cort if (p) {
6722718b568SThomas Cort if (dp != dp_x)
6732718b568SThomas Cort word = IFS_WORD;
6742718b568SThomas Cort dp = dp_x;
6752718b568SThomas Cort sp = p;
6762718b568SThomas Cort continue;
6772718b568SThomas Cort }
6782718b568SThomas Cort }
6792718b568SThomas Cort break;
6802718b568SThomas Cort }
6812718b568SThomas Cort else
6822718b568SThomas Cort quote &= ~2; /* undo temporary */
6832718b568SThomas Cort
6842718b568SThomas Cort if (make_magic) {
6852718b568SThomas Cort make_magic = 0;
6862718b568SThomas Cort fdo |= DOMAGIC_ | (f & DOGLOB);
6872718b568SThomas Cort *dp++ = MAGIC;
6882718b568SThomas Cort } else if (ISMAGIC(c)) {
6892718b568SThomas Cort fdo |= DOMAGIC_;
6902718b568SThomas Cort *dp++ = MAGIC;
6912718b568SThomas Cort }
6922718b568SThomas Cort *dp++ = c; /* save output char */
6932718b568SThomas Cort word = IFS_WORD;
6942718b568SThomas Cort }
6952718b568SThomas Cort }
6962718b568SThomas Cort }
6972718b568SThomas Cort
6982718b568SThomas Cort /*
6992718b568SThomas Cort * Prepare to generate the string returned by ${} substitution.
7002718b568SThomas Cort */
7012718b568SThomas Cort static int
varsub(xp,sp,word,stypep,slenp)7022718b568SThomas Cort varsub(xp, sp, word, stypep, slenp)
7032718b568SThomas Cort Expand *xp;
7042718b568SThomas Cort char *sp;
7052718b568SThomas Cort char *word;
7062718b568SThomas Cort int *stypep; /* becomes qualifier type */
7072718b568SThomas Cort int *slenp; /* " " len (=, :=, etc.) valid iff *stypep != 0 */
7082718b568SThomas Cort {
7092718b568SThomas Cort int c;
7102718b568SThomas Cort int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */
7112718b568SThomas Cort int stype; /* substitution type */
7122718b568SThomas Cort int slen;
7132718b568SThomas Cort char *p;
7142718b568SThomas Cort struct tbl *vp;
7152718b568SThomas Cort
7162718b568SThomas Cort if (sp[0] == '\0') /* Bad variable name */
7172718b568SThomas Cort return -1;
7182718b568SThomas Cort
7192718b568SThomas Cort xp->var = NULL;
7202718b568SThomas Cort
7212718b568SThomas Cort /* ${#var}, string length or array size */
7222718b568SThomas Cort if (sp[0] == '#' && (c = sp[1]) != '\0') {
7232718b568SThomas Cort int zero_ok = 0;
7242718b568SThomas Cort
7252718b568SThomas Cort /* Can't have any modifiers for ${#...} */
7262718b568SThomas Cort if (*word != CSUBST)
7272718b568SThomas Cort return -1;
7282718b568SThomas Cort sp++;
7292718b568SThomas Cort /* Check for size of array */
7302718b568SThomas Cort if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') {
7312718b568SThomas Cort int n = 0;
7322718b568SThomas Cort vp = global(arrayname(sp));
7332718b568SThomas Cort if (vp->flag & (ISSET|ARRAY))
7342718b568SThomas Cort zero_ok = 1;
7352718b568SThomas Cort for (; vp; vp = vp->u.array)
7362718b568SThomas Cort if (vp->flag & ISSET) {
7372718b568SThomas Cort n++;
7382718b568SThomas Cort }
7392718b568SThomas Cort c = n; /* ksh88/ksh93 go for number, not max index */
7402718b568SThomas Cort } else if (c == '*' || c == '@')
7412718b568SThomas Cort c = e->loc->argc;
7422718b568SThomas Cort else {
7432718b568SThomas Cort p = str_val(global(sp));
7442718b568SThomas Cort zero_ok = p != null;
7452718b568SThomas Cort c = strlen(p);
7462718b568SThomas Cort }
7472718b568SThomas Cort if (Flag(FNOUNSET) && c == 0 && !zero_ok)
7482718b568SThomas Cort errorf("%s: parameter not set", sp);
7492718b568SThomas Cort *stypep = 0; /* unqualified variable/string substitution */
7502718b568SThomas Cort xp->str = str_save(ulton((unsigned long)c, 10), ATEMP);
7512718b568SThomas Cort return XSUB;
7522718b568SThomas Cort }
7532718b568SThomas Cort
7542718b568SThomas Cort /* Check for qualifiers in word part */
7552718b568SThomas Cort stype = 0;
7562718b568SThomas Cort c = word[slen = 0] == CHAR ? word[1] : 0;
7572718b568SThomas Cort if (c == ':') {
7582718b568SThomas Cort slen += 2;
7592718b568SThomas Cort stype = 0x80;
7602718b568SThomas Cort c = word[slen + 0] == CHAR ? word[slen + 1] : 0;
7612718b568SThomas Cort }
7622718b568SThomas Cort if (ctype(c, C_SUBOP1)) {
7632718b568SThomas Cort slen += 2;
7642718b568SThomas Cort stype |= c;
7652718b568SThomas Cort } else if (ctype(c, C_SUBOP2)) { /* Note: ksh88 allows :%, :%%, etc */
7662718b568SThomas Cort slen += 2;
7672718b568SThomas Cort stype = c;
7682718b568SThomas Cort if (word[slen + 0] == CHAR && c == word[slen + 1]) {
7692718b568SThomas Cort stype |= 0x80;
7702718b568SThomas Cort slen += 2;
7712718b568SThomas Cort }
7722718b568SThomas Cort } else if (stype) /* : is not ok */
7732718b568SThomas Cort return -1;
7742718b568SThomas Cort if (!stype && *word != CSUBST)
7752718b568SThomas Cort return -1;
7762718b568SThomas Cort *stypep = stype;
7772718b568SThomas Cort *slenp = slen;
7782718b568SThomas Cort
7792718b568SThomas Cort c = sp[0];
7802718b568SThomas Cort if (c == '*' || c == '@') {
7812718b568SThomas Cort switch (stype & 0x7f) {
7822718b568SThomas Cort case '=': /* can't assign to a vector */
7832718b568SThomas Cort case '%': /* can't trim a vector (yet) */
7842718b568SThomas Cort case '#':
7852718b568SThomas Cort return -1;
7862718b568SThomas Cort }
7872718b568SThomas Cort if (e->loc->argc == 0) {
7882718b568SThomas Cort xp->u.strv = NULL;
7892718b568SThomas Cort xp->str = null;
7902718b568SThomas Cort state = c == '@' ? XNULLSUB : XSUB;
7912718b568SThomas Cort } else {
7922718b568SThomas Cort char **t = &e->loc->argv[1];
7932718b568SThomas Cort xp->u.strv = (void *)(uintptr_t)t;
7942718b568SThomas Cort xp->str = *xp->u.strv++;
7952718b568SThomas Cort xp->split = c == '@'; /* $@ */
7962718b568SThomas Cort state = XARG;
7972718b568SThomas Cort }
7982718b568SThomas Cort } else {
7992718b568SThomas Cort if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') {
8002718b568SThomas Cort XPtrV wv;
8012718b568SThomas Cort
8022718b568SThomas Cort switch (stype & 0x7f) {
8032718b568SThomas Cort case '=': /* can't assign to a vector */
8042718b568SThomas Cort case '%': /* can't trim a vector (yet) */
8052718b568SThomas Cort case '#':
8062718b568SThomas Cort return -1;
8072718b568SThomas Cort }
8082718b568SThomas Cort XPinit(wv, 32);
8092718b568SThomas Cort vp = global(arrayname(sp));
8102718b568SThomas Cort for (; vp; vp = vp->u.array) {
8112718b568SThomas Cort if (!(vp->flag&ISSET))
8122718b568SThomas Cort continue;
8132718b568SThomas Cort XPput(wv, str_val(vp));
8142718b568SThomas Cort }
8152718b568SThomas Cort if (XPsize(wv) == 0) {
8162718b568SThomas Cort xp->str = null;
8172718b568SThomas Cort state = p[1] == '@' ? XNULLSUB : XSUB;
8182718b568SThomas Cort XPfree(wv);
8192718b568SThomas Cort } else {
8202718b568SThomas Cort XPput(wv, 0);
8212718b568SThomas Cort xp->u.strv = (const char **) XPptrv(wv);
8222718b568SThomas Cort xp->str = *xp->u.strv++;
8232718b568SThomas Cort xp->split = p[1] == '@'; /* ${foo[@]} */
8242718b568SThomas Cort state = XARG;
8252718b568SThomas Cort }
8262718b568SThomas Cort } else {
8272718b568SThomas Cort /* Can't assign things like $! or $1 */
8282718b568SThomas Cort if ((stype & 0x7f) == '='
8292718b568SThomas Cort && (ctype(*sp, C_VAR1) || digit(*sp)))
8302718b568SThomas Cort return -1;
8312718b568SThomas Cort xp->var = global(sp);
8322718b568SThomas Cort xp->str = str_val(xp->var);
8332718b568SThomas Cort state = XSUB;
8342718b568SThomas Cort }
8352718b568SThomas Cort }
8362718b568SThomas Cort
8372718b568SThomas Cort c = stype&0x7f;
8382718b568SThomas Cort /* test the compiler's code generator */
8392718b568SThomas Cort if (ctype(c, C_SUBOP2) ||
8402718b568SThomas Cort (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
8412718b568SThomas Cort c == '=' || c == '-' || c == '?' : c == '+'))
8422718b568SThomas Cort state = XBASE; /* expand word instead of variable value */
8432718b568SThomas Cort if (Flag(FNOUNSET) && xp->str == null
8442718b568SThomas Cort && (ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))
8452718b568SThomas Cort errorf("%s: parameter not set", sp);
8462718b568SThomas Cort return state;
8472718b568SThomas Cort }
8482718b568SThomas Cort
8492718b568SThomas Cort /*
8502718b568SThomas Cort * Run the command in $(...) and read its output.
8512718b568SThomas Cort */
8522718b568SThomas Cort static int
comsub(xp,cp)8532718b568SThomas Cort comsub(xp, cp)
8542718b568SThomas Cort register Expand *xp;
8552718b568SThomas Cort char *cp;
8562718b568SThomas Cort {
8572718b568SThomas Cort Source *s, *sold;
8582718b568SThomas Cort register struct op *t;
8592718b568SThomas Cort struct shf *shf;
8602718b568SThomas Cort
8612718b568SThomas Cort s = pushs(SSTRING, ATEMP);
8622718b568SThomas Cort s->start = s->str = cp;
8632718b568SThomas Cort sold = source;
8642718b568SThomas Cort t = compile(s);
8652718b568SThomas Cort afree(s, ATEMP);
8662718b568SThomas Cort source = sold;
8672718b568SThomas Cort
8682718b568SThomas Cort if (t == NULL)
8692718b568SThomas Cort return XBASE;
8702718b568SThomas Cort
8712718b568SThomas Cort if (t != NULL && t->type == TCOM && /* $(<file) */
8722718b568SThomas Cort *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
8732718b568SThomas Cort register struct ioword *io = *t->ioact;
8742718b568SThomas Cort char *name;
8752718b568SThomas Cort
8762718b568SThomas Cort if ((io->flag&IOTYPE) != IOREAD)
8772718b568SThomas Cort errorf("funny $() command: %s",
8782718b568SThomas Cort snptreef((char *) 0, 32, "%R", io));
8792718b568SThomas Cort shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0,
8802718b568SThomas Cort SHF_MAPHI|SHF_CLEXEC);
8812718b568SThomas Cort if (shf == NULL)
8822718b568SThomas Cort errorf("%s: cannot open $() input", name);
8832718b568SThomas Cort xp->split = 0; /* no waitlast() */
8842718b568SThomas Cort } else {
8852718b568SThomas Cort int ofd1, pv[2];
8862718b568SThomas Cort openpipe(pv);
8872718b568SThomas Cort shf = shf_fdopen(pv[0], SHF_RD, (struct shf *) 0);
8882718b568SThomas Cort ofd1 = savefd(1, 0); /* fd 1 may be closed... */
8892718b568SThomas Cort if (pv[1] != 1) {
8902718b568SThomas Cort ksh_dup2(pv[1], 1, FALSE);
8912718b568SThomas Cort close(pv[1]);
8922718b568SThomas Cort }
8932718b568SThomas Cort execute(t, XFORK|XXCOM|XPIPEO);
8942718b568SThomas Cort restfd(1, ofd1);
8952718b568SThomas Cort startlast();
8962718b568SThomas Cort xp->split = 1; /* waitlast() */
8972718b568SThomas Cort }
8982718b568SThomas Cort
8992718b568SThomas Cort xp->u.shf = shf;
9002718b568SThomas Cort return XCOM;
9012718b568SThomas Cort }
9022718b568SThomas Cort
9032718b568SThomas Cort /*
9042718b568SThomas Cort * perform #pattern and %pattern substitution in ${}
9052718b568SThomas Cort */
9062718b568SThomas Cort
9072718b568SThomas Cort static char *
trimsub(str,pat,how)9082718b568SThomas Cort trimsub(str, pat, how)
9092718b568SThomas Cort register char *str;
9102718b568SThomas Cort char *pat;
9112718b568SThomas Cort int how;
9122718b568SThomas Cort {
9132718b568SThomas Cort register char *end = strchr(str, 0);
9142718b568SThomas Cort register char *p, c;
9152718b568SThomas Cort
9162718b568SThomas Cort switch (how&0xff) { /* UCHAR_MAX maybe? */
9172718b568SThomas Cort case '#': /* shortest at beginning */
9182718b568SThomas Cort for (p = str; p <= end; p++) {
9192718b568SThomas Cort c = *p; *p = '\0';
9202718b568SThomas Cort if (gmatch(str, pat, FALSE)) {
9212718b568SThomas Cort *p = c;
9222718b568SThomas Cort return p;
9232718b568SThomas Cort }
9242718b568SThomas Cort *p = c;
9252718b568SThomas Cort }
9262718b568SThomas Cort break;
9272718b568SThomas Cort case '#'|0x80: /* longest match at beginning */
9282718b568SThomas Cort for (p = end; p >= str; p--) {
9292718b568SThomas Cort c = *p; *p = '\0';
9302718b568SThomas Cort if (gmatch(str, pat, FALSE)) {
9312718b568SThomas Cort *p = c;
9322718b568SThomas Cort return p;
9332718b568SThomas Cort }
9342718b568SThomas Cort *p = c;
9352718b568SThomas Cort }
9362718b568SThomas Cort break;
9372718b568SThomas Cort case '%': /* shortest match at end */
9382718b568SThomas Cort for (p = end; p >= str; p--) {
9392718b568SThomas Cort if (gmatch(p, pat, FALSE))
9402718b568SThomas Cort return str_nsave(str, p - str, ATEMP);
9412718b568SThomas Cort }
9422718b568SThomas Cort break;
9432718b568SThomas Cort case '%'|0x80: /* longest match at end */
9442718b568SThomas Cort for (p = str; p <= end; p++) {
9452718b568SThomas Cort if (gmatch(p, pat, FALSE))
9462718b568SThomas Cort return str_nsave(str, p - str, ATEMP);
9472718b568SThomas Cort }
9482718b568SThomas Cort break;
9492718b568SThomas Cort }
9502718b568SThomas Cort
9512718b568SThomas Cort return str; /* no match, return string */
9522718b568SThomas Cort }
9532718b568SThomas Cort
9542718b568SThomas Cort /*
9552718b568SThomas Cort * glob
9562718b568SThomas Cort * Name derived from V6's /etc/glob, the program that expanded filenames.
9572718b568SThomas Cort */
9582718b568SThomas Cort
9592718b568SThomas Cort /* XXX cp not const 'cause slashes are temporarily replaced with nulls... */
9602718b568SThomas Cort static void
glob(cp,wp,markdirs)9612718b568SThomas Cort glob(cp, wp, markdirs)
9622718b568SThomas Cort char *cp;
9632718b568SThomas Cort register XPtrV *wp;
9642718b568SThomas Cort int markdirs;
9652718b568SThomas Cort {
9662718b568SThomas Cort int oldsize = XPsize(*wp);
9672718b568SThomas Cort
9682718b568SThomas Cort if (glob_str(cp, wp, markdirs) == 0)
9692718b568SThomas Cort XPput(*wp, debunk(cp, cp, strlen(cp) + 1));
9702718b568SThomas Cort else
9712718b568SThomas Cort qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize),
9722718b568SThomas Cort xstrcmp);
9732718b568SThomas Cort }
9742718b568SThomas Cort
9752718b568SThomas Cort #define GF_NONE 0
9762718b568SThomas Cort #define GF_EXCHECK BIT(0) /* do existence check on file */
9772718b568SThomas Cort #define GF_GLOBBED BIT(1) /* some globbing has been done */
9782718b568SThomas Cort #define GF_MARKDIR BIT(2) /* add trailing / to directories */
9792718b568SThomas Cort
9802718b568SThomas Cort /* Apply file globbing to cp and store the matching files in wp. Returns
9812718b568SThomas Cort * the number of matches found.
9822718b568SThomas Cort */
9832718b568SThomas Cort int
glob_str(cp,wp,markdirs)9842718b568SThomas Cort glob_str(cp, wp, markdirs)
9852718b568SThomas Cort char *cp;
9862718b568SThomas Cort XPtrV *wp;
9872718b568SThomas Cort int markdirs;
9882718b568SThomas Cort {
9892718b568SThomas Cort int oldsize = XPsize(*wp);
9902718b568SThomas Cort XString xs;
9912718b568SThomas Cort char *xp;
9922718b568SThomas Cort
9932718b568SThomas Cort Xinit(xs, xp, 256, ATEMP);
9942718b568SThomas Cort globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE);
9952718b568SThomas Cort Xfree(xs, xp);
9962718b568SThomas Cort
9972718b568SThomas Cort return XPsize(*wp) - oldsize;
9982718b568SThomas Cort }
9992718b568SThomas Cort
10002718b568SThomas Cort static void
globit(xs,xpp,sp,wp,check)10012718b568SThomas Cort globit(xs, xpp, sp, wp, check)
10022718b568SThomas Cort XString *xs; /* dest string */
10032718b568SThomas Cort char **xpp; /* ptr to dest end */
10042718b568SThomas Cort char *sp; /* source path */
10052718b568SThomas Cort register XPtrV *wp; /* output list */
10062718b568SThomas Cort int check; /* GF_* flags */
10072718b568SThomas Cort {
10082718b568SThomas Cort register char *np; /* next source component */
10092718b568SThomas Cort char *xp = *xpp;
10102718b568SThomas Cort char *se;
10112718b568SThomas Cort char odirsep;
10122718b568SThomas Cort
10132718b568SThomas Cort /* This to allow long expansions to be interrupted */
10142718b568SThomas Cort intrcheck();
10152718b568SThomas Cort
10162718b568SThomas Cort if (sp == NULL) { /* end of source path */
10172718b568SThomas Cort /* We only need to check if the file exists if a pattern
10182718b568SThomas Cort * is followed by a non-pattern (eg, foo*x/bar; no check
10192718b568SThomas Cort * is needed for foo* since the match must exist) or if
10202718b568SThomas Cort * any patterns were expanded and the markdirs option is set.
10212718b568SThomas Cort * Symlinks make things a bit tricky...
10222718b568SThomas Cort */
10232718b568SThomas Cort if ((check & GF_EXCHECK)
10242718b568SThomas Cort || ((check & GF_MARKDIR) && (check & GF_GLOBBED)))
10252718b568SThomas Cort {
10262718b568SThomas Cort #define stat_check() (stat_done ? stat_done : \
10272718b568SThomas Cort (stat_done = stat(Xstring(*xs, xp), &statb) < 0 \
10282718b568SThomas Cort ? -1 : 1))
10292718b568SThomas Cort struct stat lstatb, statb;
10302718b568SThomas Cort int stat_done = 0; /* -1: failed, 1 ok */
10312718b568SThomas Cort
10322718b568SThomas Cort if (lstat(Xstring(*xs, xp), &lstatb) < 0)
10332718b568SThomas Cort return;
10342718b568SThomas Cort /* special case for systems which strip trailing
10352718b568SThomas Cort * slashes from regular files (eg, /etc/passwd/).
10362718b568SThomas Cort * SunOS 4.1.3 does this...
10372718b568SThomas Cort */
10382718b568SThomas Cort if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp)
10392718b568SThomas Cort && ISDIRSEP(xp[-1]) && !S_ISDIR(lstatb.st_mode)
10402718b568SThomas Cort #ifdef S_ISLNK
10412718b568SThomas Cort && (!S_ISLNK(lstatb.st_mode)
10422718b568SThomas Cort || stat_check() < 0
10432718b568SThomas Cort || !S_ISDIR(statb.st_mode))
10442718b568SThomas Cort #endif /* S_ISLNK */
10452718b568SThomas Cort )
10462718b568SThomas Cort return;
10472718b568SThomas Cort /* Possibly tack on a trailing / if there isn't already
10482718b568SThomas Cort * one and if the file is a directory or a symlink to a
10492718b568SThomas Cort * directory
10502718b568SThomas Cort */
10512718b568SThomas Cort if (((check & GF_MARKDIR) && (check & GF_GLOBBED))
10522718b568SThomas Cort && xp > Xstring(*xs, xp) && !ISDIRSEP(xp[-1])
10532718b568SThomas Cort && (S_ISDIR(lstatb.st_mode)
10542718b568SThomas Cort #ifdef S_ISLNK
10552718b568SThomas Cort || (S_ISLNK(lstatb.st_mode)
10562718b568SThomas Cort && stat_check() > 0
10572718b568SThomas Cort && S_ISDIR(statb.st_mode))
10582718b568SThomas Cort #endif /* S_ISLNK */
10592718b568SThomas Cort ))
10602718b568SThomas Cort {
10612718b568SThomas Cort *xp++ = DIRSEP;
10622718b568SThomas Cort *xp = '\0';
10632718b568SThomas Cort }
10642718b568SThomas Cort }
10652718b568SThomas Cort #ifdef OS2 /* Done this way to avoid bug in gcc 2.7.2... */
10662718b568SThomas Cort /* Ugly kludge required for command
10672718b568SThomas Cort * completion - see how search_access()
10682718b568SThomas Cort * is implemented for OS/2...
10692718b568SThomas Cort */
10702718b568SThomas Cort # define KLUDGE_VAL 4
10712718b568SThomas Cort #else /* OS2 */
10722718b568SThomas Cort # define KLUDGE_VAL 0
10732718b568SThomas Cort #endif /* OS2 */
10742718b568SThomas Cort XPput(*wp, str_nsave(Xstring(*xs, xp), Xlength(*xs, xp)
10752718b568SThomas Cort + KLUDGE_VAL, ATEMP));
10762718b568SThomas Cort return;
10772718b568SThomas Cort }
10782718b568SThomas Cort
10792718b568SThomas Cort if (xp > Xstring(*xs, xp))
10802718b568SThomas Cort *xp++ = DIRSEP;
10812718b568SThomas Cort while (ISDIRSEP(*sp)) {
10822718b568SThomas Cort Xcheck(*xs, xp);
10832718b568SThomas Cort *xp++ = *sp++;
10842718b568SThomas Cort }
10852718b568SThomas Cort np = ksh_strchr_dirsep(sp);
10862718b568SThomas Cort if (np != NULL) {
10872718b568SThomas Cort se = np;
10882718b568SThomas Cort odirsep = *np; /* don't assume DIRSEP, can be multiple kinds */
10892718b568SThomas Cort *np++ = '\0';
10902718b568SThomas Cort } else {
10912718b568SThomas Cort odirsep = '\0'; /* keep gcc quiet */
10922718b568SThomas Cort se = sp + strlen(sp);
10932718b568SThomas Cort }
10942718b568SThomas Cort
10952718b568SThomas Cort
10962718b568SThomas Cort /* Check if sp needs globbing - done to avoid pattern checks for strings
10972718b568SThomas Cort * containing MAGIC characters, open ['s without the matching close ],
10982718b568SThomas Cort * etc. (otherwise opendir() will be called which may fail because the
10992718b568SThomas Cort * directory isn't readable - if no globbing is needed, only execute
11002718b568SThomas Cort * permission should be required (as per POSIX)).
11012718b568SThomas Cort */
11022718b568SThomas Cort if (!has_globbing(sp, se)) {
11032718b568SThomas Cort XcheckN(*xs, xp, se - sp + 1);
11042718b568SThomas Cort debunk(xp, sp, Xnleft(*xs, xp));
11052718b568SThomas Cort xp += strlen(xp);
11062718b568SThomas Cort *xpp = xp;
11072718b568SThomas Cort globit(xs, xpp, np, wp, check);
11082718b568SThomas Cort } else {
11092718b568SThomas Cort DIR *dirp;
11102718b568SThomas Cort struct dirent *d;
11112718b568SThomas Cort char *name;
11122718b568SThomas Cort int len;
11132718b568SThomas Cort int prefix_len;
11142718b568SThomas Cort
11152718b568SThomas Cort /* xp = *xpp; copy_non_glob() may have re-alloc'd xs */
11162718b568SThomas Cort *xp = '\0';
11172718b568SThomas Cort prefix_len = Xlength(*xs, xp);
11182718b568SThomas Cort dirp = ksh_opendir(prefix_len ? Xstring(*xs, xp) : ".");
11192718b568SThomas Cort if (dirp == NULL)
11202718b568SThomas Cort goto Nodir;
11212718b568SThomas Cort while ((d = readdir(dirp)) != NULL) {
11222718b568SThomas Cort name = d->d_name;
11232718b568SThomas Cort if ((*name == '.' && *sp != '.')
11242718b568SThomas Cort || !gmatch(name, sp, TRUE))
11252718b568SThomas Cort continue;
11262718b568SThomas Cort
11272718b568SThomas Cort len = NLENGTH(d) + 1;
11282718b568SThomas Cort XcheckN(*xs, xp, len);
11292718b568SThomas Cort memcpy(xp, name, len);
11302718b568SThomas Cort *xpp = xp + len - 1;
11312718b568SThomas Cort globit(xs, xpp, np, wp,
11322718b568SThomas Cort (check & GF_MARKDIR) | GF_GLOBBED
11332718b568SThomas Cort | (np ? GF_EXCHECK : GF_NONE));
11342718b568SThomas Cort xp = Xstring(*xs, xp) + prefix_len;
11352718b568SThomas Cort }
11362718b568SThomas Cort closedir(dirp);
11372718b568SThomas Cort Nodir:;
11382718b568SThomas Cort }
11392718b568SThomas Cort
11402718b568SThomas Cort if (np != NULL)
11412718b568SThomas Cort *--np = odirsep;
11422718b568SThomas Cort }
11432718b568SThomas Cort
11442718b568SThomas Cort #if 0
11452718b568SThomas Cort /* Check if p contains something that needs globbing; if it does, 0 is
11462718b568SThomas Cort * returned; if not, p is copied into xs/xp after stripping any MAGICs
11472718b568SThomas Cort */
11482718b568SThomas Cort static int copy_non_glob ARGS((XString *xs, char **xpp, char *p));
11492718b568SThomas Cort static int
11502718b568SThomas Cort copy_non_glob(xs, xpp, p)
11512718b568SThomas Cort XString *xs;
11522718b568SThomas Cort char **xpp;
11532718b568SThomas Cort char *p;
11542718b568SThomas Cort {
11552718b568SThomas Cort char *xp;
11562718b568SThomas Cort int len = strlen(p);
11572718b568SThomas Cort
11582718b568SThomas Cort XcheckN(*xs, *xpp, len);
11592718b568SThomas Cort xp = *xpp;
11602718b568SThomas Cort for (; *p; p++) {
11612718b568SThomas Cort if (ISMAGIC(*p)) {
11622718b568SThomas Cort int c = *++p;
11632718b568SThomas Cort
11642718b568SThomas Cort if (c == '*' || c == '?')
11652718b568SThomas Cort return 0;
11662718b568SThomas Cort if (*p == '[') {
11672718b568SThomas Cort char *q = p + 1;
11682718b568SThomas Cort
11692718b568SThomas Cort if (ISMAGIC(*q) && q[1] == NOT)
11702718b568SThomas Cort q += 2;
11712718b568SThomas Cort if (ISMAGIC(*q) && q[1] == ']')
11722718b568SThomas Cort q += 2;
11732718b568SThomas Cort for (; *q; q++)
11742718b568SThomas Cort if (ISMAGIC(*q) && *++q == ']')
11752718b568SThomas Cort return 0;
11762718b568SThomas Cort /* pass a literal [ through */
11772718b568SThomas Cort }
11782718b568SThomas Cort /* must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, etc. */
11792718b568SThomas Cort }
11802718b568SThomas Cort *xp++ = *p;
11812718b568SThomas Cort }
11822718b568SThomas Cort *xp = '\0';
11832718b568SThomas Cort *xpp = xp;
11842718b568SThomas Cort return 1;
11852718b568SThomas Cort }
11862718b568SThomas Cort #endif /* 0 */
11872718b568SThomas Cort
11882718b568SThomas Cort /* remove MAGIC from string */
11892718b568SThomas Cort char *
debunk(dp,sp,dlen)11902718b568SThomas Cort debunk(dp, sp, dlen)
11912718b568SThomas Cort char *dp;
11922718b568SThomas Cort const char *sp;
11932718b568SThomas Cort size_t dlen;
11942718b568SThomas Cort {
11952718b568SThomas Cort char *d, *s;
11962718b568SThomas Cort
11972718b568SThomas Cort if ((s = strchr(sp, MAGIC))) {
11982718b568SThomas Cort if (s - sp >= (ptrdiff_t)dlen)
11992718b568SThomas Cort return dp;
12002718b568SThomas Cort memcpy(dp, sp, s - sp);
12012718b568SThomas Cort for (d = dp + (s - sp); *s && (d - dp < (ptrdiff_t)dlen); s++)
12022718b568SThomas Cort if (!ISMAGIC(*s) || !(*++s & 0x80)
12032718b568SThomas Cort || !strchr("*+?@! ", *s & 0x7f))
12042718b568SThomas Cort *d++ = *s;
12052718b568SThomas Cort else {
12062718b568SThomas Cort /* extended pattern operators: *+?@! */
12072718b568SThomas Cort if ((*s & 0x7f) != ' ')
12082718b568SThomas Cort *d++ = *s & 0x7f;
12092718b568SThomas Cort if (d - dp < (ptrdiff_t)dlen)
12102718b568SThomas Cort *d++ = '(';
12112718b568SThomas Cort }
12122718b568SThomas Cort *d = '\0';
12132718b568SThomas Cort } else if (dp != sp)
12142718b568SThomas Cort strlcpy(dp, sp, dlen);
12152718b568SThomas Cort return dp;
12162718b568SThomas Cort }
12172718b568SThomas Cort
12182718b568SThomas Cort /* Check if p is an unquoted name, possibly followed by a / or :. If so
12192718b568SThomas Cort * puts the expanded version in *dcp,dp and returns a pointer in p just
12202718b568SThomas Cort * past the name, otherwise returns 0.
12212718b568SThomas Cort */
12222718b568SThomas Cort static char *
maybe_expand_tilde(p,dsp,dpp,isassign)12232718b568SThomas Cort maybe_expand_tilde(p, dsp, dpp, isassign)
12242718b568SThomas Cort char *p;
12252718b568SThomas Cort XString *dsp;
12262718b568SThomas Cort char **dpp;
12272718b568SThomas Cort int isassign;
12282718b568SThomas Cort {
12292718b568SThomas Cort XString ts;
12302718b568SThomas Cort char *dp = *dpp;
12312718b568SThomas Cort char *tp, *r;
12322718b568SThomas Cort
12332718b568SThomas Cort Xinit(ts, tp, 16, ATEMP);
12342718b568SThomas Cort /* : only for DOASNTILDE form */
12352718b568SThomas Cort while (p[0] == CHAR && !ISDIRSEP(p[1])
12362718b568SThomas Cort && (!isassign || p[1] != PATHSEP))
12372718b568SThomas Cort {
12382718b568SThomas Cort Xcheck(ts, tp);
12392718b568SThomas Cort *tp++ = p[1];
12402718b568SThomas Cort p += 2;
12412718b568SThomas Cort }
12422718b568SThomas Cort *tp = '\0';
12432718b568SThomas Cort r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ? tilde(Xstring(ts, tp)) : (char *) 0;
12442718b568SThomas Cort Xfree(ts, tp);
12452718b568SThomas Cort if (r) {
12462718b568SThomas Cort while (*r) {
12472718b568SThomas Cort Xcheck(*dsp, dp);
12482718b568SThomas Cort if (ISMAGIC(*r))
12492718b568SThomas Cort *dp++ = MAGIC;
12502718b568SThomas Cort *dp++ = *r++;
12512718b568SThomas Cort }
12522718b568SThomas Cort *dpp = dp;
12532718b568SThomas Cort r = p;
12542718b568SThomas Cort }
12552718b568SThomas Cort return r;
12562718b568SThomas Cort }
12572718b568SThomas Cort
12582718b568SThomas Cort /*
12592718b568SThomas Cort * tilde expansion
12602718b568SThomas Cort *
12612718b568SThomas Cort * based on a version by Arnold Robbins
12622718b568SThomas Cort */
12632718b568SThomas Cort
12642718b568SThomas Cort static char *
tilde(cp)12652718b568SThomas Cort tilde(cp)
12662718b568SThomas Cort char *cp;
12672718b568SThomas Cort {
12682718b568SThomas Cort char *dp;
12692718b568SThomas Cort
12702718b568SThomas Cort if (cp[0] == '\0')
12712718b568SThomas Cort dp = str_val(global("HOME"));
12722718b568SThomas Cort else if (cp[0] == '+' && cp[1] == '\0')
12732718b568SThomas Cort dp = str_val(global("PWD"));
12742718b568SThomas Cort else if (cp[0] == '-' && cp[1] == '\0')
12752718b568SThomas Cort dp = str_val(global("OLDPWD"));
12762718b568SThomas Cort else
12772718b568SThomas Cort dp = homedir(cp);
12782718b568SThomas Cort /* If HOME, PWD or OLDPWD are not set, don't expand ~ */
12792718b568SThomas Cort if (dp == null)
12802718b568SThomas Cort dp = (char *) 0;
12812718b568SThomas Cort return dp;
12822718b568SThomas Cort }
12832718b568SThomas Cort
12842718b568SThomas Cort /*
12852718b568SThomas Cort * map userid to user's home directory.
12862718b568SThomas Cort * note that 4.3's getpw adds more than 6K to the shell,
12872718b568SThomas Cort * and the YP version probably adds much more.
12882718b568SThomas Cort * we might consider our own version of getpwnam() to keep the size down.
12892718b568SThomas Cort */
12902718b568SThomas Cort
12912718b568SThomas Cort static char *
homedir(name)12922718b568SThomas Cort homedir(name)
12932718b568SThomas Cort char *name;
12942718b568SThomas Cort {
12952718b568SThomas Cort register struct tbl *ap;
12962718b568SThomas Cort
12972718b568SThomas Cort ap = tenter(&homedirs, name, hash(name));
12982718b568SThomas Cort if (!(ap->flag & ISSET)) {
12992718b568SThomas Cort #ifdef OS2
13002718b568SThomas Cort /* No usernames in OS2 - punt */
13012718b568SThomas Cort return NULL;
13022718b568SThomas Cort #else /* OS2 */
13032718b568SThomas Cort struct passwd *pw;
13042718b568SThomas Cort size_t n;
13052718b568SThomas Cort
13062718b568SThomas Cort pw = getpwnam(name);
13072718b568SThomas Cort if (pw == NULL)
13082718b568SThomas Cort return NULL;
13092718b568SThomas Cort n = strlen(pw->pw_dir);
13102718b568SThomas Cort if (n > 0 && '/' != pw->pw_dir[n - 1]) {
13112718b568SThomas Cort ap->val.s = str_nsave(pw->pw_dir, n + 1, APERM);
13122718b568SThomas Cort ap->val.s[n] = '/';
13132718b568SThomas Cort ap->val.s[n + 1] = '\0';
13142718b568SThomas Cort } else {
13152718b568SThomas Cort ap->val.s = str_save(pw->pw_dir, APERM);
13162718b568SThomas Cort }
13172718b568SThomas Cort ap->flag |= DEFINED|ISSET|ALLOC;
13182718b568SThomas Cort #endif /* OS2 */
13192718b568SThomas Cort }
13202718b568SThomas Cort return ap->val.s;
13212718b568SThomas Cort }
13222718b568SThomas Cort
13232718b568SThomas Cort #ifdef BRACE_EXPAND
13242718b568SThomas Cort static void
alt_expand(wp,start,exp_start,end,fdo)13252718b568SThomas Cort alt_expand(wp, start, exp_start, end, fdo)
13262718b568SThomas Cort XPtrV *wp;
13272718b568SThomas Cort char *start, *exp_start;
13282718b568SThomas Cort char *end;
13292718b568SThomas Cort int fdo;
13302718b568SThomas Cort {
13312718b568SThomas Cort int UNINITIALIZED(count);
13322718b568SThomas Cort char *brace_start, *brace_end, *UNINITIALIZED(comma);
13332718b568SThomas Cort char *field_start;
13342718b568SThomas Cort char *p;
13352718b568SThomas Cort
13362718b568SThomas Cort /* search for open brace */
13372718b568SThomas Cort for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != OBRACE; p += 2)
13382718b568SThomas Cort ;
13392718b568SThomas Cort brace_start = p;
13402718b568SThomas Cort
13412718b568SThomas Cort /* find matching close brace, if any */
13422718b568SThomas Cort if (p) {
13432718b568SThomas Cort comma = (char *) 0;
13442718b568SThomas Cort count = 1;
13452718b568SThomas Cort for (p += 2; *p && count; p++) {
13462718b568SThomas Cort if (ISMAGIC(*p)) {
13472718b568SThomas Cort if (*++p == OBRACE)
13482718b568SThomas Cort count++;
13492718b568SThomas Cort else if (*p == CBRACE)
13502718b568SThomas Cort --count;
13512718b568SThomas Cort else if (*p == ',' && count == 1)
13522718b568SThomas Cort comma = p;
13532718b568SThomas Cort }
13542718b568SThomas Cort }
13552718b568SThomas Cort }
13562718b568SThomas Cort /* no valid expansions... */
13572718b568SThomas Cort if (!p || count != 0) {
13582718b568SThomas Cort /* Note that given a{{b,c} we do not expand anything (this is
13592718b568SThomas Cort * what at&t ksh does. This may be changed to do the {b,c}
13602718b568SThomas Cort * expansion. }
13612718b568SThomas Cort */
13622718b568SThomas Cort if (fdo & DOGLOB)
13632718b568SThomas Cort glob(start, wp, fdo & DOMARKDIRS);
13642718b568SThomas Cort else
13652718b568SThomas Cort XPput(*wp, debunk(start, start, end - start));
13662718b568SThomas Cort return;
13672718b568SThomas Cort }
13682718b568SThomas Cort brace_end = p;
13692718b568SThomas Cort if (!comma) {
13702718b568SThomas Cort alt_expand(wp, start, brace_end, end, fdo);
13712718b568SThomas Cort return;
13722718b568SThomas Cort }
13732718b568SThomas Cort
13742718b568SThomas Cort /* expand expression */
13752718b568SThomas Cort field_start = brace_start + 2;
13762718b568SThomas Cort count = 1;
13772718b568SThomas Cort for (p = brace_start + 2; p != brace_end; p++) {
13782718b568SThomas Cort if (ISMAGIC(*p)) {
13792718b568SThomas Cort if (*++p == OBRACE)
13802718b568SThomas Cort count++;
13812718b568SThomas Cort else if ((*p == CBRACE && --count == 0)
13822718b568SThomas Cort || (*p == ',' && count == 1))
13832718b568SThomas Cort {
13842718b568SThomas Cort char *new;
13852718b568SThomas Cort int l1, l2, l3;
13862718b568SThomas Cort
13872718b568SThomas Cort l1 = brace_start - start;
13882718b568SThomas Cort l2 = (p - 1) - field_start;
13892718b568SThomas Cort l3 = end - brace_end;
13902718b568SThomas Cort new = (char *) alloc(l1 + l2 + l3 + 1, ATEMP);
13912718b568SThomas Cort memcpy(new, start, l1);
13922718b568SThomas Cort memcpy(new + l1, field_start, l2);
13932718b568SThomas Cort memcpy(new + l1 + l2, brace_end, l3);
13942718b568SThomas Cort new[l1 + l2 + l3] = '\0';
13952718b568SThomas Cort alt_expand(wp, new, new + l1,
13962718b568SThomas Cort new + l1 + l2 + l3, fdo);
13972718b568SThomas Cort field_start = p + 1;
13982718b568SThomas Cort }
13992718b568SThomas Cort }
14002718b568SThomas Cort }
14012718b568SThomas Cort return;
14022718b568SThomas Cort }
14032718b568SThomas Cort #endif /* BRACE_EXPAND */
1404