mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-06-01 12:56:04 +02:00
10547: (#s) and (#e) pattern assertions
This commit is contained in:
parent
181811bf80
commit
d2330ba055
|
@ -1,3 +1,9 @@
|
||||||
|
2000-04-06 Peter Stephenson <pws@pwstephenson.fsnet.co.uk>
|
||||||
|
|
||||||
|
* 10547: Doc/Zsh/expn.yo, Misc/globtests, Src/pattern.c,
|
||||||
|
Src/subst.c, Test/11glob.ztst, Test/ztst.zsh: add
|
||||||
|
(#s) and (#e) to match at start and end of string.
|
||||||
|
|
||||||
2000-04-06 Andrew Main <zefram@zsh.org>
|
2000-04-06 Andrew Main <zefram@zsh.org>
|
||||||
|
|
||||||
* zefram2: Src/lex.c: Support "3&> foo" etc.
|
* zefram2: Src/lex.c: Support "3&> foo" etc.
|
||||||
|
|
|
@ -1299,6 +1299,17 @@ item(tt(a)var(num))(
|
||||||
Approximate matching: var(num) errors are allowed in the string matched by
|
Approximate matching: var(num) errors are allowed in the string matched by
|
||||||
the pattern. The rules for this are described in the next subsection.
|
the pattern. The rules for this are described in the next subsection.
|
||||||
)
|
)
|
||||||
|
item(tt(s), tt(e))(
|
||||||
|
Unlike the other flags, these have only a local effect, and each must
|
||||||
|
appear on its own: `tt((#s))' and `tt((#e))' are the only valid forms.
|
||||||
|
The `tt((#s))' flag succeeds only at the start of the test string, and the
|
||||||
|
`tt((#e))' flag succeeds only at the end of the test string; they
|
||||||
|
correspond to `tt(^)' and `tt($)' in standard regular expressions. They
|
||||||
|
are useful for matching path segments in patterns. For example,
|
||||||
|
`tt(*((#s)|/)test((#e)|/)*)' matches a path segment `tt(test)' in any of
|
||||||
|
the following strings: tt(test), tt(test/at/start), tt(at/end/test),
|
||||||
|
tt(in/test/middle).
|
||||||
|
)
|
||||||
enditem()
|
enditem()
|
||||||
|
|
||||||
For example, the test string tt(fooxx) can be matched by the pattern
|
For example, the test string tt(fooxx) can be matched by the pattern
|
||||||
|
|
|
@ -14,6 +14,13 @@ while read res str pat; do
|
||||||
(( failed++ ))
|
(( failed++ ))
|
||||||
fi
|
fi
|
||||||
done <<EOT
|
done <<EOT
|
||||||
|
# a few simple things certain nameless idiots have been known to mess up
|
||||||
|
t foo~ foo~
|
||||||
|
t foo~ (foo~)
|
||||||
|
t foo~ (foo~|)
|
||||||
|
t foo.c *.c~boo*
|
||||||
|
f foo.c *.c~boo*~foo*
|
||||||
|
# closures
|
||||||
t fofo (fo#)#
|
t fofo (fo#)#
|
||||||
t ffo (fo#)#
|
t ffo (fo#)#
|
||||||
t foooofo (fo#)#
|
t foooofo (fo#)#
|
||||||
|
@ -75,6 +82,7 @@ f mad.moo.cow (*~*.*).(*~*.*)
|
||||||
t moo.cow (^*.*).(^*.*)
|
t moo.cow (^*.*).(^*.*)
|
||||||
f sane.moo.cow (^*.*).(^*.*)
|
f sane.moo.cow (^*.*).(^*.*)
|
||||||
f mucca.pazza mu(^c#)?.pa(^z#)?
|
f mucca.pazza mu(^c#)?.pa(^z#)?
|
||||||
|
f _foo~ _(|*[^~])
|
||||||
t fff ((^f))
|
t fff ((^f))
|
||||||
t fff ((^f)#)
|
t fff ((^f)#)
|
||||||
t fff ((^f)##)
|
t fff ((^f)##)
|
||||||
|
@ -94,6 +102,8 @@ t zoox (^z*|*x)
|
||||||
t foo (^foo)#
|
t foo (^foo)#
|
||||||
f foob (^foo)b*
|
f foob (^foo)b*
|
||||||
t foobb (^foo)b*
|
t foobb (^foo)b*
|
||||||
|
f foob (*~foo)b*
|
||||||
|
t foobb (*~foo)b*
|
||||||
f zsh ^z*
|
f zsh ^z*
|
||||||
t a%1X [[:alpha:][:punct:]]#[[:digit:]][^[:lower:]]
|
t a%1X [[:alpha:][:punct:]]#[[:digit:]][^[:lower:]]
|
||||||
f a%1 [[:alpha:][:punct:]]#[[:digit:]][^[:lower:]]
|
f a%1 [[:alpha:][:punct:]]#[[:digit:]][^[:lower:]]
|
||||||
|
@ -103,5 +113,71 @@ t :] [:]]#
|
||||||
t [ [[]
|
t [ [[]
|
||||||
t ] []]
|
t ] []]
|
||||||
t [] [^]]]
|
t [] [^]]]
|
||||||
|
# Case insensitive matching
|
||||||
|
t fooxx (#i)FOOXX
|
||||||
|
f fooxx (#l)FOOXX
|
||||||
|
t FOOXX (#l)fooxx
|
||||||
|
f fooxx (#i)FOO(#I)X(#i)X
|
||||||
|
t fooXx (#i)FOO(#I)X(#i)X
|
||||||
|
t fooxx ((#i)FOOX)x
|
||||||
|
f fooxx ((#i)FOOX)X
|
||||||
|
f BAR (bar|(#i)foo)
|
||||||
|
t FOO (bar|(#i)foo)
|
||||||
|
t Modules (#i)*m*
|
||||||
|
t fooGRUD (#i)(bar|(#I)foo|(#i)rod)grud
|
||||||
|
f FOOGRUD (#i)(bar|(#I)foo|(#i)rod)grud
|
||||||
|
t readme (#i)readme~README|readme
|
||||||
|
# the readme doesn't get excluded the second time...
|
||||||
|
t readme (#i)readme~README|readme~README
|
||||||
|
# Ranges with backtracking
|
||||||
|
t 633 <1-1000>33
|
||||||
|
t 633 <-1000>33
|
||||||
|
t 633 <1->33
|
||||||
|
t 633 <->33
|
||||||
|
# Approximate matching
|
||||||
|
t READ.ME (#ia1)readme
|
||||||
|
f READ..ME (#ia1)readme
|
||||||
|
t README (#ia1)readm
|
||||||
|
t READM (#ia1)readme
|
||||||
|
t README (#ia1)eadme
|
||||||
|
t EADME (#ia1)readme
|
||||||
|
t READEM (#ia1)readme
|
||||||
|
f ADME (#ia1)readme
|
||||||
|
f README (#ia1)read
|
||||||
|
t bob (#a1)[b][b]
|
||||||
|
f bob (#a1)[b][b]a
|
||||||
|
t bob (#a1)[b]o[b]a
|
||||||
|
f bob (#a1)[c]o[b]
|
||||||
|
t abcd (#a2)XbcX
|
||||||
|
t abcd (#a2)ad
|
||||||
|
t ad (#a2)abcd
|
||||||
|
t abcd (#a2)bd
|
||||||
|
t bd (#a2)abcd
|
||||||
|
t badc (#a2)abcd
|
||||||
|
# This next one is a little tricky: a[d]bc[] = a[]bc[d]
|
||||||
|
t adbc (#a2)abcd
|
||||||
|
f dcba (#a2)abcd
|
||||||
|
# the next one is [d][cb][a] = [a][bc][d] with a transposition
|
||||||
|
t dcba (#a3)abcd
|
||||||
|
t aabaXaaabY (#a1)(a#b)#Y
|
||||||
|
t aabaXaaabY (#a1)(a#b)(a#b)Y
|
||||||
|
t aaXaaaaabY (#a1)(a#b)(a#b)Y
|
||||||
|
t aaaXaaabY (#a1)(a##b)##Y
|
||||||
|
t aaaXbaabY (#a1)(a##b)##Y
|
||||||
|
f read.me (#ia1)README~READ.ME
|
||||||
|
t read.me (#ia1)README~READ_ME
|
||||||
|
f read.me (#ia1)README~(#a1)READ_ME
|
||||||
|
t test *((#s)|/)test((#e)|/)*
|
||||||
|
t test/path *((#s)|/)test((#e)|/)*
|
||||||
|
t path/test *((#s)|/)test((#e)|/)*
|
||||||
|
t path/test/ohyes *((#s)|/)test((#e)|/)*
|
||||||
|
f atest *((#s)|/)test((#e)|/)*
|
||||||
|
f testy *((#s)|/)test((#e)|/)*
|
||||||
|
f testy/path *((#s)|/)test((#e)|/)*
|
||||||
|
f path/atest *((#s)|/)test((#e)|/)*
|
||||||
|
f atest/path *((#s)|/)test((#e)|/)*
|
||||||
|
f path/testy *((#s)|/)test((#e)|/)*
|
||||||
|
f path/testy/ohyes *((#s)|/)test((#e)|/)*
|
||||||
|
f path/atest/ohyes *((#s)|/)test((#e)|/)*
|
||||||
EOT
|
EOT
|
||||||
print "$failed tests failed."
|
print "$failed tests failed."
|
||||||
|
|
|
@ -83,6 +83,8 @@ typedef union upat *Upat;
|
||||||
#define P_ONEHASH 0x06 /* node Match this (simple) thing 0 or more times. */
|
#define P_ONEHASH 0x06 /* node Match this (simple) thing 0 or more times. */
|
||||||
#define P_TWOHASH 0x07 /* node Match this (simple) thing 1 or more times. */
|
#define P_TWOHASH 0x07 /* node Match this (simple) thing 1 or more times. */
|
||||||
#define P_GFLAGS 0x08 /* long Match nothing and set globbing flags */
|
#define P_GFLAGS 0x08 /* long Match nothing and set globbing flags */
|
||||||
|
#define P_ISSTART 0x09 /* no Match start of string. */
|
||||||
|
#define P_ISEND 0x0a /* no Match end of string. */
|
||||||
/* numbered so we can test bit 5 for a branch */
|
/* numbered so we can test bit 5 for a branch */
|
||||||
#define P_BRANCH 0x20 /* node Match this alternative, or the next... */
|
#define P_BRANCH 0x20 /* node Match this alternative, or the next... */
|
||||||
#define P_WBRANCH 0x21 /* uc* node P_BRANCH, but match at least 1 char */
|
#define P_WBRANCH 0x21 /* uc* node P_BRANCH, but match at least 1 char */
|
||||||
|
@ -645,34 +647,44 @@ patcompbranch(int *flagp)
|
||||||
/* Globbing flags. */
|
/* Globbing flags. */
|
||||||
char *pp1 = patparse;
|
char *pp1 = patparse;
|
||||||
int oldglobflags = patglobflags;
|
int oldglobflags = patglobflags;
|
||||||
|
long assert;
|
||||||
patparse += (*patparse == '@') ? 3 : 2;
|
patparse += (*patparse == '@') ? 3 : 2;
|
||||||
if (!patgetglobflags(&patparse))
|
if (!patgetglobflags(&patparse, &assert))
|
||||||
return 0;
|
return 0;
|
||||||
if (pp1 == patstart) {
|
if (assert) {
|
||||||
/* Right at start of pattern, the simplest case.
|
/*
|
||||||
* Put them into the flags and don't emit anything.
|
* Start/end assertion looking like flags, but
|
||||||
|
* actually handled as a normal node
|
||||||
*/
|
*/
|
||||||
((Patprog)patout)->globflags = patglobflags;
|
latest = patnode(assert);
|
||||||
continue;
|
flags = 0;
|
||||||
} else if (!*patparse) {
|
|
||||||
/* Right at the end, so just leave the flags for
|
|
||||||
* the next Patprog in the chain to pick up.
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Otherwise, we have to stick them in as a pattern
|
|
||||||
* matching nothing.
|
|
||||||
*/
|
|
||||||
if (oldglobflags != patglobflags) {
|
|
||||||
/* Flags changed */
|
|
||||||
union upat up;
|
|
||||||
latest = patnode(P_GFLAGS);
|
|
||||||
up.l = patglobflags;
|
|
||||||
patadd((char *)&up, 0, sizeof(union upat), 0);
|
|
||||||
} else {
|
} else {
|
||||||
/* No effect. */
|
if (pp1 == patstart) {
|
||||||
continue;
|
/* Right at start of pattern, the simplest case.
|
||||||
|
* Put them into the flags and don't emit anything.
|
||||||
|
*/
|
||||||
|
((Patprog)patout)->globflags = patglobflags;
|
||||||
|
continue;
|
||||||
|
} else if (!*patparse) {
|
||||||
|
/* Right at the end, so just leave the flags for
|
||||||
|
* the next Patprog in the chain to pick up.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Otherwise, we have to stick them in as a pattern
|
||||||
|
* matching nothing.
|
||||||
|
*/
|
||||||
|
if (oldglobflags != patglobflags) {
|
||||||
|
/* Flags changed */
|
||||||
|
union upat up;
|
||||||
|
latest = patnode(P_GFLAGS);
|
||||||
|
up.l = patglobflags;
|
||||||
|
patadd((char *)&up, 0, sizeof(union upat), 0);
|
||||||
|
} else {
|
||||||
|
/* No effect. */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (isset(EXTENDEDGLOB) && *patparse == Hat) {
|
} else if (isset(EXTENDEDGLOB) && *patparse == Hat) {
|
||||||
/*
|
/*
|
||||||
|
@ -707,10 +719,12 @@ patcompbranch(int *flagp)
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
int
|
int
|
||||||
patgetglobflags(char **strp)
|
patgetglobflags(char **strp, long *assertp)
|
||||||
{
|
{
|
||||||
char *nptr, *ptr = *strp;
|
char *nptr, *ptr = *strp;
|
||||||
zlong ret;
|
zlong ret;
|
||||||
|
|
||||||
|
*assertp = 0;
|
||||||
/* (#X): assumes we are still positioned on the first X */
|
/* (#X): assumes we are still positioned on the first X */
|
||||||
for (; *ptr && *ptr != Outpar; ptr++) {
|
for (; *ptr && *ptr != Outpar; ptr++) {
|
||||||
switch (*ptr) {
|
switch (*ptr) {
|
||||||
|
@ -763,12 +777,23 @@ patgetglobflags(char **strp)
|
||||||
patglobflags &= ~GF_MATCHREF;
|
patglobflags &= ~GF_MATCHREF;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
*assertp = P_ISSTART;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'e':
|
||||||
|
*assertp = P_ISEND;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (*ptr != Outpar)
|
if (*ptr != Outpar)
|
||||||
return 0;
|
return 0;
|
||||||
|
/* Start/end assertions must appear on their own. */
|
||||||
|
if (*assertp && (*strp)[1] != Outpar)
|
||||||
|
return 0;
|
||||||
*strp = ptr + 1;
|
*strp = ptr + 1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -1989,6 +2014,14 @@ patmatch(Upat prog)
|
||||||
* anything here.
|
* anything here.
|
||||||
*/
|
*/
|
||||||
return 0;
|
return 0;
|
||||||
|
case P_ISSTART:
|
||||||
|
if (patinput != patinstart)
|
||||||
|
fail = 1;
|
||||||
|
break;
|
||||||
|
case P_ISEND:
|
||||||
|
if (*patinput)
|
||||||
|
fail = 1;
|
||||||
|
break;
|
||||||
case P_END:
|
case P_END:
|
||||||
if (!(fail = (*patinput && !(patflags & PAT_NOANCH))))
|
if (!(fail = (*patinput && !(patflags & PAT_NOANCH))))
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -2387,6 +2420,12 @@ patprop(Upat op)
|
||||||
case P_GFLAGS:
|
case P_GFLAGS:
|
||||||
p = "GFLAGS";
|
p = "GFLAGS";
|
||||||
break;
|
break;
|
||||||
|
case P_ISSTART:
|
||||||
|
p = "ISSTART";
|
||||||
|
break;
|
||||||
|
case P_ISEND:
|
||||||
|
p = "ISEND";
|
||||||
|
break;
|
||||||
case P_NOTHING:
|
case P_NOTHING:
|
||||||
p = "NOTHING";
|
p = "NOTHING";
|
||||||
break;
|
break;
|
||||||
|
|
862
Src/subst.c
862
Src/subst.c
|
@ -42,27 +42,22 @@ char nulstring[] = {Nularg, '\0'};
|
||||||
* - Brace expansion
|
* - Brace expansion
|
||||||
* - Tilde and equals substitution
|
* - Tilde and equals substitution
|
||||||
*
|
*
|
||||||
* Bits 0 and 1 of flags are used in filesub.
|
* PF_* flags are defined in zsh.h
|
||||||
* 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
|
mod_export void
|
||||||
prefork(LinkList list, int flags)
|
prefork(LinkList list, int flags)
|
||||||
{
|
{
|
||||||
LinkNode node;
|
LinkNode node;
|
||||||
|
|
||||||
MUSTUSEHEAP("prefork");
|
|
||||||
for (node = firstnode(list); node; incnode(node)) {
|
for (node = firstnode(list); node; incnode(node)) {
|
||||||
char *str, *str3;
|
char *str, c;
|
||||||
|
|
||||||
str = str3 = (char *)getdata(node);
|
str = (char *)getdata(node);
|
||||||
if ((*str == Inang || *str == Outang || *str == Equals) &&
|
if (((c = *str) == Inang || c == Outang || c == Equals) &&
|
||||||
str[1] == Inpar) {
|
str[1] == Inpar) {
|
||||||
if (*str == Inang || *str == Outang)
|
if (c == Inang || c == Outang)
|
||||||
setdata(node, (void *) getproc(str)); /* <(...) or >(...) */
|
setdata(node, (void *) getproc(str)); /* <(...) or >(...) */
|
||||||
else
|
else
|
||||||
setdata(node, (void *) getoutputfile(str)); /* =(...) */
|
setdata(node, (void *) getoutputfile(str)); /* =(...) */
|
||||||
|
@ -70,20 +65,22 @@ prefork(LinkList list, int flags)
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (isset(SHFILEEXPANSION))
|
if (isset(SHFILEEXPANSION))
|
||||||
filesub((char **)getaddrdata(node), flags & 3);
|
filesub((char **)getaddrdata(node),
|
||||||
if (!(node = stringsubst(list, node, flags & 4)))
|
flags & (PF_TYPESET|PF_ASSIGN));
|
||||||
|
if (!(node = stringsubst(list, node, flags & PF_SINGLE)))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (node = firstnode(list); node; incnode(node)) {
|
for (node = firstnode(list); node; incnode(node)) {
|
||||||
if (*(char *)getdata(node)) {
|
if (*(char *)getdata(node)) {
|
||||||
remnulargs(getdata(node));
|
remnulargs(getdata(node));
|
||||||
if (unset(IGNOREBRACES) && !(flags & 4))
|
if (unset(IGNOREBRACES) && !(flags & PF_SINGLE))
|
||||||
while (hasbraces(getdata(node)))
|
while (hasbraces(getdata(node)))
|
||||||
xpandbraces(list, &node);
|
xpandbraces(list, &node);
|
||||||
if (unset(SHFILEEXPANSION))
|
if (unset(SHFILEEXPANSION))
|
||||||
filesub((char **)getaddrdata(node), flags & 3);
|
filesub((char **)getaddrdata(node),
|
||||||
} else if (!(flags & 4))
|
flags & (PF_TYPESET|PF_ASSIGN));
|
||||||
|
} else if (!(flags & PF_SINGLE))
|
||||||
uremnode(list, node);
|
uremnode(list, node);
|
||||||
if (errflag)
|
if (errflag)
|
||||||
return;
|
return;
|
||||||
|
@ -96,14 +93,16 @@ stringsubst(LinkList list, LinkNode node, int ssub)
|
||||||
{
|
{
|
||||||
int qt;
|
int qt;
|
||||||
char *str3 = (char *)getdata(node);
|
char *str3 = (char *)getdata(node);
|
||||||
char *str = str3;
|
char *str = str3, c;
|
||||||
|
|
||||||
while (!errflag && *str) {
|
while (!errflag && (c = *str)) {
|
||||||
if ((qt = *str == Qstring) || *str == String)
|
if ((qt = c == Qstring) || c == String) {
|
||||||
if (str[1] == Inpar) {
|
if ((c = str[1]) == Inpar) {
|
||||||
|
if (!qt)
|
||||||
|
mult_isarr = 1;
|
||||||
str++;
|
str++;
|
||||||
goto comsub;
|
goto comsub;
|
||||||
} else if (str[1] == Inbrack) {
|
} else if (c == Inbrack) {
|
||||||
/* $[...] */
|
/* $[...] */
|
||||||
char *str2 = str;
|
char *str2 = str;
|
||||||
str2++;
|
str2++;
|
||||||
|
@ -115,7 +114,7 @@ stringsubst(LinkList list, LinkNode node, int ssub)
|
||||||
str = arithsubst(str + 2, &str3, str2);
|
str = arithsubst(str + 2, &str3, str2);
|
||||||
setdata(node, (void *) str3);
|
setdata(node, (void *) str3);
|
||||||
continue;
|
continue;
|
||||||
} else if (str[1] == Snull) {
|
} else if (c == Snull) {
|
||||||
str = getkeystring(str, NULL, 4, NULL);
|
str = getkeystring(str, NULL, 4, NULL);
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
@ -125,21 +124,25 @@ stringsubst(LinkList list, LinkNode node, int ssub)
|
||||||
str3 = (char *)getdata(node);
|
str3 = (char *)getdata(node);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if ((qt = *str == Qtick) || *str == Tick)
|
} else if ((qt = c == Qtick) || c == Tick)
|
||||||
comsub: {
|
comsub: {
|
||||||
LinkList pl;
|
LinkList pl;
|
||||||
char *s, *str2 = str;
|
char *s, *str2 = str;
|
||||||
char endchar;
|
char endchar;
|
||||||
int l1, l2;
|
int l1, l2;
|
||||||
|
|
||||||
if (*str == Inpar) {
|
if (c == Inpar) {
|
||||||
endchar = Outpar;
|
endchar = Outpar;
|
||||||
str[-1] = '\0';
|
str[-1] = '\0';
|
||||||
|
#ifdef DEBUG
|
||||||
if (skipparens(Inpar, Outpar, &str))
|
if (skipparens(Inpar, Outpar, &str))
|
||||||
DPUTS(1, "BUG: parse error in command substitution");
|
dputs("BUG: parse error in command substitution");
|
||||||
|
#else
|
||||||
|
skipparens(Inpar, Outpar, &str);
|
||||||
|
#endif
|
||||||
str--;
|
str--;
|
||||||
} else {
|
} else {
|
||||||
endchar = *str;
|
endchar = c;
|
||||||
*str = '\0';
|
*str = '\0';
|
||||||
|
|
||||||
while (*++str != endchar)
|
while (*++str != endchar)
|
||||||
|
@ -148,7 +151,8 @@ stringsubst(LinkList list, LinkNode node, int ssub)
|
||||||
*str++ = '\0';
|
*str++ = '\0';
|
||||||
if (endchar == Outpar && str2[1] == '(' && str[-2] == ')') {
|
if (endchar == Outpar && str2[1] == '(' && str[-2] == ')') {
|
||||||
/* Math substitution of the form $((...)) */
|
/* Math substitution of the form $((...)) */
|
||||||
str = arithsubst(str2 + 1, &str3, str);
|
str[-2] = '\0';
|
||||||
|
str = arithsubst(str2 + 2, &str3, str);
|
||||||
setdata(node, (void *) str3);
|
setdata(node, (void *) str3);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -159,12 +163,12 @@ stringsubst(LinkList list, LinkNode node, int ssub)
|
||||||
* be left unchanged. Note that the lexer doesn't tokenize *
|
* be left unchanged. Note that the lexer doesn't tokenize *
|
||||||
* the body of a command substitution so if there are some *
|
* the body of a command substitution so if there are some *
|
||||||
* tokens here they are from a ${(e)~...} substitution. */
|
* tokens here they are from a ${(e)~...} substitution. */
|
||||||
for (str = str2; *++str; )
|
for (str = str2; (c = *++str); )
|
||||||
if (itok(*str) && *str != Nularg &&
|
if (itok(c) && c != Nularg &&
|
||||||
!(endchar != Outpar && *str == Bnull &&
|
!(endchar != Outpar && c == Bnull &&
|
||||||
(str[1] == '$' || str[1] == '\\' || str[1] == '`' ||
|
(str[1] == '$' || str[1] == '\\' || str[1] == '`' ||
|
||||||
(qt && str[1] == '"'))))
|
(qt && str[1] == '"'))))
|
||||||
*str = ztokens[*str - Pound];
|
*str = ztokens[c - Pound];
|
||||||
str++;
|
str++;
|
||||||
if (!(pl = getoutput(str2 + 1, qt || ssub))) {
|
if (!(pl = getoutput(str2 + 1, qt || ssub))) {
|
||||||
zerr("parse error in command substitution", NULL, 0);
|
zerr("parse error in command substitution", NULL, 0);
|
||||||
|
@ -182,7 +186,7 @@ stringsubst(LinkList list, LinkNode node, int ssub)
|
||||||
l2 = strlen(s);
|
l2 = strlen(s);
|
||||||
if (nonempty(pl)) {
|
if (nonempty(pl)) {
|
||||||
LinkNode n = lastnode(pl);
|
LinkNode n = lastnode(pl);
|
||||||
str2 = (char *) ncalloc(l1 + l2 + 1);
|
str2 = (char *) hcalloc(l1 + l2 + 1);
|
||||||
strcpy(str2, str3);
|
strcpy(str2, str3);
|
||||||
strcpy(str2 + l1, s);
|
strcpy(str2 + l1, s);
|
||||||
setdata(node, str2);
|
setdata(node, str2);
|
||||||
|
@ -191,7 +195,7 @@ stringsubst(LinkList list, LinkNode node, int ssub)
|
||||||
l1 = 0;
|
l1 = 0;
|
||||||
l2 = strlen(s);
|
l2 = strlen(s);
|
||||||
}
|
}
|
||||||
str2 = (char *) ncalloc(l1 + l2 + strlen(str) + 1);
|
str2 = (char *) hcalloc(l1 + l2 + strlen(str) + 1);
|
||||||
if (l1)
|
if (l1)
|
||||||
strcpy(str2, str3);
|
strcpy(str2, str3);
|
||||||
strcpy(str2 + l1, s);
|
strcpy(str2 + l1, s);
|
||||||
|
@ -206,15 +210,15 @@ stringsubst(LinkList list, LinkNode node, int ssub)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
void
|
mod_export void
|
||||||
globlist(LinkList list)
|
globlist(LinkList list, int nountok)
|
||||||
{
|
{
|
||||||
LinkNode node, next;
|
LinkNode node, next;
|
||||||
|
|
||||||
badcshglob = 0;
|
badcshglob = 0;
|
||||||
for (node = firstnode(list); !errflag && node; node = next) {
|
for (node = firstnode(list); !errflag && node; node = next) {
|
||||||
next = nextnode(node);
|
next = nextnode(node);
|
||||||
glob(list, node);
|
glob(list, node, nountok);
|
||||||
}
|
}
|
||||||
if (badcshglob == 1)
|
if (badcshglob == 1)
|
||||||
zerr("no match", NULL, 0);
|
zerr("no match", NULL, 0);
|
||||||
|
@ -223,71 +227,84 @@ globlist(LinkList list)
|
||||||
/* perform substitution on a single word */
|
/* perform substitution on a single word */
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
void
|
mod_export void
|
||||||
singsub(char **s)
|
singsub(char **s)
|
||||||
{
|
{
|
||||||
LinkList foo;
|
local_list1(foo);
|
||||||
|
|
||||||
foo = newlinklist();
|
init_list1(foo, *s);
|
||||||
addlinknode(foo, *s);
|
|
||||||
prefork(foo, 4);
|
prefork(&foo, PF_SINGLE);
|
||||||
if (errflag)
|
if (errflag)
|
||||||
return;
|
return;
|
||||||
*s = (char *) ugetnode(foo);
|
*s = (char *) ugetnode(&foo);
|
||||||
DPUTS(nonempty(foo), "BUG: singsub() produced more than one word!");
|
DPUTS(nonempty(&foo), "BUG: singsub() produced more than one word!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform substitution on a single word. Unlike with singsub, the *
|
/* Perform substitution on a single word. Unlike with singsub, the *
|
||||||
* result can have more than one words. A single word result is sroted *
|
* result can have more than one word. A single word result is stored *
|
||||||
* in *s and *isarr is set to zero; otherwise *isarr is set to 1 and *
|
* 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 *
|
* 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 *
|
* 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 *
|
* is returned in *s. The return value is true iff the expansion *
|
||||||
* resulted in an empty list */
|
* resulted in an empty list. *
|
||||||
|
* The mult_isarr variable is used by paramsubst() to tell if it yields *
|
||||||
|
* an array. */
|
||||||
|
|
||||||
|
/**/
|
||||||
|
static int mult_isarr;
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
static int
|
static int
|
||||||
multsub(char **s, char ***a, int *isarr, char *sep)
|
multsub(char **s, char ***a, int *isarr, char *sep)
|
||||||
{
|
{
|
||||||
LinkList foo;
|
int l, omi = mult_isarr;
|
||||||
int l;
|
|
||||||
char **r, **p;
|
char **r, **p;
|
||||||
|
local_list1(foo);
|
||||||
|
|
||||||
foo = newlinklist();
|
mult_isarr = 0;
|
||||||
addlinknode(foo, *s);
|
init_list1(foo, *s);
|
||||||
prefork(foo, 0);
|
prefork(&foo, 0);
|
||||||
if (errflag) {
|
if (errflag) {
|
||||||
|
if (isarr)
|
||||||
|
*isarr = 0;
|
||||||
|
mult_isarr = omi;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((l = countlinknodes(&foo))) {
|
||||||
|
p = r = hcalloc((l + 1) * sizeof(char*));
|
||||||
|
while (nonempty(&foo))
|
||||||
|
*p++ = (char *)ugetnode(&foo);
|
||||||
|
*p = NULL;
|
||||||
|
if (a && mult_isarr) {
|
||||||
|
*a = r;
|
||||||
|
*isarr = SCANPM_MATCHMANY;
|
||||||
|
mult_isarr = omi;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*s = sepjoin(r, NULL, 1);
|
||||||
|
mult_isarr = omi;
|
||||||
if (isarr)
|
if (isarr)
|
||||||
*isarr = 0;
|
*isarr = 0;
|
||||||
return 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)
|
if (l)
|
||||||
*s = (char *) ugetnode(foo);
|
*s = (char *) ugetnode(&foo);
|
||||||
else
|
else
|
||||||
*s = dupstring("");
|
*s = dupstring("");
|
||||||
if (isarr)
|
if (isarr)
|
||||||
*isarr = 0;
|
*isarr = 0;
|
||||||
|
mult_isarr = omi;
|
||||||
return !l;
|
return !l;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ~, = subs: assign = 2 => typeset; assign = 1 => something that looks
|
/*
|
||||||
like an assignment but may not be; assign = 3 => normal assignment */
|
* ~, = subs: assign & PF_TYPESET => typeset or magic equals
|
||||||
|
* assign & PF_ASSIGN => normal assignment
|
||||||
|
*/
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
void
|
mod_export void
|
||||||
filesub(char **namptr, int assign)
|
filesub(char **namptr, int assign)
|
||||||
{
|
{
|
||||||
char *sub = NULL, *str, *ptr;
|
char *sub = NULL, *str, *ptr;
|
||||||
|
@ -298,12 +315,8 @@ filesub(char **namptr, int assign)
|
||||||
if (!assign)
|
if (!assign)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (assign < 3)
|
if (assign & PF_TYPESET) {
|
||||||
if ((*namptr)[1] && (sub = strchr(*namptr + 1, Equals))) {
|
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;
|
str = sub + 1;
|
||||||
if ((sub[1] == Tilde || sub[1] == Equals) && filesubstr(&str, assign)) {
|
if ((sub[1] == Tilde || sub[1] == Equals) && filesubstr(&str, assign)) {
|
||||||
sub[1] = '\0';
|
sub[1] = '\0';
|
||||||
|
@ -311,6 +324,7 @@ filesub(char **namptr, int assign)
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ptr = *namptr;
|
ptr = *namptr;
|
||||||
while ((sub = strchr(ptr, ':'))) {
|
while ((sub = strchr(ptr, ':'))) {
|
||||||
|
@ -325,7 +339,7 @@ filesub(char **namptr, int assign)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
int
|
mod_export int
|
||||||
filesubstr(char **namptr, int assign)
|
filesubstr(char **namptr, int assign)
|
||||||
{
|
{
|
||||||
#define isend(c) ( !(c) || (c)=='/' || (c)==Inpar || (assign && (c)==':') )
|
#define isend(c) ( !(c) || (c)=='/' || (c)==Inpar || (assign && (c)==':') )
|
||||||
|
@ -382,11 +396,11 @@ filesubstr(char **namptr, int assign)
|
||||||
for (pp = str + 1; !isend2(*pp); pp++);
|
for (pp = str + 1; !isend2(*pp); pp++);
|
||||||
sav = *pp;
|
sav = *pp;
|
||||||
*pp = 0;
|
*pp = 0;
|
||||||
if (!(cnam = findcmd(str + 1))) {
|
if (!(cnam = findcmd(str + 1, 1))) {
|
||||||
Alias a = (Alias) aliastab->getnode(aliastab, str + 1);
|
Alias a = (Alias) aliastab->getnode(aliastab, str + 1);
|
||||||
|
|
||||||
if (a)
|
if (a)
|
||||||
cnam = ztrdup(a->text);
|
cnam = a->text;
|
||||||
else {
|
else {
|
||||||
if (isset(NOMATCH))
|
if (isset(NOMATCH))
|
||||||
zerr("%s not found", str + 1, 0);
|
zerr("%s not found", str + 1, 0);
|
||||||
|
@ -394,7 +408,6 @@ filesubstr(char **namptr, int assign)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*namptr = dupstring(cnam);
|
*namptr = dupstring(cnam);
|
||||||
zsfree(cnam);
|
|
||||||
if (sav) {
|
if (sav) {
|
||||||
*pp = sav;
|
*pp = sav;
|
||||||
*namptr = dyncat(*namptr, pp);
|
*namptr = dyncat(*namptr, pp);
|
||||||
|
@ -408,20 +421,27 @@ filesubstr(char **namptr, int assign)
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
static char *
|
static char *
|
||||||
strcatsub(char **d, char *pb, char *pe, char *src, int l, char *s, int glbsub)
|
strcatsub(char **d, char *pb, char *pe, char *src, int l, char *s, int glbsub,
|
||||||
|
int copied)
|
||||||
{
|
{
|
||||||
|
char *dest;
|
||||||
int pl = pe - pb;
|
int pl = pe - pb;
|
||||||
char *dest = ncalloc(pl + l + (s ? strlen(s) : 0) + 1);
|
|
||||||
|
|
||||||
*d = dest;
|
if (!pl && (!s || !*s)) {
|
||||||
strncpy(dest, pb, pl);
|
*d = dest = (copied ? src : dupstring(src));
|
||||||
dest += pl;
|
if (glbsub)
|
||||||
strcpy(dest, src);
|
tokenize(dest);
|
||||||
if (glbsub)
|
} else {
|
||||||
tokenize(dest);
|
*d = dest = hcalloc(pl + l + (s ? strlen(s) : 0) + 1);
|
||||||
dest += l;
|
strncpy(dest, pb, pl);
|
||||||
if (s)
|
dest += pl;
|
||||||
strcpy(dest, s);
|
strcpy(dest, src);
|
||||||
|
if (glbsub)
|
||||||
|
tokenize(dest);
|
||||||
|
dest += l;
|
||||||
|
if (s)
|
||||||
|
strcpy(dest, s);
|
||||||
|
}
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,7 +549,7 @@ dopadding(char *str, int prenum, int postnum, char *preone, char *postone, char
|
||||||
if (lr == ls)
|
if (lr == ls)
|
||||||
return str;
|
return str;
|
||||||
|
|
||||||
r = ret = (char *)halloc(lr + 1);
|
r = ret = (char *)zhalloc(lr + 1);
|
||||||
|
|
||||||
if (prenum) {
|
if (prenum) {
|
||||||
if (postnum) {
|
if (postnum) {
|
||||||
|
@ -657,7 +677,7 @@ get_intarg(char **s)
|
||||||
{
|
{
|
||||||
char *t = get_strarg(*s + 1);
|
char *t = get_strarg(*s + 1);
|
||||||
char *p, sav;
|
char *p, sav;
|
||||||
long ret;
|
zlong ret;
|
||||||
|
|
||||||
if (!*t)
|
if (!*t)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -671,7 +691,7 @@ get_intarg(char **s)
|
||||||
singsub(&p);
|
singsub(&p);
|
||||||
if (errflag)
|
if (errflag)
|
||||||
return -1;
|
return -1;
|
||||||
ret = matheval(p);
|
ret = mathevali(p);
|
||||||
if (errflag)
|
if (errflag)
|
||||||
return -1;
|
return -1;
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -679,6 +699,26 @@ get_intarg(char **s)
|
||||||
return ret < 0 ? -ret : ret;
|
return ret < 0 ? -ret : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Parsing for the (e) flag. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
subst_parse_str(char **sp, int single)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
*sp = s = dupstring(*sp);
|
||||||
|
|
||||||
|
if (!parsestr(s)) {
|
||||||
|
if (!single) {
|
||||||
|
for (; *s; s++)
|
||||||
|
if (*s == Qstring)
|
||||||
|
*s = String;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* parameter substitution */
|
/* parameter substitution */
|
||||||
|
|
||||||
#define isstring(c) ((c) == '$' || (char)(c) == String || (char)(c) == Qstring)
|
#define isstring(c) ((c) == '$' || (char)(c) == String || (char)(c) == Qstring)
|
||||||
|
@ -688,10 +728,9 @@ get_intarg(char **s)
|
||||||
LinkNode
|
LinkNode
|
||||||
paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
{
|
{
|
||||||
char *aptr = *str;
|
char *aptr = *str, c, cc;
|
||||||
char *s = aptr, *fstr, *idbeg, *idend, *ostr = (char *) getdata(n);
|
char *s = aptr, *fstr, *idbeg, *idend, *ostr = (char *) getdata(n);
|
||||||
int colf; /* != 0 means we found a colon after the name */
|
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 isarr = 0;
|
||||||
int plan9 = isset(RCEXPANDPARAM);
|
int plan9 = isset(RCEXPANDPARAM);
|
||||||
int globsubst = isset(GLOBSUBST);
|
int globsubst = isset(GLOBSUBST);
|
||||||
|
@ -699,45 +738,58 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
int whichlen = 0;
|
int whichlen = 0;
|
||||||
int chkset = 0;
|
int chkset = 0;
|
||||||
int vunset = 0;
|
int vunset = 0;
|
||||||
|
int wantt = 0;
|
||||||
int spbreak = isset(SHWORDSPLIT) && !ssub && !qt;
|
int spbreak = isset(SHWORDSPLIT) && !ssub && !qt;
|
||||||
char *val = NULL, **aval = NULL;
|
char *val = NULL, **aval = NULL;
|
||||||
unsigned int fwidth = 0;
|
unsigned int fwidth = 0;
|
||||||
Value v;
|
struct value vbuf;
|
||||||
|
Value v = NULL;
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
int flnum = 0;
|
int flnum = 0;
|
||||||
int substr = 0;
|
|
||||||
int sortit = 0, casind = 0;
|
int sortit = 0, casind = 0;
|
||||||
int casmod = 0;
|
int casmod = 0;
|
||||||
|
int quotemod = 0, quotetype = 0, quoteerr = 0;
|
||||||
|
int visiblemod = 0;
|
||||||
char *sep = NULL, *spsep = NULL;
|
char *sep = NULL, *spsep = NULL;
|
||||||
char *premul = NULL, *postmul = NULL, *preone = NULL, *postone = NULL;
|
char *premul = NULL, *postmul = NULL, *preone = NULL, *postone = NULL;
|
||||||
long prenum = 0, postnum = 0;
|
char *replstr = NULL; /* replacement string for /orig/repl */
|
||||||
|
zlong prenum = 0, postnum = 0;
|
||||||
int copied = 0;
|
int copied = 0;
|
||||||
int arrasg = 0;
|
int arrasg = 0;
|
||||||
int eval = 0;
|
int eval = 0;
|
||||||
|
int aspar = 0;
|
||||||
|
int presc = 0;
|
||||||
int nojoin = 0;
|
int nojoin = 0;
|
||||||
char inbrace = 0; /* != 0 means ${...}, otherwise $... */
|
char inbrace = 0; /* != 0 means ${...}, otherwise $... */
|
||||||
|
char hkeys = 0;
|
||||||
|
char hvals = 0;
|
||||||
|
int subexp;
|
||||||
|
|
||||||
*s++ = '\0';
|
*s++ = '\0';
|
||||||
if (!ialnum(*s) && *s != '#' && *s != Pound && *s != '-' &&
|
if (!ialnum(c = *s) && c != '#' && c != Pound && c != '-' &&
|
||||||
*s != '!' && *s != '$' && *s != String && *s != Qstring &&
|
c != '!' && c != '$' && c != String && c != Qstring &&
|
||||||
*s != '?' && *s != Quest && *s != '_' &&
|
c != '?' && c != Quest && c != '_' &&
|
||||||
*s != '*' && *s != Star && *s != '@' && *s != '{' &&
|
c != '*' && c != Star && c != '@' && c != '{' &&
|
||||||
*s != Inbrace && *s != '=' && *s != Equals && *s != Hat &&
|
c != Inbrace && c != '=' && c != Equals && c != Hat &&
|
||||||
*s != '^' && *s != '~' && *s != Tilde && *s != '+') {
|
c != '^' && c != '~' && c != Tilde && c != '+') {
|
||||||
s[-1] = '$';
|
s[-1] = '$';
|
||||||
*str = s;
|
*str = s;
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
DPUTS(*s == '{', "BUG: inbrace == '{' in paramsubst()");
|
DPUTS(c == '{', "BUG: inbrace == '{' in paramsubst()");
|
||||||
if (*s == Inbrace) {
|
if (c == Inbrace) {
|
||||||
inbrace = 1;
|
inbrace = 1;
|
||||||
s++;
|
s++;
|
||||||
if (*s == '(' || *s == Inpar) {
|
if ((c = *s) == '!' && s[1] != Outbrace && emulation == EMULATE_KSH) {
|
||||||
|
hkeys = SCANPM_WANTKEYS;
|
||||||
|
s++;
|
||||||
|
} else if (c == '(' || c == Inpar) {
|
||||||
char *t, sav;
|
char *t, sav;
|
||||||
int tt = 0;
|
int tt = 0;
|
||||||
long num;
|
zlong num;
|
||||||
int escapes = 0;
|
int escapes = 0;
|
||||||
int klen;
|
int klen;
|
||||||
|
#define UNTOK(C) (itok(C) ? ztokens[(C) - Pound] : (C))
|
||||||
#define UNTOK_AND_ESCAPE(X) {\
|
#define UNTOK_AND_ESCAPE(X) {\
|
||||||
untokenize(X = dupstring(s + 1));\
|
untokenize(X = dupstring(s + 1));\
|
||||||
if (escapes) {\
|
if (escapes) {\
|
||||||
|
@ -746,34 +798,34 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
}\
|
}\
|
||||||
}
|
}
|
||||||
|
|
||||||
for (s++; *s != ')' && *s != Outpar; s++, tt = 0) {
|
for (s++; (c = *s) != ')' && c != Outpar; s++, tt = 0) {
|
||||||
switch (*s) {
|
switch (c) {
|
||||||
case ')':
|
case ')':
|
||||||
case Outpar:
|
case Outpar:
|
||||||
break;
|
break;
|
||||||
case 'A':
|
case 'A':
|
||||||
arrasg = 1;
|
++arrasg;
|
||||||
break;
|
break;
|
||||||
case '@':
|
case '@':
|
||||||
nojoin = 1;
|
nojoin = 1;
|
||||||
break;
|
break;
|
||||||
case 'M':
|
case 'M':
|
||||||
flags |= 8;
|
flags |= SUB_MATCH;
|
||||||
break;
|
break;
|
||||||
case 'R':
|
case 'R':
|
||||||
flags |= 16;
|
flags |= SUB_REST;
|
||||||
break;
|
break;
|
||||||
case 'B':
|
case 'B':
|
||||||
flags |= 32;
|
flags |= SUB_BIND;
|
||||||
break;
|
break;
|
||||||
case 'E':
|
case 'E':
|
||||||
flags |= 64;
|
flags |= SUB_EIND;
|
||||||
break;
|
break;
|
||||||
case 'N':
|
case 'N':
|
||||||
flags |= 128;
|
flags |= SUB_LEN;
|
||||||
break;
|
break;
|
||||||
case 'S':
|
case 'S':
|
||||||
substr = 1;
|
flags |= SUB_SUBSTR;
|
||||||
break;
|
break;
|
||||||
case 'I':
|
case 'I':
|
||||||
flnum = get_intarg(&s);
|
flnum = get_intarg(&s);
|
||||||
|
@ -800,9 +852,27 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
case 'i':
|
case 'i':
|
||||||
casind = 1;
|
casind = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'V':
|
||||||
|
visiblemod++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'q':
|
||||||
|
quotemod++, quotetype++;
|
||||||
|
break;
|
||||||
|
case 'Q':
|
||||||
|
quotemod--;
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
quoteerr = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'e':
|
case 'e':
|
||||||
eval = 1;
|
eval = 1;
|
||||||
break;
|
break;
|
||||||
|
case 'P':
|
||||||
|
aspar = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'c':
|
case 'c':
|
||||||
whichlen = 1;
|
whichlen = 1;
|
||||||
|
@ -851,7 +921,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
prenum = num;
|
prenum = num;
|
||||||
else
|
else
|
||||||
postnum = num;
|
postnum = num;
|
||||||
if (s[1] != sav)
|
if (UNTOK(s[1]) != UNTOK(sav))
|
||||||
break;
|
break;
|
||||||
t = get_strarg(++s);
|
t = get_strarg(++s);
|
||||||
if (!*t)
|
if (!*t)
|
||||||
|
@ -865,7 +935,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
*t = sav;
|
*t = sav;
|
||||||
sav = *s;
|
sav = *s;
|
||||||
s = t + 1;
|
s = t + 1;
|
||||||
if (*s != sav) {
|
if (UNTOK(*s) != UNTOK(sav)) {
|
||||||
s--;
|
s--;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -886,6 +956,21 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
escapes = 1;
|
escapes = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'k':
|
||||||
|
hkeys = SCANPM_WANTKEYS;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
hvals = SCANPM_WANTVALS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 't':
|
||||||
|
wantt = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '%':
|
||||||
|
presc++;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
flagerr:
|
flagerr:
|
||||||
zerr("error in flags", NULL, 0);
|
zerr("error in flags", NULL, 0);
|
||||||
|
@ -904,31 +989,33 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
postmul = " ";
|
postmul = " ";
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (*s == '^' || *s == Hat) {
|
if ((c = *s) == '^' || c == Hat) {
|
||||||
if (*++s == '^' || *s == Hat) {
|
if ((c = *++s) == '^' || c == Hat) {
|
||||||
plan9 = 0;
|
plan9 = 0;
|
||||||
s++;
|
s++;
|
||||||
} else
|
} else
|
||||||
plan9 = 1;
|
plan9 = 1;
|
||||||
} else if (*s == '=' || *s == Equals) {
|
} else if ((c = *s) == '=' || c == Equals) {
|
||||||
if (*++s == '=' || *s == Equals) {
|
if ((c = *++s) == '=' || c == Equals) {
|
||||||
spbreak = 0;
|
spbreak = 0;
|
||||||
s++;
|
s++;
|
||||||
} else
|
} else
|
||||||
spbreak = 1;
|
spbreak = 1;
|
||||||
} else if ((*s == '#' || *s == Pound) &&
|
} else if ((c == '#' || c == Pound) &&
|
||||||
(iident(s[1])
|
(iident(cc = s[1])
|
||||||
|| s[1] == '*' || s[1] == Star || s[1] == '@'
|
|| cc == '*' || cc == Star || cc == '@'
|
||||||
|| (isstring(s[1]) && (s[2] == Inbrace || s[2] == Inpar))))
|
|| cc == '-' || (cc == ':' && s[2] == '-')
|
||||||
|
|| (isstring(cc) && (s[2] == Inbrace || s[2] == Inpar))))
|
||||||
getlen = 1 + whichlen, s++;
|
getlen = 1 + whichlen, s++;
|
||||||
else if (*s == '~' || *s == Tilde) {
|
else if (c == '~' || c == Tilde) {
|
||||||
if (*++s == '~' || *s == Tilde) {
|
if ((c = *++s) == '~' || c == Tilde) {
|
||||||
globsubst = 0;
|
globsubst = 0;
|
||||||
s++;
|
s++;
|
||||||
} else
|
} else
|
||||||
globsubst = 1;
|
globsubst = 1;
|
||||||
} else if (*s == '+')
|
} else if (c == '+') {
|
||||||
if (iident(s[1]))
|
if (iident(s[1]) || (aspar && isstring(s[1]) &&
|
||||||
|
(s[2] == Inbrace || s[2] == Inpar)))
|
||||||
chkset = 1, s++;
|
chkset = 1, s++;
|
||||||
else if (!inbrace) {
|
else if (!inbrace) {
|
||||||
*aptr = '$';
|
*aptr = '$';
|
||||||
|
@ -938,13 +1025,16 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
zerr("bad substitution", NULL, 0);
|
zerr("bad substitution", NULL, 0);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
} else if (inbrace && INULL(*s))
|
||||||
|
s++;
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
globsubst = globsubst && !qt;
|
globsubst = globsubst && !qt;
|
||||||
|
|
||||||
idbeg = s;
|
idbeg = s;
|
||||||
if (s[-1] && isstring(*s) && (s[1] == Inbrace || s[1] == Inpar)) {
|
if ((subexp = (inbrace && s[-1] && isstring(*s) &&
|
||||||
|
(s[1] == Inbrace || s[1] == Inpar)))) {
|
||||||
int sav;
|
int sav;
|
||||||
int quoted = *s == Qstring;
|
int quoted = *s == Qstring;
|
||||||
|
|
||||||
|
@ -952,17 +1042,78 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
skipparens(*s, *s == Inpar ? Outpar : Outbrace, &s);
|
skipparens(*s, *s == Inpar ? Outpar : Outbrace, &s);
|
||||||
sav = *s;
|
sav = *s;
|
||||||
*s = 0;
|
*s = 0;
|
||||||
if (multsub(&val, &aval, &isarr, NULL) && quoted) {
|
if (multsub(&val, (aspar ? NULL : &aval), &isarr, NULL) && quoted) {
|
||||||
isarr = -1;
|
isarr = -1;
|
||||||
aval = alloc(sizeof(char *));
|
aval = (char **) hcalloc(sizeof(char *));
|
||||||
}
|
aspar = 0;
|
||||||
if (isarr)
|
} else if (aspar)
|
||||||
isarr = -1;
|
idbeg = val;
|
||||||
copied = 1;
|
|
||||||
*s = sav;
|
*s = sav;
|
||||||
|
while (INULL(*s))
|
||||||
|
s++;
|
||||||
v = (Value) NULL;
|
v = (Value) NULL;
|
||||||
} else if (!(v = getvalue(&s, (unset(KSHARRAYS) || inbrace) ? 1 : -1)))
|
} else if (aspar) {
|
||||||
vunset = 1;
|
if ((v = getvalue(&vbuf, &s, 1))) {
|
||||||
|
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))) ||
|
||||||
|
(v->pm && (v->pm->flags & PM_UNSET)))
|
||||||
|
vunset = 1;
|
||||||
|
|
||||||
|
if (wantt) {
|
||||||
|
if (v && v->pm && !(v->pm->flags & PM_UNSET)) {
|
||||||
|
int f = v->pm->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_SPECIAL)
|
||||||
|
val = dyncat(val, "-special");
|
||||||
|
vunset = 0;
|
||||||
|
} else
|
||||||
|
val = dupstring("");
|
||||||
|
|
||||||
|
v = NULL;
|
||||||
|
isarr = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
while (v || ((inbrace || (unset(KSHARRAYS) && vunset)) && isbrack(*s))) {
|
while (v || ((inbrace || (unset(KSHARRAYS) && vunset)) && isbrack(*s))) {
|
||||||
if (!v) {
|
if (!v) {
|
||||||
Param pm;
|
Param pm;
|
||||||
|
@ -986,9 +1137,15 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
if (getindex(&s, v) || s == os)
|
if (getindex(&s, v) || s == os)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ((isarr = v->isarr))
|
if ((isarr = v->isarr)) {
|
||||||
aval = getarrvalue(v);
|
/* No way to get here with v->inv != 0, so getvaluearr() *
|
||||||
else {
|
* is called by getarrvalue(); needn't test PM_HASHED. */
|
||||||
|
if (v->isarr == SCANPM_WANTINDEX) {
|
||||||
|
isarr = v->isarr = 0;
|
||||||
|
val = dupstring(v->pm->nam);
|
||||||
|
} else
|
||||||
|
aval = getarrvalue(v);
|
||||||
|
} else {
|
||||||
if (v->pm->flags & PM_ARRAY) {
|
if (v->pm->flags & PM_ARRAY) {
|
||||||
int tmplen = arrlen(v->pm->gets.afn(v->pm));
|
int tmplen = arrlen(v->pm->gets.afn(v->pm));
|
||||||
|
|
||||||
|
@ -1013,7 +1170,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
else
|
else
|
||||||
while (iblank(*t))
|
while (iblank(*t))
|
||||||
t++;
|
t++;
|
||||||
val = (char *)ncalloc(fwidth + 1);
|
val = (char *) hcalloc(fwidth + 1);
|
||||||
val[fwidth] = '\0';
|
val[fwidth] = '\0';
|
||||||
if ((t0 = strlen(t)) > fwidth)
|
if ((t0 = strlen(t)) > fwidth)
|
||||||
t0 = fwidth;
|
t0 = fwidth;
|
||||||
|
@ -1023,18 +1180,28 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
case PM_RIGHT_B:
|
case PM_RIGHT_B:
|
||||||
case PM_RIGHT_Z:
|
case PM_RIGHT_Z:
|
||||||
case PM_RIGHT_Z | PM_RIGHT_B:
|
case PM_RIGHT_Z | PM_RIGHT_B:
|
||||||
if (strlen(val) < fwidth) {
|
{
|
||||||
t = (char *)ncalloc(fwidth + 1);
|
int zero = 1;
|
||||||
memset(t, (v->pm->flags & PM_RIGHT_B) ? ' ' : '0', fwidth);
|
|
||||||
if ((t0 = strlen(val)) > fwidth)
|
if (strlen(val) < fwidth) {
|
||||||
t0 = fwidth;
|
if (v->pm->flags & PM_RIGHT_Z) {
|
||||||
strcpy(t + (fwidth - t0), val);
|
for (t = val; iblank(*t); t++);
|
||||||
val = t;
|
if (!*t || !idigit(*t))
|
||||||
} else {
|
zero = 0;
|
||||||
t = (char *)ncalloc(fwidth + 1);
|
}
|
||||||
t[fwidth] = '\0';
|
t = (char *) hcalloc(fwidth + 1);
|
||||||
strncpy(t, val + strlen(val) - fwidth, fwidth);
|
memset(t, (((v->pm->flags & PM_RIGHT_B) || !zero) ?
|
||||||
val = t;
|
' ' : '0'), fwidth);
|
||||||
|
if ((t0 = strlen(val)) > fwidth)
|
||||||
|
t0 = fwidth;
|
||||||
|
strcpy(t + (fwidth - t0), val);
|
||||||
|
val = t;
|
||||||
|
} else {
|
||||||
|
t = (char *) hcalloc(fwidth + 1);
|
||||||
|
t[fwidth] = '\0';
|
||||||
|
strncpy(t, val + strlen(val) - fwidth, fwidth);
|
||||||
|
val = t;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1043,13 +1210,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
|
|
||||||
case PM_LOWER:
|
case PM_LOWER:
|
||||||
t = val;
|
t = val;
|
||||||
for (; *t; t++)
|
for (; (c = *t); t++)
|
||||||
*t = tulower(*t);
|
*t = tulower(c);
|
||||||
break;
|
break;
|
||||||
case PM_UPPER:
|
case PM_UPPER:
|
||||||
t = val;
|
t = val;
|
||||||
for (; *t; t++)
|
for (; (c = *t); t++)
|
||||||
*t = tuupper(*t);
|
*t = tuupper(c);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1062,12 +1229,15 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
if (nojoin)
|
if (nojoin)
|
||||||
isarr = -1;
|
isarr = -1;
|
||||||
if (qt && !getlen && isarr > 0) {
|
if (qt && !getlen && isarr > 0) {
|
||||||
val = sepjoin(aval, sep);
|
val = sepjoin(aval, sep, 1);
|
||||||
isarr = 0;
|
isarr = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
idend = s;
|
idend = s;
|
||||||
|
if (inbrace)
|
||||||
|
while (INULL(*s))
|
||||||
|
s++;
|
||||||
if ((colf = *s == ':'))
|
if ((colf = *s == ':'))
|
||||||
s++;
|
s++;
|
||||||
|
|
||||||
|
@ -1077,12 +1247,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
fstr = s;
|
fstr = s;
|
||||||
if (inbrace) {
|
if (inbrace) {
|
||||||
int bct;
|
int bct;
|
||||||
for (bct = 1;; fstr++) {
|
for (bct = 1; (c = *fstr); fstr++) {
|
||||||
if (!*fstr)
|
if (c == Inbrace)
|
||||||
break;
|
|
||||||
else if (*fstr == Inbrace)
|
|
||||||
bct++;
|
bct++;
|
||||||
else if (*fstr == Outbrace && !--bct)
|
else if (c == Outbrace && !--bct)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1091,36 +1259,83 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
zerr("closing brace expected", NULL, 0);
|
zerr("closing brace expected", NULL, 0);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (*fstr)
|
if (c)
|
||||||
*fstr++ = '\0';
|
*fstr++ = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for ${..?..} or ${..=..} or one of those. *
|
/* Check for ${..?..} or ${..=..} or one of those. *
|
||||||
* Only works if the name is in braces. */
|
* Only works if the name is in braces. */
|
||||||
|
|
||||||
if (inbrace && (*s == '-' ||
|
if (inbrace && ((c = *s) == '-' ||
|
||||||
*s == '+' ||
|
c == '+' ||
|
||||||
*s == ':' ||
|
c == ':' ||
|
||||||
*s == '=' || *s == Equals ||
|
c == '=' || c == Equals ||
|
||||||
*s == '%' ||
|
c == '%' ||
|
||||||
*s == '#' || *s == Pound ||
|
c == '#' || c == Pound ||
|
||||||
*s == '?' || *s == Quest)) {
|
c == '?' || c == Quest ||
|
||||||
|
c == '/')) {
|
||||||
|
|
||||||
if (!flnum)
|
if (!flnum)
|
||||||
flnum++;
|
flnum++;
|
||||||
if (*s == '%')
|
if (c == '%')
|
||||||
flags |= 1;
|
flags |= SUB_END;
|
||||||
|
|
||||||
/* Check for ${..%%..} or ${..##..} */
|
/* Check for ${..%%..} or ${..##..} */
|
||||||
if ((*s == '%' || *s == '#' || *s == Pound) && *s == s[1]) {
|
if ((c == '%' || c == '#' || c == Pound) && c == s[1]) {
|
||||||
s++;
|
s++;
|
||||||
doub = 1;
|
/* we have %%, not %, or ##, not # */
|
||||||
|
flags |= SUB_LONG;
|
||||||
}
|
}
|
||||||
s++;
|
s++;
|
||||||
|
if (s[-1] == '/') {
|
||||||
|
char *ptr;
|
||||||
|
/*
|
||||||
|
* previous flags are irrelevant, except for (S) which
|
||||||
|
* indicates shortest substring; else look for longest.
|
||||||
|
*/
|
||||||
|
flags = (flags & SUB_SUBSTR) ? 0 : SUB_LONG;
|
||||||
|
if ((c = *s) == '/') {
|
||||||
|
/* doubled, so replace all occurrences */
|
||||||
|
flags |= SUB_GLOBAL;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
/* Check for anchored substitution */
|
||||||
|
if (c == '%') {
|
||||||
|
/* anchor at tail */
|
||||||
|
flags |= SUB_END;
|
||||||
|
s++;
|
||||||
|
} else if (c == '#' || c == Pound) {
|
||||||
|
/* anchor at head: this is the `normal' case in getmatch */
|
||||||
|
s++;
|
||||||
|
} else
|
||||||
|
flags |= SUB_SUBSTR;
|
||||||
|
/*
|
||||||
|
* Find the / marking the end of the search pattern.
|
||||||
|
* If there isn't one, we're just going to delete that,
|
||||||
|
* i.e. replace it with an empty string.
|
||||||
|
*
|
||||||
|
* This allows quotation of the slash with '\\/'. Why
|
||||||
|
* two? Well, for a non-quoted string we can check for
|
||||||
|
* Bnull+/, which is what you get from `\/', but inside
|
||||||
|
* double quotes the Bnull isn't there, so it's not
|
||||||
|
* consistent.
|
||||||
|
*/
|
||||||
|
for (ptr = s; (c = *ptr) && c != '/'; ptr++)
|
||||||
|
if (c == '\\' && ptr[1] == '/')
|
||||||
|
chuck(ptr);
|
||||||
|
replstr = (*ptr && ptr[1]) ? ptr+1 : "";
|
||||||
|
*ptr = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
flags |= (doub << 1) | (substr << 2) | (colf << 8);
|
if (colf)
|
||||||
if (!(flags & 0xf8))
|
flags |= SUB_ALL;
|
||||||
flags |= 16;
|
/*
|
||||||
|
* With no special flags, i.e. just a # or % or whatever,
|
||||||
|
* the matched portion is removed and we keep the rest.
|
||||||
|
* We also want the rest when we're doing a substitution.
|
||||||
|
*/
|
||||||
|
if (!(flags & (SUB_MATCH|SUB_REST|SUB_BIND|SUB_EIND|SUB_LEN)))
|
||||||
|
flags |= SUB_REST;
|
||||||
|
|
||||||
if (colf && !vunset)
|
if (colf && !vunset)
|
||||||
vunset = (isarr) ? !*aval : !*val || (*val == Nularg && !val[1]);
|
vunset = (isarr) ? !*aval : !*val || (*val == Nularg && !val[1]);
|
||||||
|
@ -1138,7 +1353,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
case '-':
|
case '-':
|
||||||
if (vunset) {
|
if (vunset) {
|
||||||
val = dupstring(s);
|
val = dupstring(s);
|
||||||
multsub(&val, &aval, &isarr, NULL);
|
multsub(&val, NULL, &isarr, NULL);
|
||||||
copied = 1;
|
copied = 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1164,7 +1379,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
if (arrasg) {
|
if (arrasg) {
|
||||||
char *arr[2], **t, **a, **p;
|
char *arr[2], **t, **a, **p;
|
||||||
if (spsep || spbreak) {
|
if (spsep || spbreak) {
|
||||||
aval = sepsplit(val, spsep, 0);
|
aval = sepsplit(val, spsep, 0, 1);
|
||||||
isarr = 2;
|
isarr = 2;
|
||||||
sep = spsep = NULL;
|
sep = spsep = NULL;
|
||||||
spbreak = 0;
|
spbreak = 0;
|
||||||
|
@ -1176,10 +1391,15 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
else
|
else
|
||||||
t = aval;
|
t = aval;
|
||||||
} else if (!isarr) {
|
} else if (!isarr) {
|
||||||
arr[0] = val;
|
if (!*val && arrasg > 1) {
|
||||||
arr[1] = NULL;
|
arr[0] = NULL;
|
||||||
|
l = 0;
|
||||||
|
} else {
|
||||||
|
arr[0] = val;
|
||||||
|
arr[1] = NULL;
|
||||||
|
l = 1;
|
||||||
|
}
|
||||||
t = aval = arr;
|
t = aval = arr;
|
||||||
l = 1;
|
|
||||||
} else
|
} else
|
||||||
l = arrlen(aval), t = aval;
|
l = arrlen(aval), t = aval;
|
||||||
p = a = zalloc(sizeof(char *) * (l + 1));
|
p = a = zalloc(sizeof(char *) * (l + 1));
|
||||||
|
@ -1188,7 +1408,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
*p++ = ztrdup(*t++);
|
*p++ = ztrdup(*t++);
|
||||||
}
|
}
|
||||||
*p++ = NULL;
|
*p++ = NULL;
|
||||||
setaparam(idbeg, a);
|
if (arrasg > 1) {
|
||||||
|
Param pm = sethparam(idbeg, a);
|
||||||
|
if (pm)
|
||||||
|
aval = paramvalarr(pm->gets.hfn(pm), hkeys|hvals);
|
||||||
|
} else
|
||||||
|
setaparam(idbeg, a);
|
||||||
} else {
|
} else {
|
||||||
untokenize(val);
|
untokenize(val);
|
||||||
setsparam(idbeg, ztrdup(val));
|
setsparam(idbeg, ztrdup(val));
|
||||||
|
@ -1214,27 +1439,47 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
case '%':
|
case '%':
|
||||||
case '#':
|
case '#':
|
||||||
case Pound:
|
case Pound:
|
||||||
if (qt)
|
case '/':
|
||||||
if (parse_subst_string(s)) {
|
if (qt) {
|
||||||
|
int one = noerrs, oef = errflag, haserr;
|
||||||
|
|
||||||
|
if (!quoteerr)
|
||||||
|
noerrs = 1;
|
||||||
|
haserr = parse_subst_string(s);
|
||||||
|
noerrs = one;
|
||||||
|
if (!quoteerr) {
|
||||||
|
errflag = oef;
|
||||||
|
if (haserr)
|
||||||
|
tokenize(s);
|
||||||
|
} else if (haserr || errflag) {
|
||||||
zerr("parse error in ${...%c...} substitution",
|
zerr("parse error in ${...%c...} substitution",
|
||||||
NULL, s[-1]);
|
NULL, s[-1]);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
singsub(&s);
|
}
|
||||||
|
{
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!vunset && isarr) {
|
if (!vunset && isarr) {
|
||||||
char **ap = aval;
|
getmatcharr(&aval, s, flags, flnum, replstr);
|
||||||
char **pp = aval = (char **)ncalloc(sizeof(char *) * (arrlen(aval) + 1));
|
|
||||||
|
|
||||||
while ((*pp = *ap++)) {
|
|
||||||
if (getmatch(pp, s, flags, flnum))
|
|
||||||
pp++;
|
|
||||||
}
|
|
||||||
copied = 1;
|
copied = 1;
|
||||||
} else {
|
} else {
|
||||||
if (vunset)
|
if (vunset)
|
||||||
val = dupstring("");
|
val = dupstring("");
|
||||||
getmatch(&val, s, flags, flnum);
|
getmatch(&val, s, flags, flnum, replstr);
|
||||||
copied = 1;
|
copied = 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1259,7 +1504,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
else {
|
else {
|
||||||
char *ss;
|
char *ss;
|
||||||
char **ap = aval;
|
char **ap = aval;
|
||||||
char **pp = aval = (char **)ncalloc(sizeof(char *) * (arrlen(aval) + 1));
|
char **pp = aval = (char **) hcalloc(sizeof(char *) *
|
||||||
|
(arrlen(aval) + 1));
|
||||||
|
|
||||||
while ((*pp = *ap++)) {
|
while ((*pp = *ap++)) {
|
||||||
ss = s;
|
ss = s;
|
||||||
|
@ -1272,6 +1518,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
}
|
}
|
||||||
s = ss;
|
s = ss;
|
||||||
}
|
}
|
||||||
|
copied = 1;
|
||||||
if (inbrace && *s) {
|
if (inbrace && *s) {
|
||||||
if (*s == ':' && !imeta(s[1]))
|
if (*s == ':' && !imeta(s[1]))
|
||||||
zerr("unrecognized modifier `%c'", NULL, s[1]);
|
zerr("unrecognized modifier `%c'", NULL, s[1]);
|
||||||
|
@ -1316,10 +1563,15 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
val = dupstring(buf);
|
val = dupstring(buf);
|
||||||
isarr = 0;
|
isarr = 0;
|
||||||
}
|
}
|
||||||
|
mult_isarr = isarr;
|
||||||
if (isarr > 0 && !plan9 && (!aval || !aval[0])) {
|
if (isarr > 0 && !plan9 && (!aval || !aval[0])) {
|
||||||
val = dupstring("");
|
val = dupstring("");
|
||||||
isarr = 0;
|
isarr = 0;
|
||||||
} else if (isarr && aval && aval[0] && !aval[1]) {
|
} else if (isarr && aval && aval[0] && !aval[1]) {
|
||||||
|
/* treat a one-element array as a scalar for purposes of *
|
||||||
|
* concatenation with surrounding text (some${param}thing) *
|
||||||
|
* and rc_expand_param handling. Note: mult_isarr (above) *
|
||||||
|
* propagates the true array type from nested expansions. */
|
||||||
val = aval[0];
|
val = aval[0];
|
||||||
isarr = 0;
|
isarr = 0;
|
||||||
}
|
}
|
||||||
|
@ -1327,9 +1579,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
* It means that we must join arrays and should not split words. */
|
* It means that we must join arrays and should not split words. */
|
||||||
if (ssub || spbreak || spsep || sep) {
|
if (ssub || spbreak || spsep || sep) {
|
||||||
if (isarr)
|
if (isarr)
|
||||||
val = sepjoin(aval, sep), isarr = 0;
|
val = sepjoin(aval, sep, 1), isarr = 0;
|
||||||
if (!ssub && (spbreak || spsep)) {
|
if (!ssub && (spbreak || spsep)) {
|
||||||
aval = sepsplit(val, spsep, 0);
|
aval = sepsplit(val, spsep, 0, 1);
|
||||||
if (!aval || !aval[0])
|
if (!aval || !aval[0])
|
||||||
val = dupstring("");
|
val = dupstring("");
|
||||||
else if (!aval[1])
|
else if (!aval[1])
|
||||||
|
@ -1337,6 +1589,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
else
|
else
|
||||||
isarr = 2;
|
isarr = 2;
|
||||||
}
|
}
|
||||||
|
mult_isarr = isarr;
|
||||||
}
|
}
|
||||||
if (casmod) {
|
if (casmod) {
|
||||||
if (isarr) {
|
if (isarr) {
|
||||||
|
@ -1367,6 +1620,134 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
makecapitals(&val);
|
makecapitals(&val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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++) {
|
||||||
|
unmetafy(*ap, &len);
|
||||||
|
untokenize(*ap);
|
||||||
|
*ap = unmetafy(promptexpand(metafy(*ap, len, META_NOALLOC),
|
||||||
|
0, NULL, NULL), &len);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!copied)
|
||||||
|
val = dupstring(val), copied = 1;
|
||||||
|
unmetafy(val, &len);
|
||||||
|
untokenize(val);
|
||||||
|
val = unmetafy(promptexpand(metafy(val, len, META_NOALLOC),
|
||||||
|
0, NULL, NULL), &len);
|
||||||
|
}
|
||||||
|
opts[PROMPTSUBST] = ops;
|
||||||
|
opts[PROMPTBANG] = opb;
|
||||||
|
opts[PROMPTPERCENT] = opp;
|
||||||
|
}
|
||||||
|
if (quotemod) {
|
||||||
|
if (--quotetype > 3)
|
||||||
|
quotetype = 3;
|
||||||
|
if (isarr) {
|
||||||
|
char **ap;
|
||||||
|
|
||||||
|
if (!copied)
|
||||||
|
aval = arrdup(aval), copied = 1;
|
||||||
|
ap = aval;
|
||||||
|
|
||||||
|
if (quotemod > 0) {
|
||||||
|
if (quotetype) {
|
||||||
|
int sl;
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
|
for (; *ap; ap++) {
|
||||||
|
int pre = quotetype != 3 ? 1 : 2;
|
||||||
|
tmp = bslashquote(*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 != 2 ? '\'' : '"');
|
||||||
|
ap[0][pre + sl + 1] = '\0';
|
||||||
|
if (quotetype == 3)
|
||||||
|
ap[0][0] = '$';
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
for (; *ap; ap++)
|
||||||
|
*ap = bslashquote(*ap, NULL, 0);
|
||||||
|
} 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", NULL, 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!copied)
|
||||||
|
val = dupstring(val), copied = 1;
|
||||||
|
if (quotemod > 0) {
|
||||||
|
if (quotetype) {
|
||||||
|
int pre = quotetype != 3 ? 1 : 2;
|
||||||
|
int sl;
|
||||||
|
char *tmp;
|
||||||
|
tmp = bslashquote(val, NULL, quotetype);
|
||||||
|
sl = strlen(tmp);
|
||||||
|
val = (char *) zhalloc(pre + sl + 2);
|
||||||
|
strcpy(val + pre, tmp);
|
||||||
|
val[pre - 1] = val[pre + sl] = (quotetype != 2 ? '\'' : '"');
|
||||||
|
val[pre + sl + 1] = '\0';
|
||||||
|
if (quotetype == 3)
|
||||||
|
val[0] = '$';
|
||||||
|
} else
|
||||||
|
val = bslashquote(val, NULL, 0);
|
||||||
|
} 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", NULL, 0);
|
||||||
|
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 (isarr) {
|
if (isarr) {
|
||||||
char *x;
|
char *x;
|
||||||
char *y;
|
char *y;
|
||||||
|
@ -1378,7 +1759,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
if (aptr > (char *) getdata(n) &&
|
if (aptr > (char *) getdata(n) &&
|
||||||
aptr[-1] == Dnull && *fstr == Dnull)
|
aptr[-1] == Dnull && *fstr == Dnull)
|
||||||
*--aptr = '\0', fstr++;
|
*--aptr = '\0', fstr++;
|
||||||
y = (char *)ncalloc((aptr - ostr) + strlen(fstr) + 1);
|
y = (char *) hcalloc((aptr - ostr) + strlen(fstr) + 1);
|
||||||
strcpy(y, ostr);
|
strcpy(y, ostr);
|
||||||
*str = y + (aptr - ostr);
|
*str = y + (aptr - ostr);
|
||||||
strcpy(*str, fstr);
|
strcpy(*str, fstr);
|
||||||
|
@ -1398,26 +1779,27 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
qsort(aval, i, sizeof(char *), sortfn[sortit-1]);
|
qsort(aval, i, sizeof(char *), sortfn[sortit-1]);
|
||||||
}
|
}
|
||||||
if (plan9) {
|
if (plan9) {
|
||||||
LinkList tl = newlinklist();
|
|
||||||
LinkNode tn;
|
LinkNode tn;
|
||||||
|
local_list1(tl);
|
||||||
|
|
||||||
*--fstr = Marker;
|
*--fstr = Marker;
|
||||||
addlinknode(tl, fstr);
|
init_list1(tl, fstr);
|
||||||
if (!eval && !stringsubst(tl, firstnode(tl), ssub))
|
if (!eval && !stringsubst(&tl, firstnode(&tl), ssub))
|
||||||
return NULL;
|
return NULL;
|
||||||
*str = aptr;
|
*str = aptr;
|
||||||
tn = firstnode(tl);
|
tn = firstnode(&tl);
|
||||||
while ((x = *aval++)) {
|
while ((x = *aval++)) {
|
||||||
if (prenum || postnum)
|
if (prenum || postnum)
|
||||||
x = dopadding(x, prenum, postnum, preone, postone,
|
x = dopadding(x, prenum, postnum, preone, postone,
|
||||||
premul, postmul);
|
premul, postmul);
|
||||||
if (eval && parsestr(x))
|
if (eval && subst_parse_str(&x, (qt && !nojoin)))
|
||||||
return NULL;
|
return NULL;
|
||||||
xlen = strlen(x);
|
xlen = strlen(x);
|
||||||
for (tn = firstnode(tl);
|
for (tn = firstnode(&tl);
|
||||||
tn && *(y = (char *) getdata(tn)) == Marker;
|
tn && *(y = (char *) getdata(tn)) == Marker;
|
||||||
incnode(tn)) {
|
incnode(tn)) {
|
||||||
strcatsub(&y, ostr, aptr, x, xlen, y + 1, globsubst);
|
strcatsub(&y, ostr, aptr, x, xlen, y + 1, globsubst,
|
||||||
|
copied);
|
||||||
if (qt && !*y && isarr != 2)
|
if (qt && !*y && isarr != 2)
|
||||||
y = dupstring(nulstring);
|
y = dupstring(nulstring);
|
||||||
if (plan9)
|
if (plan9)
|
||||||
|
@ -1446,10 +1828,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
if (prenum || postnum)
|
if (prenum || postnum)
|
||||||
x = dopadding(x, prenum, postnum, preone, postone,
|
x = dopadding(x, prenum, postnum, preone, postone,
|
||||||
premul, postmul);
|
premul, postmul);
|
||||||
if (eval && parsestr(x))
|
if (eval && subst_parse_str(&x, (qt && !nojoin)))
|
||||||
return NULL;
|
return NULL;
|
||||||
xlen = strlen(x);
|
xlen = strlen(x);
|
||||||
strcatsub(&y, ostr, aptr, x, xlen, NULL, globsubst);
|
strcatsub(&y, ostr, aptr, x, xlen, NULL, globsubst, copied);
|
||||||
if (qt && !*y && isarr != 2)
|
if (qt && !*y && isarr != 2)
|
||||||
y = dupstring(nulstring);
|
y = dupstring(nulstring);
|
||||||
setdata(n, (void *) y);
|
setdata(n, (void *) y);
|
||||||
|
@ -1461,7 +1843,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
if (prenum || postnum)
|
if (prenum || postnum)
|
||||||
x = dopadding(x, prenum, postnum, preone, postone,
|
x = dopadding(x, prenum, postnum, preone, postone,
|
||||||
premul, postmul);
|
premul, postmul);
|
||||||
if (eval && parsestr(x))
|
if (eval && subst_parse_str(&x, (qt && !nojoin)))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (qt && !*x && isarr != 2)
|
if (qt && !*x && isarr != 2)
|
||||||
y = dupstring(nulstring);
|
y = dupstring(nulstring);
|
||||||
|
@ -1477,10 +1859,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
if (prenum || postnum)
|
if (prenum || postnum)
|
||||||
x = dopadding(x, prenum, postnum, preone, postone,
|
x = dopadding(x, prenum, postnum, preone, postone,
|
||||||
premul, postmul);
|
premul, postmul);
|
||||||
if (eval && parsestr(x))
|
if (eval && subst_parse_str(&x, (qt && !nojoin)))
|
||||||
return NULL;
|
return NULL;
|
||||||
xlen = strlen(x);
|
xlen = strlen(x);
|
||||||
*str = strcatsub(&y, aptr, aptr, x, xlen, fstr, globsubst);
|
*str = strcatsub(&y, aptr, aptr, x, xlen, fstr, globsubst, copied);
|
||||||
if (qt && !*y && isarr != 2)
|
if (qt && !*y && isarr != 2)
|
||||||
y = dupstring(nulstring);
|
y = dupstring(nulstring);
|
||||||
insertlinknode(l, n, (void *) y), incnode(n);
|
insertlinknode(l, n, (void *) y), incnode(n);
|
||||||
|
@ -1496,11 +1878,11 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
if (prenum || postnum)
|
if (prenum || postnum)
|
||||||
x = dopadding(x, prenum, postnum, preone, postone,
|
x = dopadding(x, prenum, postnum, preone, postone,
|
||||||
premul, postmul);
|
premul, postmul);
|
||||||
if (eval && parsestr(x))
|
if (eval && subst_parse_str(&x, (qt && !nojoin)))
|
||||||
return NULL;
|
return NULL;
|
||||||
xlen = strlen(x);
|
xlen = strlen(x);
|
||||||
*str = strcatsub(&y, ostr, aptr, x, xlen, fstr, globsubst);
|
*str = strcatsub(&y, ostr, aptr, x, xlen, fstr, globsubst, copied);
|
||||||
if (qt && !*y && isarr != 2)
|
if (qt && !*y)
|
||||||
y = dupstring(nulstring);
|
y = dupstring(nulstring);
|
||||||
setdata(n, (void *) y);
|
setdata(n, (void *) y);
|
||||||
}
|
}
|
||||||
|
@ -1522,14 +1904,18 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
||||||
static char *
|
static char *
|
||||||
arithsubst(char *a, char **bptr, char *rest)
|
arithsubst(char *a, char **bptr, char *rest)
|
||||||
{
|
{
|
||||||
char *s = *bptr, *t, buf[DIGBUFSIZE];
|
char *s = *bptr, *t;
|
||||||
char *b = buf;
|
char buf[DIGBUFSIZE], *b = buf;
|
||||||
long v;
|
mnumber v;
|
||||||
|
|
||||||
singsub(&a);
|
singsub(&a);
|
||||||
v = matheval(a);
|
v = matheval(a);
|
||||||
sprintf(buf, "%ld", v);
|
if (v.type & MN_FLOAT)
|
||||||
t = *bptr = (char *)ncalloc(strlen(*bptr) + strlen(buf) + strlen(rest) + 1);
|
b = convfloat(v.u.d, 0, 0, NULL);
|
||||||
|
else
|
||||||
|
convbase(buf, v.u.l, 0);
|
||||||
|
t = *bptr = (char *) hcalloc(strlen(*bptr) + strlen(b) +
|
||||||
|
strlen(rest) + 1);
|
||||||
t--;
|
t--;
|
||||||
while ((*++t = *s++));
|
while ((*++t = *s++));
|
||||||
t--;
|
t--;
|
||||||
|
@ -1567,6 +1953,8 @@ modify(char **str, char **ptr)
|
||||||
case 't':
|
case 't':
|
||||||
case 'l':
|
case 'l':
|
||||||
case 'u':
|
case 'u':
|
||||||
|
case 'q':
|
||||||
|
case 'Q':
|
||||||
c = **ptr;
|
c = **ptr;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1587,7 +1975,7 @@ modify(char **str, char **ptr)
|
||||||
if (*ptr1) {
|
if (*ptr1) {
|
||||||
zsfree(hsubl);
|
zsfree(hsubl);
|
||||||
hsubl = ztrdup(ptr1);
|
hsubl = ztrdup(ptr1);
|
||||||
}
|
}
|
||||||
if (!hsubl) {
|
if (!hsubl) {
|
||||||
zerr("no previous substitution", NULL, 0);
|
zerr("no previous substitution", NULL, 0);
|
||||||
return;
|
return;
|
||||||
|
@ -1685,11 +2073,26 @@ modify(char **str, char **ptr)
|
||||||
if (hsubl && hsubr)
|
if (hsubl && hsubr)
|
||||||
subst(©, hsubl, hsubr, gbal);
|
subst(©, hsubl, hsubr, gbal);
|
||||||
break;
|
break;
|
||||||
|
case 'q':
|
||||||
|
copy = bslashquote(copy, NULL, 0);
|
||||||
|
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;
|
tc = *tt;
|
||||||
*tt = '\0';
|
*tt = '\0';
|
||||||
nl = al + strlen(t) + strlen(copy);
|
nl = al + strlen(t) + strlen(copy);
|
||||||
ptr1 = tmp = (char *)halloc(nl + 1);
|
ptr1 = tmp = (char *)zhalloc(nl + 1);
|
||||||
if (all)
|
if (all)
|
||||||
for (ptr2 = all; *ptr2;)
|
for (ptr2 = all; *ptr2;)
|
||||||
*ptr1++ = *ptr2++;
|
*ptr1++ = *ptr2++;
|
||||||
|
@ -1736,6 +2139,21 @@ modify(char **str, char **ptr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'q':
|
||||||
|
*str = bslashquote(*str, NULL, 0);
|
||||||
|
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 (rec < 0) {
|
||||||
|
@ -1765,6 +2183,8 @@ dstackent(char ch, int val)
|
||||||
else
|
else
|
||||||
for (end=NULL, n=firstnode(dirstack); n && val; val--, n=nextnode(n));
|
for (end=NULL, n=firstnode(dirstack); n && val; val--, n=nextnode(n));
|
||||||
if (n == end) {
|
if (n == end) {
|
||||||
|
if (backwards && !val)
|
||||||
|
return pwd;
|
||||||
if (isset(NOMATCH))
|
if (isset(NOMATCH))
|
||||||
zerr("not enough directory stack entries.", NULL, 0);
|
zerr("not enough directory stack entries.", NULL, 0);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -162,6 +162,18 @@
|
||||||
>1: [[ read.me = (#ia1)README~READ.ME ]]
|
>1: [[ read.me = (#ia1)README~READ.ME ]]
|
||||||
>0: [[ read.me = (#ia1)README~READ_ME ]]
|
>0: [[ read.me = (#ia1)README~READ_ME ]]
|
||||||
>1: [[ read.me = (#ia1)README~(#a1)READ_ME ]]
|
>1: [[ read.me = (#ia1)README~(#a1)READ_ME ]]
|
||||||
|
>0: [[ test = *((#s)|/)test((#e)|/)* ]]
|
||||||
|
>0: [[ test/path = *((#s)|/)test((#e)|/)* ]]
|
||||||
|
>0: [[ path/test = *((#s)|/)test((#e)|/)* ]]
|
||||||
|
>0: [[ path/test/ohyes = *((#s)|/)test((#e)|/)* ]]
|
||||||
|
>1: [[ atest = *((#s)|/)test((#e)|/)* ]]
|
||||||
|
>1: [[ testy = *((#s)|/)test((#e)|/)* ]]
|
||||||
|
>1: [[ testy/path = *((#s)|/)test((#e)|/)* ]]
|
||||||
|
>1: [[ path/atest = *((#s)|/)test((#e)|/)* ]]
|
||||||
|
>1: [[ atest/path = *((#s)|/)test((#e)|/)* ]]
|
||||||
|
>1: [[ path/testy = *((#s)|/)test((#e)|/)* ]]
|
||||||
|
>1: [[ path/testy/ohyes = *((#s)|/)test((#e)|/)* ]]
|
||||||
|
>1: [[ path/atest/ohyes = *((#s)|/)test((#e)|/)* ]]
|
||||||
>0 tests failed.
|
>0 tests failed.
|
||||||
|
|
||||||
globtest globtests.ksh
|
globtest globtests.ksh
|
||||||
|
|
|
@ -14,16 +14,21 @@
|
||||||
|
|
||||||
# Produce verbose messages if non-zero.
|
# Produce verbose messages if non-zero.
|
||||||
# If 1, produce reports of tests executed; if 2, also report on progress.
|
# If 1, produce reports of tests executed; if 2, also report on progress.
|
||||||
ZTST_verbose=0
|
# Defined in such a way that any value from the environment is used.
|
||||||
|
: ${ZTST_verbose:=0}
|
||||||
|
|
||||||
# We require all options to be reset, not just emulation options.
|
# We require all options to be reset, not just emulation options.
|
||||||
# Unfortunately, due to the crud which may be in /etc/zshenv this might
|
# Unfortunately, due to the crud which may be in /etc/zshenv this might
|
||||||
# still not be good enough. Maybe we should trick it somehow.
|
# still not be good enough. Maybe we should trick it somehow.
|
||||||
emulate -R zsh
|
emulate -R zsh
|
||||||
|
|
||||||
|
# Set the module load path to correspond to this build of zsh.
|
||||||
|
# This Modules directory should have been created by "make check".
|
||||||
|
[[ -d Modules/zsh ]] && module_path=( $PWD/Modules )
|
||||||
|
|
||||||
# We need to be able to save and restore the options used in the test.
|
# We need to be able to save and restore the options used in the test.
|
||||||
# We use the $options variable of the parameter module for this.
|
# We use the $options variable of the parameter module for this.
|
||||||
zmodload -i parameter
|
zmodload -i zsh/parameter
|
||||||
|
|
||||||
# Note that both the following are regular arrays, since we only use them
|
# Note that both the following are regular arrays, since we only use them
|
||||||
# in whole array assignments to/from $options.
|
# in whole array assignments to/from $options.
|
||||||
|
@ -42,18 +47,31 @@ ZTST_mainopts=(${(kv)options})
|
||||||
ZTST_testdir=$PWD
|
ZTST_testdir=$PWD
|
||||||
ZTST_testname=$1
|
ZTST_testname=$1
|
||||||
|
|
||||||
|
# The source directory is not necessarily the current directory,
|
||||||
|
# but if $0 doesn't contain a `/' assume it is.
|
||||||
|
if [[ $0 = */* ]]; then
|
||||||
|
ZTST_srcdir=${0%/*}
|
||||||
|
else
|
||||||
|
ZTST_srcdir=$PWD
|
||||||
|
fi
|
||||||
|
[[ $ZTST_srcdir = /* ]] || ZTST_srcdir="$ZTST_testdir/$ZTST_srcdir"
|
||||||
|
|
||||||
|
# Set the function autoload paths to correspond to this build of zsh.
|
||||||
|
fpath=( $ZTST_srcdir/../(Completion|Functions)/*~*/CVS(/) )
|
||||||
|
|
||||||
|
: ${TMPPREFIX:=/tmp/zsh}
|
||||||
# Temporary files for redirection inside tests.
|
# Temporary files for redirection inside tests.
|
||||||
ZTST_in=${TMPPREFIX-:/tmp/zsh}.ztst.in.$$
|
ZTST_in=${TMPPREFIX}.ztst.in.$$
|
||||||
# hold the expected output
|
# hold the expected output
|
||||||
ZTST_out=${TMPPREFIX-:/tmp/zsh}.ztst.out.$$
|
ZTST_out=${TMPPREFIX}.ztst.out.$$
|
||||||
ZTST_err=${TMPPREFIX-:/tmp/zsh}.ztst.err.$$
|
ZTST_err=${TMPPREFIX}.ztst.err.$$
|
||||||
# hold the actual output from the test
|
# hold the actual output from the test
|
||||||
ZTST_tout=${TMPPREFIX-:/tmp/zsh}.ztst.tout.$$
|
ZTST_tout=${TMPPREFIX}.ztst.tout.$$
|
||||||
ZTST_terr=${TMPPREFIX-:/tmp/zsh}.ztst.terr.$$
|
ZTST_terr=${TMPPREFIX}.ztst.terr.$$
|
||||||
|
|
||||||
ZTST_cleanup() {
|
ZTST_cleanup() {
|
||||||
rm -rf $ZTST_testdir/dummy.tmp $ZTST_testdir/*.tmp \
|
cd $ZTST_testdir
|
||||||
$ZTST_in $ZTST_out $ZTST_err $ZTST_tout $ZTST_terr
|
rm -rf $ZTST_testdir/dummy.tmp $ZTST_testdir/*.tmp ${TMPPREFIX}.ztst*$$
|
||||||
}
|
}
|
||||||
|
|
||||||
# This cleanup always gets performed, even if we abort. Later,
|
# This cleanup always gets performed, even if we abort. Later,
|
||||||
|
@ -67,10 +85,11 @@ rm -rf dummy.tmp *.tmp
|
||||||
# Report failure. Note that all output regarding the tests goes to stdout.
|
# Report failure. Note that all output regarding the tests goes to stdout.
|
||||||
# That saves an unpleasant mixture of stdout and stderr to sort out.
|
# That saves an unpleasant mixture of stdout and stderr to sort out.
|
||||||
ZTST_testfailed() {
|
ZTST_testfailed() {
|
||||||
print "Test $ZTST_testname failed: $1"
|
print -r "Test $ZTST_testname failed: $1"
|
||||||
if [[ -n $ZTST_message ]]; then
|
if [[ -n $ZTST_message ]]; then
|
||||||
print "Was testing: $ZTST_message"
|
print -r "Was testing: $ZTST_message"
|
||||||
fi
|
fi
|
||||||
|
print -r "$ZTST_testname: test failed."
|
||||||
ZTST_cleanup
|
ZTST_cleanup
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
@ -79,7 +98,7 @@ ZTST_testfailed() {
|
||||||
ZTST_verbose() {
|
ZTST_verbose() {
|
||||||
local lev=$1
|
local lev=$1
|
||||||
shift
|
shift
|
||||||
[[ -n $ZTST_verbose && $ZTST_verbose -ge $lev ]] && print $* >&8
|
[[ -n $ZTST_verbose && $ZTST_verbose -ge $lev ]] && print -- $* >&8
|
||||||
}
|
}
|
||||||
|
|
||||||
[[ ! -r $ZTST_testname ]] && ZTST_testfailed "can't read test file."
|
[[ ! -r $ZTST_testname ]] && ZTST_testfailed "can't read test file."
|
||||||
|
@ -97,7 +116,7 @@ ZTST_cursect=''
|
||||||
ZTST_getline() {
|
ZTST_getline() {
|
||||||
local IFS=
|
local IFS=
|
||||||
while true; do
|
while true; do
|
||||||
read ZTST_curline <&9 || return 1
|
read -r ZTST_curline <&9 || return 1
|
||||||
[[ $ZTST_curline == \#* ]] || return 0
|
[[ $ZTST_curline == \#* ]] || return 0
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
@ -144,7 +163,7 @@ $ZTST_code"
|
||||||
|
|
||||||
# Read in a piece for redirection.
|
# Read in a piece for redirection.
|
||||||
ZTST_getredir() {
|
ZTST_getredir() {
|
||||||
local char=${ZTST_curline[1]}
|
local char=${ZTST_curline[1]} fn
|
||||||
ZTST_redir=${ZTST_curline[2,-1]}
|
ZTST_redir=${ZTST_curline[2,-1]}
|
||||||
while ZTST_getline; do
|
while ZTST_getline; do
|
||||||
[[ $ZTST_curline[1] = $char ]] || break
|
[[ $ZTST_curline[1] = $char ]] || break
|
||||||
|
@ -153,6 +172,22 @@ ${ZTST_curline[2,-1]}"
|
||||||
done
|
done
|
||||||
ZTST_verbose 2 "ZTST_getredir: read redir for '$char':
|
ZTST_verbose 2 "ZTST_getredir: read redir for '$char':
|
||||||
$ZTST_redir"
|
$ZTST_redir"
|
||||||
|
|
||||||
|
case $char in
|
||||||
|
'<') fn=$ZTST_in
|
||||||
|
;;
|
||||||
|
'>') fn=$ZTST_out
|
||||||
|
;;
|
||||||
|
'?') fn=$ZTST_err
|
||||||
|
;;
|
||||||
|
*) ZTST_testfailed "bad redir operator: $char"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
if [[ $ZTST_flags = *q* ]]; then
|
||||||
|
print -r -- "${(e)ZTST_redir}" >>$fn
|
||||||
|
else
|
||||||
|
print -r -- "$ZTST_redir" >>$fn
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Execute an indented chunk. Redirections will already have
|
# Execute an indented chunk. Redirections will already have
|
||||||
|
@ -209,27 +244,24 @@ $ZTST_curline"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
[[:space:]]##[^[:space:]]*) ZTST_getchunk
|
[[:space:]]##[^[:space:]]*) ZTST_getchunk
|
||||||
[[ $ZTST_curline != [-0-9]* ]] &&
|
if [[ $ZTST_curline == (#b)([-0-9]##)([[:alpha:]]#)(:*)# ]]; then
|
||||||
ZTST_testfailed "expecting test status at:
|
|
||||||
$ZTST_curline"
|
|
||||||
ZTST_xstatus=$ZTST_curline
|
|
||||||
if [[ $ZTST_curline == (#b)([^:]##):(*) ]]; then
|
|
||||||
ZTST_xstatus=$match[1]
|
ZTST_xstatus=$match[1]
|
||||||
ZTST_message=$match[2]
|
ZTST_flags=$match[2]
|
||||||
|
ZTST_message=${match[3]:+${match[3][2,-1]}}
|
||||||
|
else
|
||||||
|
ZTST_testfailed "expecting test status at:
|
||||||
|
$ZTST_curline"
|
||||||
fi
|
fi
|
||||||
ZTST_getline
|
ZTST_getline
|
||||||
found=1
|
found=1
|
||||||
;;
|
;;
|
||||||
'<'*) ZTST_getredir
|
'<'*) ZTST_getredir
|
||||||
print -r "${(e)ZTST_redir}" >>$ZTST_in
|
|
||||||
found=1
|
found=1
|
||||||
;;
|
;;
|
||||||
'>'*) ZTST_getredir
|
'>'*) ZTST_getredir
|
||||||
print -r "${(e)ZTST_redir}" >>$ZTST_out
|
|
||||||
found=1
|
found=1
|
||||||
;;
|
;;
|
||||||
'?'*) ZTST_getredir
|
'?'*) ZTST_getredir
|
||||||
print -r "${(e)ZTST_redir}" >>$ZTST_err
|
|
||||||
found=1
|
found=1
|
||||||
;;
|
;;
|
||||||
*) ZTST_testfailed "bad line in test block:
|
*) ZTST_testfailed "bad line in test block:
|
||||||
|
@ -240,8 +272,7 @@ $ZTST_curline"
|
||||||
|
|
||||||
# If we found some code to execute...
|
# If we found some code to execute...
|
||||||
if [[ -n $ZTST_code ]]; then
|
if [[ -n $ZTST_code ]]; then
|
||||||
ZTST_verbose 1 "Running test:
|
ZTST_verbose 1 "Running test: $ZTST_message"
|
||||||
$ZTST_message"
|
|
||||||
ZTST_verbose 2 "ZTST_test: expecting status: $ZTST_xstatus"
|
ZTST_verbose 2 "ZTST_test: expecting status: $ZTST_xstatus"
|
||||||
|
|
||||||
ZTST_execchunk <$ZTST_in >$ZTST_tout 2>$ZTST_terr
|
ZTST_execchunk <$ZTST_in >$ZTST_tout 2>$ZTST_terr
|
||||||
|
@ -249,7 +280,9 @@ $ZTST_message"
|
||||||
# First check we got the right status, if specified.
|
# First check we got the right status, if specified.
|
||||||
if [[ $ZTST_xstatus != - && $ZTST_xstatus != $ZTST_status ]]; then
|
if [[ $ZTST_xstatus != - && $ZTST_xstatus != $ZTST_status ]]; then
|
||||||
ZTST_testfailed "bad status $ZTST_status, expected $ZTST_xstatus from:
|
ZTST_testfailed "bad status $ZTST_status, expected $ZTST_xstatus from:
|
||||||
$ZTST_code"
|
$ZTST_code${$(<$ZTST_terr):+
|
||||||
|
Error output:
|
||||||
|
$(<$ZTST_terr)}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ZTST_verbose 2 "ZTST_test: test produced standard output:
|
ZTST_verbose 2 "ZTST_test: test produced standard output:
|
||||||
|
@ -258,11 +291,13 @@ ZTST_test: and standard error:
|
||||||
$(<$ZTST_terr)"
|
$(<$ZTST_terr)"
|
||||||
|
|
||||||
# Now check output and error.
|
# Now check output and error.
|
||||||
if ! diff -c $ZTST_out $ZTST_tout; then
|
if [[ $ZTST_flags != *d* ]] && ! diff -c $ZTST_out $ZTST_tout; then
|
||||||
ZTST_testfailed "output differs from expected as shown above for:
|
ZTST_testfailed "output differs from expected as shown above for:
|
||||||
$ZTST_code"
|
$ZTST_code${$(<$ZTST_terr):+
|
||||||
|
Error output:
|
||||||
|
$(<$ZTST_terr)}"
|
||||||
fi
|
fi
|
||||||
if ! diff -c $ZTST_err $ZTST_terr; then
|
if [[ $ZTST_flags != *D* ]] && ! diff -c $ZTST_err $ZTST_terr; then
|
||||||
ZTST_testfailed "error output differs from expected as shown above for:
|
ZTST_testfailed "error output differs from expected as shown above for:
|
||||||
$ZTST_code"
|
$ZTST_code"
|
||||||
fi
|
fi
|
||||||
|
|
Loading…
Reference in New Issue