ash: move parse-time quote flag detection to run-time

Because the parser does not recursively parse parameter expansion
with respect to quotes, we can't accurately determine quote status at
parse time.  This patch works around this by moving the quote detection
to run-time where we do interpret it recursively.

Test case:
   foo=\\ echo "<${foo#[\\]}>"
Old result:
   <\>
New result:
   <>

Do not quote back slashes in parameter expansions outside quotes.

Test case:
   a=/b/c/*
   b=\\
   echo ${a%$b*}
Old result:
   /b/c/*
New result:
   /b/c/

Based on commits 880d952, 7cfd8be, 0d7d660 and a7c21a6 from
git://git.kernel.org/pub/scm/utils/dash/dash.git by Herbert Xu

function                                             old     new   delta
argstr                                              1164    1193     +29
memtodest                                            147     174     +27
subevalvar                                          1153    1177     +24
redirect                                            1279    1282      +3
dolatstr                                               5       7      +2
static.spclchars                                      10       9      -1
expandarg                                            962     960      -2
evalcase                                             273     271      -2
evalcommand                                         1204    1197      -7
rmescapes                                            236     227      -9
preglob                                               27       8     -19
evalvar                                              604     582     -22
cmdputs                                              389     334     -55
readtoken1                                          3163    3061    -102
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 5/9 up/down: 85/-219)          Total: -134 bytes

Signed-off-by: Ron Yorston <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Ron Yorston 2015-05-18 09:57:51 +02:00 committed by Denys Vlasenko
parent ad88bdee0c
commit 549deab5ab
5 changed files with 73 additions and 99 deletions

View File

@ -598,8 +598,6 @@ out2str(const char *p)
#define CTLVAR ((unsigned char)'\202') /* variable defn */ #define CTLVAR ((unsigned char)'\202') /* variable defn */
#define CTLENDVAR ((unsigned char)'\203') #define CTLENDVAR ((unsigned char)'\203')
#define CTLBACKQ ((unsigned char)'\204') #define CTLBACKQ ((unsigned char)'\204')
#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
/* CTLBACKQ | CTLQUOTE == '\205' */
#define CTLARI ((unsigned char)'\206') /* arithmetic expression */ #define CTLARI ((unsigned char)'\206') /* arithmetic expression */
#define CTLENDARI ((unsigned char)'\207') #define CTLENDARI ((unsigned char)'\207')
#define CTLQUOTEMARK ((unsigned char)'\210') #define CTLQUOTEMARK ((unsigned char)'\210')
@ -608,7 +606,6 @@ out2str(const char *p)
/* variable substitution byte (follows CTLVAR) */ /* variable substitution byte (follows CTLVAR) */
#define VSTYPE 0x0f /* type of variable substitution */ #define VSTYPE 0x0f /* type of variable substitution */
#define VSNUL 0x10 /* colon--treat the empty string as unset */ #define VSNUL 0x10 /* colon--treat the empty string as unset */
#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
/* values of VSTYPE field */ /* values of VSTYPE field */
#define VSNORMAL 0x1 /* normal variable: $var or ${var} */ #define VSNORMAL 0x1 /* normal variable: $var or ${var} */
@ -628,8 +625,9 @@ out2str(const char *p)
#endif #endif
static const char dolatstr[] ALIGN1 = { static const char dolatstr[] ALIGN1 = {
CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0'
}; };
#define DOLATSTRLEN 6
#define NCMD 0 #define NCMD 0
#define NPIPE 1 #define NPIPE 1
@ -865,9 +863,7 @@ trace_puts_quoted(char *s)
case '\\': c = '\\'; goto backslash; case '\\': c = '\\'; goto backslash;
case CTLESC: c = 'e'; goto backslash; case CTLESC: c = 'e'; goto backslash;
case CTLVAR: c = 'v'; goto backslash; case CTLVAR: c = 'v'; goto backslash;
case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
case CTLBACKQ: c = 'q'; goto backslash; case CTLBACKQ: c = 'q'; goto backslash;
case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
backslash: backslash:
putc('\\', tracefile); putc('\\', tracefile);
putc(c, tracefile); putc(c, tracefile);
@ -1030,7 +1026,6 @@ sharg(union node *arg, FILE *fp)
putc('}', fp); putc('}', fp);
break; break;
case CTLBACKQ: case CTLBACKQ:
case CTLBACKQ|CTLQUOTE:
putc('$', fp); putc('$', fp);
putc('(', fp); putc('(', fp);
shtree(bqlist->n, -1, NULL, fp); shtree(bqlist->n, -1, NULL, fp);
@ -4413,11 +4408,7 @@ cmdputs(const char *s)
str = "${#"; str = "${#";
else else
str = "${"; str = "${";
if (!(subtype & VSQUOTE) == !(quoted & 1)) goto dostr;
goto dostr;
quoted ^= 1;
c = '"';
break;
case CTLENDVAR: case CTLENDVAR:
str = "\"}" + !(quoted & 1); str = "\"}" + !(quoted & 1);
quoted >>= 1; quoted >>= 1;
@ -4426,9 +4417,6 @@ cmdputs(const char *s)
case CTLBACKQ: case CTLBACKQ:
str = "$(...)"; str = "$(...)";
goto dostr; goto dostr;
case CTLBACKQ+CTLQUOTE:
str = "\"$(...)\"";
goto dostr;
#if ENABLE_SH_MATH_SUPPORT #if ENABLE_SH_MATH_SUPPORT
case CTLARI: case CTLARI:
str = "$(("; str = "$((";
@ -5526,7 +5514,7 @@ ash_arith(const char *s)
#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ #define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ #define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */ #define EXP_QPAT 0x20 /* pattern in quoted parameter expansion */
#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ #define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
#define EXP_WORD 0x80 /* expand word in parameter expansion */ #define EXP_WORD 0x80 /* expand word in parameter expansion */
#define EXP_QUOTED 0x100 /* expand word in double quotes */ #define EXP_QUOTED 0x100 /* expand word in double quotes */
@ -5535,12 +5523,11 @@ ash_arith(const char *s)
*/ */
#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ #define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ #define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
/* Add CTLESC when necessary. */ /* Add CTLESC when necessary. */
#define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_REDIR) #define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT | EXP_REDIR)
/* Do not skip NUL characters. */ /* Do not skip NUL characters. */
#define QUOTES_KEEPNUL EXP_TILDE #define QUOTES_KEEPNUL EXP_TILDE
@ -5641,13 +5628,11 @@ rmescapes(char *str, int flag)
} }
} }
inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; inquotes = 0;
globbing = flag & RMESCAPE_GLOB; globbing = flag & RMESCAPE_GLOB;
protect_against_glob = globbing; protect_against_glob = globbing;
while (*p) { while (*p) {
if ((unsigned char)*p == CTLQUOTEMARK) { if ((unsigned char)*p == CTLQUOTEMARK) {
// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
// Note: both inquotes and protect_against_glob only affect whether // Note: both inquotes and protect_against_glob only affect whether
// CTLESC,<ch> gets converted to <ch> or to \<ch> // CTLESC,<ch> gets converted to <ch> or to \<ch>
inquotes = ~inquotes; inquotes = ~inquotes;
@ -5655,17 +5640,16 @@ rmescapes(char *str, int flag)
protect_against_glob = globbing; protect_against_glob = globbing;
continue; continue;
} }
if (*p == '\\') { if ((unsigned char)*p == CTLESC) {
p++;
if (protect_against_glob) {
*q++ = '\\';
}
} else if (*p == '\\' && !inquotes) {
/* naked back slash */ /* naked back slash */
protect_against_glob = 0; protect_against_glob = 0;
goto copy; goto copy;
} }
if ((unsigned char)*p == CTLESC) {
p++;
if (protect_against_glob && inquotes && *p != '/') {
*q++ = '\\';
}
}
protect_against_glob = globbing; protect_against_glob = globbing;
copy: copy:
*q++ = *p++; *q++ = *p++;
@ -5685,13 +5669,9 @@ rmescapes(char *str, int flag)
* Returns an stalloced string. * Returns an stalloced string.
*/ */
static char * static char *
preglob(const char *pattern, int quoted, int flag) preglob(const char *pattern, int flag)
{ {
flag |= RMESCAPE_GLOB; return rmescapes((char *)pattern, flag | RMESCAPE_GLOB);
if (quoted) {
flag |= RMESCAPE_QUOTED;
}
return rmescapes((char *)pattern, flag);
} }
/* /*
@ -5712,7 +5692,9 @@ memtodest(const char *p, size_t len, int syntax, int quotes)
if (c) { if (c) {
int n = SIT(c, syntax); int n = SIT(c, syntax);
if ((quotes & QUOTES_ESC) && if ((quotes & QUOTES_ESC) &&
(n == CCTL || n == CBACK)) ((n == CCTL) ||
(((quotes & EXP_FULL) || syntax != BASESYNTAX) &&
n == CBACK)))
USTPUTC(CTLESC, q); USTPUTC(CTLESC, q);
} else if (!(quotes & QUOTES_KEEPNUL)) } else if (!(quotes & QUOTES_KEEPNUL))
continue; continue;
@ -5905,7 +5887,7 @@ evalbackcmd(union node *n, struct backcmd *result)
* Expand stuff in backwards quotes. * Expand stuff in backwards quotes.
*/ */
static void static void
expbackq(union node *cmd, int quoted, int quotes) expbackq(union node *cmd, int flag)
{ {
struct backcmd in; struct backcmd in;
int i; int i;
@ -5913,7 +5895,7 @@ expbackq(union node *cmd, int quoted, int quotes)
char *p; char *p;
char *dest; char *dest;
int startloc; int startloc;
int syntax = quoted ? DQSYNTAX : BASESYNTAX; int syntax = flag & EXP_QUOTED ? DQSYNTAX : BASESYNTAX;
struct stackmark smark; struct stackmark smark;
INT_OFF; INT_OFF;
@ -5929,7 +5911,7 @@ expbackq(union node *cmd, int quoted, int quotes)
if (i == 0) if (i == 0)
goto read; goto read;
for (;;) { for (;;) {
memtodest(p, i, syntax, quotes); memtodest(p, i, syntax, flag & QUOTES_ESC);
read: read:
if (in.fd < 0) if (in.fd < 0)
break; break;
@ -5953,7 +5935,7 @@ expbackq(union node *cmd, int quoted, int quotes)
STUNPUTC(dest); STUNPUTC(dest);
expdest = dest; expdest = dest;
if (quoted == 0) if (!(flag & EXP_QUOTED))
recordregion(startloc, dest - (char *)stackblock(), 0); recordregion(startloc, dest - (char *)stackblock(), 0);
TRACE(("evalbackq: size:%d:'%.*s'\n", TRACE(("evalbackq: size:%d:'%.*s'\n",
(int)((dest - (char *)stackblock()) - startloc), (int)((dest - (char *)stackblock()) - startloc),
@ -5967,11 +5949,10 @@ expbackq(union node *cmd, int quoted, int quotes)
* evaluate, place result in (backed up) result, adjust string position. * evaluate, place result in (backed up) result, adjust string position.
*/ */
static void static void
expari(int quotes) expari(int flag)
{ {
char *p, *start; char *p, *start;
int begoff; int begoff;
int flag;
int len; int len;
/* ifsfree(); */ /* ifsfree(); */
@ -6009,16 +5990,14 @@ expari(int quotes)
removerecordregions(begoff); removerecordregions(begoff);
flag = p[1];
expdest = p; expdest = p;
if (quotes) if (flag & QUOTES_ESC)
rmescapes(p + 2, 0); rmescapes(p + 1, 0);
len = cvtnum(ash_arith(p + 2)); len = cvtnum(ash_arith(p + 1));
if (flag != '"') if (!(flag & EXP_QUOTED))
recordregion(begoff, begoff + len, 0); recordregion(begoff, begoff + len, 0);
} }
#endif #endif
@ -6046,14 +6025,12 @@ argstr(char *p, int flags, struct strlist *var_str_list)
CTLESC, CTLESC,
CTLVAR, CTLVAR,
CTLBACKQ, CTLBACKQ,
CTLBACKQ | CTLQUOTE,
#if ENABLE_SH_MATH_SUPPORT #if ENABLE_SH_MATH_SUPPORT
CTLENDARI, CTLENDARI,
#endif #endif
'\0' '\0'
}; };
const char *reject = spclchars; const char *reject = spclchars;
int quotes = flags & QUOTES_ESC;
int breakall = (flags & (EXP_WORD | EXP_QUOTED)) == EXP_WORD; int breakall = (flags & (EXP_WORD | EXP_QUOTED)) == EXP_WORD;
int inquotes; int inquotes;
size_t length; size_t length;
@ -6128,19 +6105,14 @@ argstr(char *p, int flags, struct strlist *var_str_list)
case CTLENDVAR: /* ??? */ case CTLENDVAR: /* ??? */
goto breakloop; goto breakloop;
case CTLQUOTEMARK: case CTLQUOTEMARK:
inquotes ^= EXP_QUOTED;
/* "$@" syntax adherence hack */ /* "$@" syntax adherence hack */
if (!inquotes if (inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) {
&& memcmp(p, dolatstr, 4) == 0 p = evalvar(p + 1, flags | inquotes, /* var_str_list: */ NULL) + 1;
&& ( p[4] == (char)CTLQUOTEMARK
|| (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
)
) {
p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
goto start; goto start;
} }
inquotes = !inquotes;
addquote: addquote:
if (quotes) { if (flags & QUOTES_ESC) {
p--; p--;
length++; length++;
startloc++; startloc++;
@ -6149,22 +6121,30 @@ argstr(char *p, int flags, struct strlist *var_str_list)
case CTLESC: case CTLESC:
startloc++; startloc++;
length++; length++;
/*
* Quoted parameter expansion pattern: remove quote
* unless inside inner quotes or we have a literal
* backslash.
*/
if (((flags | inquotes) & (EXP_QPAT | EXP_QUOTED)) ==
EXP_QPAT && *p != '\\')
break;
goto addquote; goto addquote;
case CTLVAR: case CTLVAR:
TRACE(("argstr: evalvar('%s')\n", p)); TRACE(("argstr: evalvar('%s')\n", p));
p = evalvar(p, flags, var_str_list); p = evalvar(p, flags | inquotes, var_str_list);
TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock()));
goto start; goto start;
case CTLBACKQ: case CTLBACKQ:
c = '\0'; expbackq(argbackq->n, flags | inquotes);
case CTLBACKQ|CTLQUOTE:
expbackq(argbackq->n, c, quotes);
argbackq = argbackq->next; argbackq = argbackq->next;
goto start; goto start;
#if ENABLE_SH_MATH_SUPPORT #if ENABLE_SH_MATH_SUPPORT
case CTLENDARI: case CTLENDARI:
p--; p--;
expari(quotes); expari(flags | inquotes);
goto start; goto start;
#endif #endif
} }
@ -6296,13 +6276,13 @@ varunset(const char *end, const char *var, const char *umsg, int varflags)
#if ENABLE_ASH_BASH_COMPAT #if ENABLE_ASH_BASH_COMPAT
static char * static char *
parse_sub_pattern(char *arg, int varflags) parse_sub_pattern(char *arg, int quoted)
{ {
char *idx, *repl = NULL; char *idx, *repl = NULL;
unsigned char c; unsigned char c;
//char *org_arg = arg; //char *org_arg = arg;
//bb_error_msg("arg:'%s' varflags:%x", arg, varflags); //bb_error_msg("arg:'%s' quoted:%x", arg, quoted);
idx = arg; idx = arg;
while (1) { while (1) {
c = *arg; c = *arg;
@ -6318,7 +6298,7 @@ parse_sub_pattern(char *arg, int varflags)
*idx++ = c; *idx++ = c;
arg++; arg++;
/* /*
* Example: v='ab\c'; echo ${v/\\b/_\\_\z_} * Example: v='a\bc'; echo ${v/\\b/_\\_\z_}
* The result is a_\_z_c (not a\_\_z_c)! * The result is a_\_z_c (not a\_\_z_c)!
* *
* Enable debug prints in this function and you'll see: * Enable debug prints in this function and you'll see:
@ -6328,7 +6308,7 @@ parse_sub_pattern(char *arg, int varflags)
* IOW: search pattern and replace string treat backslashes * IOW: search pattern and replace string treat backslashes
* differently! That is the reason why we check repl below: * differently! That is the reason why we check repl below:
*/ */
if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE)) if (c == '\\' && *arg == '\\' && repl && !quoted)
arg++; /* skip both '\', not just first one */ arg++; /* skip both '\', not just first one */
} }
*idx = c; /* NUL */ *idx = c; /* NUL */
@ -6340,9 +6320,10 @@ parse_sub_pattern(char *arg, int varflags)
static const char * static const char *
subevalvar(char *p, char *varname, int strloc, int subtype, subevalvar(char *p, char *varname, int strloc, int subtype,
int startloc, int varflags, int quotes, struct strlist *var_str_list) int startloc, int varflags, int flag, struct strlist *var_str_list)
{ {
struct nodelist *saveargbackq = argbackq; struct nodelist *saveargbackq = argbackq;
int quotes = flag & QUOTES_ESC;
char *startp; char *startp;
char *loc; char *loc;
char *rmesc, *rmescend; char *rmesc, *rmescend;
@ -6360,7 +6341,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
herefd = -1; herefd = -1;
argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ? argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ?
EXP_CASE : 0), var_str_list); (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE) : 0),
var_str_list);
STPUTC('\0', expdest); STPUTC('\0', expdest);
herefd = saveherefd; herefd = saveherefd;
argbackq = saveargbackq; argbackq = saveargbackq;
@ -6471,7 +6453,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
} }
rmescend--; rmescend--;
str = (char *)stackblock() + strloc; str = (char *)stackblock() + strloc;
preglob(str, varflags & VSQUOTE, 0); preglob(str, 0);
#if ENABLE_ASH_BASH_COMPAT #if ENABLE_ASH_BASH_COMPAT
workloc = expdest - (char *)stackblock(); workloc = expdest - (char *)stackblock();
@ -6479,7 +6461,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
char *idx, *end; char *idx, *end;
if (!repl) { if (!repl) {
repl = parse_sub_pattern(str, varflags); repl = parse_sub_pattern(str, flag & EXP_QUOTED);
//bb_error_msg("repl:'%s'", repl); //bb_error_msg("repl:'%s'", repl);
if (!repl) if (!repl)
repl = nullstr; repl = nullstr;
@ -6618,7 +6600,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
int i; int i;
ssize_t len = 0; ssize_t len = 0;
int sep; int sep;
int quoted = varflags & VSQUOTE; int quoted = flags & EXP_QUOTED;
int subtype = varflags & VSTYPE; int subtype = varflags & VSTYPE;
int discard = subtype == VSPLUS || subtype == VSLENGTH; int discard = subtype == VSPLUS || subtype == VSLENGTH;
int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL;
@ -6758,7 +6740,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
{ {
char varflags; char varflags;
char subtype; char subtype;
char quoted; int quoted;
char easy; char easy;
char *var; char *var;
int patloc; int patloc;
@ -6767,7 +6749,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
varflags = (unsigned char) *p++; varflags = (unsigned char) *p++;
subtype = varflags & VSTYPE; subtype = varflags & VSTYPE;
quoted = varflags & VSQUOTE; quoted = flags & EXP_QUOTED;
var = p; var = p;
easy = (!quoted || (*var == '@' && shellparam.nparam)); easy = (!quoted || (*var == '@' && shellparam.nparam));
startloc = expdest - (char *)stackblock(); startloc = expdest - (char *)stackblock();
@ -6788,7 +6770,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
if (varlen < 0) { if (varlen < 0) {
argstr( argstr(
p, p,
flags | EXP_TILDE | EXP_WORD | (quoted ? EXP_QUOTED : 0), flags | EXP_TILDE | EXP_WORD,
var_str_list var_str_list
); );
goto end; goto end;
@ -6802,7 +6784,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
if (varlen < 0) { if (varlen < 0) {
if (subevalvar(p, var, /* strloc: */ 0, if (subevalvar(p, var, /* strloc: */ 0,
subtype, startloc, varflags, subtype, startloc, varflags,
/* quotes: */ 0, /* quotes: */ flags & ~QUOTES_ESC,
var_str_list) var_str_list)
) { ) {
varflags &= ~VSNUL; varflags &= ~VSNUL;
@ -6859,10 +6841,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
STPUTC('\0', expdest); STPUTC('\0', expdest);
patloc = expdest - (char *)stackblock(); patloc = expdest - (char *)stackblock();
if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype, if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
startloc, varflags, startloc, varflags, flags, var_str_list)) {
/* quotes: */ flags & QUOTES_ESC,
var_str_list)
) {
int amount = expdest - ( int amount = expdest - (
(char *)stackblock() + patloc - 1 (char *)stackblock() + patloc - 1
); );
@ -6881,7 +6860,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
unsigned char c = *p++; unsigned char c = *p++;
if (c == CTLESC) if (c == CTLESC)
p++; p++;
else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { else if (c == CTLBACKQ) {
if (varlen >= 0) if (varlen >= 0)
argbackq = argbackq->next; argbackq = argbackq->next;
} else if (c == CTLVAR) { } else if (c == CTLVAR) {
@ -7217,7 +7196,7 @@ expandmeta(struct strlist *str /*, int flag*/)
savelastp = exparg.lastp; savelastp = exparg.lastp;
INT_OFF; INT_OFF;
p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP); p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
{ {
int i = strlen(str->text); int i = strlen(str->text);
expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
@ -7307,7 +7286,7 @@ static void
expandhere(union node *arg, int fd) expandhere(union node *arg, int fd)
{ {
herefd = fd; herefd = fd;
expandarg(arg, (struct arglist *)NULL, 0); expandarg(arg, (struct arglist *)NULL, EXP_QUOTED);
full_write(fd, stackblock(), expdest - (char *)stackblock()); full_write(fd, stackblock(), expdest - (char *)stackblock());
} }
@ -7317,7 +7296,7 @@ expandhere(union node *arg, int fd)
static int static int
patmatch(char *pattern, const char *string) patmatch(char *pattern, const char *string)
{ {
return pmatch(preglob(pattern, 0, 0), string); return pmatch(preglob(pattern, 0), string);
} }
/* /*
@ -8570,7 +8549,7 @@ evalfor(union node *n, int flags)
arglist.list = NULL; arglist.list = NULL;
arglist.lastp = &arglist.list; arglist.lastp = &arglist.list;
for (argp = n->nfor.args; argp; argp = argp->narg.next) { for (argp = n->nfor.args; argp; argp = argp->narg.next) {
expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD); expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
/* XXX */ /* XXX */
if (evalskip) if (evalskip)
goto out; goto out;
@ -11260,11 +11239,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
&& c != '$' && c != '$'
&& (c != '"' || eofmark != NULL) && (c != '"' || eofmark != NULL)
) { ) {
USTPUTC(CTLESC, out);
USTPUTC('\\', out); USTPUTC('\\', out);
} }
if (SIT(c, SQSYNTAX) == CCTL) USTPUTC(CTLESC, out);
USTPUTC(CTLESC, out);
USTPUTC(c, out); USTPUTC(c, out);
quotef = 1; quotef = 1;
} }
@ -11637,8 +11614,6 @@ parsesub: {
do_pungetc: do_pungetc:
pungetc(); pungetc();
} }
if (dblquote || arinest)
flags |= VSQUOTE;
((unsigned char *)stackblock())[typeloc] = subtype | flags; ((unsigned char *)stackblock())[typeloc] = subtype | flags;
if (subtype != VSNORMAL) { if (subtype != VSNORMAL) {
varnest++; varnest++;
@ -11792,10 +11767,7 @@ parsebackq: {
} }
parsebackquote = savepbq; parsebackquote = savepbq;
exception_handler = savehandler; exception_handler = savehandler;
if (arinest || dblquote) USTPUTC(CTLBACKQ, out);
USTPUTC(CTLBACKQ | CTLQUOTE, out);
else
USTPUTC(CTLBACKQ, out);
if (oldstyle) if (oldstyle)
goto parsebackq_oldreturn; goto parsebackq_oldreturn;
goto parsebackq_newreturn; goto parsebackq_newreturn;
@ -11811,10 +11783,6 @@ parsearith: {
syntax = ARISYNTAX; syntax = ARISYNTAX;
} }
USTPUTC(CTLARI, out); USTPUTC(CTLARI, out);
if (dblquote)
USTPUTC('"', out);
else
USTPUTC(' ', out);
goto parsearith_return; goto parsearith_return;
} }
#endif #endif
@ -12129,7 +12097,7 @@ expandstr(const char *ps)
n.narg.text = wordtext; n.narg.text = wordtext;
n.narg.backquote = backquotelist; n.narg.backquote = backquotelist;
expandarg(&n, NULL, 0); expandarg(&n, NULL, EXP_QUOTED);
return stackblock(); return stackblock();
} }
#endif #endif

View File

@ -0,0 +1,3 @@
a=/b/c/*
b=\\
echo ${a%$b*}

View File

@ -0,0 +1 @@
<>

View File

@ -0,0 +1 @@
foo=\\ echo "<${foo#[\\]}>"