home *** CD-ROM | disk | FTP | other *** search
- /*
- * $Id: subst.c,v 2.52 1996/10/15 20:16:35 hzoli Exp $
- *
- * subst.c - various substitutions
- *
- * This file is part of zsh, the Z shell.
- *
- * Copyright (c) 1992-1996 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.h"
-
- /* 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;
- }
- }
-
- /**/
- 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 {
- 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, "Oops. parse error in command substitution");
- str--;
- } else {
- endchar = *str;
- *str = '\0';
-
- while (*++str != endchar)
- DPUTS(!*str, "Oops. 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 */
-
- /**/
- 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
- }
-
- /**/
- 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
- # ifdef __GNUC__
- char c[strlen(*(char **) a) + 1];
- char d[strlen(*(char **) b) + 1];
- # else
- char *c = halloc(strlen(*(char **) a) + 1);
- char *d = halloc(strlen(*(char **) b) + 1);
- # endif
- 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
- # ifdef __GNUC__
- char c[strlen(*(char **) a) + 1];
- char d[strlen(*(char **) b) + 1];
- # else
- char *c = halloc(strlen(*(char **) a) + 1);
- char *d = halloc(strlen(*(char **) b) + 1);
- # endif
- 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
- }
-
- /**/
- 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;
- }
-
- /**/
- 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, *u, *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_USEHEAP);\
- }\
- }
-
- 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++;
-
- /* 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)) {
- int bct;
-
- if (!flnum)
- flnum++;
- if (*s == '%')
- flags |= 1;
-
- /* Check for ${..%%..} or ${..##..} */
- if ((*s == '%' || *s == '#' || *s == Pound) && *s == s[1]) {
- s++;
- doub = 1;
- }
- u = s + 1;
-
- flags |= (doub << 1) | (substr << 2) | (colf << 8);
- if (!(flags & 0xf8))
- flags |= 16;
-
- for (bct = 1; bct && *++s;) {
- if (*s == Inbrace)
- bct++;
- else if (*s == Outbrace)
- bct--;
- }
-
- if (bct)
- goto noclosebrace;
-
- if (*s)
- *s++ = '\0';
- if (colf && !vunset)
- vunset = (isarr) ? !*aval : !*val || (*val == Nularg && !val[1]);
-
- switch (u[-1]) {
- case '+':
- if (vunset) {
- val = dupstring("");
- copied = 1;
- isarr = 0;
- break;
- }
- vunset = 1;
- /* Fall Through! */
- case '-':
- if (vunset) {
- val = dupstring(u);
- multsub(&val, &aval, &isarr, NULL);
- copied = 1;
- }
- break;
- case ':':
- if (*u != '=' && *u != Equals)
- goto noclosebrace;
- vunset = 1;
- u++;
- /* Fall through */
- case '=':
- case Equals:
- if (vunset) {
- char sav = *idend;
- int l;
-
- *idend = '\0';
- val = dupstring(u);
- 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, ": ", *u ? u : "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(u)) {
- zerr("parse error in ${...%c...} substitution",
- NULL, u[-1]);
- return NULL;
- }
- singsub(&u);
-
- if (!vunset && isarr) {
- char **ap = aval;
- char **pp = aval = (char **)ncalloc(sizeof(char *) * (arrlen(aval) + 1));
-
- while ((*pp = *ap++)) {
- if (getmatch(pp, u, flags, flnum))
- pp++;
- }
- copied = 1;
- } else {
- if (vunset)
- val = dupstring("");
- getmatch(&val, u, 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 != Outbrace) {
- if (*s == ':' && !imeta(s[1]))
- zerr("unrecognized modifier `%c'", NULL, s[1]);
- else
- zerr("unrecognized modifier", NULL, 0);
- return NULL;
- }
- }
- }
- if (inbrace) {
- if (*s != Outbrace) {
- noclosebrace:
- zerr("closing brace expected", NULL, 0);
- return NULL;
- }
- 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 && *s == Dnull)
- *--aptr = '\0', s++;
- y = (char *)ncalloc((aptr - ostr) + strlen(s) + 1);
- strcpy(y, ostr);
- *str = y + (aptr - ostr);
- strcpy(*str, s);
- 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;
-
- *--s = Marker;
- addlinknode(tl, s);
- 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 NULL;
- }
- } 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, s, 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, s, 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.
- */
-
- /**/
- 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 */
-
- /**/
- 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);
- }
-