Currently various paths will reprocess data when performing word
expansion. For example, expari will skip backwards looking for
the start of the arithmetic expansion, while evalvar will skip
unexpanded words manually.
This is cumbersome and error-prone. This patch fixes this by
making word expansions proceed in a linear fashion. This means
changing argstr and the various expansion functions such as expari
and subevalvar to return the next character to be expanded.
This is inspired by similar code from FreeBSD. However, we take
things one step further and completely remove the manual word
skipping in evalvar. This is accomplished by introducing a new
EXP_DISCARD flag that tells argstr to only parse and not produce
any actual expansions.
Incidentally, argstr will now always NUL-terminate the expansion
unless the EXP_WORD flag is set. This is because all but one
caller of argstr wants the result to be NUL-termianted.
Signed-off-by: Herbert Xu
diff --git a/src/expand.c b/src/expand.c
index 7ed1bc0..3a1bf94 100644
--- a/src/expand.c
+++ b/src/expand.c
@@ -110,10 +110,10 @@ static struct ifsregion *ifslastp;
/* holds expanded arg list */
static struct arglist exparg;
-STATIC void argstr(char *, int);
-STATIC char *exptilde(char *, char *, int);
+static char *argstr(char *p, int flag);
+static char *exptilde(char *startp, int flag);
+static char *expari(char *start, int flag);
STATIC void expbackq(union node *, int);
-STATIC const char *subevalvar(char *, char *, int, int, int, int, int);
STATIC char *evalvar(char *, int);
static size_t strtodest(const char *p, int flags);
static void memtodest(const char *p, size_t len, int flags);
@@ -192,13 +192,11 @@ expandarg(union node *arg, struct arglist *arglist, int
flag)
argbackq = arg->narg.backquote;
STARTSTACKSTR(expdest);
argstr(arg->narg.text, flag);
- p = _STPUTC('\0', expdest);
- expdest = p - 1;
if (arglist == NULL) {
/* here document expanded */
goto out;
}
- p = grabstackstr(p);
+ p = grabstackstr(expdest);
exparg.lastp =
/*
* TODO - EXP_REDIR
@@ -232,8 +230,7 @@ out:
* $@ like $* since no splitting will be performed.
*/
-STATIC void
-argstr(char *p, int flag)
+static char *argstr(char *p, int flag)
{
static const char spclchars[] = {
'=',
@@ -243,6 +240,7 @@ argstr(char *p, int flag)
CTLESC,
CTLVAR,
CTLBACKQ,
+ CTLARI,
CTLENDARI,
0
};
@@ -253,35 +251,41 @@ argstr(char *p, int flag)
size_t length;
int startloc;
- if (!(flag & EXP_VARTILDE)) {
- reject += 2;
- } else if (flag & EXP_VARTILDE2) {
- reject++;
- }
+ reject += !!(flag & EXP_VARTILDE2);
+ reject += flag & EXP_VARTILDE ? 0 : 2;
inquotes = 0;
length = 0;
if (flag & EXP_TILDE) {
- char *q;
-
flag &= ~EXP_TILDE;
tilde:
- q = p;
- if (*q == '~')
- p = exptilde(p, q, flag);
+ if (*p == '~')
+ p = exptilde(p, flag);
}
start:
startloc = expdest - (char *)stackblock();
for (;;) {
+ int end;
+
length += strcspn(p + length, reject);
+ end = 0;
c = (signed char)p[length];
- if (c && (!(c & 0x80) || c == CTLENDARI)) {
- /* c == '=' || c == ':' || c == CTLENDARI */
+ if (!(c & 0x80) || c == CTLENDARI || c == CTLENDVAR) {
+ /*
+* c == '=' || c == ':' || c == '\0' ||
+* c == CTLENDARI || c == CTLENDVAR
+*/
length++;
+ /* c == '\0' || c == CTLENDARI || c == CTLENDVAR */
+ end = !!((c - 1) & 0x80);
}
- if (length > 0) {
+ if (length > 0 && !(flag & EXP_DISCARD)) {
int newloc;
- expdest = stnputs(p, length, expdest);
- newloc = expdest - (char *)stackblock();
+ char *q;
+
+ q = stnputs(p, length, expdest);
+ q[-1] &= end - 1;
+ expdest = q - (flag & EXP_WORD ? end : 0);
+ newloc = expdest - (char *)stackblock() - end;
if (breakall && !inquotes && newloc > startloc) {
recordregion(startloc, newloc, 0);
}
@@ -290,14 +294,11 @@ start:
p += length + 1;
length = 0;
+ if (end)
+ break;
+
switch (c) {
- case '\0':
- goto breakloop;