#include "zsh.mdh"
#include "subst.pro"
#define LF_ARRAY 1
char nulstring[] = {Nularg, '\0'};
mod_export void
prefork(LinkList list, int flags)
{
LinkNode node, stop = 0;
int keep = 0, asssub = (flags & PF_TYPESET) && isset(KSHTYPESET);
queue_signals();
for (node = firstnode(list); node; incnode(node)) {
char *str, c;
str = (char *)getdata(node);
if (((c = *str) == Inang || c == Outang || c == Equals) &&
str[1] == Inpar) {
if (c == Inang || c == Outang)
setdata(node, (void *) getproc(str));
else
setdata(node, (void *) getoutputfile(str));
if (!getdata(node)) {
setdata(node, dupstring(""));
unqueue_signals();
return;
}
} else {
if (isset(SHFILEEXPANSION)) {
char *cptr = (char *)getdata(node);
filesub(&cptr, flags & (PF_TYPESET|PF_ASSIGN));
setdata(node, cptr);
}
if (!(node = stringsubst(list, node, flags & PF_SINGLE, asssub))) {
unqueue_signals();
return;
}
}
}
for (node = firstnode(list); node; incnode(node)) {
if (node == stop)
keep = 0;
if (*(char *)getdata(node)) {
remnulargs(getdata(node));
if (unset(IGNOREBRACES) && !(flags & PF_SINGLE)) {
if (!keep)
stop = nextnode(node);
while (hasbraces(getdata(node))) {
keep = 1;
xpandbraces(list, &node);
}
}
if (unset(SHFILEEXPANSION)) {
char *cptr = (char *)getdata(node);
filesub(&cptr, flags & (PF_TYPESET|PF_ASSIGN));
setdata(node, cptr);
}
} else if (!(flags & PF_SINGLE) && !keep)
uremnode(list, node);
if (errflag) {
unqueue_signals();
return;
}
}
unqueue_signals();
}
static char *
stringsubstquote(char *strstart, char **pstrdpos)
{
int len;
char *strdpos = *pstrdpos, *strsub, *strret;
strsub = getkeystring(strdpos+2, &len,
GETKEYS_DOLLARS_QUOTE, NULL);
len += 2;
if (strstart != strdpos) {
*strdpos = '\0';
if (strdpos[len])
strret = zhtricat(strstart, strsub, strdpos + len);
else
strret = dyncat(strstart, strsub);
} else if (strdpos[len])
strret = dyncat(strsub, strdpos + len);
else
strret = strsub;
*pstrdpos = strret + (strdpos - strstart) + strlen(strsub);
return strret;
}
static LinkNode
stringsubst(LinkList list, LinkNode node, int ssub, int asssub)
{
int qt;
char *str3 = (char *)getdata(node);
char *str = str3, c;
while (!errflag && (c = *str)) {
if ((qt = c == Qstring) || c == String) {
if ((c = str[1]) == Inpar) {
if (!qt)
list->list.flags |= LF_ARRAY;
str++;
goto comsub;
} else if (c == Inbrack) {
char *str2 = str;
str2++;
if (skipparens(Inbrack, Outbrack, &str2)) {
zerr("closing bracket missing");
return NULL;
}
str2[-1] = *str = '\0';
str = arithsubst(str + 2, &str3, str2);
setdata(node, (void *) str3);
continue;
} else if (c == Snull) {
str3 = stringsubstquote(str3, &str);
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 = c == Qtick) || (c == Tick ? (list->list.flags |= LF_ARRAY) : 0))
comsub: {
LinkList pl;
char *s, *str2 = str;
char endchar;
int l1, l2;
if (c == Inpar) {
endchar = Outpar;
str[-1] = '\0';
#ifdef DEBUG
if (skipparens(Inpar, Outpar, &str))
dputs("BUG: parse error in command substitution");
#else
skipparens(Inpar, Outpar, &str);
#endif
str--;
} else {
endchar = c;
*str = '\0';
while (*++str != endchar)
DPUTS(!*str, "BUG: parse error in command substitution");
}
*str++ = '\0';
if (endchar == Outpar && str2[1] == '(' && str[-2] == ')') {
str[-2] = '\0';
str = arithsubst(str2 + 2, &str3, str);
setdata(node, (void *) str3);
continue;
}
for (str = str2; (c = *++str); )
if (itok(c) && c != Nularg &&
!(endchar != Outpar && c == Bnull &&
(str[1] == '$' || str[1] == '\\' || str[1] == '`' ||
(qt && str[1] == '"'))))
*str = ztokens[c - Pound];
str++;
if (!(pl = getoutput(str2 + 1, qt || ssub))) {
zerr("parse error in command substitution");
return NULL;
}
if (endchar == Outpar)
str2--;
if (!(s = (char *) ugetnode(pl))) {
str = strcpy(str2, str);
continue;
}
if (!qt && ssub && isset(GLOBSUBST))
shtokenize(s);
l1 = str2 - str3;
l2 = strlen(s);
if (nonempty(pl)) {
LinkNode n = lastnode(pl);
str2 = (char *) hcalloc(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 *) hcalloc(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;
} else if (asssub && ((c == '=') || c == Equals) && str != str3) {
ssub = 1;
}
str++;
}
return errflag ? NULL : node;
}
char *
quotesubst(char *str)
{
char *s = str;
while (*s) {
if (*s == String && s[1] == Snull) {
str = stringsubstquote(str, &s);
} else {
s++;
}
}
remnulargs(str);
return str;
}
mod_export void
globlist(LinkList list, int nountok)
{
LinkNode node, next;
badcshglob = 0;
for (node = firstnode(list); !errflag && node; node = next) {
next = nextnode(node);
zglob(list, node, nountok);
}
if (badcshglob == 1)
zerr("no match");
}
mod_export void
singsub(char **s)
{
local_list1(foo);
init_list1(foo, *s);
prefork(&foo, PF_SINGLE);
if (errflag)
return;
*s = (char *) ugetnode(&foo);
DPUTS(nonempty(&foo), "BUG: singsub() produced more than one word!");
}
static int
multsub(char **s, int split, char ***a, int *isarr, char *sep)
{
int l;
char **r, **p, *x = *s;
local_list1(foo);
if (split) {
for ( ; *x; x += l) {
char c = (l = *x == Meta) ? x[1] ^ 32 : *x;
l++;
if (!iwsep(STOUC(c)))
break;
}
}
init_list1(foo, x);
if (split) {
LinkNode n = firstnode(&foo);
int inq = 0, inp = 0;
MB_METACHARINIT();
for ( ; *x; x += l) {
int rawc = -1;
convchar_t c;
if (itok(STOUC(*x))) {
rawc = *x;
l = 1;
} else {
l = MB_METACHARLENCONV(x, &c);
if (!inq && !inp && WC_ZISTYPE(c, ISEP)) {
*x = '\0';
for (x += l; *x; x += l) {
if (itok(STOUC(*x))) {
rawc = *x;
l = 1;
break;
}
l = MB_METACHARLENCONV(x, &c);
if (!WC_ZISTYPE(c, ISEP))
break;
}
if (!*x)
break;
insertlinknode(&foo, n, (void *)x), incnode(n);
}
}
switch (rawc) {
case Dnull:
case Snull:
case Tick:
inq = !inq;
break;
case Inpar:
inp++;
break;
case Outpar:
inp--;
break;
case Bnull:
case Bnullkeep:
x += l;
l = MB_METACHARLEN(x);
break;
}
}
}
prefork(&foo, 0);
if (errflag) {
if (isarr)
*isarr = 0;
return 0;
}
if ((l = countlinknodes(&foo)) > 1 || (foo.list.flags & LF_ARRAY && a)) {
p = r = hcalloc((l + 1) * sizeof(char*));
while (nonempty(&foo))
*p++ = (char *)ugetnode(&foo);
*p = NULL;
if (a && (l > 1 || foo.list.flags & LF_ARRAY)) {
*a = r;
*isarr = SCANPM_MATCHMANY;
return 0;
}
*s = sepjoin(r, sep, 1);
if (isarr)
*isarr = 0;
return 0;
}
if (l)
*s = (char *) ugetnode(&foo);
else
*s = dupstring("");
if (isarr)
*isarr = 0;
return !l;
}
mod_export void
filesub(char **namptr, int assign)
{
char *eql = NULL, *sub = NULL, *str, *ptr;
int len;
filesubstr(namptr, assign);
if (!assign)
return;
if (assign & PF_TYPESET) {
if ((*namptr)[1] && (eql = sub = strchr(*namptr + 1, Equals))) {
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 > eql &&
(sub[1] == Tilde || sub[1] == Equals) &&
filesubstr(&str, assign)) {
sub[1] = '\0';
*namptr = dyncat(*namptr, str);
}
ptr = *namptr + len + 1;
}
}
mod_export 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) {
Shfunc dirfunc;
char *ptr, *tmp, *res, *ptr2;
int val;
val = zstrtol(str + 1, &ptr, 10);
if (isend(str[1])) {
*namptr = dyncat(home ? 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])) {
*namptr = dyncat((tmp = oldpwd) ? tmp : pwd, str + 2);
return 1;
} else if (str[1] == Inbrack &&
(dirfunc = getshfunc("zsh_directory_name")) &&
(ptr2 = strchr(str+2, Outbrack))) {
char **arr;
untokenize(tmp = dupstrpfx(str+2, ptr2 - (str+2)));
remnulargs(tmp);
arr = subst_string_by_func(dirfunc, "n", tmp);
res = arr ? *arr : NULL;
if (res) {
*namptr = dyncat(res, ptr2+1);
return 1;
}
if (isset(NOMATCH))
zerr("no directory expansion: ~[%s]", tmp);
return 0;
} 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 ((ptr = itype_end(str+1, IUSER, 0)) != str+1) {
char *hom, save;
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);
*ptr = save;
return 0;
}
*ptr = save;
*namptr = dyncat(hom, ptr);
return 1;
}
} else if (*str == Equals && isset(EQUALS) && str[1]) {
char *pp, *cnam, *cmdstr, *str1 = str+1;
for (pp = str1; !isend2(*pp); pp++)
;
cmdstr = dupstrpfx(str1, pp-str1);
untokenize(cmdstr);
remnulargs(cmdstr);
if (!(cnam = findcmd(cmdstr, 1))) {
if (isset(NOMATCH))
zerr("%s not found", cmdstr);
return 0;
}
*namptr = dupstring(cnam);
if (*pp)
*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 copied)
{
char *dest;
int pl = pe - pb;
if (!pl && (!s || !*s)) {
*d = dest = (copied ? src : dupstring(src));
if (glbsub)
shtokenize(dest);
} else {
*d = dest = hcalloc(pl + l + (s ? strlen(s) : 0) + 1);
strncpy(dest, pb, pl);
dest += pl;
strcpy(dest, src);
if (glbsub)
shtokenize(dest);
dest += l;
if (s)
strcpy(dest, s);
}
return dest;
}
static char *
dopadding(char *str, int prenum, int postnum, char *preone, char *postone,
char *premul, char *postmul
#ifdef MULTIBYTE_SUPPORT
, int multi_width
#endif
)
{
#ifdef MULTIBYTE_SUPPORT
#define WCPADWIDTH(cchar) (multi_width ? WCWIDTH(cchar) : 1)
#else
#define WCPADWIDTH(cchar) (1)
#endif
char *def, *ret, *t, *r;
int ls, ls2, lpreone, lpostone, lpremul, lpostmul, lr, f, m, c, cc, cl;
convchar_t cchar;
MB_METACHARINIT();
if (*ifs)
def = dupstrpfx(ifs, MB_METACHARLEN(ifs));
else
def = "";
if (preone && !*preone)
preone = def;
if (postone && !*postone)
postone = def;
if (!premul || !*premul)
premul = def;
if (!postmul || !*postmul)
postmul = def;
ls = MB_METASTRLEN2(str, multi_width);
lpreone = preone ? MB_METASTRLEN2(preone, multi_width) : 0;
lpostone = postone ? MB_METASTRLEN2(postone, multi_width) : 0;
lpremul = MB_METASTRLEN2(premul, multi_width);
lpostmul = MB_METASTRLEN2(postmul, multi_width);
if (prenum + postnum == ls)
return str;
lr = strlen(str) + strlen(premul) * prenum + strlen(postmul) * postnum;
if (preone)
lr += strlen(preone);
if (postone)
lr += strlen(postone);
r = ret = (char *)zhalloc(lr + 1);
if (prenum) {
if (postnum) {
ls2 = ls / 2;
f = prenum - ls2;
if (f <= 0) {
f = -f;
MB_METACHARINIT();
while (f > 0) {
str += MB_METACHARLENCONV(str, &cchar);
f -= WCPADWIDTH(cchar);
}
for (c = prenum; c > 0; ) {
cl = MB_METACHARLENCONV(str, &cchar);
while (cl--)
*r++ = *str++;
c -= WCPADWIDTH(cchar);
}
} else {
if (f <= lpreone) {
if (preone) {
MB_METACHARINIT();
f = lpreone - f;
for (t = preone; f > 0; ) {
t += MB_METACHARLENCONV(t, &cchar);
f -= WCPADWIDTH(cchar);
}
while (*t)
*r++ = *t++;
}
} else {
f -= lpreone;
if (lpremul) {
if ((m = f % lpremul)) {
MB_METACHARINIT();
m = lpremul - m;
for (t = premul; m > 0; ) {
t += MB_METACHARLENCONV(t, &cchar);
m -= WCPADWIDTH(cchar);
}
while (*t)
*r++ = *t++;
}
for (cc = f / lpremul; cc--;) {
MB_METACHARINIT();
for (c = lpremul, t = premul; c > 0; ) {
cl = MB_METACHARLENCONV(t, &cchar);
while (cl--)
*r++ = *t++;
c -= WCPADWIDTH(cchar);
}
}
}
if (preone) {
while (*preone)
*r++ = *preone++;
}
}
for (c = ls2; c > 0; ) {
cl = MB_METACHARLENCONV(str, &cchar);
c -= WCPADWIDTH(cchar);
while (cl--)
*r++ = *str++;
}
}
ls2 = ls - ls2;
f = postnum - ls2;
if (f <= 0) {
MB_METACHARINIT();
for (c = postnum; c > 0; ) {
cl = MB_METACHARLENCONV(str, &cchar);
c -= WCPADWIDTH(cchar);
while (cl--)
*r++ = *str++;
}
} else {
while (*str)
*r++ = *str++;
if (f <= lpostone) {
if (postone) {
for (c = f; c > 0; ) {
cl = MB_METACHARLENCONV(postone, &cchar);
c -= WCPADWIDTH(cchar);
while (cl--)
*r++ = *postone++;
}
}
} else {
if (postone) {
f -= lpostone;
while (*postone)
*r++ = *postone++;
}
if (lpostmul) {
for (cc = f / lpostmul; cc--;) {
for (t = postmul; *t; )
*r++ = *t++;
}
if ((m = f % lpostmul)) {
MB_METACHARINIT();
while (m > 0) {
cl = MB_METACHARLENCONV(postmul, &cchar);
m -= WCPADWIDTH(cchar);
while (cl--)
*r++ = *postmul++;
}
}
}
}
}
} else {
f = prenum - ls;
if (f <= 0) {
f = -f;
MB_METACHARINIT();
while (f > 0) {
str += MB_METACHARLENCONV(str, &cchar);
f -= WCPADWIDTH(cchar);
}
for (c = prenum; c > 0; ) {
cl = MB_METACHARLENCONV(str, &cchar);
while (cl--)
*r++ = *str++;
c -= WCPADWIDTH(cchar);
}
} else {
if (f <= lpreone) {
if (preone) {
c = f;
f = lpreone - f;
MB_METACHARINIT();
for (t = preone; f > 0; ) {
t += MB_METACHARLENCONV(t, &cchar);
f -= WCPADWIDTH(cchar);
}
while (*t)
*r++ = *t++;
}
} else {
f -= lpreone;
if (lpremul) {
if ((m = f % lpremul)) {
c = m;
m = lpremul - m;
MB_METACHARINIT();
for (t = premul; m > 0; ) {
t += MB_METACHARLENCONV(t, &cchar);
m -= WCPADWIDTH(cchar);
}
while (c > 0) {
cl = MB_METACHARLENCONV(t, &cchar);
while (cl--)
*r++ = *t++;
c -= WCPADWIDTH(cchar);
}
}
for (cc = f / lpremul; cc--;) {
MB_METACHARINIT();
for (c = lpremul, t = premul; c > 0; ) {
cl = MB_METACHARLENCONV(t, &cchar);
while (cl--)
*r++ = *t++;
c -= WCPADWIDTH(cchar);
}
}
}
if (preone) {
while (*preone)
*r++ = *preone++;
}
}
while (*str)
*r++ = *str++;
}
}
} else if (postnum) {
f = postnum - ls;
MB_METACHARINIT();
if (f <= 0) {
for (c = postnum; c > 0; ) {
cl = MB_METACHARLENCONV(str, &cchar);
while (cl--)
*r++ = *str++;
c -= WCPADWIDTH(cchar);
}
} else {
for (c = ls; *str; ) {
cl = MB_METACHARLENCONV(str, &cchar);
while (cl--)
*r++ = *str++;
c -= WCPADWIDTH(cchar);
}
MB_METACHARINIT();
if (f <= lpostone) {
if (postone) {
for (c = f; c > 0; ) {
cl = MB_METACHARLENCONV(postone, &cchar);
while (cl--)
*r++ = *postone++;
c -= WCPADWIDTH(cchar);
}
}
} else {
if (postone) {
f -= lpostone;
for (c = lpostone; *postone; ) {
cl = MB_METACHARLENCONV(postone, &cchar);
while (cl--)
*r++ = *postone++;
c -= WCPADWIDTH(cchar);
}
}
if (lpostmul) {
for (cc = f / lpostmul; cc--;) {
MB_METACHARINIT();
for (c = lpostmul, t = postmul; *t; ) {
cl = MB_METACHARLENCONV(t, &cchar);
while (cl--)
*r++ = *t++;
c -= WCPADWIDTH(cchar);
}
}
if ((m = f % lpostmul)) {
MB_METACHARINIT();
while (m > 0) {
cl = MB_METACHARLENCONV(postmul, &cchar);
while (cl--)
*r++ = *postmul++;
m -= WCPADWIDTH(cchar);
}
}
}
}
}
}
*r = '\0';
return ret;
}
char *
get_strarg(char *s, int *lenp)
{
convchar_t del;
int len;
char tok = 0;
MB_METACHARINIT();
len = MB_METACHARLENCONV(s, &del);
if (!len) {
*lenp = 0;
return s;
}
#ifdef MULTIBYTE_SUPPORT
if (del == WEOF)
del = (wint_t)((*s == Meta) ? s[1] ^ 32 : *s);
#endif
s += len;
switch (del) {
case ZWC('('):
del = ZWC(')');
break;
case '[':
del = ZWC(']');
break;
case '{':
del = ZWC('}');
break;
case '<':
del = ZWC('>');
break;
case Inpar:
tok = Outpar;
break;
case Inang:
tok = Outang;
break;
case Inbrace:
tok = Outbrace;
break;
case Inbrack:
tok = Outbrack;
break;
}
if (tok) {
while (*s && *s != tok)
s++;
} else {
convchar_t del2;
len = 0;
while (*s) {
len = MB_METACHARLENCONV(s, &del2);
#ifdef MULTIBYTE_SUPPORT
if (del2 == WEOF)
del2 = (wint_t)((*s == Meta) ? s[1] ^ 32 : *s);
#endif
if (del == del2)
break;
s += len;
}
}
*lenp = len;
return s;
}
static int
get_intarg(char **s, int *delmatchp)
{
int arglen;
char *t = get_strarg(*s, &arglen);
char *p, sav;
zlong ret;
*delmatchp = 0;
if (!*t)
return -1;
sav = *t;
*t = '\0';
p = dupstring(*s + arglen);
*s = t + arglen;
*t = sav;
if (parsestr(p))
return -1;
singsub(&p);
if (errflag)
return -1;
ret = mathevali(p);
if (errflag)
return -1;
if (ret < 0)
ret = -ret;
*delmatchp = arglen;
return ret < 0 ? -ret : ret;
}
static int
subst_parse_str(char **sp, int single, int err)
{
char *s;
*sp = s = dupstring(*sp);
if (!(err ? parsestr(s) : parsestrnoerr(s))) {
if (!single) {
int qt = 0;
for (; *s; s++)
if (!qt) {
if (*s == Qstring)
*s = String;
else if (*s == Qtick)
*s = Tick;
} else if (*s == Dnull)
qt = !qt;
}
return 0;
}
return 1;
}
static char *
substevalchar(char *ptr)
{
zlong ires = mathevali(ptr);
int len = 0;
if (errflag)
return NULL;
#ifdef MULTIBYTE_SUPPORT
if (isset(MULTIBYTE) && ires > 127) {
char buf[11];
sprintf(buf, "\\U%.8x", (unsigned int)ires & 0xFFFFFFFFu);
ptr = getkeystring(buf, &len, GETKEYS_BINDKEY, NULL);
}
if (len == 0)
#endif
{
ptr = zhalloc(2);
len = 1;
sprintf(ptr, "%c", (int)ires);
}
return metafy(ptr, len, META_USEHEAP);
}
static char *
untok_and_escape(char *s, int escapes, int tok_arg)
{
int klen;
char *dst;
untokenize(dst = dupstring(s));
if (escapes) {
dst = getkeystring(dst, &klen, GETKEYS_SEP, NULL);
dst = metafy(dst, klen, META_HREALLOC);
}
if (tok_arg)
shtokenize(dst);
return dst;
}
#define isstring(c) ((c) == '$' || (char)(c) == String || (char)(c) == Qstring)
#define isbrack(c) ((c) == '[' || (char)(c) == Inbrack)
static LinkNode
paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
{
char *aptr = *str, c, cc;
char *s = aptr, *fstr, *idbeg, *idend, *ostr = (char *) getdata(n);
int colf;
int isarr = 0;
int plan9 = isset(RCEXPANDPARAM);
int globsubst = isset(GLOBSUBST);
int evalchar = 0;
int getlen = 0;
int whichlen = 0;
int chkset = 0;
int vunset = 0;
int wantt = 0;
int spbreak = isset(SHWORDSPLIT) && !ssub && !qt;
char *val = NULL, **aval = NULL;
struct value vbuf;
Value v = NULL;
int flags = 0;
int flnum = 0;
int sortit = SORTIT_ANYOLDHOW, indord = 0;
int unique = 0;
int casmod = CASMOD_NONE;
int quotemod = 0, quotetype = QT_NONE, quoteerr = 0;
int visiblemod = 0;
int shsplit = 0;
char *sep = NULL, *spsep = NULL;
char *premul = NULL, *postmul = NULL, *preone = NULL, *postone = NULL;
char *replstr = NULL;
zlong prenum = 0, postnum = 0;
#ifdef MULTIBYTE_SUPPORT
int multi_width = 0;
#endif
int copied = 0;
int arrasg = 0;
int eval = 0;
int aspar = 0;
int presc = 0;
int nojoin = 0;
char inbrace = 0;
char hkeys = 0;
char hvals = 0;
int subexp;
*s++ = '\0';
c = *s;
if (itype_end(s, IIDENT, 1) == s && *s != '#' && c != Pound &&
c != '-' && c != '!' && c != '$' && c != String && c != Qstring &&
c != '?' && c != Quest &&
c != '*' && c != Star && c != '@' && c != '{' &&
c != Inbrace && c != '=' && c != Equals && c != Hat &&
c != '^' && c != '~' && c != Tilde && c != '+') {
s[-1] = '$';
*str = s;
return n;
}
DPUTS(c == '{', "BUG: inbrace == '{' in paramsubst()");
if (c == Inbrace) {
inbrace = 1;
s++;
if ((c = *s) == '!' && s[1] != Outbrace && emulation == EMULATE_KSH) {
hkeys = SCANPM_WANTKEYS;
s++;
} else if (c == '(' || c == Inpar) {
char *t, sav;
int tt = 0;
zlong num;
int escapes = 0;
int tok_arg = 0;
for (s++; (c = *s) != ')' && c != Outpar; s++, tt = 0) {
int arglen;
int dellen;
char *del0;
switch (c) {
case ')':
case Outpar:
break;
case '~':
case Tilde:
tok_arg = !tok_arg;
break;
case 'A':
++arrasg;
break;
case '@':
nojoin = 1;
break;
case 'M':
flags |= SUB_MATCH;
break;
case 'R':
flags |= SUB_REST;
break;
case 'B':
flags |= SUB_BIND;
break;
case 'E':
flags |= SUB_EIND;
break;
case 'N':
flags |= SUB_LEN;
break;
case 'S':
flags |= SUB_SUBSTR;
break;
case 'I':
s++;
flnum = get_intarg(&s, &dellen);
if (flnum < 0)
goto flagerr;
s--;
break;
case 'L':
casmod = CASMOD_LOWER;
break;
case 'U':
casmod = CASMOD_UPPER;
break;
case 'C':
casmod = CASMOD_CAPS;
break;
case 'o':
if (!sortit)
sortit |= SORTIT_SOMEHOW;
break;
case 'O':
sortit |= SORTIT_BACKWARDS;
break;
case 'i':
sortit |= SORTIT_IGNORING_CASE;
break;
case 'n':
sortit |= SORTIT_NUMERICALLY;
break;
case 'a':
sortit |= SORTIT_SOMEHOW;
indord = 1;
break;
case 'V':
visiblemod++;
break;
case 'q':
quotemod++, quotetype++;
break;
case 'Q':
quotemod--;
break;
case 'X':
quoteerr = 1;
break;
case 'e':
eval = 1;
break;
case 'P':
aspar = 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 '0':
spsep = zhalloc(3);
spsep[0] = Meta;
spsep[1] = '\0' ^ 32;
spsep[2] = '\0';
break;
case 's':
tt = 1;
case 'j':
t = get_strarg(++s, &arglen);
if (*t) {
sav = *t;
*t = '\0';
if (tt)
spsep = untok_and_escape(s + arglen,
escapes, tok_arg);
else
sep = untok_and_escape(s + arglen,
escapes, tok_arg);
*t = sav;
s = t + arglen - 1;
} else
goto flagerr;
break;
case 'l':
tt = 1;
case 'r':
s++;
del0 = s;
num = get_intarg(&s, &dellen);
if (num < 0)
goto flagerr;
if (tt)
prenum = num;
else
postnum = num;
if (!dellen || memcmp(del0, s, dellen)) {
s--;
break;
}
t = get_strarg(s, &arglen);
if (!*t)
goto flagerr;
sav = *t;
*t = '\0';
if (tt)
premul = untok_and_escape(s + arglen, escapes,
tok_arg);
else
postmul = untok_and_escape(s + arglen, escapes,
tok_arg);
*t = sav;
sav = *s;
s = t + arglen;
if (memcmp(del0, s, dellen)) {
s--;
break;
}
t = get_strarg(s, &arglen);
if (!*t)
goto flagerr;
sav = *t;
*t = '\0';
if (tt)
preone = untok_and_escape(s + arglen,
escapes, tok_arg);
else
postone = untok_and_escape(s + arglen,
escapes, tok_arg);
*t = sav;
s = t + arglen - 1;
break;
case 'm':
#ifdef MULTIBYTE_SUPPORT
multi_width = 1;
#endif
break;
case 'p':
escapes = 1;
break;
case 'k':
hkeys = SCANPM_WANTKEYS;
break;
case 'v':
hvals = SCANPM_WANTVALS;
break;
case 't':
wantt = 1;
break;
case '%':
presc++;
break;
case 'z':
shsplit = 1;
break;
case 'u':
unique = 1;
break;
case '#':
case Pound:
evalchar = 1;
break;
default:
flagerr:
zerr("error in flags");
return NULL;
}
}
s++;
}
}
if (!premul)
premul = " ";
if (!postmul)
postmul = " ";
for (;;) {
if ((c = *s) == '^' || c == Hat) {
if ((c = *++s) == '^' || c == Hat) {
plan9 = 0;
s++;
} else
plan9 = 1;
} else if ((c = *s) == '=' || c == Equals) {
if ((c = *++s) == '=' || c == Equals) {
spbreak = 0;
s++;
} else
spbreak = 2;
} else if ((c == '#' || c == Pound) &&
(itype_end(s+1, IIDENT, 0) != s + 1
|| (cc = s[1]) == '*' || cc == Star || cc == '@'
|| cc == '-' || (cc == ':' && s[2] == '-')
|| (isstring(cc) && (s[2] == Inbrace || s[2] == Inpar)))) {
getlen = 1 + whichlen, s++;
} else if (c == '~' || c == Tilde) {
if ((c = *++s) == '~' || c == Tilde) {
globsubst = 0;
s++;
} else
globsubst = 1;
} else if (c == '+') {
if (itype_end(s+1, IIDENT, 0) != s+1 || (aspar && isstring(s[1]) &&
(s[2] == Inbrace || s[2] == Inpar)))
chkset = 1, s++;
else if (!inbrace) {
*aptr = '$';
*str = aptr + 1;
return n;
} else {
zerr("bad substitution");
return NULL;
}
} else if (inbrace && inull(*s)) {
s++;
} else
break;
}
globsubst = globsubst && !qt;
idbeg = s;
if ((subexp = (inbrace && 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, 0, (aspar ? NULL : &aval), &isarr, NULL) && quoted) {
isarr = -1;
aval = (char **) hcalloc(sizeof(char *));
aspar = 0;
} else if (aspar)
idbeg = val;
*s = sav;
while (inull(*s))
s++;
v = (Value) NULL;
} else if (aspar) {
if ((v = fetchvalue(&vbuf, &s, 1, (qt ? SCANPM_DQUOTED : 0)))) {
val = idbeg = getstrvalue(v);
subexp = 1;
} else
vunset = 1;
}
if (!subexp || aspar) {
char *ov = val;
if (!(v = fetchvalue(&vbuf, (subexp ? &ov : &s),
(wantt ? -1 :
((unset(KSHARRAYS) || inbrace) ? 1 : -1)),
hkeys|hvals|
(arrasg ? SCANPM_ASSIGNING : 0)|
(qt ? SCANPM_DQUOTED : 0))) ||
(v->pm && (v->pm->node.flags & PM_UNSET)) ||
(v->flags & VALFLAG_EMPTY))
vunset = 1;
if (wantt) {
if (v && v->pm && !(v->pm->node.flags & PM_UNSET)) {
int f = v->pm->node.flags;
switch (PM_TYPE(f)) {
case PM_SCALAR: val = "scalar"; break;
case PM_ARRAY: val = "array"; break;
case PM_INTEGER: val = "integer"; break;
case PM_EFLOAT:
case PM_FFLOAT: val = "float"; break;
case PM_HASHED: val = "association"; break;
}
val = dupstring(val);
if (v->pm->level)
val = dyncat(val, "-local");
if (f & PM_LEFT)
val = dyncat(val, "-left");
if (f & PM_RIGHT_B)
val = dyncat(val, "-right_blanks");
if (f & PM_RIGHT_Z)
val = dyncat(val, "-right_zeros");
if (f & PM_LOWER)
val = dyncat(val, "-lower");
if (f & PM_UPPER)
val = dyncat(val, "-upper");
if (f & PM_READONLY)
val = dyncat(val, "-readonly");
if (f & PM_TAGGED)
val = dyncat(val, "-tag");
if (f & PM_EXPORTED)
val = dyncat(val, "-export");
if (f & PM_UNIQUE)
val = dyncat(val, "-unique");
if (f & PM_HIDE)
val = dyncat(val, "-hide");
if (f & PM_HIDE)
val = dyncat(val, "-hideval");
if (f & PM_SPECIAL)
val = dyncat(val, "-special");
vunset = 0;
} else
val = dupstring("");
v = NULL;
isarr = 0;
}
}
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);
DPUTS(!pm, "BUG: parameter not created");
if (isarr)
pm->u.arr = aval;
else
pm->u.str = val;
v = (Value) hcalloc(sizeof *v);
v->isarr = isarr;
v->pm = pm;
v->end = -1;
if (getindex(&s, v, qt ? SCANPM_DQUOTED : 0) || s == os)
break;
}
if ((isarr = v->isarr)) {
if (v->isarr == SCANPM_WANTINDEX) {
isarr = v->isarr = 0;
val = dupstring(v->pm->node.nam);
} else
aval = getarrvalue(v);
} else {
if (v->pm->node.flags & PM_ARRAY) {
int tmplen = arrlen(v->pm->gsu.a->getfn(v->pm));
if (v->start < 0)
v->start += tmplen + ((v->flags & VALFLAG_INV) ? 1 : 0);
if (!(v->flags & VALFLAG_INV) &&
(v->start >= tmplen || v->start < 0))
vunset = 1;
}
if (!vunset) {
v->flags |= VALFLAG_SUBST;
val = getstrvalue(v);
}
}
v = NULL;
if (!inbrace)
break;
}
if (inbrace &&
(c = *s) != '-' && c != '+' && c != ':' && c != '%' && c != '/' &&
c != '=' && c != Equals &&
c != '#' && c != Pound &&
c != '?' && c != Quest &&
c != '}' && c != Outbrace) {
zerr("bad substitution");
return NULL;
}
if (isarr) {
if (nojoin)
isarr = -1;
if (qt && !getlen && isarr > 0) {
val = sepjoin(aval, sep, 1);
isarr = 0;
}
}
idend = s;
if (inbrace) {
while (inull(*s))
s++;
}
if ((colf = *s == ':'))
s++;
fstr = s;
if (inbrace) {
int bct;
for (bct = 1; (c = *fstr); fstr++) {
if (c == Inbrace)
bct++;
else if (c == Outbrace && !--bct)
break;
}
if (bct) {
noclosebrace:
zerr("closing brace expected");
return NULL;
}
if (c)
*fstr++ = '\0';
}
if (inbrace && ((c = *s) == '-' ||
c == '+' ||
c == ':' ||
c == '=' || c == Equals ||
c == '%' ||
c == '#' || c == Pound ||
c == '?' || c == Quest ||
c == '/')) {
if (!flnum)
flnum++;
if (c == '%')
flags |= SUB_END;
if ((c == '%' || c == '#' || c == Pound) && c == s[1]) {
s++;
flags |= SUB_LONG;
}
s++;
if (s[-1] == '/') {
char *ptr;
flags = (flags & SUB_SUBSTR) ? 0 : SUB_LONG;
if ((c = *s) == '/') {
flags |= SUB_GLOBAL;
c = *++s;
}
if (c == '#' || c == Pound) {
flags |= SUB_START;
s++;
}
if (*s == '%') {
flags |= SUB_END;
s++;
}
if (!(flags & (SUB_START|SUB_END))) {
flags |= SUB_SUBSTR;
}
for (ptr = s; (c = *ptr) && c != '/'; ptr++)
{
if ((c == Bnull || c == Bnullkeep || c == '\\') && ptr[1])
{
if (ptr[1] == '/')
chuck(ptr);
else
ptr++;
}
}
replstr = (*ptr && ptr[1]) ? ptr+1 : "";
*ptr = '\0';
}
if (colf)
flags |= SUB_ALL;
if (!(flags & (SUB_MATCH|SUB_REST|SUB_BIND|SUB_EIND|SUB_LEN)))
flags |= SUB_REST;
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;
case '-':
if (vunset) {
int ws = opts[SHWORDSPLIT];
val = dupstring(s);
opts[SHWORDSPLIT] = spbreak;
multsub(&val, spbreak && !aspar, (aspar ? NULL : &aval), &isarr, NULL);
opts[SHWORDSPLIT] = ws;
copied = 1;
spbreak = 0;
}
break;
case ':':
if (*s != '=' && *s != Equals)
goto noclosebrace;
vunset = 1;
s++;
case '=':
case Equals:
if (vunset) {
int ws = opts[SHWORDSPLIT];
char sav = *idend;
int l;
*idend = '\0';
val = dupstring(s);
if (spsep || !arrasg) {
opts[SHWORDSPLIT] = 0;
multsub(&val, 0, NULL, &isarr, NULL);
} else {
opts[SHWORDSPLIT] = spbreak;
multsub(&val, spbreak, &aval, &isarr, NULL);
spbreak = 0;
}
opts[SHWORDSPLIT] = ws;
if (arrasg) {
char *arr[2], **t, **a, **p;
if (spsep || spbreak) {
aval = sepsplit(val, spsep, 0, 1);
isarr = nojoin ? 1 : 2;
l = arrlen(aval);
if (l && !*(aval[l-1]))
l--;
if (l && !**aval)
l--, t = aval + 1;
else
t = aval;
} else if (!isarr) {
if (!*val && arrasg > 1) {
arr[0] = NULL;
l = 0;
} else {
arr[0] = val;
arr[1] = NULL;
l = 1;
}
t = aval = arr;
} else
l = arrlen(aval), t = aval;
p = a = zalloc(sizeof(char *) * (l + 1));
while (l--) {
untokenize(*t);
*p++ = ztrdup(*t++);
}
*p++ = NULL;
if (arrasg > 1) {
Param pm = sethparam(idbeg, a);
if (pm)
aval = paramvalarr(pm->gsu.h->getfn(pm), hkeys|hvals);
} else
setaparam(idbeg, a);
} else {
untokenize(val);
setsparam(idbeg, ztrdup(val));
}
*idend = sav;
copied = 1;
if (isarr) {
if (nojoin)
isarr = -1;
if (qt && !getlen && isarr > 0 && !spsep && spbreak < 2) {
val = sepjoin(aval, sep, 1);
isarr = 0;
}
sep = spsep = NULL;
spbreak = 0;
}
}
break;
case '?':
case Quest:
if (vunset) {
*idend = '\0';
zerr("%s: %s", idbeg, *s ? s : "parameter not set");
if (!interact) {
if (mypid == getpid()) {
stopmsg = 1;
zexit(1, 0);
} else
_exit(1);
}
return NULL;
}
break;
case '%':
case '#':
case Pound:
case '/':
{
int one = noerrs, oef = errflag, haserr;
if (!quoteerr)
noerrs = 1;
haserr = parse_subst_string(s);
noerrs = one;
if (!quoteerr) {
errflag = oef;
if (haserr)
shtokenize(s);
} else if (haserr || errflag) {
zerr("parse error in ${...%c...} substitution", s[-1]);
return NULL;
}
}
{
#if 0
char t = s[-1];
singsub(&s);
if (t == '/' && (flags & SUB_SUBSTR)) {
if ((c = *s) == '#' || c == '%') {
flags &= ~SUB_SUBSTR;
if (c == '%')
flags |= SUB_END;
s++;
} else if (c == '\\') {
s++;
}
}
#else
singsub(&s);
#endif
}
if (!vunset && isarr) {
char **ap;
if (!copied) {
aval = arrdup(aval);
copied = 1;
}
for (ap = aval; *ap; ap++) {
untokenize(*ap);
}
getmatcharr(&aval, s, flags, flnum, replstr);
} else {
if (vunset)
val = dupstring("");
if (!copied) {
val = dupstring(val);
copied = 1;
untokenize(val);
}
getmatch(&val, s, flags, flnum, replstr);
}
break;
}
} else {
if (chkset) {
val = dupstring(vunset ? "0" : "1");
isarr = 0;
} else if (vunset) {
if (unset(UNSET)) {
*idend = '\0';
zerr("%s: parameter not set", idbeg);
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 **) hcalloc(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;
}
copied = 1;
if (inbrace && *s) {
if (*s == ':' && !imeta(s[1]))
zerr("unrecognized modifier `%c'", s[1]);
else
zerr("unrecognized modifier");
return NULL;
}
}
}
if (!inbrace)
fstr = s;
}
if (errflag)
return NULL;
if (evalchar) {
int one = noerrs, oef = errflag, haserr = 0;
if (!quoteerr)
noerrs = 1;
if (isarr) {
char **aval2, **avptr, **av2ptr;
aval2 = (char **)zhalloc((arrlen(aval)+1)*sizeof(char *));
for (avptr = aval, av2ptr = aval2; *avptr; avptr++, av2ptr++)
{
if (!(*av2ptr = substevalchar(*avptr))) {
haserr = 1;
break;
}
}
*av2ptr = NULL;
aval = aval2;
} else {
if (!(val = substevalchar(val)))
haserr = 1;
}
noerrs = one;
if (!quoteerr)
errflag = oef;
if (haserr || errflag)
return NULL;
}
if (getlen) {
long len = 0;
char buf[14];
if (isarr) {
char **ctr;
int sl = sep ? MB_METASTRLEN(sep) : 1;
if (getlen == 1)
for (ctr = aval; *ctr; ctr++, len++);
else if (getlen == 2) {
if (*aval)
for (len = -sl, ctr = aval;
len += sl + MB_METASTRLEN2(*ctr, multi_width),
*++ctr;);
}
else
for (ctr = aval;
*ctr;
len += wordcount(*ctr, spsep, getlen > 3), ctr++);
} else {
if (getlen < 3)
len = MB_METASTRLEN2(val, multi_width);
else
len = wordcount(val, spsep, getlen > 3);
}
sprintf(buf, "%ld", len);
val = dupstring(buf);
isarr = 0;
}
if (isarr)
l->list.flags |= LF_ARRAY;
else
l->list.flags &= ~LF_ARRAY;
if (isarr > 0 && !plan9 && (!aval || !aval[0])) {
val = dupstring("");
isarr = 0;
} else if (isarr && aval && aval[0] && !aval[1]) {
val = aval[0];
isarr = 0;
}
if (ssub || spbreak || spsep || sep) {
if (isarr) {
val = sepjoin(aval, sep, 1);
isarr = 0;
}
if (!ssub && (spbreak || spsep)) {
aval = sepsplit(val, spsep, 0, 1);
if (!aval || !aval[0])
val = dupstring("");
else if (!aval[1])
val = aval[0];
else
isarr = nojoin ? 1 : 2;
}
if (isarr)
l->list.flags |= LF_ARRAY;
else
l->list.flags &= ~LF_ARRAY;
}
if (casmod != CASMOD_NONE) {
copied = 1;
if (isarr) {
char **ap, **ap2;
ap = aval;
ap2 = aval = (char **) zhalloc(sizeof(char *) * (arrlen(aval)+1));
while (*ap)
*ap2++ = casemodify(*ap++, casmod);
*ap2++ = NULL;
} else {
val = casemodify(val, casmod);
}
}
if (presc) {
int ops = opts[PROMPTSUBST], opb = opts[PROMPTBANG];
int opp = opts[PROMPTPERCENT], len;
if (presc < 2) {
opts[PROMPTPERCENT] = 1;
opts[PROMPTSUBST] = opts[PROMPTBANG] = 0;
}
if (isarr) {
char **ap;
if (!copied)
aval = arrdup(aval), copied = 1;
ap = aval;
for (; *ap; ap++) {
char *tmps;
unmetafy(*ap, &len);
untokenize(*ap);
tmps = promptexpand(metafy(*ap, len, META_NOALLOC),
0, NULL, NULL, NULL);
*ap = dupstring(tmps);
free(tmps);
}
} else {
char *tmps;
if (!copied)
val = dupstring(val), copied = 1;
unmetafy(val, &len);
untokenize(val);
tmps = promptexpand(metafy(val, len, META_NOALLOC),
0, NULL, NULL, NULL);
val = dupstring(tmps);
free(tmps);
}
opts[PROMPTSUBST] = ops;
opts[PROMPTBANG] = opb;
opts[PROMPTPERCENT] = opp;
}
if (quotemod) {
if (quotetype > QT_DOLLARS)
quotetype = QT_DOLLARS;
if (isarr) {
char **ap;
if (!copied)
aval = arrdup(aval), copied = 1;
ap = aval;
if (quotemod > 0) {
if (quotetype > QT_BACKSLASH) {
int sl;
char *tmp;
for (; *ap; ap++) {
int pre = quotetype != QT_DOLLARS ? 1 : 2;
tmp = quotestring(*ap, NULL, quotetype);
sl = strlen(tmp);
*ap = (char *) zhalloc(pre + sl + 2);
strcpy((*ap) + pre, tmp);
ap[0][pre - 1] = ap[0][pre + sl] =
(quotetype != QT_DOUBLE ? '\'' : '"');
ap[0][pre + sl + 1] = '\0';
if (quotetype == QT_DOLLARS)
ap[0][0] = '$';
}
} else
for (; *ap; ap++)
*ap = quotestring(*ap, NULL, QT_BACKSLASH);
} else {
int one = noerrs, oef = errflag, haserr = 0;
if (!quoteerr)
noerrs = 1;
for (; *ap; ap++) {
haserr |= parse_subst_string(*ap);
remnulargs(*ap);
untokenize(*ap);
}
noerrs = one;
if (!quoteerr)
errflag = oef;
else if (haserr || errflag) {
zerr("parse error in parameter value");
return NULL;
}
}
} else {
if (!copied)
val = dupstring(val), copied = 1;
if (quotemod > 0) {
if (quotetype > QT_BACKSLASH) {
int pre = quotetype != QT_DOLLARS ? 1 : 2;
int sl;
char *tmp;
tmp = quotestring(val, NULL, quotetype);
sl = strlen(tmp);
val = (char *) zhalloc(pre + sl + 2);
strcpy(val + pre, tmp);
val[pre - 1] = val[pre + sl] =
(quotetype != QT_DOUBLE ? '\'' : '"');
val[pre + sl + 1] = '\0';
if (quotetype == QT_DOLLARS)
val[0] = '$';
} else
val = quotestring(val, NULL, QT_BACKSLASH);
} else {
int one = noerrs, oef = errflag, haserr;
if (!quoteerr)
noerrs = 1;
haserr = parse_subst_string(val);
noerrs = one;
if (!quoteerr)
errflag = oef;
else if (haserr || errflag) {
zerr("parse error in parameter value");
return NULL;
}
remnulargs(val);
untokenize(val);
}
}
}
if (visiblemod) {
if (isarr) {
char **ap;
if (!copied)
aval = arrdup(aval), copied = 1;
for (ap = aval; *ap; ap++)
*ap = nicedupstring(*ap);
} else {
if (!copied)
val = dupstring(val), copied = 1;
val = nicedupstring(val);
}
}
if (shsplit) {
LinkList list = NULL;
if (isarr) {
char **ap;
for (ap = aval; *ap; ap++)
list = bufferwords(list, *ap, NULL);
isarr = 0;
} else
list = bufferwords(NULL, val, NULL);
if (!list || !firstnode(list))
val = dupstring("");
else if (!nextnode(firstnode(list)))
val = getdata(firstnode(list));
else {
aval = hlinklist2array(list, 0);
isarr = nojoin ? 1 : 2;
l->list.flags |= LF_ARRAY;
}
copied = 1;
}
if (isarr) {
char *x;
char *y;
int xlen;
int i;
LinkNode on = n;
if (unique) {
if(!copied)
aval = arrdup(aval);
i = arrlen(aval);
if (i > 1)
zhuniqarray(aval);
}
if ((!aval[0] || !aval[1]) && !plan9) {
int vallen;
if (aptr > (char *) getdata(n) &&
aptr[-1] == Dnull && *fstr == Dnull)
*--aptr = '\0', fstr++;
vallen = aval[0] ? strlen(aval[0]) : 0;
y = (char *) hcalloc((aptr - ostr) + vallen + strlen(fstr) + 1);
strcpy(y, ostr);
*str = y + (aptr - ostr);
if (vallen)
{
strcpy(*str, aval[0]);
*str += vallen;
}
strcpy(*str, fstr);
setdata(n, y);
return n;
}
if (sortit != SORTIT_ANYOLDHOW) {
if (!copied)
aval = arrdup(aval);
if (indord) {
if (sortit & SORTIT_BACKWARDS) {
char *copy;
char **end = aval + arrlen(aval) - 1, **start = aval;
while (start < end) {
copy = *end;
*end-- = *start;
*start++ = copy;
}
}
} else {
strmetasort(aval, sortit, NULL);
}
}
if (plan9) {
LinkNode tn;
local_list1(tl);
*--fstr = Marker;
init_list1(tl, fstr);
if (!eval && !stringsubst(&tl, firstnode(&tl), ssub, 0))
return NULL;
*str = aptr;
tn = firstnode(&tl);
while ((x = *aval++)) {
if (prenum || postnum)
x = dopadding(x, prenum, postnum, preone, postone,
premul, postmul
#ifdef MULTIBYTE_SUPPORT
, multi_width
#endif
);
if (eval && subst_parse_str(&x, (qt && !nojoin), quoteerr))
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,
copied);
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
#ifdef MULTIBYTE_SUPPORT
, multi_width
#endif
);
if (eval && subst_parse_str(&x, (qt && !nojoin), quoteerr))
return NULL;
xlen = strlen(x);
strcatsub(&y, ostr, aptr, x, xlen, NULL, globsubst, copied);
if (qt && !*y && isarr != 2)
y = dupstring(nulstring);
setdata(n, (void *) y);
i = 1;
while (aval[i + 1]) {
x = aval[i++];
if (prenum || postnum)
x = dopadding(x, prenum, postnum, preone, postone,
premul, postmul
#ifdef MULTIBYTE_SUPPORT
, multi_width
#endif
);
if (eval && subst_parse_str(&x, (qt && !nojoin), quoteerr))
return NULL;
if (qt && !*x && isarr != 2)
y = dupstring(nulstring);
else {
y = dupstring(x);
if (globsubst)
shtokenize(y);
}
insertlinknode(l, n, (void *) y), incnode(n);
}
x = aval[i];
if (prenum || postnum)
x = dopadding(x, prenum, postnum, preone, postone,
premul, postmul
#ifdef MULTIBYTE_SUPPORT
, multi_width
#endif
);
if (eval && subst_parse_str(&x, (qt && !nojoin), quoteerr))
return NULL;
xlen = strlen(x);
*str = strcatsub(&y, aptr, aptr, x, xlen, fstr, globsubst, copied);
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
#ifdef MULTIBYTE_SUPPORT
, multi_width
#endif
);
if (eval && subst_parse_str(&x, (qt && !nojoin), quoteerr))
return NULL;
xlen = strlen(x);
*str = strcatsub(&y, ostr, aptr, x, xlen, fstr, globsubst, copied);
if (qt && !*y)
y = dupstring(nulstring);
setdata(n, (void *) y);
}
if (eval)
*str = (char *) getdata(n);
return n;
}
static char *
arithsubst(char *a, char **bptr, char *rest)
{
char *s = *bptr, *t;
char buf[BDIGBUFSIZE], *b = buf;
mnumber v;
singsub(&a);
v = matheval(a);
if ((v.type & MN_FLOAT) && !outputradix)
b = convfloat(v.u.d, 0, 0, NULL);
else {
if (v.type & MN_FLOAT)
v.u.l = (zlong) v.u.d;
convbase(buf, v.u.l, outputradix);
}
t = *bptr = (char *) hcalloc(strlen(*bptr) + strlen(b) +
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, *lptr, c, *test, *sep, *t, *tt, tc, *e;
char *copy, *all, *tmp, sav, sav1, *ptr1end;
int gbal, wall, rec, al, nl, charlen, dellen;
convchar_t del;
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':
case 'q':
case 'Q':
c = **ptr;
break;
case 's':
c = **ptr;
(*ptr)++;
ptr1 = *ptr;
MB_METACHARINIT();
charlen = MB_METACHARLENCONV(ptr1, &del);
#ifdef MULTIBYTE_SUPPORT
if (del == WEOF)
del = (wint_t)((*ptr1 == Meta) ? ptr1[1] ^ 32 : *ptr1);
#endif
ptr1 += charlen;
for (ptr2 = ptr1, charlen = 0; *ptr2; ptr2 += charlen) {
convchar_t del2;
charlen = MB_METACHARLENCONV(ptr2, &del2);
#ifdef MULTIBYTE_SUPPORT
if (del2 == WEOF)
del2 = (wint_t)((*ptr2 == Meta) ?
ptr2[1] ^ 32 : *ptr2);
#endif
if (del2 == del)
break;
}
if (!*ptr2) {
zerr("bad substitution");
return;
}
ptr1end = ptr2;
ptr2 += charlen;
sav1 = *ptr1end;
*ptr1end = '\0';
for (ptr3 = ptr2, charlen = 0; *ptr3; ptr3 += charlen) {
convchar_t del3;
charlen = MB_METACHARLENCONV(ptr3, &del3);
#ifdef MULTIBYTE_SUPPORT
if (del3 == WEOF)
del3 = (wint_t)((*ptr3 == Meta) ?
ptr3[1] ^ 32 : *ptr3);
#endif
if (del3 == del)
break;
}
sav = *ptr3;
*ptr3 = '\0';
if (*ptr1) {
zsfree(hsubl);
hsubl = ztrdup(ptr1);
}
if (!hsubl) {
zerr("no previous substitution");
return;
}
zsfree(hsubr);
for (tt = hsubl; *tt; tt++)
if (inull(*tt) && *tt != Bnullkeep)
chuck(tt--);
if (!isset(HISTSUBSTPATTERN))
untokenize(hsubl);
for (tt = hsubr = ztrdup(ptr2); *tt; tt++)
if (inull(*tt) && *tt != Bnullkeep)
chuck(tt--);
*ptr1end = sav1;
*ptr3 = sav;
*ptr = ptr3 - 1;
if (*ptr3) {
*ptr += charlen;
}
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, &charlen);
if ((sav = *ptr1))
*ptr1 = '\0';
sep = dupstring(ptr2 + charlen);
if (sav)
*ptr1 = sav;
*ptr = ptr1 + charlen;
c = '\0';
break;
case 'f':
rec = -1;
(*ptr)++;
break;
case 'F':
(*ptr)++;
rec = get_intarg(ptr, &dellen);
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';
if (c != 'l' && c != 'u')
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':
copy = casemodify(tt, CASMOD_LOWER);
break;
case 'u':
copy = casemodify(tt, CASMOD_UPPER);
break;
case 's':
if (hsubl && hsubr)
subst(©, hsubl, hsubr, gbal);
break;
case 'q':
copy = quotestring(copy, NULL, QT_BACKSLASH);
break;
case 'Q':
{
int one = noerrs, oef = errflag;
noerrs = 1;
parse_subst_string(copy);
noerrs = one;
errflag = oef;
remnulargs(copy);
untokenize(copy);
}
break;
}
tc = *tt;
*tt = '\0';
nl = al + strlen(t) + strlen(copy);
ptr1 = tmp = (char *)zhalloc(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':
*str = casemodify(*str, CASMOD_LOWER);
break;
case 'u':
*str = casemodify(*str, CASMOD_UPPER);
break;
case 's':
if (hsubl && hsubr)
subst(str, hsubl, hsubr, gbal);
break;
case 'q':
*str = quotestring(*str, NULL, QT_BACKSLASH);
break;
case 'Q':
{
int one = noerrs, oef = errflag;
noerrs = 1;
parse_subst_string(*str);
noerrs = one;
errflag = oef;
remnulargs(*str);
untokenize(*str);
}
break;
}
}
if (rec < 0) {
if (!strcmp(test, *str))
rec = 0;
else
test = dupstring(*str);
}
}
}
}
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 (backwards && !val)
return pwd;
if (isset(NOMATCH))
zerr("not enough directory stack entries.");
return NULL;
}
return (char *)getdata(n);
}