* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 4. Neither the name of the University nor the names of its contributors
+ * 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
#endif
#endif /* not lint */
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/bin/sh/expand.c 303586 2016-07-31 13:11:34Z jilles $");
+__FBSDID("$FreeBSD: head/bin/sh/expand.c 318269 2017-05-14 13:14:19Z jilles $");
#include <sys/types.h>
#include <sys/time.h>
};
static char *expdest; /* output of current string */
-static struct nodelist *argbackq; /* list of back quote expressions */
-static const char *argstr(const char *, int, struct worddest *);
+static const char *argstr(const char *, struct nodelist **restrict, int,
+ struct worddest *);
static const char *exptilde(const char *, int);
-static const char *expari(const char *, int, struct worddest *);
+static const char *expari(const char *, struct nodelist **restrict, int,
+ struct worddest *);
static void expbackq(union node *, int, int, struct worddest *);
-static void subevalvar_trim(const char *, int, int, int);
-static int subevalvar_misc(const char *, const char *, int, int, int);
-static const char *evalvar(const char *, int, struct worddest *);
+static const char *subevalvar_trim(const char *, struct nodelist **restrict,
+ int, int, int);
+static const char *subevalvar_misc(const char *, struct nodelist **restrict,
+ const char *, int, int, int);
+static const char *evalvar(const char *, struct nodelist **restrict, int,
+ struct worddest *);
static int varisset(const char *, int);
static void strtodest(const char *, int, int, int, struct worddest *);
static void reprocess(int, int, int, int, struct worddest *);
static int
collate_range_cmp(wchar_t c1, wchar_t c2)
{
- static wchar_t s1[2], s2[2];
+ wchar_t s1[2], s2[2];
s1[0] = c1;
+ s1[1] = L'\0';
s2[0] = c2;
+ s2[1] = L'\0';
return (wcscoll(s1, s2));
}
* The result is left in the stack string.
* When arglist is NULL, perform here document expansion.
*
- * Caution: this function uses global state and is not reentrant.
- * However, a new invocation after an interrupted invocation is safe
- * and will reset the global state for the new call.
+ * When doing something that may cause this to be re-entered, make sure
+ * the stack string is empty via grabstackstr() and do not assume expdest
+ * remains valid.
*/
void
expandarg(union node *arg, struct arglist *arglist, int flag)
{
struct worddest exparg;
+ struct nodelist *argbackq;
if (fflag)
flag &= ~EXP_GLOB;
exparg.list = arglist;
exparg.state = WORD_IDLE;
STARTSTACKSTR(expdest);
- argstr(arg->narg.text, flag, &exparg);
+ argstr(arg->narg.text, &argbackq, flag, &exparg);
if (arglist == NULL) {
STACKSTRNUL(expdest);
return; /* here document expanded */
* If EXP_SPLIT is set, dst receives any complete words produced.
*/
static const char *
-argstr(const char *p, int flag, struct worddest *dst)
+argstr(const char *p, struct nodelist **restrict argbackq, int flag,
+ struct worddest *dst)
{
char c;
int quotes = flag & (EXP_GLOB | EXP_CASE); /* do CTLESC */
USTPUTC(c, expdest);
break;
case CTLVAR:
- p = evalvar(p, flag, dst);
+ p = evalvar(p, argbackq, flag, dst);
break;
case CTLBACKQ:
case CTLBACKQ|CTLQUOTE:
- expbackq(argbackq->n, c & CTLQUOTE, flag, dst);
- argbackq = argbackq->next;
+ expbackq((*argbackq)->n, c & CTLQUOTE, flag, dst);
+ *argbackq = (*argbackq)->next;
break;
case CTLARI:
- p = expari(p, flag, dst);
+ p = expari(p, argbackq, flag, dst);
break;
case ':':
case '=':
* Expand arithmetic expression.
*/
static const char *
-expari(const char *p, int flag, struct worddest *dst)
+expari(const char *p, struct nodelist **restrict argbackq, int flag,
+ struct worddest *dst)
{
char *q, *start;
arith_t result;
quoted = *p++ == '"';
begoff = expdest - stackblock();
- p = argstr(p, 0, NULL);
+ p = argstr(p, argbackq, 0, NULL);
STPUTC('\0', expdest);
start = stackblock() + begoff;
fmtstr(expdest, DIGITS(result), ARITH_FORMAT_STR, result);
adj = strlen(expdest);
STADJUST(adj, expdest);
- if (!quoted)
- reprocess(expdest - adj - stackblock(), flag, VSNORMAL, 0, dst);
+ /*
+ * If this is quoted, a '-' must not indicate a range in [...].
+ * If this is not quoted, splitting may occur.
+ */
+ if (quoted ?
+ result < 0 && begoff > 1 && flag & (EXP_GLOB | EXP_CASE) :
+ flag & EXP_SPLIT)
+ reprocess(expdest - adj - stackblock(), flag, VSNORMAL, quoted,
+ dst);
return p;
}
char buf[128];
char *p;
char *dest = expdest;
- struct nodelist *saveargbackq;
char lastc;
char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
int quotes = flag & (EXP_GLOB | EXP_CASE);
size_t nnl;
const char *ifs;
+ int startloc;
INTOFF;
- saveargbackq = argbackq;
p = grabstackstr(dest);
evalbackcmd(cmd, &in);
ungrabstackstr(p, dest);
- argbackq = saveargbackq;
p = in.buf;
+ startloc = dest - stackblock();
nnl = 0;
if (!quoted && flag & EXP_SPLIT)
ifs = ifsset() ? ifsval() : " \t\n";
else
ifs = "";
- /* Don't copy trailing newlines */
+ /* Remove trailing newlines */
for (;;) {
if (--in.nleft < 0) {
if (in.fd < 0)
lastc = *p++;
if (lastc == '\0')
continue;
- if (lastc == '\n') {
- nnl++;
- } else {
- if (nnl > 0) {
- if (strchr(ifs, '\n') != NULL) {
- NEXTWORD('\n', flag, dest, dst);
- nnl = 0;
- } else {
- CHECKSTRSPACE(nnl + 2, dest);
- while (nnl > 0) {
- nnl--;
- USTPUTC('\n', dest);
- }
- }
- }
- if (strchr(ifs, lastc) != NULL)
+ if (nnl > 0 && lastc != '\n') {
+ NEXTWORD('\n', flag, dest, dst);
+ nnl = 0;
+ }
+ if (strchr(ifs, lastc) != NULL) {
+ if (lastc == '\n')
+ nnl++;
+ else
NEXTWORD(lastc, flag, dest, dst);
- else {
- CHECKSTRSPACE(2, dest);
- if (quotes && syntax[(int)lastc] == CCTL)
- USTPUTC(CTLESC, dest);
- USTPUTC(lastc, dest);
- }
+ } else {
+ CHECKSTRSPACE(2, dest);
+ if (quotes && syntax[(int)lastc] == CCTL)
+ USTPUTC(CTLESC, dest);
+ USTPUTC(lastc, dest);
}
}
+ while (dest > stackblock() + startloc && STTOPC(dest) == '\n')
+ STUNPUTC(dest);
if (in.fd >= 0)
close(in.fd);
if (in.buf)
ckfree(in.buf);
- if (in.jp)
+ if (in.jp) {
+ p = grabstackstr(dest);
exitstatus = waitforjob(in.jp, (int *)NULL);
- TRACE(("expbackq: size=%td: \"%.*s\"\n",
- ((dest - stackblock()) - startloc),
- (int)((dest - stackblock()) - startloc),
- stackblock() + startloc));
+ ungrabstackstr(p, dest);
+ }
+ TRACE(("expbackq: done\n"));
expdest = dest;
INTON;
}
*startp++ = *loc++;
}
-static void
-subevalvar_trim(const char *p, int strloc, int subtype, int startloc)
+static const char *
+subevalvar_trim(const char *p, struct nodelist **restrict argbackq, int strloc,
+ int subtype, int startloc)
{
char *startp;
char *loc = NULL;
char *str;
int c = 0;
- struct nodelist *saveargbackq = argbackq;
int amount;
- argstr(p, EXP_CASE | EXP_TILDE, NULL);
+ p = argstr(p, argbackq, EXP_CASE | EXP_TILDE, NULL);
STACKSTRNUL(expdest);
- argbackq = saveargbackq;
startp = stackblock() + startloc;
str = stackblock() + strloc;
if (patmatch(str, startp)) {
*loc = c;
recordleft(str, loc, startp);
- return;
+ return p;
}
*loc = c;
}
if (patmatch(str, startp)) {
*loc = c;
recordleft(str, loc, startp);
- return;
+ return p;
}
*loc = c;
loc--;
if (patmatch(str, loc)) {
amount = loc - expdest;
STADJUST(amount, expdest);
- return;
+ return p;
}
loc--;
}
if (patmatch(str, loc)) {
amount = loc - expdest;
STADJUST(amount, expdest);
- return;
+ return p;
}
}
break;
}
amount = (expdest - stackblock() - strloc) + 1;
STADJUST(-amount, expdest);
+ return p;
}
-static int
-subevalvar_misc(const char *p, const char *var, int subtype, int startloc,
- int varflags)
+static const char *
+subevalvar_misc(const char *p, struct nodelist **restrict argbackq,
+ const char *var, int subtype, int startloc, int varflags)
{
char *startp;
- struct nodelist *saveargbackq = argbackq;
int amount;
- argstr(p, EXP_TILDE, NULL);
+ p = argstr(p, argbackq, EXP_TILDE, NULL);
STACKSTRNUL(expdest);
- argbackq = saveargbackq;
startp = stackblock() + startloc;
switch (subtype) {
setvar(var, startp, 0);
amount = startp - expdest;
STADJUST(amount, expdest);
- return 1;
+ return p;
case VSQUESTION:
if (*p != CTLENDVAR) {
}
error("%.*s: parameter %snot set", (int)(p - var - 1),
var, (varflags & VSNUL) ? "null or " : "");
- return 0;
default:
abort();
*/
static const char *
-evalvar(const char *p, int flag, struct worddest *dst)
+evalvar(const char *p, struct nodelist **restrict argbackq, int flag,
+ struct worddest *dst)
{
int subtype;
int varflags;
if (! is_name(*p))
special = 1;
p = strchr(p, '=') + 1;
-again: /* jump here after setting a variable with ${var=text} */
if (varflags & VSLINENO) {
set = 1;
special = 1;
break;
case VSNORMAL:
- break;
+ return p;
case VSPLUS:
case VSMINUS:
if (!set) {
- argstr(p, flag | (flag & EXP_SPLIT ? EXP_SPLIT_LIT : 0) |
+ return argstr(p, argbackq,
+ flag | (flag & EXP_SPLIT ? EXP_SPLIT_LIT : 0) |
(varflags & VSQUOTE ? EXP_LIT_QUOTED : 0), dst);
- break;
}
break;
*/
STPUTC('\0', expdest);
patloc = expdest - stackblock();
- subevalvar_trim(p, patloc, subtype, startloc);
+ p = subevalvar_trim(p, argbackq, patloc, subtype, startloc);
reprocess(startloc, flag, VSNORMAL, varflags & VSQUOTE, dst);
if (flag & EXP_SPLIT && *var == '@' && varflags & VSQUOTE)
dst->state = WORD_QUOTEMARK;
- break;
+ return p;
case VSASSIGN:
case VSQUESTION:
if (!set) {
- if (subevalvar_misc(p, var, subtype, startloc,
- varflags)) {
- varflags &= ~VSNUL;
- goto again;
- }
- break;
+ p = subevalvar_misc(p, argbackq, var, subtype,
+ startloc, varflags);
+ /* assert(subtype == VSASSIGN); */
+ val = lookupvar(var);
+ strtodest(val, flag, subtype, varflags & VSQUOTE, dst);
+ return p;
}
break;
abort();
}
- if (subtype != VSNORMAL) { /* skip to end of alternative */
+ { /* skip to end of alternative */
int nesting = 1;
for (;;) {
if ((c = *p++) == CTLESC)
p++;
- else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
- if (set)
- argbackq = argbackq->next;
- } else if (c == CTLVAR) {
+ else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE))
+ *argbackq = (*argbackq)->next;
+ else if (c == CTLVAR) {
if ((*p++ & VSTYPE) != VSNORMAL)
nesting++;
} else if (c == CTLENDVAR) {
/*
- * Test whether a specialized variable is set.
+ * Test whether a special or positional parameter is set.
*/
static int
}
/*
- * Add the value of a specialized variable to the stack string.
+ * Add the value of a special or positional parameter to the stack string.
*/
static void
casematch(union node *pattern, const char *val)
{
struct stackmark smark;
+ struct nodelist *argbackq;
int result;
char *p;
setstackmark(&smark);
argbackq = pattern->narg.backquote;
STARTSTACKSTR(expdest);
- argstr(pattern->narg.text, EXP_TILDE | EXP_CASE, NULL);
+ argstr(pattern->narg.text, &argbackq, EXP_TILDE | EXP_CASE, NULL);
STPUTC('\0', expdest);
p = grabstackstr(expdest);
result = patmatch(p, val);