#define EXP_WORD 0x40 /* expand word in parameter expansion */
#define EXP_QUOTED 0x100 /* expand word in double quotes */
#define EXP_KEEPNUL 0x200 /* do not skip NUL characters */
+#define EXP_DISCARD 0x400 /* discard result of expansion */
/*
* rmescape() flags
}
static char *
-exptilde(char *startp, char *p, int flag)
+exptilde(char *startp, int flag)
{
unsigned char c;
char *name;
struct passwd *pw;
const char *home;
+ char *p;
+ p = startp;
name = p + 1;
while ((c = *++p) != '\0') {
}
}
done:
+ if (flag & EXP_DISCARD)
+ goto out;
*p = '\0';
if (*name == '\0') {
home = lookupvar("HOME");
goto lose;
home = pw->pw_dir;
}
+ *p = c;
if (!home)
goto lose;
- *p = c;
strtodest(home, flag | EXP_QUOTED);
+ out:
return p;
lose:
- *p = c;
return startp;
}
int startloc;
struct stackmark smark;
+ if (flag & EXP_DISCARD)
+ goto out;
+
INT_OFF;
startloc = expdest - (char *)stackblock();
pushstackmark(&smark, startloc);
(int)((dest - (char *)stackblock()) - startloc),
(int)((dest - (char *)stackblock()) - startloc),
stackblock() + startloc));
+
+ out:
+ argbackq = argbackq->next;
}
+/* expari needs it */
+static char *argstr(char *p, int flag);
+
#if ENABLE_FEATURE_SH_MATH
/*
* Expand arithmetic expression. Backup to start of expression,
* evaluate, place result in (backed up) result, adjust string position.
*/
-static void
-expari(int flag)
+static char *
+expari(char *start, int flag)
{
- char *p, *start;
+ struct stackmark sm;
int begoff;
+ int endoff;
int len;
+ arith_t result;
+ char *p;
- /* ifsfree(); */
-
- /*
- * This routine is slightly over-complicated for
- * efficiency. Next we scan backwards looking for the
- * start of arithmetic.
- */
- start = stackblock();
- p = expdest - 1;
- *p = '\0';
- p--;
- while (1) {
- int esc;
-
- while ((unsigned char)*p != CTLARI) {
- p--;
-#if DEBUG
- if (p < start) {
- ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
- }
-#endif
- }
-
- esc = esclen(start, p);
- if (!(esc % 2)) {
- break;
- }
+ p = stackblock();
+ begoff = expdest - p;
+ p = argstr(start, flag & EXP_DISCARD);
- p -= esc + 1;
- }
+ if (flag & EXP_DISCARD)
+ goto out;
- begoff = p - start;
+ start = stackblock();
+ endoff = expdest - start;
+ start += begoff;
+ STADJUST(start - expdest, expdest);
removerecordregions(begoff);
- expdest = p;
-
if (flag & QUOTES_ESC)
- rmescapes(p + 1, 0, NULL);
+ rmescapes(start, 0, NULL);
+
+ pushstackmark(&sm, endoff);
+ result = ash_arith(start);
+ popstackmark(&sm);
- len = cvtnum(ash_arith(p + 1), flag);
+ len = cvtnum(result, flag);
if (!(flag & EXP_QUOTED))
recordregion(begoff, begoff + len, 0);
+
+ out:
+ return p;
}
#endif
* characters to allow for further processing. Otherwise treat
* $@ like $* since no splitting will be performed.
*/
-static void
+static char *
argstr(char *p, int flag)
{
static const char spclchars[] ALIGN1 = {
CTLVAR,
CTLBACKQ,
#if ENABLE_FEATURE_SH_MATH
+ CTLARI,
CTLENDARI,
#endif
'\0'
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;
unsigned char c;
length += strcspn(p + length, reject);
+ end = 0;
c = p[length];
- if (c) {
- if (!(c & 0x80)
- IF_FEATURE_SH_MATH(|| c == CTLENDARI)
- ) {
- /* c == '=' || c == ':' || c == CTLENDARI */
- length++;
- }
+ if (!(c & 0x80)
+ IF_FEATURE_SH_MATH(|| 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 = q - (char *)stackblock() - end;
if (breakall && !inquotes && newloc > startloc) {
recordregion(startloc, newloc, 0);
}
p += length + 1;
length = 0;
+ if (end)
+ break;
+
switch (c) {
- case '\0':
- goto breakloop;
case '=':
- if (flag & EXP_VARTILDE2) {
- p--;
- continue;
- }
flag |= EXP_VARTILDE2;
reject++;
/* fall through */
goto tilde;
}
continue;
- }
-
- switch (c) {
- case CTLENDVAR: /* ??? */
- goto breakloop;
case CTLQUOTEMARK:
/* "$@" syntax adherence hack */
if (!inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) {
goto start;
case CTLBACKQ:
expbackq(argbackq->n, flag | inquotes);
- argbackq = argbackq->next;
goto start;
#if ENABLE_FEATURE_SH_MATH
- case CTLENDARI:
- p--;
- expari(flag | inquotes);
+ case CTLARI:
+ p = expari(p, flag | inquotes);
goto start;
#endif
}
}
- breakloop: ;
+ return p - 1;
}
static char *
ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
}
-static const char *
-subevalvar(char *p, char *str, int strloc, int subtype,
+static char *
+subevalvar(char *start, char *str, int strloc,
int startloc, int varflags, int flag)
{
- struct nodelist *saveargbackq = argbackq;
+ int subtype = varflags & VSTYPE;
int quotes = flag & QUOTES_ESC;
char *startp;
char *loc;
char *rmesc, *rmescend;
- int amount, resetloc;
+ long amount;
+ int resetloc;
int argstr_flags;
IF_BASH_PATTERN_SUBST(int workloc;)
IF_BASH_PATTERN_SUBST(int slash_pos;)
IF_BASH_PATTERN_SUBST(char *repl;)
int zero;
char *(*scan)(char*, char*, char*, char*, int, int);
+ char *p;
- //bb_error_msg("subevalvar(p:'%s',str:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
- // p, str, strloc, subtype, startloc, varflags, quotes);
+ //bb_error_msg("subevalvar(start:'%s',str:'%s',strloc:%d,startloc:%d,varflags:%x,quotes:%d)",
+ // start, str, strloc, startloc, varflags, quotes);
#if BASH_PATTERN_SUBST
/* For "${v/pattern/repl}", we must find the delimiter _before_
repl = NULL;
if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
/* Find '/' and replace with NUL */
- repl = p;
+ repl = start;
/* The pattern can't be empty.
* IOW: if the first char after "${v//" is a slash,
* it does not terminate the pattern - it's the first char of the pattern:
}
}
#endif
- argstr_flags = EXP_TILDE;
- if (subtype != VSASSIGN
- && subtype != VSQUESTION
+ argstr_flags = (flag & EXP_DISCARD) | EXP_TILDE;
+ if (!str
#if BASH_SUBSTR
&& subtype != VSSUBSTR
#endif
) {
/* EXP_CASE keeps CTLESC's */
- argstr_flags = EXP_TILDE | EXP_CASE;
+ argstr_flags |= EXP_CASE;
}
- argstr(p, argstr_flags);
+ p = argstr(start, argstr_flags);
+
//bb_error_msg("str0:'%s'", (char *)stackblock() + strloc);
#if BASH_PATTERN_SUBST
slash_pos = -1;
slash_pos = expdest - ((char *)stackblock() + strloc);
STPUTC('/', expdest);
//bb_error_msg("repl+1:'%s'", repl + 1);
- argstr(repl + 1, EXP_TILDE); /* EXP_TILDE: echo "${v/x/~}" expands ~ ! */
+ p = argstr(repl + 1, (flag & EXP_DISCARD) | EXP_TILDE); /* EXP_TILDE: echo "${v/x/~}" expands ~ ! */
*repl = '/';
}
#endif
- STPUTC('\0', expdest);
- argbackq = saveargbackq;
+ if (flag & EXP_DISCARD)
+ return p;
+
startp = (char *)stackblock() + startloc;
//bb_error_msg("str1:'%s'", (char *)stackblock() + strloc);
switch (subtype) {
case VSASSIGN:
setvar0(str, startp);
- amount = startp - expdest;
- STADJUST(amount, expdest);
- return startp;
+
+ loc = startp;
+ goto out;
case VSQUESTION:
- varunset(p, str, startp, varflags);
+ varunset(start, str, startp, varflags);
/* NOTREACHED */
#if BASH_SUBSTR
*loc++ = *vstr++;
}
*loc = '\0';
- amount = loc - expdest;
- STADJUST(amount, expdest);
- return loc;
+ goto out;
}
#endif /* BASH_SUBSTR */
}
/* If there's no pattern to match, return the expansion unmolested */
if (str[0] == '\0')
- return NULL;
+ goto out1;
len = 0;
idx = startp;
startp = (char *)stackblock() + startloc;
memmove(startp, (char *)stackblock() + workloc, len + 1);
//bb_error_msg("startp:'%s'", startp);
- amount = expdest - (startp + len);
- STADJUST(-amount, expdest);
- return startp;
+ loc = startp + len;
+ goto out;
}
#endif /* BASH_PATTERN_SUBST */
loc = startp + (str - loc) - 1;
}
*loc = '\0';
- amount = loc - expdest;
- STADJUST(amount, expdest);
- }
- return loc;
+ } else
+ loc = str - 1;
+
+ out:
+ amount = loc - expdest;
+ STADJUST(amount, expdest);
+ out1:
+ /* Remove any recorded regions beyond start of variable */
+ removerecordregions(startloc);
+
+ return p;
}
/*
ssize_t len = 0;
int sep;
int subtype = varflags & VSTYPE;
- int discard = subtype == VSPLUS || subtype == VSLENGTH;
+ int discard = (subtype == VSPLUS || subtype == VSLENGTH) | (flags & EXP_DISCARD);
+
+ if (!subtype) {
+ if (discard)
+ return -1;
+
+ raise_error_syntax("bad substitution");
+ }
flags |= EXP_KEEPNUL;
flags &= discard ? ~QUOTES_ESC : ~0;
if (discard)
STADJUST(-len, expdest);
+
return len;
}
{
char varflags;
char subtype;
- int quoted;
char *var;
int patloc;
int startloc;
ssize_t varlen;
+ int quoted;
varflags = (unsigned char) *p++;
subtype = varflags & VSTYPE;
- if (!subtype)
- raise_error_syntax("bad substitution");
-
quoted = flag & EXP_QUOTED;
var = p;
startloc = expdest - (char *)stackblock();
if (varflags & VSNUL)
varlen--;
- if (subtype == VSPLUS) {
+ switch (subtype) {
+ case VSPLUS:
varlen = -1 - varlen;
- goto vsplus;
- }
-
- if (subtype == VSMINUS) {
- vsplus:
- if (varlen < 0) {
- argstr(
- p,
- flag | EXP_TILDE | EXP_WORD
- );
- goto end;
- }
+ /* fall through */
+ case 0:
+ case VSMINUS:
+ p = argstr(p, flag | EXP_TILDE | EXP_WORD);
+ if (varlen < 0)
+ return p;
goto record;
- }
- if (subtype == VSASSIGN || subtype == VSQUESTION) {
+ case VSASSIGN:
+ case VSQUESTION:
if (varlen >= 0)
goto record;
- subevalvar(p, var, 0, subtype, startloc, varflags,
+ p = subevalvar(p, var, 0, startloc, varflags,
flag & ~QUOTES_ESC);
+
+ if (flag & EXP_DISCARD)
+ return p;
+
varflags &= ~VSNUL;
- /*
- * Remove any recorded regions beyond
- * start of variable
- */
- removerecordregions(startloc);
goto again;
}
varunset(p, var, 0, 0);
if (subtype == VSLENGTH) {
+ p++;
+ if (flag & EXP_DISCARD)
+ return p;
cvtnum(varlen > 0 ? varlen : 0, flag);
goto record;
}
- if (subtype == VSNORMAL) {
- record:
- if (quoted) {
- quoted = *var == '@' && shellparam.nparam;
- if (!quoted)
- goto end;
- }
- recordregion(startloc, expdest - (char *)stackblock(), quoted);
- goto end;
- }
+ if (subtype == VSNORMAL)
+ goto record;
#if DEBUG
switch (subtype) {
}
#endif
- if (varlen >= 0) {
+ flag |= varlen < 0 ? EXP_DISCARD : 0;
+ if (!(flag & EXP_DISCARD)) {
/*
* Terminate the string and start recording the pattern
* right after it
*/
STPUTC('\0', expdest);
- patloc = expdest - (char *)stackblock();
- if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
- startloc, varflags, flag)) {
- int amount = expdest - (
- (char *)stackblock() + patloc - 1
- );
- STADJUST(-amount, expdest);
- }
- /* Remove any recorded regions beyond start of variable */
- removerecordregions(startloc);
- goto record;
}
- varlen = 0;
+ patloc = expdest - (char *)stackblock();
+ p = subevalvar(p, NULL, patloc, startloc, varflags, flag);
- end:
- if (subtype != VSNORMAL) { /* skip to end of alternative */
- int nesting = 1;
- for (;;) {
- unsigned char c = *p++;
- if (c == CTLESC)
- p++;
- else if (c == CTLBACKQ) {
- if (varlen >= 0)
- argbackq = argbackq->next;
- } else if (c == CTLVAR) {
- if ((*p++ & VSTYPE) != VSNORMAL)
- nesting++;
- } else if (c == CTLENDVAR) {
- if (--nesting == 0)
- break;
- }
- }
+ record:
+ if (flag & EXP_DISCARD)
+ return p;
+
+ if (quoted) {
+ quoted = *var == '@' && shellparam.nparam;
+ if (!quoted)
+ return p;
}
+ recordregion(startloc, expdest - (char *)stackblock(), quoted);
return p;
}
STARTSTACKSTR(expdest);
TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag));
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);
TRACE(("expandarg: p:'%s'\n", p));
exparg.lastp = &exparg.list;
/*
argbackq = pattern->narg.backquote;
STARTSTACKSTR(expdest);
argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
- STACKSTRNUL(expdest);
ifsfree();
result = patmatch(stackblock(), val);
popstackmark(&smark);