1*d47295ccSrillig /* $NetBSD: eval.c,v 1.27 2024/10/03 20:14:01 rillig Exp $ */ 22ab2e20cStls 3e1b2664cSjtc /* 4e1b2664cSjtc * Expansion - quoting, separation, substitution, globbing 5e1b2664cSjtc */ 66377cac7Sagc #include <sys/cdefs.h> 76377cac7Sagc 86377cac7Sagc #ifndef lint 9*d47295ccSrillig __RCSID("$NetBSD: eval.c,v 1.27 2024/10/03 20:14:01 rillig Exp $"); 106377cac7Sagc #endif 116377cac7Sagc 12fac4b394Skamil #include <sys/stat.h> 130a54ac30Sdholland #include <stdint.h> 140a54ac30Sdholland #include <pwd.h> 15e1b2664cSjtc 16e1b2664cSjtc #include "sh.h" 17e1b2664cSjtc #include "ksh_dir.h" 18e1b2664cSjtc 19e1b2664cSjtc /* 20e1b2664cSjtc * string expansion 21e1b2664cSjtc * 22e1b2664cSjtc * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution. 23e1b2664cSjtc * second pass: alternation ({,}), filename expansion (*?[]). 24e1b2664cSjtc */ 25e1b2664cSjtc 26e1b2664cSjtc /* expansion generator state */ 27e1b2664cSjtc typedef struct Expand { 28e1b2664cSjtc /* int type; */ /* see expand() */ 29e1b2664cSjtc const char *str; /* string */ 30e1b2664cSjtc union { 31e1b2664cSjtc const char **strv;/* string[] */ 32e1b2664cSjtc struct shf *shf;/* file */ 33e1b2664cSjtc } u; /* source */ 34e1b2664cSjtc struct tbl *var; /* variable in ${var..} */ 35e1b2664cSjtc short split; /* split "$@" / call waitlast $() */ 36e1b2664cSjtc } Expand; 37e1b2664cSjtc 38e1b2664cSjtc #define XBASE 0 /* scanning original */ 39e1b2664cSjtc #define XSUB 1 /* expanding ${} string */ 40e1b2664cSjtc #define XARGSEP 2 /* ifs0 between "$*" */ 41e1b2664cSjtc #define XARG 3 /* expanding $*, $@ */ 42e1b2664cSjtc #define XCOM 4 /* expanding $() */ 43e1b2664cSjtc #define XNULLSUB 5 /* "$@" when $# is 0 (don't generate word) */ 44e1b2664cSjtc 45e1b2664cSjtc /* States used for field splitting */ 46e1b2664cSjtc #define IFS_WORD 0 /* word has chars (or quotes) */ 47e1b2664cSjtc #define IFS_WS 1 /* have seen IFS white-space */ 48e1b2664cSjtc #define IFS_NWS 2 /* have seen IFS non-white-space */ 49e1b2664cSjtc 5048ee8d12Shubertf static int varsub ARGS((Expand *xp, char *sp, char *word, int *stypep, int *slenp)); 51e1b2664cSjtc static int comsub ARGS((Expand *xp, char *cp)); 52e1b2664cSjtc static char *trimsub ARGS((char *str, char *pat, int how)); 53b9cf72acSkamil static void ksh_glob ARGS((char *cp, XPtrV *wp, int markdirs)); 54e1b2664cSjtc static void globit ARGS((XString *xs, char **xpp, char *sp, XPtrV *wp, 55e1b2664cSjtc int check)); 56e1b2664cSjtc static char *maybe_expand_tilde ARGS((char *p, XString *dsp, char **dpp, 57e1b2664cSjtc int isassign)); 58e1b2664cSjtc static char *tilde ARGS((char *acp)); 59e1b2664cSjtc static char *homedir ARGS((char *name)); 60e1b2664cSjtc #ifdef BRACE_EXPAND 61e1b2664cSjtc static void alt_expand ARGS((XPtrV *wp, char *start, char *exp_start, 62e1b2664cSjtc char *end, int fdo)); 63e1b2664cSjtc #endif 64e1b2664cSjtc 65e1b2664cSjtc /* compile and expand word */ 66e1b2664cSjtc char * 67e1b2664cSjtc substitute(cp, f) 68e1b2664cSjtc const char *cp; 69e1b2664cSjtc int f; 70e1b2664cSjtc { 71e1b2664cSjtc struct source *s, *sold; 72e1b2664cSjtc 73e1b2664cSjtc sold = source; 74e1b2664cSjtc s = pushs(SWSTR, ATEMP); 75e1b2664cSjtc s->start = s->str = cp; 76e1b2664cSjtc source = s; 77e1b2664cSjtc if (yylex(ONEWORD) != LWORD) 78e1b2664cSjtc internal_errorf(1, "substitute"); 79e1b2664cSjtc source = sold; 80e1b2664cSjtc afree(s, ATEMP); 81e1b2664cSjtc return evalstr(yylval.cp, f); 82e1b2664cSjtc } 83e1b2664cSjtc 84e1b2664cSjtc /* 85e1b2664cSjtc * expand arg-list 86e1b2664cSjtc */ 87e1b2664cSjtc char ** 88e1b2664cSjtc eval(ap, f) 890f2b5450Skamil char **ap; 90e1b2664cSjtc int f; 91e1b2664cSjtc { 92e1b2664cSjtc XPtrV w; 93e1b2664cSjtc 94e1b2664cSjtc if (*ap == NULL) 95e1b2664cSjtc return ap; 96e1b2664cSjtc XPinit(w, 32); 97e1b2664cSjtc XPput(w, NULL); /* space for shell name */ 98e1b2664cSjtc while (*ap != NULL) 99e1b2664cSjtc expand(*ap++, &w, f); 100e1b2664cSjtc XPput(w, NULL); 101e1b2664cSjtc return (char **) XPclose(w) + 1; 102e1b2664cSjtc } 103e1b2664cSjtc 104e1b2664cSjtc /* 105e1b2664cSjtc * expand string 106e1b2664cSjtc */ 107e1b2664cSjtc char * 108e1b2664cSjtc evalstr(cp, f) 109e1b2664cSjtc char *cp; 110e1b2664cSjtc int f; 111e1b2664cSjtc { 112e1b2664cSjtc XPtrV w; 113e1b2664cSjtc 114e1b2664cSjtc XPinit(w, 1); 115e1b2664cSjtc expand(cp, &w, f); 116e1b2664cSjtc cp = (XPsize(w) == 0) ? null : (char*) *XPptrv(w); 117e1b2664cSjtc XPfree(w); 118e1b2664cSjtc return cp; 119e1b2664cSjtc } 120e1b2664cSjtc 121e1b2664cSjtc /* 122e1b2664cSjtc * expand string - return only one component 123e1b2664cSjtc * used from iosetup to expand redirection files 124e1b2664cSjtc */ 125e1b2664cSjtc char * 126e1b2664cSjtc evalonestr(cp, f) 1270f2b5450Skamil char *cp; 128e1b2664cSjtc int f; 129e1b2664cSjtc { 130e1b2664cSjtc XPtrV w; 131e1b2664cSjtc 132e1b2664cSjtc XPinit(w, 1); 133e1b2664cSjtc expand(cp, &w, f); 134e1b2664cSjtc switch (XPsize(w)) { 135e1b2664cSjtc case 0: 136e1b2664cSjtc cp = null; 137e1b2664cSjtc break; 138e1b2664cSjtc case 1: 139e1b2664cSjtc cp = (char*) *XPptrv(w); 140e1b2664cSjtc break; 141e1b2664cSjtc default: 142e1b2664cSjtc cp = evalstr(cp, f&~DOGLOB); 143e1b2664cSjtc break; 144e1b2664cSjtc } 145e1b2664cSjtc XPfree(w); 146e1b2664cSjtc return cp; 147e1b2664cSjtc } 148e1b2664cSjtc 149e1b2664cSjtc /* for nested substitution: ${var:=$var2} */ 150e1b2664cSjtc typedef struct SubType { 151e1b2664cSjtc short stype; /* [=+-?%#] action after expanded word */ 152e1b2664cSjtc short base; /* begin position of expanded word */ 153e1b2664cSjtc short f; /* saved value of f (DOPAT, etc) */ 154e1b2664cSjtc struct tbl *var; /* variable for ${var..} */ 155e1b2664cSjtc short quote; /* saved value of quote (for ${..[%#]..}) */ 156e1b2664cSjtc struct SubType *prev; /* old type */ 157e1b2664cSjtc struct SubType *next; /* poped type (to avoid re-allocating) */ 158e1b2664cSjtc } SubType; 159e1b2664cSjtc 160e1b2664cSjtc void 161e1b2664cSjtc expand(cp, wp, f) 162e1b2664cSjtc char *cp; /* input word */ 1630f2b5450Skamil XPtrV *wp; /* output words */ 164e1b2664cSjtc int f; /* DO* flags */ 165e1b2664cSjtc { 1660f2b5450Skamil int UNINITIALIZED(c); 1670f2b5450Skamil int type; /* expansion type */ 1680f2b5450Skamil int quote = 0; /* quoted */ 169e1b2664cSjtc XString ds; /* destination string */ 1700f2b5450Skamil char *dp, *sp; /* dest., source */ 171e1b2664cSjtc int fdo, word; /* second pass flags; have word */ 172f662a744Smycroft int doblank; /* field splitting of parameter/command subst */ 173e1b2664cSjtc Expand x; /* expansion variables */ 174e1b2664cSjtc SubType st_head, *st; 175e1b2664cSjtc int UNINITIALIZED(newlines); /* For trailing newlines in COMSUB */ 176d6d5f49cSkamil int saw_eq; 177d6d5f49cSkamil unsigned int tilde_ok; 178e1b2664cSjtc int make_magic; 179f662a744Smycroft size_t len; 180e1b2664cSjtc 181084c0528Smrg x.split = 0; /* XXX gcc */ 182084c0528Smrg x.str = NULL; /* XXX gcc */ 1830fa13341Schristos x.u.strv = NULL;/* XXX gcc */ 184e1b2664cSjtc if (cp == NULL) 185e1b2664cSjtc internal_errorf(1, "expand(NULL)"); 186e1b2664cSjtc /* for alias, readonly, set, typeset commands */ 187e1b2664cSjtc if ((f & DOVACHECK) && is_wdvarassign(cp)) { 188e1b2664cSjtc f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE); 189e1b2664cSjtc f |= DOASNTILDE; 190e1b2664cSjtc } 191e1b2664cSjtc if (Flag(FNOGLOB)) 192e1b2664cSjtc f &= ~DOGLOB; 193e1b2664cSjtc if (Flag(FMARKDIRS)) 194e1b2664cSjtc f |= DOMARKDIRS; 195e1b2664cSjtc #ifdef BRACE_EXPAND 196e1b2664cSjtc if (Flag(FBRACEEXPAND) && (f & DOGLOB)) 197e1b2664cSjtc f |= DOBRACE_; 198e1b2664cSjtc #endif /* BRACE_EXPAND */ 199e1b2664cSjtc 200e1b2664cSjtc Xinit(ds, dp, 128, ATEMP); /* init dest. string */ 201e1b2664cSjtc type = XBASE; 202e1b2664cSjtc sp = cp; 203e1b2664cSjtc fdo = 0; 204e1b2664cSjtc saw_eq = 0; 205e1b2664cSjtc tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; /* must be 1/0 */ 206e1b2664cSjtc doblank = 0; 207e1b2664cSjtc make_magic = 0; 208e1b2664cSjtc word = (f&DOBLANK) ? IFS_WS : IFS_WORD; 209e1b2664cSjtc st_head.next = (SubType *) 0; 210e1b2664cSjtc st = &st_head; 211e1b2664cSjtc 212e1b2664cSjtc while (1) { 213e1b2664cSjtc Xcheck(ds, dp); 214e1b2664cSjtc 215e1b2664cSjtc switch (type) { 216e1b2664cSjtc case XBASE: /* original prefixed string */ 217e1b2664cSjtc c = *sp++; 218e1b2664cSjtc switch (c) { 219e1b2664cSjtc case EOS: 220e1b2664cSjtc c = 0; 221e1b2664cSjtc break; 222e1b2664cSjtc case CHAR: 223e1b2664cSjtc c = *sp++; 224e1b2664cSjtc break; 225e1b2664cSjtc case QCHAR: 226e1b2664cSjtc quote |= 2; /* temporary quote */ 227e1b2664cSjtc c = *sp++; 228e1b2664cSjtc break; 229e1b2664cSjtc case OQUOTE: 230e1b2664cSjtc word = IFS_WORD; 231e1b2664cSjtc tilde_ok = 0; 232e1b2664cSjtc quote = 1; 233e1b2664cSjtc continue; 234e1b2664cSjtc case CQUOTE: 235e1b2664cSjtc quote = 0; 236e1b2664cSjtc continue; 237e1b2664cSjtc case COMSUB: 238e1b2664cSjtc tilde_ok = 0; 239e1b2664cSjtc if (f & DONTRUNCOMMAND) { 240e1b2664cSjtc word = IFS_WORD; 241e1b2664cSjtc *dp++ = '$'; *dp++ = '('; 242e1b2664cSjtc while (*sp != '\0') { 243e1b2664cSjtc Xcheck(ds, dp); 244e1b2664cSjtc *dp++ = *sp++; 245e1b2664cSjtc } 246e1b2664cSjtc *dp++ = ')'; 247e1b2664cSjtc } else { 248e1b2664cSjtc type = comsub(&x, sp); 249e1b2664cSjtc if (type == XCOM && (f&DOBLANK)) 250e1b2664cSjtc doblank++; 251e1b2664cSjtc sp = strchr(sp, 0) + 1; 252e1b2664cSjtc newlines = 0; 253e1b2664cSjtc } 254e1b2664cSjtc continue; 255e1b2664cSjtc case EXPRSUB: 256e1b2664cSjtc word = IFS_WORD; 257e1b2664cSjtc tilde_ok = 0; 258e1b2664cSjtc if (f & DONTRUNCOMMAND) { 259e1b2664cSjtc *dp++ = '$'; *dp++ = '('; *dp++ = '('; 260e1b2664cSjtc while (*sp != '\0') { 261e1b2664cSjtc Xcheck(ds, dp); 262e1b2664cSjtc *dp++ = *sp++; 263e1b2664cSjtc } 264e1b2664cSjtc *dp++ = ')'; *dp++ = ')'; 265e1b2664cSjtc } else { 266e1b2664cSjtc struct tbl v; 267e1b2664cSjtc char *p; 268e1b2664cSjtc 269e1b2664cSjtc v.flag = DEFINED|ISSET|INTEGER; 270e1b2664cSjtc v.type = 10; /* not default */ 271e1b2664cSjtc v.name[0] = '\0'; 272e1b2664cSjtc v_evaluate(&v, substitute(sp, 0), 27348ee8d12Shubertf KSH_UNWIND_ERROR); 274e1b2664cSjtc sp = strchr(sp, 0) + 1; 275e1b2664cSjtc for (p = str_val(&v); *p; ) { 276e1b2664cSjtc Xcheck(ds, dp); 277e1b2664cSjtc *dp++ = *p++; 278e1b2664cSjtc } 279e1b2664cSjtc } 280e1b2664cSjtc continue; 281e1b2664cSjtc case OSUBST: /* ${{#}var{:}[=+-?#%]word} */ 282e1b2664cSjtc /* format is: 28348ee8d12Shubertf * OSUBST [{x] plain-variable-part \0 28448ee8d12Shubertf * compiled-word-part CSUBST [}x] 285e1b2664cSjtc * This is were all syntax checking gets done... 286e1b2664cSjtc */ 287e1b2664cSjtc { 28848ee8d12Shubertf char *varname = ++sp; /* skip the { or x (}) */ 289e1b2664cSjtc int stype; 29048ee8d12Shubertf int slen; 291e1b2664cSjtc 292084c0528Smrg slen = -1; /* XXX gcc */ 293e1b2664cSjtc sp = strchr(sp, '\0') + 1; /* skip variable */ 29448ee8d12Shubertf type = varsub(&x, varname, sp, &stype, &slen); 295e1b2664cSjtc if (type < 0) { 296e1b2664cSjtc char endc; 297e1b2664cSjtc char *str, *end; 298e1b2664cSjtc 299e1b2664cSjtc end = (char *) wdscan(sp, CSUBST); 30048ee8d12Shubertf /* ({) the } or x is already skipped */ 301e1b2664cSjtc endc = *end; 302e1b2664cSjtc *end = EOS; 303e1b2664cSjtc str = snptreef((char *) 0, 64, "%S", 304e1b2664cSjtc varname - 1); 305e1b2664cSjtc *end = endc; 306e1b2664cSjtc errorf("%s: bad substitution", str); 307e1b2664cSjtc } 308e1b2664cSjtc if (f&DOBLANK) 309e1b2664cSjtc doblank++; 310e1b2664cSjtc tilde_ok = 0; 311e1b2664cSjtc if (type == XBASE) { /* expand? */ 312e1b2664cSjtc if (!st->next) { 313e1b2664cSjtc SubType *newst; 314e1b2664cSjtc 315e1b2664cSjtc newst = (SubType *) alloc( 316e1b2664cSjtc sizeof(SubType), ATEMP); 317e1b2664cSjtc newst->next = (SubType *) 0; 318e1b2664cSjtc newst->prev = st; 319e1b2664cSjtc st->next = newst; 320e1b2664cSjtc } 321e1b2664cSjtc st = st->next; 322e1b2664cSjtc st->stype = stype; 323e1b2664cSjtc st->base = Xsavepos(ds, dp); 324e1b2664cSjtc st->f = f; 325e1b2664cSjtc st->var = x.var; 326e1b2664cSjtc st->quote = quote; 327e1b2664cSjtc /* skip qualifier(s) */ 32848ee8d12Shubertf if (stype) 32948ee8d12Shubertf sp += slen; 330e1b2664cSjtc switch (stype & 0x7f) { 331e1b2664cSjtc case '#': 332e1b2664cSjtc case '%': 333e1b2664cSjtc /* ! DOBLANK,DOBRACE_,DOTILDE */ 334e1b2664cSjtc f = DOPAT | (f&DONTRUNCOMMAND) 335e1b2664cSjtc | DOTEMP_; 336e1b2664cSjtc quote = 0; 33748ee8d12Shubertf /* Prepend open pattern (so | 33848ee8d12Shubertf * in a trim will work as 33948ee8d12Shubertf * expected) 34048ee8d12Shubertf */ 34148ee8d12Shubertf *dp++ = MAGIC; 342acfe9a77Sjoerg *dp++ = (char)('@' + 0x80); 343e1b2664cSjtc break; 344e1b2664cSjtc case '=': 345e1b2664cSjtc /* Enabling tilde expansion 346e1b2664cSjtc * after :'s here is 347e1b2664cSjtc * non-standard ksh, but is 348e1b2664cSjtc * consistent with rules for 349e1b2664cSjtc * other assignments. Not 350e1b2664cSjtc * sure what POSIX thinks of 351e1b2664cSjtc * this. 352e1b2664cSjtc * Not doing tilde expansion 353e1b2664cSjtc * for integer variables is a 354e1b2664cSjtc * non-POSIX thing - makes 355e1b2664cSjtc * sense though, since ~ is 356e1b2664cSjtc * a arithmetic operator. 357e1b2664cSjtc */ 358e1b2664cSjtc if (!(x.var->flag & INTEGER)) 359e1b2664cSjtc f |= DOASNTILDE|DOTILDE; 360e1b2664cSjtc f |= DOTEMP_; 361e1b2664cSjtc /* These will be done after the 362e1b2664cSjtc * value has been assigned. 363e1b2664cSjtc */ 364e1b2664cSjtc f &= ~(DOBLANK|DOGLOB|DOBRACE_); 365e1b2664cSjtc tilde_ok = 1; 366e1b2664cSjtc break; 367e1b2664cSjtc case '?': 368e1b2664cSjtc f &= ~DOBLANK; 369e1b2664cSjtc f |= DOTEMP_; 370e1b2664cSjtc /* fall through */ 371e1b2664cSjtc default: 372e1b2664cSjtc /* Enable tilde expansion */ 373e1b2664cSjtc tilde_ok = 1; 374e1b2664cSjtc f |= DOTILDE; 375e1b2664cSjtc } 376e1b2664cSjtc } else 377e1b2664cSjtc /* skip word */ 378e1b2664cSjtc sp = (char *) wdscan(sp, CSUBST); 379e1b2664cSjtc continue; 380e1b2664cSjtc } 381e1b2664cSjtc case CSUBST: /* only get here if expanding word */ 38248ee8d12Shubertf sp++; /* ({) skip the } or x */ 383e1b2664cSjtc tilde_ok = 0; /* in case of ${unset:-} */ 384e1b2664cSjtc *dp = '\0'; 385e1b2664cSjtc quote = st->quote; 386e1b2664cSjtc f = st->f; 387e1b2664cSjtc if (f&DOBLANK) 388e1b2664cSjtc doblank--; 389e1b2664cSjtc switch (st->stype&0x7f) { 390e1b2664cSjtc case '#': 391e1b2664cSjtc case '%': 39248ee8d12Shubertf /* Append end-pattern */ 39348ee8d12Shubertf *dp++ = MAGIC; *dp++ = ')'; *dp = '\0'; 394e1b2664cSjtc dp = Xrestpos(ds, dp, st->base); 395e1b2664cSjtc /* Must use st->var since calling 396e1b2664cSjtc * global would break things 397e1b2664cSjtc * like x[i+=1]. 398e1b2664cSjtc */ 399e1b2664cSjtc x.str = trimsub(str_val(st->var), 400e1b2664cSjtc dp, st->stype); 401e1b2664cSjtc type = XSUB; 402e1b2664cSjtc if (f&DOBLANK) 403e1b2664cSjtc doblank++; 404e1b2664cSjtc st = st->prev; 405e1b2664cSjtc continue; 406e1b2664cSjtc case '=': 407e1b2664cSjtc /* Restore our position and substitute 408e1b2664cSjtc * the value of st->var (may not be 409e1b2664cSjtc * the assigned value in the presence 410e1b2664cSjtc * of integer/right-adj/etc attributes). 411e1b2664cSjtc */ 412e1b2664cSjtc dp = Xrestpos(ds, dp, st->base); 413e1b2664cSjtc /* Must use st->var since calling 414e1b2664cSjtc * global would cause with things 415e1b2664cSjtc * like x[i+=1] to be evaluated twice. 416e1b2664cSjtc */ 41748ee8d12Shubertf /* Note: not exported by FEXPORT 41848ee8d12Shubertf * in at&t ksh. 41948ee8d12Shubertf */ 42048ee8d12Shubertf /* XXX POSIX says readonly is only 42148ee8d12Shubertf * fatal for special builtins (setstr 42248ee8d12Shubertf * does readonly check). 42348ee8d12Shubertf */ 424f662a744Smycroft len = strlen(dp) + 1; 425f662a744Smycroft setstr(st->var, 426f662a744Smycroft debunk((char *) alloc(len, ATEMP), 427f662a744Smycroft dp, len), 42848ee8d12Shubertf KSH_UNWIND_ERROR); 429e1b2664cSjtc x.str = str_val(st->var); 430e1b2664cSjtc type = XSUB; 431e1b2664cSjtc if (f&DOBLANK) 432e1b2664cSjtc doblank++; 433e1b2664cSjtc st = st->prev; 434e1b2664cSjtc continue; 435e1b2664cSjtc case '?': 436e1b2664cSjtc { 437e1b2664cSjtc char *s = Xrestpos(ds, dp, st->base); 438e1b2664cSjtc 439e1b2664cSjtc errorf("%s: %s", st->var->name, 440e1b2664cSjtc dp == s ? 441e1b2664cSjtc "parameter null or not set" 442f662a744Smycroft : (debunk(s, s, strlen(s) + 1), s)); 443e1b2664cSjtc } 444e1b2664cSjtc } 445e1b2664cSjtc st = st->prev; 446e1b2664cSjtc type = XBASE; 447e1b2664cSjtc continue; 448e1b2664cSjtc 449e1b2664cSjtc case OPAT: /* open pattern: *(foo|bar) */ 450e1b2664cSjtc /* Next char is the type of pattern */ 451e1b2664cSjtc make_magic = 1; 452e1b2664cSjtc c = *sp++ + 0x80; 453e1b2664cSjtc break; 454e1b2664cSjtc 455f662a744Smycroft case SPAT: /* pattern separator (|) */ 456e1b2664cSjtc make_magic = 1; 457e1b2664cSjtc c = '|'; 458e1b2664cSjtc break; 459e1b2664cSjtc 460e1b2664cSjtc case CPAT: /* close pattern */ 461e1b2664cSjtc make_magic = 1; 462e1b2664cSjtc c = /*(*/ ')'; 463e1b2664cSjtc break; 464e1b2664cSjtc } 465e1b2664cSjtc break; 466e1b2664cSjtc 467e1b2664cSjtc case XNULLSUB: 468e1b2664cSjtc /* Special case for "$@" (and "${foo[@]}") - no 469e1b2664cSjtc * word is generated if $# is 0 (unless there is 470e1b2664cSjtc * other stuff inside the quotes). 471e1b2664cSjtc */ 472e1b2664cSjtc type = XBASE; 473e1b2664cSjtc if (f&DOBLANK) { 474e1b2664cSjtc doblank--; 475e1b2664cSjtc /* not really correct: x=; "$x$@" should 476e1b2664cSjtc * generate a null argument and 477e1b2664cSjtc * set A; "${@:+}" shouldn't. 478e1b2664cSjtc */ 479e1b2664cSjtc if (dp == Xstring(ds, dp)) 480e1b2664cSjtc word = IFS_WS; 481e1b2664cSjtc } 482e1b2664cSjtc continue; 483e1b2664cSjtc 484e1b2664cSjtc case XSUB: 485e1b2664cSjtc if ((c = *x.str++) == 0) { 486e1b2664cSjtc type = XBASE; 487e1b2664cSjtc if (f&DOBLANK) 488e1b2664cSjtc doblank--; 489e1b2664cSjtc continue; 490e1b2664cSjtc } 491e1b2664cSjtc break; 492e1b2664cSjtc 493e1b2664cSjtc case XARGSEP: 494e1b2664cSjtc type = XARG; 495e1b2664cSjtc quote = 1; 4966883e45bSmlelstv /* FALLTHROUGH */ 497e1b2664cSjtc case XARG: 498e1b2664cSjtc if ((c = *x.str++) == '\0') { 499e1b2664cSjtc /* force null words to be created so 500e1b2664cSjtc * set -- '' 2 ''; foo "$@" will do 501e1b2664cSjtc * the right thing 502e1b2664cSjtc */ 503e1b2664cSjtc if (quote && x.split) 504e1b2664cSjtc word = IFS_WORD; 505e1b2664cSjtc if ((x.str = *x.u.strv++) == NULL) { 506e1b2664cSjtc type = XBASE; 507e1b2664cSjtc if (f&DOBLANK) 508e1b2664cSjtc doblank--; 509e1b2664cSjtc continue; 510e1b2664cSjtc } 511e1b2664cSjtc c = ifs0; 512e1b2664cSjtc if (c == 0) { 513e1b2664cSjtc if (quote && !x.split) 514e1b2664cSjtc continue; 515e1b2664cSjtc c = ' '; 516e1b2664cSjtc } 517e1b2664cSjtc if (quote && x.split) { 518e1b2664cSjtc /* terminate word for "$@" */ 519e1b2664cSjtc type = XARGSEP; 520e1b2664cSjtc quote = 0; 521e1b2664cSjtc } 522e1b2664cSjtc } 523e1b2664cSjtc break; 524e1b2664cSjtc 525e1b2664cSjtc case XCOM: 526e1b2664cSjtc if (newlines) { /* Spit out saved nl's */ 527e1b2664cSjtc c = '\n'; 528e1b2664cSjtc --newlines; 529e1b2664cSjtc } else { 530e1b2664cSjtc while ((c = shf_getc(x.u.shf)) == 0 || c == '\n') 531e1b2664cSjtc if (c == '\n') 532e1b2664cSjtc newlines++; /* Save newlines */ 533e1b2664cSjtc if (newlines && c != EOF) { 534e1b2664cSjtc shf_ungetc(c, x.u.shf); 535e1b2664cSjtc c = '\n'; 536e1b2664cSjtc --newlines; 537e1b2664cSjtc } 538e1b2664cSjtc } 539e1b2664cSjtc if (c == EOF) { 540e1b2664cSjtc newlines = 0; 541e1b2664cSjtc shf_close(x.u.shf); 542e1b2664cSjtc if (x.split) 543e1b2664cSjtc subst_exstat = waitlast(); 544e1b2664cSjtc type = XBASE; 545e1b2664cSjtc if (f&DOBLANK) 546e1b2664cSjtc doblank--; 547e1b2664cSjtc continue; 548e1b2664cSjtc } 549e1b2664cSjtc break; 550e1b2664cSjtc } 551e1b2664cSjtc 552e1b2664cSjtc /* check for end of word or IFS separation */ 553e1b2664cSjtc if (c == 0 || (!quote && (f & DOBLANK) && doblank && !make_magic 554e1b2664cSjtc && ctype(c, C_IFS))) 555e1b2664cSjtc { 556e1b2664cSjtc /* How words are broken up: 557e1b2664cSjtc * | value of c 558e1b2664cSjtc * word | ws nws 0 559e1b2664cSjtc * ----------------------------------- 560e1b2664cSjtc * IFS_WORD w/WS w/NWS w 561e1b2664cSjtc * IFS_WS -/WS w/NWS - 562e1b2664cSjtc * IFS_NWS -/NWS w/NWS w 563e1b2664cSjtc * (w means generate a word) 564e1b2664cSjtc * Note that IFS_NWS/0 generates a word (at&t ksh 565e1b2664cSjtc * doesn't do this, but POSIX does). 566e1b2664cSjtc */ 567e1b2664cSjtc if (word == IFS_WORD 568e1b2664cSjtc || (!ctype(c, C_IFSWS) && (c || word == IFS_NWS))) 569e1b2664cSjtc { 570e1b2664cSjtc char *p; 571e1b2664cSjtc 572e1b2664cSjtc *dp++ = '\0'; 573e1b2664cSjtc p = Xclose(ds, dp); 574e1b2664cSjtc #ifdef BRACE_EXPAND 575e1b2664cSjtc if (fdo & DOBRACE_) 576e1b2664cSjtc /* also does globbing */ 577e1b2664cSjtc alt_expand(wp, p, p, 578e1b2664cSjtc p + Xlength(ds, (dp - 1)), 579e1b2664cSjtc fdo | (f & DOMARKDIRS)); 580e1b2664cSjtc else 581e1b2664cSjtc #endif /* BRACE_EXPAND */ 582e1b2664cSjtc if (fdo & DOGLOB) 583b9cf72acSkamil ksh_glob(p, wp, f & DOMARKDIRS); 584e1b2664cSjtc else if ((f & DOPAT) || !(fdo & DOMAGIC_)) 585e1b2664cSjtc XPput(*wp, p); 586e1b2664cSjtc else 587f662a744Smycroft XPput(*wp, debunk(p, p, strlen(p) + 1)); 588e1b2664cSjtc fdo = 0; 589e1b2664cSjtc saw_eq = 0; 590e1b2664cSjtc tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; 591e1b2664cSjtc if (c != 0) 592e1b2664cSjtc Xinit(ds, dp, 128, ATEMP); 593e1b2664cSjtc } 594e1b2664cSjtc if (c == 0) 595e1b2664cSjtc return; 596e1b2664cSjtc if (word != IFS_NWS) 597e1b2664cSjtc word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS; 598e1b2664cSjtc } else { 599e1b2664cSjtc /* age tilde_ok info - ~ code tests second bit */ 600e1b2664cSjtc tilde_ok <<= 1; 601e1b2664cSjtc /* mark any special second pass chars */ 602e1b2664cSjtc if (!quote) 603e1b2664cSjtc switch (c) { 604e1b2664cSjtc case '[': 605e1b2664cSjtc case NOT: 606e1b2664cSjtc case '-': 607e1b2664cSjtc case ']': 608e1b2664cSjtc /* For character classes - doesn't hurt 609e1b2664cSjtc * to have magic !,-,]'s outside of 610e1b2664cSjtc * [...] expressions. 611e1b2664cSjtc */ 612e1b2664cSjtc if (f & (DOPAT | DOGLOB)) { 613e1b2664cSjtc fdo |= DOMAGIC_; 614e1b2664cSjtc if (c == '[') 615e1b2664cSjtc fdo |= f & DOGLOB; 616e1b2664cSjtc *dp++ = MAGIC; 617e1b2664cSjtc } 618e1b2664cSjtc break; 619e1b2664cSjtc case '*': 620e1b2664cSjtc case '?': 621e1b2664cSjtc if (f & (DOPAT | DOGLOB)) { 622e1b2664cSjtc fdo |= DOMAGIC_ | (f & DOGLOB); 623e1b2664cSjtc *dp++ = MAGIC; 624e1b2664cSjtc } 625e1b2664cSjtc break; 626e1b2664cSjtc #ifdef BRACE_EXPAND 627e1b2664cSjtc case OBRACE: 628e1b2664cSjtc case ',': 629e1b2664cSjtc case CBRACE: 630e1b2664cSjtc if ((f & DOBRACE_) && (c == OBRACE 631e1b2664cSjtc || (fdo & DOBRACE_))) 632e1b2664cSjtc { 633e1b2664cSjtc fdo |= DOBRACE_|DOMAGIC_; 634e1b2664cSjtc *dp++ = MAGIC; 635e1b2664cSjtc } 636e1b2664cSjtc break; 637e1b2664cSjtc #endif /* BRACE_EXPAND */ 638e1b2664cSjtc case '=': 639e1b2664cSjtc /* Note first unquoted = for ~ */ 640e1b2664cSjtc if (!(f & DOTEMP_) && !saw_eq) { 641e1b2664cSjtc saw_eq = 1; 642e1b2664cSjtc tilde_ok = 1; 643e1b2664cSjtc } 644e1b2664cSjtc break; 645e1b2664cSjtc case PATHSEP: /* : */ 646e1b2664cSjtc /* Note unquoted : for ~ */ 647e1b2664cSjtc if (!(f & DOTEMP_) && (f & DOASNTILDE)) 648e1b2664cSjtc tilde_ok = 1; 649e1b2664cSjtc break; 650e1b2664cSjtc case '~': 651e1b2664cSjtc /* tilde_ok is reset whenever 652e1b2664cSjtc * any of ' " $( $(( ${ } are seen. 653e1b2664cSjtc * Note that tilde_ok must be preserved 654e1b2664cSjtc * through the sequence ${A=a=}~ 655e1b2664cSjtc */ 656e1b2664cSjtc if (type == XBASE 657e1b2664cSjtc && (f & (DOTILDE|DOASNTILDE)) 658e1b2664cSjtc && (tilde_ok & 2)) 659e1b2664cSjtc { 660e1b2664cSjtc char *p, *dp_x; 661e1b2664cSjtc 662e1b2664cSjtc dp_x = dp; 663e1b2664cSjtc p = maybe_expand_tilde(sp, 664e1b2664cSjtc &ds, &dp_x, 665e1b2664cSjtc f & DOASNTILDE); 666e1b2664cSjtc if (p) { 667e1b2664cSjtc if (dp != dp_x) 668e1b2664cSjtc word = IFS_WORD; 669e1b2664cSjtc dp = dp_x; 670e1b2664cSjtc sp = p; 671e1b2664cSjtc continue; 672e1b2664cSjtc } 673e1b2664cSjtc } 674e1b2664cSjtc break; 675e1b2664cSjtc } 676e1b2664cSjtc else 677e1b2664cSjtc quote &= ~2; /* undo temporary */ 678e1b2664cSjtc 679e1b2664cSjtc if (make_magic) { 680e1b2664cSjtc make_magic = 0; 681e1b2664cSjtc fdo |= DOMAGIC_ | (f & DOGLOB); 682e1b2664cSjtc *dp++ = MAGIC; 683e1b2664cSjtc } else if (ISMAGIC(c)) { 684e1b2664cSjtc fdo |= DOMAGIC_; 685e1b2664cSjtc *dp++ = MAGIC; 686e1b2664cSjtc } 687e1b2664cSjtc *dp++ = c; /* save output char */ 688e1b2664cSjtc word = IFS_WORD; 689e1b2664cSjtc } 690e1b2664cSjtc } 691e1b2664cSjtc } 692e1b2664cSjtc 693e1b2664cSjtc /* 694e1b2664cSjtc * Prepare to generate the string returned by ${} substitution. 695e1b2664cSjtc */ 696e1b2664cSjtc static int 69748ee8d12Shubertf varsub(xp, sp, word, stypep, slenp) 698e1b2664cSjtc Expand *xp; 699e1b2664cSjtc char *sp; 700e1b2664cSjtc char *word; 70148ee8d12Shubertf int *stypep; /* becomes qualifier type */ 70248ee8d12Shubertf int *slenp; /* " " len (=, :=, etc.) valid iff *stypep != 0 */ 703e1b2664cSjtc { 704e1b2664cSjtc int c; 705e1b2664cSjtc int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */ 706e1b2664cSjtc int stype; /* substitution type */ 70748ee8d12Shubertf int slen; 708e1b2664cSjtc char *p; 709e1b2664cSjtc struct tbl *vp; 710e1b2664cSjtc 711e1b2664cSjtc if (sp[0] == '\0') /* Bad variable name */ 712e1b2664cSjtc return -1; 713e1b2664cSjtc 7140fa13341Schristos xp->var = NULL; 715e1b2664cSjtc 716e1b2664cSjtc /* ${#var}, string length or array size */ 717e1b2664cSjtc if (sp[0] == '#' && (c = sp[1]) != '\0') { 718e1b2664cSjtc int zero_ok = 0; 719e1b2664cSjtc 720e1b2664cSjtc /* Can't have any modifiers for ${#...} */ 721e1b2664cSjtc if (*word != CSUBST) 722e1b2664cSjtc return -1; 723e1b2664cSjtc sp++; 724e1b2664cSjtc /* Check for size of array */ 725e1b2664cSjtc if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') { 72648ee8d12Shubertf int n = 0; 727e1b2664cSjtc vp = global(arrayname(sp)); 728e1b2664cSjtc if (vp->flag & (ISSET|ARRAY)) 729e1b2664cSjtc zero_ok = 1; 730e1b2664cSjtc for (; vp; vp = vp->u.array) 73148ee8d12Shubertf if (vp->flag & ISSET) { 73248ee8d12Shubertf n++; 73348ee8d12Shubertf } 73448ee8d12Shubertf c = n; /* ksh88/ksh93 go for number, not max index */ 735e1b2664cSjtc } else if (c == '*' || c == '@') 736e1b2664cSjtc c = e->loc->argc; 737e1b2664cSjtc else { 738e1b2664cSjtc p = str_val(global(sp)); 739e1b2664cSjtc zero_ok = p != null; 740e1b2664cSjtc c = strlen(p); 741e1b2664cSjtc } 742e1b2664cSjtc if (Flag(FNOUNSET) && c == 0 && !zero_ok) 743e1b2664cSjtc errorf("%s: parameter not set", sp); 744e1b2664cSjtc *stypep = 0; /* unqualified variable/string substitution */ 745e1b2664cSjtc xp->str = str_save(ulton((unsigned long)c, 10), ATEMP); 746e1b2664cSjtc return XSUB; 747e1b2664cSjtc } 748e1b2664cSjtc 749e1b2664cSjtc /* Check for qualifiers in word part */ 750e1b2664cSjtc stype = 0; 75148ee8d12Shubertf c = word[slen = 0] == CHAR ? word[1] : 0; 752e1b2664cSjtc if (c == ':') { 75348ee8d12Shubertf slen += 2; 754e1b2664cSjtc stype = 0x80; 75548ee8d12Shubertf c = word[slen + 0] == CHAR ? word[slen + 1] : 0; 756e1b2664cSjtc } 75748ee8d12Shubertf if (ctype(c, C_SUBOP1)) { 75848ee8d12Shubertf slen += 2; 759e1b2664cSjtc stype |= c; 76048ee8d12Shubertf } else if (ctype(c, C_SUBOP2)) { /* Note: ksh88 allows :%, :%%, etc */ 76148ee8d12Shubertf slen += 2; 762e1b2664cSjtc stype = c; 76348ee8d12Shubertf if (word[slen + 0] == CHAR && c == word[slen + 1]) { 764e1b2664cSjtc stype |= 0x80; 76548ee8d12Shubertf slen += 2; 766e1b2664cSjtc } 76748ee8d12Shubertf } else if (stype) /* : is not ok */ 76848ee8d12Shubertf return -1; 769e1b2664cSjtc if (!stype && *word != CSUBST) 770e1b2664cSjtc return -1; 771e1b2664cSjtc *stypep = stype; 77248ee8d12Shubertf *slenp = slen; 773e1b2664cSjtc 774e1b2664cSjtc c = sp[0]; 775e1b2664cSjtc if (c == '*' || c == '@') { 776e1b2664cSjtc switch (stype & 0x7f) { 777e1b2664cSjtc case '=': /* can't assign to a vector */ 77848ee8d12Shubertf case '%': /* can't trim a vector (yet) */ 779e1b2664cSjtc case '#': 780e1b2664cSjtc return -1; 781e1b2664cSjtc } 782e1b2664cSjtc if (e->loc->argc == 0) { 7830fa13341Schristos xp->u.strv = NULL; 784e1b2664cSjtc xp->str = null; 785e1b2664cSjtc state = c == '@' ? XNULLSUB : XSUB; 786e1b2664cSjtc } else { 7870fa13341Schristos char **t = &e->loc->argv[1]; 7880fa13341Schristos xp->u.strv = (void *)(uintptr_t)t; 789e1b2664cSjtc xp->str = *xp->u.strv++; 790e1b2664cSjtc xp->split = c == '@'; /* $@ */ 791e1b2664cSjtc state = XARG; 792e1b2664cSjtc } 793e1b2664cSjtc } else { 794e1b2664cSjtc if ((p=strchr(sp,'[')) && (p[1]=='*'||p[1]=='@') && p[2]==']') { 795e1b2664cSjtc XPtrV wv; 796e1b2664cSjtc 797e1b2664cSjtc switch (stype & 0x7f) { 798e1b2664cSjtc case '=': /* can't assign to a vector */ 79948ee8d12Shubertf case '%': /* can't trim a vector (yet) */ 800e1b2664cSjtc case '#': 801e1b2664cSjtc return -1; 802e1b2664cSjtc } 803e1b2664cSjtc XPinit(wv, 32); 804e1b2664cSjtc vp = global(arrayname(sp)); 805e1b2664cSjtc for (; vp; vp = vp->u.array) { 806e1b2664cSjtc if (!(vp->flag&ISSET)) 807e1b2664cSjtc continue; 808e1b2664cSjtc XPput(wv, str_val(vp)); 809e1b2664cSjtc } 810e1b2664cSjtc if (XPsize(wv) == 0) { 811e1b2664cSjtc xp->str = null; 812e1b2664cSjtc state = p[1] == '@' ? XNULLSUB : XSUB; 813e1b2664cSjtc XPfree(wv); 814e1b2664cSjtc } else { 815e1b2664cSjtc XPput(wv, 0); 816e1b2664cSjtc xp->u.strv = (const char **) XPptrv(wv); 817e1b2664cSjtc xp->str = *xp->u.strv++; 818e1b2664cSjtc xp->split = p[1] == '@'; /* ${foo[@]} */ 819e1b2664cSjtc state = XARG; 820e1b2664cSjtc } 821e1b2664cSjtc } else { 822e1b2664cSjtc /* Can't assign things like $! or $1 */ 823e1b2664cSjtc if ((stype & 0x7f) == '=' 824e1b2664cSjtc && (ctype(*sp, C_VAR1) || digit(*sp))) 825e1b2664cSjtc return -1; 826e1b2664cSjtc xp->var = global(sp); 827e1b2664cSjtc xp->str = str_val(xp->var); 828e1b2664cSjtc state = XSUB; 829e1b2664cSjtc } 830e1b2664cSjtc } 831e1b2664cSjtc 832e1b2664cSjtc c = stype&0x7f; 833e1b2664cSjtc /* test the compiler's code generator */ 834e1b2664cSjtc if (ctype(c, C_SUBOP2) || 835e1b2664cSjtc (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */ 836e1b2664cSjtc c == '=' || c == '-' || c == '?' : c == '+')) 837e1b2664cSjtc state = XBASE; /* expand word instead of variable value */ 838e1b2664cSjtc if (Flag(FNOUNSET) && xp->str == null 839e1b2664cSjtc && (ctype(c, C_SUBOP2) || (state != XBASE && c != '+'))) 840e1b2664cSjtc errorf("%s: parameter not set", sp); 841e1b2664cSjtc return state; 842e1b2664cSjtc } 843e1b2664cSjtc 844e1b2664cSjtc /* 845e1b2664cSjtc * Run the command in $(...) and read its output. 846e1b2664cSjtc */ 847e1b2664cSjtc static int 848e1b2664cSjtc comsub(xp, cp) 8490f2b5450Skamil Expand *xp; 850e1b2664cSjtc char *cp; 851e1b2664cSjtc { 852e1b2664cSjtc Source *s, *sold; 8530f2b5450Skamil struct op *t; 854e1b2664cSjtc struct shf *shf; 855e1b2664cSjtc 856e1b2664cSjtc s = pushs(SSTRING, ATEMP); 857e1b2664cSjtc s->start = s->str = cp; 858e1b2664cSjtc sold = source; 859e1b2664cSjtc t = compile(s); 8604f633ea0Schristos afree(s, ATEMP); 861e1b2664cSjtc source = sold; 862e1b2664cSjtc 863e1b2664cSjtc if (t == NULL) 864e1b2664cSjtc return XBASE; 865e1b2664cSjtc 866e1b2664cSjtc if (t != NULL && t->type == TCOM && /* $(<file) */ 867e1b2664cSjtc *t->args == NULL && *t->vars == NULL && t->ioact != NULL) { 8680f2b5450Skamil struct ioword *io = *t->ioact; 869e1b2664cSjtc char *name; 870e1b2664cSjtc 871e1b2664cSjtc if ((io->flag&IOTYPE) != IOREAD) 872e1b2664cSjtc errorf("funny $() command: %s", 873e1b2664cSjtc snptreef((char *) 0, 32, "%R", io)); 874e1b2664cSjtc shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0, 875e1b2664cSjtc SHF_MAPHI|SHF_CLEXEC); 876e1b2664cSjtc if (shf == NULL) 877e1b2664cSjtc errorf("%s: cannot open $() input", name); 878e1b2664cSjtc xp->split = 0; /* no waitlast() */ 879e1b2664cSjtc } else { 880e1b2664cSjtc int ofd1, pv[2]; 881e1b2664cSjtc openpipe(pv); 882e1b2664cSjtc shf = shf_fdopen(pv[0], SHF_RD, (struct shf *) 0); 883e1b2664cSjtc ofd1 = savefd(1, 0); /* fd 1 may be closed... */ 884f662a744Smycroft if (pv[1] != 1) { 885dd8a75d5Skamil ksh_dup2(pv[1], 1, false); 886e1b2664cSjtc close(pv[1]); 887f662a744Smycroft } 888e1b2664cSjtc execute(t, XFORK|XXCOM|XPIPEO); 889e1b2664cSjtc restfd(1, ofd1); 890e1b2664cSjtc startlast(); 891e1b2664cSjtc xp->split = 1; /* waitlast() */ 892e1b2664cSjtc } 893e1b2664cSjtc 894e1b2664cSjtc xp->u.shf = shf; 895e1b2664cSjtc return XCOM; 896e1b2664cSjtc } 897e1b2664cSjtc 898e1b2664cSjtc /* 899e1b2664cSjtc * perform #pattern and %pattern substitution in ${} 900e1b2664cSjtc */ 901e1b2664cSjtc 902e1b2664cSjtc static char * 903e1b2664cSjtc trimsub(str, pat, how) 9040f2b5450Skamil char *str; 905e1b2664cSjtc char *pat; 906e1b2664cSjtc int how; 907e1b2664cSjtc { 9080f2b5450Skamil char *end = strchr(str, 0); 9090f2b5450Skamil char *p, c; 910e1b2664cSjtc 911e1b2664cSjtc switch (how&0xff) { /* UCHAR_MAX maybe? */ 912f662a744Smycroft case '#': /* shortest at beginning */ 913e1b2664cSjtc for (p = str; p <= end; p++) { 914e1b2664cSjtc c = *p; *p = '\0'; 915dd8a75d5Skamil if (gmatch(str, pat, false)) { 916e1b2664cSjtc *p = c; 917e1b2664cSjtc return p; 918e1b2664cSjtc } 919e1b2664cSjtc *p = c; 920e1b2664cSjtc } 921e1b2664cSjtc break; 922f662a744Smycroft case '#'|0x80: /* longest match at beginning */ 923e1b2664cSjtc for (p = end; p >= str; p--) { 924e1b2664cSjtc c = *p; *p = '\0'; 925dd8a75d5Skamil if (gmatch(str, pat, false)) { 926e1b2664cSjtc *p = c; 927e1b2664cSjtc return p; 928e1b2664cSjtc } 929e1b2664cSjtc *p = c; 930e1b2664cSjtc } 931e1b2664cSjtc break; 932e1b2664cSjtc case '%': /* shortest match at end */ 933e1b2664cSjtc for (p = end; p >= str; p--) { 934dd8a75d5Skamil if (gmatch(p, pat, false)) 935e1b2664cSjtc return str_nsave(str, p - str, ATEMP); 936e1b2664cSjtc } 937e1b2664cSjtc break; 938e1b2664cSjtc case '%'|0x80: /* longest match at end */ 939e1b2664cSjtc for (p = str; p <= end; p++) { 940dd8a75d5Skamil if (gmatch(p, pat, false)) 941e1b2664cSjtc return str_nsave(str, p - str, ATEMP); 942e1b2664cSjtc } 943e1b2664cSjtc break; 944e1b2664cSjtc } 945e1b2664cSjtc 946e1b2664cSjtc return str; /* no match, return string */ 947e1b2664cSjtc } 948e1b2664cSjtc 949e1b2664cSjtc /* 950b9cf72acSkamil * ksh_glob 951e1b2664cSjtc * Name derived from V6's /etc/glob, the program that expanded filenames. 952e1b2664cSjtc */ 953e1b2664cSjtc 954e1b2664cSjtc /* XXX cp not const 'cause slashes are temporarily replaced with nulls... */ 955e1b2664cSjtc static void 956b9cf72acSkamil ksh_glob(cp, wp, markdirs) 957e1b2664cSjtc char *cp; 9580f2b5450Skamil XPtrV *wp; 959e1b2664cSjtc int markdirs; 960e1b2664cSjtc { 961e1b2664cSjtc int oldsize = XPsize(*wp); 962e1b2664cSjtc 963e1b2664cSjtc if (glob_str(cp, wp, markdirs) == 0) 964f662a744Smycroft XPput(*wp, debunk(cp, cp, strlen(cp) + 1)); 965e1b2664cSjtc else 966e1b2664cSjtc qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize), 967e1b2664cSjtc xstrcmp); 968e1b2664cSjtc } 969e1b2664cSjtc 970e1b2664cSjtc #define GF_NONE 0 971f662a744Smycroft #define GF_EXCHECK BIT(0) /* do existence check on file */ 972e1b2664cSjtc #define GF_GLOBBED BIT(1) /* some globbing has been done */ 973e1b2664cSjtc #define GF_MARKDIR BIT(2) /* add trailing / to directories */ 974e1b2664cSjtc 975e1b2664cSjtc /* Apply file globbing to cp and store the matching files in wp. Returns 976e1b2664cSjtc * the number of matches found. 977e1b2664cSjtc */ 978e1b2664cSjtc int 979e1b2664cSjtc glob_str(cp, wp, markdirs) 980e1b2664cSjtc char *cp; 981e1b2664cSjtc XPtrV *wp; 982e1b2664cSjtc int markdirs; 983e1b2664cSjtc { 984e1b2664cSjtc int oldsize = XPsize(*wp); 985e1b2664cSjtc XString xs; 986e1b2664cSjtc char *xp; 987e1b2664cSjtc 988e1b2664cSjtc Xinit(xs, xp, 256, ATEMP); 989e1b2664cSjtc globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE); 990e1b2664cSjtc Xfree(xs, xp); 991e1b2664cSjtc 992e1b2664cSjtc return XPsize(*wp) - oldsize; 993e1b2664cSjtc } 994e1b2664cSjtc 995e1b2664cSjtc static void 996e1b2664cSjtc globit(xs, xpp, sp, wp, check) 997e1b2664cSjtc XString *xs; /* dest string */ 998e1b2664cSjtc char **xpp; /* ptr to dest end */ 999e1b2664cSjtc char *sp; /* source path */ 10000f2b5450Skamil XPtrV *wp; /* output list */ 1001e1b2664cSjtc int check; /* GF_* flags */ 1002e1b2664cSjtc { 10030f2b5450Skamil char *np; /* next source component */ 1004e1b2664cSjtc char *xp = *xpp; 1005e1b2664cSjtc char *se; 1006e1b2664cSjtc char odirsep; 1007e1b2664cSjtc 1008e1b2664cSjtc /* This to allow long expansions to be interrupted */ 1009e1b2664cSjtc intrcheck(); 1010e1b2664cSjtc 1011e1b2664cSjtc if (sp == NULL) { /* end of source path */ 1012e1b2664cSjtc /* We only need to check if the file exists if a pattern 1013e1b2664cSjtc * is followed by a non-pattern (eg, foo*x/bar; no check 1014e1b2664cSjtc * is needed for foo* since the match must exist) or if 1015e1b2664cSjtc * any patterns were expanded and the markdirs option is set. 1016e1b2664cSjtc * Symlinks make things a bit tricky... 1017e1b2664cSjtc */ 1018e1b2664cSjtc if ((check & GF_EXCHECK) 1019e1b2664cSjtc || ((check & GF_MARKDIR) && (check & GF_GLOBBED))) 1020e1b2664cSjtc { 1021e1b2664cSjtc #define stat_check() (stat_done ? stat_done : \ 1022e1b2664cSjtc (stat_done = stat(Xstring(*xs, xp), &statb) < 0 \ 1023e1b2664cSjtc ? -1 : 1)) 1024e1b2664cSjtc struct stat lstatb, statb; 1025e1b2664cSjtc int stat_done = 0; /* -1: failed, 1 ok */ 1026e1b2664cSjtc 1027e1b2664cSjtc if (lstat(Xstring(*xs, xp), &lstatb) < 0) 1028e1b2664cSjtc return; 1029e1b2664cSjtc /* special case for systems which strip trailing 1030e1b2664cSjtc * slashes from regular files (eg, /etc/passwd/). 1031e1b2664cSjtc * SunOS 4.1.3 does this... 1032e1b2664cSjtc */ 1033e1b2664cSjtc if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp) 1034e1b2664cSjtc && ISDIRSEP(xp[-1]) && !S_ISDIR(lstatb.st_mode) 1035e1b2664cSjtc #ifdef S_ISLNK 1036e1b2664cSjtc && (!S_ISLNK(lstatb.st_mode) 1037e1b2664cSjtc || stat_check() < 0 1038e1b2664cSjtc || !S_ISDIR(statb.st_mode)) 1039e1b2664cSjtc #endif /* S_ISLNK */ 1040e1b2664cSjtc ) 1041e1b2664cSjtc return; 1042e1b2664cSjtc /* Possibly tack on a trailing / if there isn't already 1043e1b2664cSjtc * one and if the file is a directory or a symlink to a 1044e1b2664cSjtc * directory 1045e1b2664cSjtc */ 1046e1b2664cSjtc if (((check & GF_MARKDIR) && (check & GF_GLOBBED)) 1047e1b2664cSjtc && xp > Xstring(*xs, xp) && !ISDIRSEP(xp[-1]) 1048e1b2664cSjtc && (S_ISDIR(lstatb.st_mode) 1049e1b2664cSjtc #ifdef S_ISLNK 1050e1b2664cSjtc || (S_ISLNK(lstatb.st_mode) 1051e1b2664cSjtc && stat_check() > 0 1052e1b2664cSjtc && S_ISDIR(statb.st_mode)) 1053e1b2664cSjtc #endif /* S_ISLNK */ 1054e1b2664cSjtc )) 1055e1b2664cSjtc { 1056e1b2664cSjtc *xp++ = DIRSEP; 1057e1b2664cSjtc *xp = '\0'; 1058e1b2664cSjtc } 1059e1b2664cSjtc } 1060e1b2664cSjtc # define KLUDGE_VAL 0 1061e1b2664cSjtc XPput(*wp, str_nsave(Xstring(*xs, xp), Xlength(*xs, xp) 1062e1b2664cSjtc + KLUDGE_VAL, ATEMP)); 1063e1b2664cSjtc return; 1064e1b2664cSjtc } 1065e1b2664cSjtc 1066e1b2664cSjtc if (xp > Xstring(*xs, xp)) 1067e1b2664cSjtc *xp++ = DIRSEP; 1068e1b2664cSjtc while (ISDIRSEP(*sp)) { 1069e1b2664cSjtc Xcheck(*xs, xp); 1070e1b2664cSjtc *xp++ = *sp++; 1071e1b2664cSjtc } 1072e1b2664cSjtc np = ksh_strchr_dirsep(sp); 1073e1b2664cSjtc if (np != NULL) { 1074e1b2664cSjtc se = np; 1075e1b2664cSjtc odirsep = *np; /* don't assume DIRSEP, can be multiple kinds */ 1076e1b2664cSjtc *np++ = '\0'; 1077e1b2664cSjtc } else { 1078e1b2664cSjtc odirsep = '\0'; /* keep gcc quiet */ 1079e1b2664cSjtc se = sp + strlen(sp); 1080e1b2664cSjtc } 1081e1b2664cSjtc 1082e1b2664cSjtc 1083e1b2664cSjtc /* Check if sp needs globbing - done to avoid pattern checks for strings 1084e1b2664cSjtc * containing MAGIC characters, open ['s without the matching close ], 1085e1b2664cSjtc * etc. (otherwise opendir() will be called which may fail because the 1086e1b2664cSjtc * directory isn't readable - if no globbing is needed, only execute 1087e1b2664cSjtc * permission should be required (as per POSIX)). 1088e1b2664cSjtc */ 1089e1b2664cSjtc if (!has_globbing(sp, se)) { 1090e1b2664cSjtc XcheckN(*xs, xp, se - sp + 1); 1091f662a744Smycroft debunk(xp, sp, Xnleft(*xs, xp)); 1092e1b2664cSjtc xp += strlen(xp); 1093e1b2664cSjtc *xpp = xp; 1094e1b2664cSjtc globit(xs, xpp, np, wp, check); 1095e1b2664cSjtc } else { 1096e1b2664cSjtc DIR *dirp; 1097e1b2664cSjtc struct dirent *d; 1098e1b2664cSjtc char *name; 1099e1b2664cSjtc int len; 1100e1b2664cSjtc int prefix_len; 1101e1b2664cSjtc 1102e1b2664cSjtc /* xp = *xpp; copy_non_glob() may have re-alloc'd xs */ 1103e1b2664cSjtc *xp = '\0'; 1104e1b2664cSjtc prefix_len = Xlength(*xs, xp); 1105e1b2664cSjtc dirp = ksh_opendir(prefix_len ? Xstring(*xs, xp) : "."); 1106e1b2664cSjtc if (dirp == NULL) 1107e1b2664cSjtc goto Nodir; 1108e1b2664cSjtc while ((d = readdir(dirp)) != NULL) { 1109e1b2664cSjtc name = d->d_name; 1110e1b2664cSjtc if ((*name == '.' && *sp != '.') 1111dd8a75d5Skamil || !gmatch(name, sp, true)) 1112e1b2664cSjtc continue; 1113e1b2664cSjtc 1114e1b2664cSjtc len = NLENGTH(d) + 1; 1115e1b2664cSjtc XcheckN(*xs, xp, len); 1116e1b2664cSjtc memcpy(xp, name, len); 1117e1b2664cSjtc *xpp = xp + len - 1; 1118e1b2664cSjtc globit(xs, xpp, np, wp, 1119e1b2664cSjtc (check & GF_MARKDIR) | GF_GLOBBED 1120e1b2664cSjtc | (np ? GF_EXCHECK : GF_NONE)); 1121e1b2664cSjtc xp = Xstring(*xs, xp) + prefix_len; 1122e1b2664cSjtc } 1123e1b2664cSjtc closedir(dirp); 1124e1b2664cSjtc Nodir:; 1125e1b2664cSjtc } 1126e1b2664cSjtc 1127e1b2664cSjtc if (np != NULL) 1128e1b2664cSjtc *--np = odirsep; 1129e1b2664cSjtc } 1130e1b2664cSjtc 1131e1b2664cSjtc #if 0 1132e1b2664cSjtc /* Check if p contains something that needs globbing; if it does, 0 is 1133e1b2664cSjtc * returned; if not, p is copied into xs/xp after stripping any MAGICs 1134e1b2664cSjtc */ 1135e1b2664cSjtc static int copy_non_glob ARGS((XString *xs, char **xpp, char *p)); 1136e1b2664cSjtc static int 1137e1b2664cSjtc copy_non_glob(xs, xpp, p) 1138e1b2664cSjtc XString *xs; 1139e1b2664cSjtc char **xpp; 1140e1b2664cSjtc char *p; 1141e1b2664cSjtc { 1142e1b2664cSjtc char *xp; 1143e1b2664cSjtc int len = strlen(p); 1144e1b2664cSjtc 1145e1b2664cSjtc XcheckN(*xs, *xpp, len); 1146e1b2664cSjtc xp = *xpp; 1147e1b2664cSjtc for (; *p; p++) { 1148e1b2664cSjtc if (ISMAGIC(*p)) { 1149e1b2664cSjtc int c = *++p; 1150e1b2664cSjtc 1151e1b2664cSjtc if (c == '*' || c == '?') 1152e1b2664cSjtc return 0; 1153e1b2664cSjtc if (*p == '[') { 1154e1b2664cSjtc char *q = p + 1; 1155e1b2664cSjtc 1156e1b2664cSjtc if (ISMAGIC(*q) && q[1] == NOT) 1157e1b2664cSjtc q += 2; 1158e1b2664cSjtc if (ISMAGIC(*q) && q[1] == ']') 1159e1b2664cSjtc q += 2; 1160e1b2664cSjtc for (; *q; q++) 1161e1b2664cSjtc if (ISMAGIC(*q) && *++q == ']') 1162e1b2664cSjtc return 0; 1163e1b2664cSjtc /* pass a literal [ through */ 1164e1b2664cSjtc } 1165e1b2664cSjtc /* must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, etc. */ 1166e1b2664cSjtc } 1167e1b2664cSjtc *xp++ = *p; 1168e1b2664cSjtc } 1169e1b2664cSjtc *xp = '\0'; 1170e1b2664cSjtc *xpp = xp; 1171e1b2664cSjtc return 1; 1172e1b2664cSjtc } 1173e1b2664cSjtc #endif /* 0 */ 1174e1b2664cSjtc 1175e1b2664cSjtc /* remove MAGIC from string */ 1176e1b2664cSjtc char * 1177f662a744Smycroft debunk(dp, sp, dlen) 1178e1b2664cSjtc char *dp; 1179e1b2664cSjtc const char *sp; 1180f662a744Smycroft size_t dlen; 1181e1b2664cSjtc { 1182*d47295ccSrillig char *d; 1183*d47295ccSrillig const char *s; 1184e1b2664cSjtc 1185e1b2664cSjtc if ((s = strchr(sp, MAGIC))) { 11867ca13b8bSlukem if (s - sp >= (ptrdiff_t)dlen) 1187f662a744Smycroft return dp; 1188e1b2664cSjtc memcpy(dp, sp, s - sp); 11897ca13b8bSlukem for (d = dp + (s - sp); *s && (d - dp < (ptrdiff_t)dlen); s++) 1190e1b2664cSjtc if (!ISMAGIC(*s) || !(*++s & 0x80) 1191e1b2664cSjtc || !strchr("*+?@! ", *s & 0x7f)) 1192e1b2664cSjtc *d++ = *s; 1193e1b2664cSjtc else { 1194e1b2664cSjtc /* extended pattern operators: *+?@! */ 119548ee8d12Shubertf if ((*s & 0x7f) != ' ') 1196e1b2664cSjtc *d++ = *s & 0x7f; 11977ca13b8bSlukem if (d - dp < (ptrdiff_t)dlen) 1198e1b2664cSjtc *d++ = '('; 1199e1b2664cSjtc } 1200e1b2664cSjtc *d = '\0'; 1201e1b2664cSjtc } else if (dp != sp) 1202f662a744Smycroft strlcpy(dp, sp, dlen); 1203e1b2664cSjtc return dp; 1204e1b2664cSjtc } 1205e1b2664cSjtc 1206e1b2664cSjtc /* Check if p is an unquoted name, possibly followed by a / or :. If so 1207e1b2664cSjtc * puts the expanded version in *dcp,dp and returns a pointer in p just 1208e1b2664cSjtc * past the name, otherwise returns 0. 1209e1b2664cSjtc */ 1210e1b2664cSjtc static char * 1211e1b2664cSjtc maybe_expand_tilde(p, dsp, dpp, isassign) 1212e1b2664cSjtc char *p; 1213e1b2664cSjtc XString *dsp; 1214e1b2664cSjtc char **dpp; 1215e1b2664cSjtc int isassign; 1216e1b2664cSjtc { 1217e1b2664cSjtc XString ts; 1218e1b2664cSjtc char *dp = *dpp; 1219e1b2664cSjtc char *tp, *r; 1220e1b2664cSjtc 1221e1b2664cSjtc Xinit(ts, tp, 16, ATEMP); 1222e1b2664cSjtc /* : only for DOASNTILDE form */ 1223e1b2664cSjtc while (p[0] == CHAR && !ISDIRSEP(p[1]) 1224e1b2664cSjtc && (!isassign || p[1] != PATHSEP)) 1225e1b2664cSjtc { 1226e1b2664cSjtc Xcheck(ts, tp); 1227e1b2664cSjtc *tp++ = p[1]; 1228e1b2664cSjtc p += 2; 1229e1b2664cSjtc } 1230e1b2664cSjtc *tp = '\0'; 1231e1b2664cSjtc r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ? tilde(Xstring(ts, tp)) : (char *) 0; 1232e1b2664cSjtc Xfree(ts, tp); 1233e1b2664cSjtc if (r) { 1234e1b2664cSjtc while (*r) { 1235e1b2664cSjtc Xcheck(*dsp, dp); 1236e1b2664cSjtc if (ISMAGIC(*r)) 1237e1b2664cSjtc *dp++ = MAGIC; 1238e1b2664cSjtc *dp++ = *r++; 1239e1b2664cSjtc } 1240e1b2664cSjtc *dpp = dp; 1241e1b2664cSjtc r = p; 1242e1b2664cSjtc } 1243e1b2664cSjtc return r; 1244e1b2664cSjtc } 1245e1b2664cSjtc 1246e1b2664cSjtc /* 1247e1b2664cSjtc * tilde expansion 1248e1b2664cSjtc * 1249e1b2664cSjtc * based on a version by Arnold Robbins 1250e1b2664cSjtc */ 1251e1b2664cSjtc 1252e1b2664cSjtc static char * 1253e1b2664cSjtc tilde(cp) 1254e1b2664cSjtc char *cp; 1255e1b2664cSjtc { 1256e1b2664cSjtc char *dp; 1257e1b2664cSjtc 1258e1b2664cSjtc if (cp[0] == '\0') 1259e1b2664cSjtc dp = str_val(global("HOME")); 1260e1b2664cSjtc else if (cp[0] == '+' && cp[1] == '\0') 1261e1b2664cSjtc dp = str_val(global("PWD")); 1262e1b2664cSjtc else if (cp[0] == '-' && cp[1] == '\0') 1263e1b2664cSjtc dp = str_val(global("OLDPWD")); 1264e1b2664cSjtc else 1265e1b2664cSjtc dp = homedir(cp); 1266e1b2664cSjtc /* If HOME, PWD or OLDPWD are not set, don't expand ~ */ 1267e1b2664cSjtc if (dp == null) 1268e1b2664cSjtc dp = (char *) 0; 1269e1b2664cSjtc return dp; 1270e1b2664cSjtc } 1271e1b2664cSjtc 1272e1b2664cSjtc /* 1273e1b2664cSjtc * map userid to user's home directory. 1274e1b2664cSjtc * note that 4.3's getpw adds more than 6K to the shell, 1275e1b2664cSjtc * and the YP version probably adds much more. 1276e1b2664cSjtc * we might consider our own version of getpwnam() to keep the size down. 1277e1b2664cSjtc */ 1278e1b2664cSjtc 1279e1b2664cSjtc static char * 1280e1b2664cSjtc homedir(name) 1281e1b2664cSjtc char *name; 1282e1b2664cSjtc { 12830f2b5450Skamil struct tbl *ap; 1284e1b2664cSjtc 1285e1b2664cSjtc ap = tenter(&homedirs, name, hash(name)); 1286e1b2664cSjtc if (!(ap->flag & ISSET)) { 1287e1b2664cSjtc struct passwd *pw; 1288ecc8aad2Scbiere size_t n; 1289e1b2664cSjtc 1290e1b2664cSjtc pw = getpwnam(name); 1291e1b2664cSjtc if (pw == NULL) 1292e1b2664cSjtc return NULL; 1293ecc8aad2Scbiere n = strlen(pw->pw_dir); 1294ecc8aad2Scbiere if (n > 0 && '/' != pw->pw_dir[n - 1]) { 1295ecc8aad2Scbiere ap->val.s = str_nsave(pw->pw_dir, n + 1, APERM); 1296ecc8aad2Scbiere ap->val.s[n] = '/'; 1297ecc8aad2Scbiere ap->val.s[n + 1] = '\0'; 1298ecc8aad2Scbiere } else { 1299e1b2664cSjtc ap->val.s = str_save(pw->pw_dir, APERM); 1300ecc8aad2Scbiere } 1301e1b2664cSjtc ap->flag |= DEFINED|ISSET|ALLOC; 1302e1b2664cSjtc } 1303e1b2664cSjtc return ap->val.s; 1304e1b2664cSjtc } 1305e1b2664cSjtc 1306e1b2664cSjtc #ifdef BRACE_EXPAND 1307e1b2664cSjtc static void 1308e1b2664cSjtc alt_expand(wp, start, exp_start, end, fdo) 1309e1b2664cSjtc XPtrV *wp; 1310e1b2664cSjtc char *start, *exp_start; 1311e1b2664cSjtc char *end; 1312e1b2664cSjtc int fdo; 1313e1b2664cSjtc { 1314e1b2664cSjtc int UNINITIALIZED(count); 1315e1b2664cSjtc char *brace_start, *brace_end, *UNINITIALIZED(comma); 1316e1b2664cSjtc char *field_start; 1317e1b2664cSjtc char *p; 1318e1b2664cSjtc 1319e1b2664cSjtc /* search for open brace */ 1320e1b2664cSjtc for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != OBRACE; p += 2) 1321e1b2664cSjtc ; 1322e1b2664cSjtc brace_start = p; 1323e1b2664cSjtc 1324e1b2664cSjtc /* find matching close brace, if any */ 1325e1b2664cSjtc if (p) { 1326e1b2664cSjtc comma = (char *) 0; 1327e1b2664cSjtc count = 1; 1328e1b2664cSjtc for (p += 2; *p && count; p++) { 1329e1b2664cSjtc if (ISMAGIC(*p)) { 1330e1b2664cSjtc if (*++p == OBRACE) 1331e1b2664cSjtc count++; 1332e1b2664cSjtc else if (*p == CBRACE) 1333e1b2664cSjtc --count; 1334e1b2664cSjtc else if (*p == ',' && count == 1) 1335e1b2664cSjtc comma = p; 1336e1b2664cSjtc } 1337e1b2664cSjtc } 1338e1b2664cSjtc } 1339e1b2664cSjtc /* no valid expansions... */ 1340e1b2664cSjtc if (!p || count != 0) { 1341e1b2664cSjtc /* Note that given a{{b,c} we do not expand anything (this is 1342e1b2664cSjtc * what at&t ksh does. This may be changed to do the {b,c} 1343e1b2664cSjtc * expansion. } 1344e1b2664cSjtc */ 1345e1b2664cSjtc if (fdo & DOGLOB) 1346b9cf72acSkamil ksh_glob(start, wp, fdo & DOMARKDIRS); 1347e1b2664cSjtc else 1348f662a744Smycroft XPput(*wp, debunk(start, start, end - start)); 1349e1b2664cSjtc return; 1350e1b2664cSjtc } 1351e1b2664cSjtc brace_end = p; 1352e1b2664cSjtc if (!comma) { 1353e1b2664cSjtc alt_expand(wp, start, brace_end, end, fdo); 1354e1b2664cSjtc return; 1355e1b2664cSjtc } 1356e1b2664cSjtc 1357e1b2664cSjtc /* expand expression */ 1358e1b2664cSjtc field_start = brace_start + 2; 1359e1b2664cSjtc count = 1; 1360e1b2664cSjtc for (p = brace_start + 2; p != brace_end; p++) { 1361e1b2664cSjtc if (ISMAGIC(*p)) { 1362e1b2664cSjtc if (*++p == OBRACE) 1363e1b2664cSjtc count++; 1364e1b2664cSjtc else if ((*p == CBRACE && --count == 0) 1365e1b2664cSjtc || (*p == ',' && count == 1)) 1366e1b2664cSjtc { 1367e1b2664cSjtc char *new; 1368e1b2664cSjtc int l1, l2, l3; 1369e1b2664cSjtc 1370e1b2664cSjtc l1 = brace_start - start; 1371e1b2664cSjtc l2 = (p - 1) - field_start; 1372e1b2664cSjtc l3 = end - brace_end; 1373e1b2664cSjtc new = (char *) alloc(l1 + l2 + l3 + 1, ATEMP); 1374e1b2664cSjtc memcpy(new, start, l1); 1375e1b2664cSjtc memcpy(new + l1, field_start, l2); 1376e1b2664cSjtc memcpy(new + l1 + l2, brace_end, l3); 1377e1b2664cSjtc new[l1 + l2 + l3] = '\0'; 1378e1b2664cSjtc alt_expand(wp, new, new + l1, 1379e1b2664cSjtc new + l1 + l2 + l3, fdo); 1380e1b2664cSjtc field_start = p + 1; 1381e1b2664cSjtc } 1382e1b2664cSjtc } 1383e1b2664cSjtc } 1384e1b2664cSjtc return; 1385e1b2664cSjtc } 1386e1b2664cSjtc #endif /* BRACE_EXPAND */ 1387