/* * subst.c - various substitutions * * This file is part of zsh, the Z shell. * * Copyright (c) 1992-1997 Paul Falstad * All rights reserved. * * Permission is hereby granted, without written agreement and without * license or royalty fees, to use, copy, modify, and distribute this * software and to distribute modified versions of this software for any * purpose, provided that the above copyright notice and the following * two paragraphs appear in all copies of this software. * * In no event shall Paul Falstad or the Zsh Development Group be liable * to any party for direct, indirect, special, incidental, or consequential * damages arising out of the use of this software and its documentation, * even if Paul Falstad and the Zsh Development Group have been advised of * the possibility of such damage. * * Paul Falstad and the Zsh Development Group specifically disclaim any * warranties, including, but not limited to, the implied warranties of * merchantability and fitness for a particular purpose. The software * provided hereunder is on an "as is" basis, and Paul Falstad and the * Zsh Development Group have no obligation to provide maintenance, * support, updates, enhancements, or modifications. * */ #include "zsh.mdh" #include "subst.pro" /**/ char nulstring[] = {Nularg, '\0'}; /* Do substitutions before fork. These are: * - Process substitution: <(...), >(...), =(...) * - Parameter substitution * - Command substitution * Followed by * - Quote removal * - Brace expansion * - Tilde and equals substitution * * Bits 0 and 1 of flags are used in filesub. * bit 0 is set when we are doing MAGIC_EQUALSUBST or normal * assignment but not a typeset. * bit 1 is set on a real assignment (both typeset and normal). * bit 2 is a flag to paramsubst (single word sub) */ /**/ void prefork(LinkList list, int flags) { LinkNode node; MUSTUSEHEAP("prefork"); for (node = firstnode(list); node; incnode(node)) { char *str, *str3; str = str3 = (char *)getdata(node); if ((*str == Inang || *str == Outang || *str == Equals) && str[1] == Inpar) { if (*str == Inang || *str == Outang) setdata(node, (void *) getproc(str)); /* <(...) or >(...) */ else setdata(node, (void *) getoutputfile(str)); /* =(...) */ if (!getdata(node)) return; } else { if (isset(SHFILEEXPANSION)) filesub((char **)getaddrdata(node), flags & 3); if (!(node = stringsubst(list, node, flags & 4))) return; } } for (node = firstnode(list); node; incnode(node)) { if (*(char *)getdata(node)) { remnulargs(getdata(node)); if (unset(IGNOREBRACES) && !(flags & 4)) while (hasbraces(getdata(node))) xpandbraces(list, &node); if (unset(SHFILEEXPANSION)) filesub((char **)getaddrdata(node), flags & 3); } else if (!(flags & 4)) uremnode(list, node); if (errflag) return; } } /**/ static LinkNode stringsubst(LinkList list, LinkNode node, int ssub) { int qt; char *str3 = (char *)getdata(node); char *str = str3; while (!errflag && *str) { if ((qt = *str == Qstring) || *str == String) if (str[1] == Inpar) { str++; goto comsub; } else if (str[1] == Inbrack) { /* $[...] */ char *str2 = str; str2++; if (skipparens(Inbrack, Outbrack, &str2)) { zerr("closing bracket missing", NULL, 0); return NULL; } str2[-1] = *str = '\0'; str = arithsubst(str + 2, &str3, str2); setdata(node, (void *) str3); continue; } else if (str[1] == Snull) { str = getkeystring(str, NULL, 4, NULL); continue; } else { node = paramsubst(list, node, &str, qt, ssub); if (errflag || !node) return NULL; str3 = (char *)getdata(node); continue; } else if ((qt = *str == Qtick) || *str == Tick) comsub: { LinkList pl; char *s, *str2 = str; char endchar; int l1, l2; if (*str == Inpar) { endchar = Outpar; str[-1] = '\0'; if (skipparens(Inpar, Outpar, &str)) DPUTS(1, "BUG: parse error in command substitution"); str--; } else { endchar = *str; *str = '\0'; while (*++str != endchar) DPUTS(!*str, "BUG: parse error in command substitution"); } *str++ = '\0'; if (endchar == Outpar && str2[1] == '(' && str[-2] == ')') { /* Math substitution of the form $((...)) */ str = arithsubst(str2 + 1, &str3, str); setdata(node, (void *) str3); continue; } /* It is a command substitution, which will be parsed again * * by the lexer, so we untokenize it first, but we cannot use * * untokenize() since in the case of `...` some Bnulls should * * be left unchanged. Note that the lexer doesn't tokenize * * the body of a command substitution so if there are some * * tokens here they are from a ${(e)~...} substitution. */ for (str = str2; *++str; ) if (itok(*str) && *str != Nularg && !(endchar != Outpar && *str == Bnull && (str[1] == '$' || str[1] == '\\' || str[1] == '`' || (qt && str[1] == '"')))) *str = ztokens[*str - Pound]; str++; if (!(pl = getoutput(str2 + 1, qt || ssub))) { zerr("parse error in command substitution", NULL, 0); return NULL; } if (endchar == Outpar) str2--; if (!(s = (char *) ugetnode(pl))) { str = strcpy(str2, str); continue; } if (!qt && ssub && isset(GLOBSUBST)) tokenize(s); l1 = str2 - str3; l2 = strlen(s); if (nonempty(pl)) { LinkNode n = lastnode(pl); str2 = (char *) ncalloc(l1 + l2 + 1); strcpy(str2, str3); strcpy(str2 + l1, s); setdata(node, str2); insertlinklist(pl, node, list); s = (char *) getdata(node = n); l1 = 0; l2 = strlen(s); } str2 = (char *) ncalloc(l1 + l2 + strlen(str) + 1); if (l1) strcpy(str2, str3); strcpy(str2 + l1, s); str = strcpy(str2 + l1 + l2, str); str3 = str2; setdata(node, str3); continue; } str++; } return errflag ? NULL : node; } /**/ void globlist(LinkList list) { LinkNode node, next; badcshglob = 0; for (node = firstnode(list); !errflag && node; node = next) { next = nextnode(node); glob(list, node); } if (badcshglob == 1) zerr("no match", NULL, 0); } /* perform substitution on a single word */ /**/ void singsub(char **s) { LinkList foo; foo = newlinklist(); addlinknode(foo, *s); prefork(foo, 4); if (errflag) return; *s = (char *) ugetnode(foo); DPUTS(nonempty(foo), "BUG: singsub() produced more than one word!"); } /* Perform substitution on a single word. Unlike with singsub, the * * result can have more than one words. A single word result is sroted * * in *s and *isarr is set to zero; otherwise *isarr is set to 1 and * * the result is stored in *a. If `a' is zero a multiple word result is * * joined using sep or the IFS parameter if sep is zero and the result * * is returned in *s. The return value is true iff the expansion * * resulted in an empty list */ /**/ static int multsub(char **s, char ***a, int *isarr, char *sep) { LinkList foo; int l; char **r, **p; foo = newlinklist(); addlinknode(foo, *s); prefork(foo, 0); if (errflag) { if (isarr) *isarr = 0; return 0; } if ((l = countlinknodes(foo)) > 1) { p = r = ncalloc((l + 1) * sizeof(char*)); while (nonempty(foo)) *p++ = (char *)ugetnode(foo); *p = NULL; if (a) { *a = r; *isarr = 1; return 0; } *s = sepjoin(r, NULL); return 0; } if (l) *s = (char *) ugetnode(foo); else *s = dupstring(""); if (isarr) *isarr = 0; return !l; } /* ~, = subs: assign = 2 => typeset; assign = 1 => something that looks like an assignment but may not be; assign = 3 => normal assignment */ /**/ void filesub(char **namptr, int assign) { char *sub = NULL, *str, *ptr; int len; filesubstr(namptr, assign); if (!assign) return; if (assign < 3) if ((*namptr)[1] && (sub = strchr(*namptr + 1, Equals))) { if (assign == 1) for (ptr = *namptr; ptr != sub; ptr++) if (!iident(*ptr) && !INULL(*ptr)) return; str = sub + 1; if ((sub[1] == Tilde || sub[1] == Equals) && filesubstr(&str, assign)) { sub[1] = '\0'; *namptr = dyncat(*namptr, str); } } else return; ptr = *namptr; while ((sub = strchr(ptr, ':'))) { str = sub + 1; len = sub - *namptr; if ((sub[1] == Tilde || sub[1] == Equals) && filesubstr(&str, assign)) { sub[1] = '\0'; *namptr = dyncat(*namptr, str); } ptr = *namptr + len + 1; } } /**/ int filesubstr(char **namptr, int assign) { #define isend(c) ( !(c) || (c)=='/' || (c)==Inpar || (assign && (c)==':') ) #define isend2(c) ( !(c) || (c)==Inpar || (assign && (c)==':') ) char *str = *namptr; if (*str == Tilde && str[1] != '=' && str[1] != Equals) { char *ptr; int val; val = zstrtol(str + 1, &ptr, 10); if (isend(str[1])) { /* ~ */ *namptr = dyncat(home, str + 1); return 1; } else if (str[1] == '+' && isend(str[2])) { /* ~+ */ *namptr = dyncat(pwd, str + 2); return 1; } else if (str[1] == '-' && isend(str[2])) { /* ~- */ char *tmp; *namptr = dyncat((tmp = oldpwd) ? tmp : pwd, str + 2); return 1; } else if (!inblank(str[1]) && isend(*ptr) && (!idigit(str[1]) || (ptr - str < 4))) { char *ds; if (val < 0) val = -val; ds = dstackent(str[1], val); if (!ds) return 0; *namptr = dyncat(ds, ptr); return 1; } else if (iuser(str[1])) { /* ~foo */ char *ptr, *hom, save; for (ptr = ++str; *ptr && iuser(*ptr); ptr++); save = *ptr; if (!isend(save)) return 0; *ptr = 0; if (!(hom = getnameddir(str))) { if (isset(NOMATCH)) zerr("no such user or named directory: %s", str, 0); *ptr = save; return 0; } *ptr = save; *namptr = dyncat(hom, ptr); return 1; } } else if (*str == Equals && isset(EQUALS) && str[1]) { /* =foo */ char sav, *pp, *cnam; for (pp = str + 1; !isend2(*pp); pp++); sav = *pp; *pp = 0; if (!(cnam = findcmd(str + 1))) { Alias a = (Alias) aliastab->getnode(aliastab, str + 1); if (a) cnam = ztrdup(a->text); else { if (isset(NOMATCH)) zerr("%s not found", str + 1, 0); return 0; } } *namptr = dupstring(cnam); zsfree(cnam); if (sav) { *pp = sav; *namptr = dyncat(*namptr, pp); } return 1; } return 0; #undef isend #undef isend2 } /**/ static char * strcatsub(char **d, char *pb, char *pe, char *src, int l, char *s, int glbsub) { int pl = pe - pb; char *dest = ncalloc(pl + l + (s ? strlen(s) : 0) + 1); *d = dest; strncpy(dest, pb, pl); dest += pl; strcpy(dest, src); if (glbsub) tokenize(dest); dest += l; if (s) strcpy(dest, s); return dest; } typedef int (*CompareFn) _((const void *, const void *)); /**/ int strpcmp(const void *a, const void *b) { #ifdef HAVE_STRCOLL return strcoll(*(char **)a, *(char **)b); #else return strcmp(*(char **)a, *(char **)b); #endif } /**/ int invstrpcmp(const void *a, const void *b) { #ifdef HAVE_STRCOLL return -strcoll(*(char **)a, *(char **)b); #else return -strcmp(*(char **)a, *(char **)b); #endif } /**/ int cstrpcmp(const void *a, const void *b) { #ifdef HAVE_STRCOLL VARARR(char, c, strlen(*(char **) a) + 1); VARARR(char, d, strlen(*(char **) b) + 1); char *s, *t; int cmp; for (s = *(char **) a, t = c; (*t++ = tulower(*s++));); for (s = *(char **) b, t = d; (*t++ = tulower(*s++));); cmp = strcoll(c, d); return cmp; #else char *c = *(char **)a, *d = *(char **)b; for (; *c && tulower(*c) == tulower(*d); c++, d++); return (int)STOUC(tulower(*c)) - (int)STOUC(tulower(*d)); #endif } /**/ int invcstrpcmp(const void *a, const void *b) { #ifdef HAVE_STRCOLL VARARR(char, c, strlen(*(char **) a) + 1); VARARR(char, d, strlen(*(char **) b) + 1); char *s, *t; int cmp; for (s = *(char **) a, t = c; (*t++ = tulower(*s++));); for (s = *(char **) b, t = d; (*t++ = tulower(*s++));); cmp = strcoll(c, d); return -cmp; #else char *c = *(char **)a, *d = *(char **)b; for (; *c && tulower(*c) == tulower(*d); c++, d++); return (int)STOUC(tulower(*d)) - (int)STOUC(tulower(*c)); #endif } /**/ static char * dopadding(char *str, int prenum, int postnum, char *preone, char *postone, char *premul, char *postmul) { char def[3], *ret, *t, *r; int ls, ls2, lpreone, lpostone, lpremul, lpostmul, lr, f, m, c, cc; def[0] = *ifs ? *ifs : ' '; def[1] = *ifs == Meta ? ifs[1] ^ 32 : '\0'; def[2] = '\0'; if (preone && !*preone) preone = def; if (postone && !*postone) postone = def; if (!premul || !*premul) premul = def; if (!postmul || !*postmul) postmul = def; ls = strlen(str); lpreone = preone ? strlen(preone) : 0; lpostone = postone ? strlen(postone) : 0; lpremul = strlen(premul); lpostmul = strlen(postmul); lr = prenum + postnum; if (lr == ls) return str; r = ret = (char *)halloc(lr + 1); if (prenum) { if (postnum) { ls2 = ls / 2; f = prenum - ls2; if (f <= 0) for (str -= f, c = prenum; c--; *r++ = *str++); else { if (f <= lpreone) for (c = f, t = preone + lpreone - f; c--; *r++ = *t++); else { f -= lpreone; if ((m = f % lpremul)) for (c = m, t = premul + lpremul - m; c--; *r++ = *t++); for (cc = f / lpremul; cc--;) for (c = lpremul, t = premul; c--; *r++ = *t++); for (c = lpreone; c--; *r++ = *preone++); } for (c = ls2; c--; *r++ = *str++); } ls2 = ls - ls2; f = postnum - ls2; if (f <= 0) for (c = postnum; c--; *r++ = *str++); else { for (c = ls2; c--; *r++ = *str++); if (f <= lpostone) for (c = f; c--; *r++ = *postone++); else { f -= lpostone; for (c = lpostone; c--; *r++ = *postone++); for (cc = f / lpostmul; cc--;) for (c = lpostmul, t = postmul; c--; *r++ = *t++); if ((m = f % lpostmul)) for (; m--; *r++ = *postmul++); } } } else { f = prenum - ls; if (f <= 0) for (c = prenum, str -= f; c--; *r++ = *str++); else { if (f <= lpreone) for (c = f, t = preone + lpreone - f; c--; *r++ = *t++); else { f -= lpreone; if ((m = f % lpremul)) for (c = m, t = premul + lpremul - m; c--; *r++ = *t++); for (cc = f / lpremul; cc--;) for (c = lpremul, t = premul; c--; *r++ = *t++); for (c = lpreone; c--; *r++ = *preone++); } for (c = ls; c--; *r++ = *str++); } } } else if (postnum) { f = postnum - ls; if (f <= 0) for (c = postnum; c--; *r++ = *str++); else { for (c = ls; c--; *r++ = *str++); if (f <= lpostone) for (c = f; c--; *r++ = *postone++); else { f -= lpostone; for (c = lpostone; c--; *r++ = *postone++); for (cc = f / lpostmul; cc--;) for (c = lpostmul, t = postmul; c--; *r++ = *t++); if ((m = f % lpostmul)) for (; m--; *r++ = *postmul++); } } } *r = '\0'; return ret; } /**/ char * get_strarg(char *s) { char t = *s++; if (!t) return s - 1; switch (t) { case '(': t = ')'; break; case '[': t = ']'; break; case '{': t = '}'; break; case '<': t = '>'; break; case Inpar: t = Outpar; break; case Inang: t = Outang; break; case Inbrace: t = Outbrace; break; case Inbrack: t = Outbrack; break; } while (*s && *s != t) s++; return s; } /**/ static int get_intarg(char **s) { char *t = get_strarg(*s + 1); char *p, sav; long ret; if (!*t) return -1; sav = *t; *t = '\0'; p = dupstring(*s + 2); *s = t; *t = sav; if (parsestr(p)) return -1; singsub(&p); if (errflag) return -1; ret = matheval(p); if (errflag) return -1; if (ret < 0) ret = -ret; return ret < 0 ? -ret : ret; } /* parameter substitution */ #define isstring(c) ((c) == '$' || (char)(c) == String || (char)(c) == Qstring) #define isbrack(c) ((c) == '[' || (char)(c) == Inbrack) /**/ LinkNode paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) { char *aptr = *str; char *s = aptr, *fstr, *idbeg, *idend, *ostr = (char *) getdata(n); int colf; /* != 0 means we found a colon after the name */ int doub = 0; /* != 0 means we have %%, not %, or ##, not # */ int isarr = 0; int plan9 = isset(RCEXPANDPARAM); int globsubst = isset(GLOBSUBST); int getlen = 0; int whichlen = 0; int chkset = 0; int vunset = 0; int spbreak = isset(SHWORDSPLIT) && !ssub && !qt; char *val = NULL, **aval = NULL; unsigned int fwidth = 0; Value v; int flags = 0; int flnum = 0; int substr = 0; int sortit = 0, casind = 0; int casmod = 0; char *sep = NULL, *spsep = NULL; char *premul = NULL, *postmul = NULL, *preone = NULL, *postone = NULL; long prenum = 0, postnum = 0; int copied = 0; int arrasg = 0; int eval = 0; int nojoin = 0; char inbrace = 0; /* != 0 means ${...}, otherwise $... */ *s++ = '\0'; if (!ialnum(*s) && *s != '#' && *s != Pound && *s != '-' && *s != '!' && *s != '$' && *s != String && *s != Qstring && *s != '?' && *s != Quest && *s != '_' && *s != '*' && *s != Star && *s != '@' && *s != '{' && *s != Inbrace && *s != '=' && *s != Equals && *s != Hat && *s != '^' && *s != '~' && *s != Tilde && *s != '+') { s[-1] = '$'; *str = s; return n; } DPUTS(*s == '{', "BUG: inbrace == '{' in paramsubst()"); if (*s == Inbrace) { inbrace = 1; s++; if (*s == '(' || *s == Inpar) { char *t, sav; int tt = 0; long num; int escapes = 0; int klen; #define UNTOK_AND_ESCAPE(X) {\ untokenize(X = dupstring(s + 1));\ if (escapes) {\ X = getkeystring(X, &klen, 3, NULL);\ X = metafy(X, klen, META_HREALLOC);\ }\ } for (s++; *s != ')' && *s != Outpar; s++, tt = 0) { switch (*s) { case ')': case Outpar: break; case 'A': arrasg = 1; break; case '@': nojoin = 1; break; case 'M': flags |= 8; break; case 'R': flags |= 16; break; case 'B': flags |= 32; break; case 'E': flags |= 64; break; case 'N': flags |= 128; break; case 'S': substr = 1; break; case 'I': flnum = get_intarg(&s); if (flnum < 0) goto flagerr; break; case 'L': casmod = 2; break; case 'U': casmod = 1; break; case 'C': casmod = 3; break; case 'o': sortit = 1; break; case 'O': sortit = 2; break; case 'i': casind = 1; break; case 'e': eval = 1; break; case 'c': whichlen = 1; break; case 'w': whichlen = 2; break; case 'W': whichlen = 3; break; case 'f': spsep = "\n"; break; case 'F': sep = "\n"; break; case 's': tt = 1; /* fall through */ case 'j': t = get_strarg(++s); if (*t) { sav = *t; *t = '\0'; if (tt) UNTOK_AND_ESCAPE(spsep) else UNTOK_AND_ESCAPE(sep) *t = sav; s = t; } else goto flagerr; break; case 'l': tt = 1; /* fall through */ case 'r': sav = s[1]; num = get_intarg(&s); if (num < 0) goto flagerr; if (tt) prenum = num; else postnum = num; if (s[1] != sav) break; t = get_strarg(++s); if (!*t) goto flagerr; sav = *t; *t = '\0'; if (tt) UNTOK_AND_ESCAPE(premul) else UNTOK_AND_ESCAPE(postmul) *t = sav; sav = *s; s = t + 1; if (*s != sav) { s--; break; } t = get_strarg(s); if (!*t) goto flagerr; sav = *t; *t = '\0'; if (tt) UNTOK_AND_ESCAPE(preone) else UNTOK_AND_ESCAPE(postone) *t = sav; s = t; break; case 'p': escapes = 1; break; default: flagerr: zerr("error in flags", NULL, 0); return NULL; } } s++; } } if (sortit) sortit += (casind << 1); if (!premul) premul = " "; if (!postmul) postmul = " "; for (;;) { if (*s == '^' || *s == Hat) { if (*++s == '^' || *s == Hat) { plan9 = 0; s++; } else plan9 = 1; } else if (*s == '=' || *s == Equals) { if (*++s == '=' || *s == Equals) { spbreak = 0; s++; } else spbreak = 1; } else if ((*s == '#' || *s == Pound) && (iident(s[1]) || s[1] == '*' || s[1] == Star || s[1] == '@' || (isstring(s[1]) && (s[2] == Inbrace || s[2] == Inpar)))) getlen = 1 + whichlen, s++; else if (*s == '~' || *s == Tilde) { if (*++s == '~' || *s == Tilde) { globsubst = 0; s++; } else globsubst = 1; } else if (*s == '+') if (iident(s[1])) chkset = 1, s++; else if (!inbrace) { *aptr = '$'; *str = aptr + 1; return n; } else { zerr("bad substitution", NULL, 0); return NULL; } else break; } globsubst = globsubst && !qt; idbeg = s; if (s[-1] && isstring(*s) && (s[1] == Inbrace || s[1] == Inpar)) { int sav; int quoted = *s == Qstring; val = s++; skipparens(*s, *s == Inpar ? Outpar : Outbrace, &s); sav = *s; *s = 0; if (multsub(&val, &aval, &isarr, NULL) && quoted) { isarr = -1; aval = alloc(sizeof(char *)); } if (isarr) isarr = -1; copied = 1; *s = sav; v = (Value) NULL; } else if (!(v = getvalue(&s, (unset(KSHARRAYS) || inbrace) ? 1 : -1))) vunset = 1; while (v || ((inbrace || (unset(KSHARRAYS) && vunset)) && isbrack(*s))) { if (!v) { Param pm; char *os = s; if (!isbrack(*s)) break; if (vunset) { val = dupstring(""); isarr = 0; } pm = createparam(nulstring, isarr ? PM_ARRAY : PM_SCALAR); if (isarr) pm->u.arr = aval; else pm->u.str = val; v = (Value) hcalloc(sizeof *v); v->isarr = isarr; v->pm = pm; v->b = -1; if (getindex(&s, v) || s == os) break; } if ((isarr = v->isarr)) aval = getarrvalue(v); else { if (v->pm->flags & PM_ARRAY) { int tmplen = arrlen(v->pm->gets.afn(v->pm)); if (v->a < 0) v->a += tmplen + v->inv; if (!v->inv && (v->a >= tmplen || v->a < 0)) vunset = 1; } if (!vunset) { val = getstrvalue(v); fwidth = v->pm->ct ? v->pm->ct : strlen(val); switch (v->pm->flags & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) { char *t; unsigned int t0; case PM_LEFT: case PM_LEFT | PM_RIGHT_Z: t = val; if (v->pm->flags & PM_RIGHT_Z) while (*t == '0') t++; else while (iblank(*t)) t++; val = (char *)ncalloc(fwidth + 1); val[fwidth] = '\0'; if ((t0 = strlen(t)) > fwidth) t0 = fwidth; memset(val, ' ', fwidth); strncpy(val, t, t0); break; case PM_RIGHT_B: case PM_RIGHT_Z: case PM_RIGHT_Z | PM_RIGHT_B: if (strlen(val) < fwidth) { t = (char *)ncalloc(fwidth + 1); memset(t, (v->pm->flags & PM_RIGHT_B) ? ' ' : '0', fwidth); if ((t0 = strlen(val)) > fwidth) t0 = fwidth; strcpy(t + (fwidth - t0), val); val = t; } else { t = (char *)ncalloc(fwidth + 1); t[fwidth] = '\0'; strncpy(t, val + strlen(val) - fwidth, fwidth); val = t; } break; } switch (v->pm->flags & (PM_LOWER | PM_UPPER)) { char *t; case PM_LOWER: t = val; for (; *t; t++) *t = tulower(*t); break; case PM_UPPER: t = val; for (; *t; t++) *t = tuupper(*t); break; } } } v = NULL; if (!inbrace) break; } if (isarr) { if (nojoin) isarr = -1; if (qt && !getlen && isarr > 0) { val = sepjoin(aval, sep); isarr = 0; } } idend = s; if ((colf = *s == ':')) s++; /* fstr is to be the text following the substitution. If we have * * braces, we look for it here, else we infer it later on. */ fstr = s; if (inbrace) { int bct; for (bct = 1;; fstr++) { if (!*fstr) break; else if (*fstr == Inbrace) bct++; else if (*fstr == Outbrace && !--bct) break; } if (bct) { noclosebrace: zerr("closing brace expected", NULL, 0); return NULL; } if (*fstr) *fstr++ = '\0'; } /* Check for ${..?..} or ${..=..} or one of those. * * Only works if the name is in braces. */ if (inbrace && (*s == '-' || *s == '+' || *s == ':' || *s == '=' || *s == Equals || *s == '%' || *s == '#' || *s == Pound || *s == '?' || *s == Quest)) { if (!flnum) flnum++; if (*s == '%') flags |= 1; /* Check for ${..%%..} or ${..##..} */ if ((*s == '%' || *s == '#' || *s == Pound) && *s == s[1]) { s++; doub = 1; } s++; flags |= (doub << 1) | (substr << 2) | (colf << 8); if (!(flags & 0xf8)) flags |= 16; if (colf && !vunset) vunset = (isarr) ? !*aval : !*val || (*val == Nularg && !val[1]); switch (s[-1]) { case '+': if (vunset) { val = dupstring(""); copied = 1; isarr = 0; break; } vunset = 1; /* Fall Through! */ case '-': if (vunset) { val = dupstring(s); multsub(&val, &aval, &isarr, NULL); copied = 1; } break; case ':': if (*s != '=' && *s != Equals) goto noclosebrace; vunset = 1; s++; /* Fall through */ case '=': case Equals: if (vunset) { char sav = *idend; int l; *idend = '\0'; val = dupstring(s); isarr = 0; if (spsep || spbreak || !arrasg) multsub(&val, NULL, NULL, sep); else multsub(&val, &aval, &isarr, NULL); if (arrasg) { char *arr[2], **t, **a, **p; if (spsep || spbreak) { aval = sepsplit(val, spsep, 0); isarr = 2; sep = spsep = NULL; spbreak = 0; l = arrlen(aval); if (l && !*(aval[l-1])) l--; if (l && !**aval) l--, t = aval + 1; else t = aval; } else if (!isarr) { arr[0] = val; arr[1] = NULL; t = aval = arr; l = 1; } else l = arrlen(aval), t = aval; p = a = zalloc(sizeof(char *) * (l + 1)); while (l--) { untokenize(*t); *p++ = ztrdup(*t++); } *p++ = NULL; setaparam(idbeg, a); } else { untokenize(val); setsparam(idbeg, ztrdup(val)); } *idend = sav; copied = 1; } break; case '?': case Quest: if (vunset) { char *msg; *idend = '\0'; msg = tricat(idbeg, ": ", *s ? s : "parameter not set"); zerr("%s", msg, 0); zsfree(msg); if (!interact) exit(1); return NULL; } break; case '%': case '#': case Pound: if (qt) if (parse_subst_string(s)) { zerr("parse error in ${...%c...} substitution", NULL, s[-1]); return NULL; } singsub(&s); if (!vunset && isarr) { char **ap = aval; char **pp = aval = (char **)ncalloc(sizeof(char *) * (arrlen(aval) + 1)); while ((*pp = *ap++)) { if (getmatch(pp, s, flags, flnum)) pp++; } copied = 1; } else { if (vunset) val = dupstring(""); getmatch(&val, s, flags, flnum); copied = 1; } break; } } else { /* no ${...=...} or anything, but possible modifiers. */ if (chkset) { val = dupstring(vunset ? "0" : "1"); isarr = 0; } else if (vunset) { if (unset(UNSET)) { *idend = '\0'; zerr("%s: parameter not set", idbeg, 0); return NULL; } val = dupstring(""); } if (colf) { s--; if (unset(KSHARRAYS) || inbrace) { if (!isarr) modify(&val, &s); else { char *ss; char **ap = aval; char **pp = aval = (char **)ncalloc(sizeof(char *) * (arrlen(aval) + 1)); while ((*pp = *ap++)) { ss = s; modify(pp++, &ss); } if (pp == aval) { char *t = ""; ss = s; modify(&t, &ss); } s = ss; } if (inbrace && *s) { if (*s == ':' && !imeta(s[1])) zerr("unrecognized modifier `%c'", NULL, s[1]); else zerr("unrecognized modifier", NULL, 0); return NULL; } } } if (!inbrace) fstr = s; } if (errflag) return NULL; if (getlen) { long len = 0; char buf[14]; if (isarr) { char **ctr; int sl = sep ? ztrlen(sep) : 1; if (getlen == 1) for (ctr = aval; *ctr; ctr++, len++); else if (getlen == 2) { if (*aval) for (len = -sl, ctr = aval; len += sl + ztrlen(*ctr), *++ctr;); } else for (ctr = aval; *ctr; len += wordcount(*ctr, spsep, getlen > 3), ctr++); } else { if (getlen < 3) len = ztrlen(val); else len = wordcount(val, spsep, getlen > 3); } sprintf(buf, "%ld", len); val = dupstring(buf); isarr = 0; } if (isarr > 0 && !plan9 && (!aval || !aval[0])) { val = dupstring(""); isarr = 0; } else if (isarr && aval && aval[0] && !aval[1]) { val = aval[0]; isarr = 0; } /* ssub is true when we are called from singsub (via prefork). * It means that we must join arrays and should not split words. */ if (ssub || spbreak || spsep || sep) { if (isarr) val = sepjoin(aval, sep), isarr = 0; if (!ssub && (spbreak || spsep)) { aval = sepsplit(val, spsep, 0); if (!aval || !aval[0]) val = dupstring(""); else if (!aval[1]) val = aval[0]; else isarr = 2; } } if (casmod) { if (isarr) { char **ap; if (!copied) aval = arrdup(aval), copied = 1; ap = aval; if (casmod == 1) for (; *ap; ap++) makeuppercase(ap); else if (casmod == 2) for (; *ap; ap++) makelowercase(ap); else for (; *ap; ap++) makecapitals(ap); } else { if (!copied) val = dupstring(val), copied = 1; if (casmod == 1) makeuppercase(&val); else if (casmod == 2) makelowercase(&val); else makecapitals(&val); } } if (isarr) { char *x; char *y; int xlen; int i; LinkNode on = n; if (!aval[0] && !plan9) { if (aptr > (char *) getdata(n) && aptr[-1] == Dnull && *fstr == Dnull) *--aptr = '\0', fstr++; y = (char *)ncalloc((aptr - ostr) + strlen(fstr) + 1); strcpy(y, ostr); *str = y + (aptr - ostr); strcpy(*str, fstr); setdata(n, y); return n; } if (sortit) { static CompareFn sortfn[] = { strpcmp, invstrpcmp, cstrpcmp, invcstrpcmp }; if (!copied) aval = arrdup(aval); i = arrlen(aval); if (i && (*aval[i-1] || --i)) qsort(aval, i, sizeof(char *), sortfn[sortit-1]); } if (plan9) { LinkList tl = newlinklist(); LinkNode tn; *--fstr = Marker; addlinknode(tl, fstr); if (!eval && !stringsubst(tl, firstnode(tl), ssub)) return NULL; *str = aptr; tn = firstnode(tl); while ((x = *aval++)) { if (prenum || postnum) x = dopadding(x, prenum, postnum, preone, postone, premul, postmul); if (eval && parsestr(x)) return NULL; xlen = strlen(x); for (tn = firstnode(tl); tn && *(y = (char *) getdata(tn)) == Marker; incnode(tn)) { strcatsub(&y, ostr, aptr, x, xlen, y + 1, globsubst); if (qt && !*y && isarr != 2) y = dupstring(nulstring); if (plan9) setdata(n, (void *) y), plan9 = 0; else insertlinknode(l, n, (void *) y), incnode(n); } } for (; tn; incnode(tn)) { y = (char *) getdata(tn); if (*y == Marker) continue; if (qt && !*y && isarr != 2) y = dupstring(nulstring); if (plan9) setdata(n, (void *) y), plan9 = 0; else insertlinknode(l, n, (void *) y), incnode(n); } if (plan9) { uremnode(l, n); return n; } } else { x = aval[0]; if (prenum || postnum) x = dopadding(x, prenum, postnum, preone, postone, premul, postmul); if (eval && parsestr(x)) return NULL; xlen = strlen(x); strcatsub(&y, ostr, aptr, x, xlen, NULL, globsubst); if (qt && !*y && isarr != 2) y = dupstring(nulstring); setdata(n, (void *) y); i = 1; /* aval[1] is non-null here */ while (aval[i + 1]) { x = aval[i++]; if (prenum || postnum) x = dopadding(x, prenum, postnum, preone, postone, premul, postmul); if (eval && parsestr(x)) return NULL; if (qt && !*x && isarr != 2) y = dupstring(nulstring); else { y = dupstring(x); if (globsubst) tokenize(y); } insertlinknode(l, n, (void *) y), incnode(n); } x = aval[i]; if (prenum || postnum) x = dopadding(x, prenum, postnum, preone, postone, premul, postmul); if (eval && parsestr(x)) return NULL; xlen = strlen(x); *str = strcatsub(&y, aptr, aptr, x, xlen, fstr, globsubst); if (qt && !*y && isarr != 2) y = dupstring(nulstring); insertlinknode(l, n, (void *) y), incnode(n); } if (eval) n = on; } else { int xlen; char *x; char *y; x = val; if (prenum || postnum) x = dopadding(x, prenum, postnum, preone, postone, premul, postmul); if (eval && parsestr(x)) return NULL; xlen = strlen(x); *str = strcatsub(&y, ostr, aptr, x, xlen, fstr, globsubst); if (qt && !*y && isarr != 2) y = dupstring(nulstring); setdata(n, (void *) y); } if (eval) *str = (char *) getdata(n); return n; } /* * Arithmetic substitution: `a' is the string to be evaluated, `bptr' * points to the beginning of the string containing it. The tail of * the string is given by `rest'. *bptr is modified with the substituted * string. The function returns a pointer to the tail in the substituted * string. */ /**/ static char * arithsubst(char *a, char **bptr, char *rest) { char *s = *bptr, *t, buf[DIGBUFSIZE]; char *b = buf; long v; singsub(&a); v = matheval(a); sprintf(buf, "%ld", v); t = *bptr = (char *)ncalloc(strlen(*bptr) + strlen(buf) + strlen(rest) + 1); t--; while ((*++t = *s++)); t--; while ((*++t = *b++)); strcat(t, rest); return t; } /**/ void modify(char **str, char **ptr) { char *ptr1, *ptr2, *ptr3, del, *lptr, c, *test, *sep, *t, *tt, tc, *e; char *copy, *all, *tmp, sav; int gbal, wall, rec, al, nl; test = NULL; if (**ptr == ':') *str = dupstring(*str); while (**ptr == ':') { lptr = *ptr; (*ptr)++; wall = gbal = 0; rec = 1; c = '\0'; sep = NULL; for (; !c && **ptr;) { switch (**ptr) { case 'h': case 'r': case 'e': case 't': case 'l': case 'u': c = **ptr; break; case 's': c = **ptr; (*ptr)++; ptr1 = *ptr; del = *ptr1++; for (ptr2 = ptr1; *ptr2 != del && *ptr2; ptr2++); if (!*ptr2) { zerr("bad substitution", NULL, 0); return; } *ptr2++ = '\0'; for (ptr3 = ptr2; *ptr3 != del && *ptr3; ptr3++); if ((sav = *ptr3)) *ptr3++ = '\0'; if (*ptr1) { zsfree(hsubl); hsubl = ztrdup(ptr1); } if (!hsubl) { zerr("no previous substitution", NULL, 0); return; } zsfree(hsubr); for (tt = hsubl; *tt; tt++) if (INULL(*tt)) chuck(tt--); untokenize(hsubl); for (tt = hsubr = ztrdup(ptr2); *tt; tt++) if (INULL(*tt)) chuck(tt--); ptr2[-1] = del; if (sav) ptr3[-1] = sav; *ptr = ptr3 - 1; break; case '&': c = 's'; break; case 'g': (*ptr)++; gbal = 1; break; case 'w': wall = 1; (*ptr)++; break; case 'W': wall = 1; (*ptr)++; ptr1 = get_strarg(ptr2 = *ptr); if ((sav = *ptr1)) *ptr1 = '\0'; sep = dupstring(ptr2 + 1); if (sav) *ptr1 = sav; *ptr = ptr1 + 1; c = '\0'; break; case 'f': rec = -1; (*ptr)++; break; case 'F': rec = get_intarg(ptr); (*ptr)++; break; default: *ptr = lptr; return; } } (*ptr)++; if (!c) { *ptr = lptr; return; } if (rec < 0) test = dupstring(*str); while (rec--) { if (wall) { al = 0; all = NULL; for (t = e = *str; (tt = findword(&e, sep));) { tc = *e; *e = '\0'; copy = dupstring(tt); *e = tc; switch (c) { case 'h': remtpath(©); break; case 'r': remtext(©); break; case 'e': rembutext(©); break; case 't': remlpaths(©); break; case 'l': downcase(©); break; case 'u': upcase(©); break; case 's': if (hsubl && hsubr) subst(©, hsubl, hsubr, gbal); break; } tc = *tt; *tt = '\0'; nl = al + strlen(t) + strlen(copy); ptr1 = tmp = (char *)halloc(nl + 1); if (all) for (ptr2 = all; *ptr2;) *ptr1++ = *ptr2++; for (ptr2 = t; *ptr2;) *ptr1++ = *ptr2++; *tt = tc; for (ptr2 = copy; *ptr2;) *ptr1++ = *ptr2++; *ptr1 = '\0'; al = nl; all = tmp; t = e; } *str = all; } else { switch (c) { case 'h': remtpath(str); break; case 'r': remtext(str); break; case 'e': rembutext(str); break; case 't': remlpaths(str); break; case 'l': downcase(str); break; case 'u': upcase(str); break; case 's': if (hsubl && hsubr) { char *oldstr = *str; subst(str, hsubl, hsubr, gbal); if (*str != oldstr) { *str = dupstring(oldstr = *str); zsfree(oldstr); } } break; } } if (rec < 0) { if (!strcmp(test, *str)) rec = 0; else test = dupstring(*str); } } } } /* get a directory stack entry */ /**/ static char * dstackent(char ch, int val) { int backwards; LinkNode end=(LinkNode)dirstack, n; backwards = ch == (isset(PUSHDMINUS) ? '+' : '-'); if(!backwards && !val--) return pwd; if (backwards) for (n=lastnode(dirstack); n != end && val; val--, n=prevnode(n)); else for (end=NULL, n=firstnode(dirstack); n && val; val--, n=nextnode(n)); if (n == end) { if (isset(NOMATCH)) zerr("not enough directory stack entries.", NULL, 0); return NULL; } return (char *)getdata(n); }