From 7cfd8be0dc83342b4a71f3a8e5b7efab4670e50c Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 25 Sep 2007 22:29:00 +0800 Subject: [EXPAND] 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: <> --- src/TOUR | 12 +++---- src/eval.c | 2 +- src/expand.c | 110 ++++++++++++++++++++++++++------------------------------- src/expand.h | 2 +- src/jobs.c | 10 +----- src/mystring.c | 3 +- src/mystring.h | 2 +- src/parser.c | 17 ++------- src/parser.h | 3 -- src/show.c | 3 -- 10 files changed, 63 insertions(+), 101 deletions(-) (limited to 'src') diff --git a/src/TOUR b/src/TOUR index 0c60e2a..4baac62 100644 --- a/src/TOUR +++ b/src/TOUR @@ -159,7 +159,6 @@ special codes defined in parser.h. The special codes are: CTLVAR Variable substitution CTLENDVAR End of variable substitution CTLBACKQ Command substitution - CTLBACKQ|CTLQUOTE Command substitution inside double quotes CTLESC Escape next character A variable substitution contains the following elements: @@ -179,16 +178,13 @@ stitution. The possible types are: VSASSIGN ${var=text} VSASSIGN|VSNUL ${var=text} -In addition, the type field will have the VSQUOTE flag set if the -variable is enclosed in double quotes. The name of the variable -comes next, terminated by an equals sign. If the type is not -VSNORMAL, then the text field in the substitution follows, ter- -minated by a CTLENDVAR byte. +The name of the variable comes next, terminated by an equals +sign. If the type is not VSNORMAL, then the text field in the +substitution follows, terminated by a CTLENDVAR byte. Commands in back quotes are parsed and stored in a linked list. The locations of these commands in the string are indicated by -CTLBACKQ and CTLBACKQ+CTLQUOTE characters, depending upon whether -the back quotes were enclosed in double quotes. +the CTLBACKQ character. The character CTLESC escapes the next character, so that in case any of the CTL characters mentioned above appear in the input, diff --git a/src/eval.c b/src/eval.c index fed82d5..2aa8317 100644 --- a/src/eval.c +++ b/src/eval.c @@ -377,7 +377,7 @@ evalfor(union node *n, int flags) setstackmark(&smark); arglist.lastp = &arglist.list; 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 */ if (evalskip) goto out; diff --git a/src/expand.c b/src/expand.c index c9f09ff..8c6c7f9 100644 --- a/src/expand.c +++ b/src/expand.c @@ -82,12 +82,11 @@ */ #define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ #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_HEAP 0x10 /* Malloc strings instead of stalloc */ /* Add CTLESC when necessary. */ -#define QUOTES_ESC (EXP_FULL | EXP_CASE) +#define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT) /* Do not skip NUL characters. */ #define QUOTES_KEEPNUL EXP_TILDE @@ -116,7 +115,7 @@ static struct arglist exparg; STATIC void argstr(char *, int); STATIC char *exptilde(char *, char *, int); -STATIC void expbackq(union node *, int, int); +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 *, const char *, int); @@ -158,11 +157,8 @@ STATIC void varunset(const char *, const char *, const char *, int) */ STATIC inline char * -preglob(const char *pattern, int quoted, int flag) { +preglob(const char *pattern, int flag) { flag |= RMESCAPE_GLOB; - if (quoted) { - flag |= RMESCAPE_QUOTED; - } return _rmescapes((char *)pattern, flag); } @@ -197,7 +193,7 @@ void expandhere(union node *arg, int fd) { herefd = fd; - expandarg(arg, (struct arglist *)NULL, 0); + expandarg(arg, (struct arglist *)NULL, EXP_QUOTED); xwrite(fd, stackblock(), expdest - (char *)stackblock()); } @@ -271,13 +267,11 @@ argstr(char *p, int flag) CTLESC, CTLVAR, CTLBACKQ, - CTLBACKQ | CTLQUOTE, CTLENDARI, 0 }; const char *reject = spclchars; int c; - int quotes = flag & QUOTES_ESC; int breakall = (flag & (EXP_WORD | EXP_QUOTED)) == EXP_WORD; int inquotes; size_t length; @@ -346,21 +340,15 @@ start: case CTLENDVAR: /* ??? */ goto breakloop; case CTLQUOTEMARK: + inquotes ^= EXP_QUOTED; /* "$@" syntax adherence hack */ - if ( - !inquotes && - !memcmp(p, dolatstr, DOLATSTRLEN) && - (p[4] == (char)CTLQUOTEMARK || ( - p[4] == (char)CTLENDVAR && - p[5] == (char)CTLQUOTEMARK - )) - ) { - p = evalvar(p + 1, flag) + 1; + if (inquotes && !memcmp(p, dolatstr + 1, + DOLATSTRLEN - 1)) { + p = evalvar(p + 1, flag | inquotes) + 1; goto start; } - inquotes = !inquotes; addquote: - if (quotes) { + if (flag & QUOTES_ESC) { p--; length++; startloc++; @@ -369,19 +357,27 @@ addquote: case CTLESC: startloc++; length++; + + /* + * Quoted parameter expansion pattern: remove quote + * unless inside inner quotes or we have a literal + * backslash. + */ + if (((flag | inquotes) & (EXP_QPAT | EXP_QUOTED)) == + EXP_QPAT && *p != '\\') + break; + goto addquote; case CTLVAR: - p = evalvar(p, flag); + p = evalvar(p, flag | inquotes); goto start; case CTLBACKQ: - c = 0; - case CTLBACKQ|CTLQUOTE: - expbackq(argbackq->n, c, quotes); + expbackq(argbackq->n, flag | inquotes); argbackq = argbackq->next; goto start; case CTLENDARI: p--; - expari(quotes); + expari(flag | inquotes); goto start; } } @@ -480,11 +476,10 @@ removerecordregions(int endoff) * evaluate, place result in (backed up) result, adjust string position. */ void -expari(int quotes) +expari(int flag) { char *p, *start; int begoff; - int flag; int len; /* ifsfree(); */ @@ -522,16 +517,14 @@ expari(int quotes) removerecordregions(begoff); - flag = p[1]; - expdest = p; - if (quotes) - rmescapes(p + 2); + if (flag & QUOTES_ESC) + rmescapes(p + 1); - len = cvtnum(arith(p + 2)); + len = cvtnum(arith(p + 1)); - if (flag != '"') + if (!(flag & EXP_QUOTED)) recordregion(begoff, begoff + len, 0); } @@ -541,7 +534,7 @@ expari(int quotes) */ STATIC void -expbackq(union node *cmd, int quoted, int quotes) +expbackq(union node *cmd, int flag) { struct backcmd in; int i; @@ -549,7 +542,7 @@ expbackq(union node *cmd, int quoted, int quotes) char *p; char *dest; int startloc; - char const *syntax = quoted? DQSYNTAX : BASESYNTAX; + char const *syntax = flag & EXP_QUOTED ? DQSYNTAX : BASESYNTAX; struct stackmark smark; INTOFF; @@ -565,7 +558,7 @@ expbackq(union node *cmd, int quoted, int quotes) if (i == 0) goto read; for (;;) { - memtodest(p, i, syntax, quotes); + memtodest(p, i, syntax, flag & QUOTES_ESC); read: if (in.fd < 0) break; @@ -592,7 +585,7 @@ read: STUNPUTC(dest); expdest = dest; - if (quoted == 0) + if (!(flag & EXP_QUOTED)) recordregion(startloc, dest - (char *)stackblock(), 0); TRACE(("evalbackq: size=%d: \"%.*s\"\n", (dest - (char *)stackblock()) - startloc, @@ -669,8 +662,9 @@ scanright( } STATIC const char * -subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes) +subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int flag) { + int quotes = flag & QUOTES_ESC; char *startp; char *loc; int saveherefd = herefd; @@ -682,7 +676,7 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varfla herefd = -1; argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ? - EXP_CASE : 0)); + (flag & EXP_QUOTED ? EXP_QPAT : EXP_CASE) : 0)); STPUTC('\0', expdest); herefd = saveherefd; argbackq = saveargbackq; @@ -717,7 +711,7 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varfla } rmescend--; str = stackblock() + strloc; - preglob(str, varflags & VSQUOTE, 0); + preglob(str, 0); /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */ zero = subtype >> 1; @@ -753,13 +747,11 @@ evalvar(char *p, int flag) int startloc; ssize_t varlen; int easy; - int quotes; int quoted; - quotes = flag & QUOTES_ESC; varflags = *p++; subtype = varflags & VSTYPE; - quoted = varflags & VSQUOTE; + quoted = flag & EXP_QUOTED; var = p; easy = (!quoted || (*var == '@' && shellparam.nparam)); startloc = expdest - (char *)stackblock(); @@ -778,8 +770,7 @@ again: if (subtype == VSMINUS) { vsplus: if (varlen < 0) { - argstr(p, flag | EXP_TILDE | EXP_WORD | - (quoted ? EXP_QUOTED : 0)); + argstr(p, flag | EXP_TILDE | EXP_WORD); goto end; } if (easy) @@ -790,7 +781,7 @@ vsplus: if (subtype == VSASSIGN || subtype == VSQUESTION) { if (varlen < 0) { if (subevalvar(p, var, 0, subtype, startloc, - varflags, 0)) { + varflags, flag & ~QUOTES_ESC)) { varflags &= ~VSNUL; /* * Remove any recorded regions beyond @@ -842,7 +833,7 @@ record: STPUTC('\0', expdest); patloc = expdest - (char *)stackblock(); if (subevalvar(p, NULL, patloc, subtype, - startloc, varflags, quotes) == 0) { + startloc, varflags, flag) == 0) { int amount = expdest - ( (char *)stackblock() + patloc - 1 ); @@ -859,7 +850,7 @@ end: for (;;) { if ((c = (signed char)*p++) == CTLESC) p++; - else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { + else if (c == CTLBACKQ) { if (varlen >= 0) argbackq = argbackq->next; } else if (c == CTLVAR) { @@ -930,7 +921,7 @@ varvalue(char *name, int varflags, int flags) char sepc; char **ap; char const *syntax; - int quoted = varflags & VSQUOTE; + int quoted = flags & EXP_QUOTED; int subtype = varflags & VSTYPE; int discard = subtype == VSPLUS || subtype == VSLENGTH; int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; @@ -1175,7 +1166,7 @@ expandmeta(str, flag) if (fflag) goto nometa; INTOFF; - p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP); + p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); i = glob(p, GLOB_NOMAGIC, 0, &pglob); if (p != str->text) ckfree(p); @@ -1244,7 +1235,7 @@ expandmeta(struct strlist *str, int flag) savelastp = exparg.lastp; INTOFF; - p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP); + p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); { int i = strlen(str->text); expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ @@ -1475,7 +1466,7 @@ msort(struct strlist *list, int len) STATIC inline int patmatch(char *pattern, const char *string) { - return pmatch(preglob(pattern, 0, 0), string); + return pmatch(preglob(pattern, 0), string); } @@ -1651,7 +1642,7 @@ _rmescapes(char *str, int flag) q = mempcpy(q, str, len); } } - inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; + inquotes = 0; globbing = flag & RMESCAPE_GLOB; notescaped = globbing; while (*p) { @@ -1661,15 +1652,14 @@ _rmescapes(char *str, int flag) notescaped = globbing; continue; } - if (*p == '\\') { - /* naked back slash */ - notescaped = 0; - goto copy; - } if (*p == (char)CTLESC) { p++; - if (notescaped && inquotes) + if (notescaped) *q++ = '\\'; + } else if (*p == '\\' && !inquotes) { + /* naked back slash */ + notescaped = 0; + goto copy; } notescaped = globbing; copy: diff --git a/src/expand.h b/src/expand.h index be7ec6f..4dfbc43 100644 --- a/src/expand.h +++ b/src/expand.h @@ -53,7 +53,7 @@ struct arglist { #define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ #define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ #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_WORD 0x80 /* expand word in parameter expansion */ #define EXP_QUOTED 0x100 /* expand word in double quotes */ diff --git a/src/jobs.c b/src/jobs.c index 7285d0d..529d615 100644 --- a/src/jobs.c +++ b/src/jobs.c @@ -1377,12 +1377,7 @@ cmdputs(const char *s) str = "${#"; else str = "${"; - if (!(subtype & VSQUOTE) != !(quoted & 1)) { - quoted ^= 1; - c = '"'; - } else - goto dostr; - break; + goto dostr; case CTLENDVAR: str = "\"}" + !(quoted & 1); quoted >>= 1; @@ -1391,9 +1386,6 @@ cmdputs(const char *s) case CTLBACKQ: str = "$(...)"; goto dostr; - case CTLBACKQ+CTLQUOTE: - str = "\"$(...)\""; - goto dostr; case CTLARI: str = "$(("; goto dostr; diff --git a/src/mystring.c b/src/mystring.c index 49201a9..7d937a8 100644 --- a/src/mystring.c +++ b/src/mystring.c @@ -55,7 +55,8 @@ char nullstr[1]; /* zero length string */ const char spcstr[] = " "; const char snlfmt[] = "%s\n"; -const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' }; +const char dolatstr[] = { CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', + CTLQUOTEMARK, '\0' }; const char illnum[] = "Illegal number: %s"; const char homestr[] = "HOME"; diff --git a/src/mystring.h b/src/mystring.h index 44fd7e4..f451cc2 100644 --- a/src/mystring.h +++ b/src/mystring.h @@ -39,7 +39,7 @@ extern const char snlfmt[]; extern const char spcstr[]; extern const char dolatstr[]; -#define DOLATSTRLEN 4 +#define DOLATSTRLEN 6 extern const char illnum[]; extern const char homestr[]; diff --git a/src/parser.c b/src/parser.c index e00fd4b..1a483d4 100644 --- a/src/parser.c +++ b/src/parser.c @@ -911,11 +911,9 @@ readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) eofmark != NULL ) ) { - USTPUTC(CTLESC, out); USTPUTC('\\', out); } - if (SQSYNTAX[c] == CCTL) - USTPUTC(CTLESC, out); + USTPUTC(CTLESC, out); USTPUTC(c, out); quotef++; } @@ -1221,8 +1219,6 @@ badsub: synerror("Bad substitution"); } else { pungetc(); } - if (dblquote || arinest) - flags |= VSQUOTE; *((char *)stackblock() + typeloc) = subtype | flags; if (subtype != VSNORMAL) { varnest++; @@ -1353,10 +1349,7 @@ done: memcpy(out, str, savelen); STADJUST(savelen, out); } - if (arinest || dblquote) - USTPUTC(CTLBACKQ | CTLQUOTE, out); - else - USTPUTC(CTLBACKQ, out); + USTPUTC(CTLBACKQ, out); if (oldstyle) goto parsebackq_oldreturn; else @@ -1373,10 +1366,6 @@ parsearith: { syntax = ARISYNTAX; } USTPUTC(CTLARI, out); - if (dblquote) - USTPUTC('"',out); - else - USTPUTC(' ',out); goto parsearith_return; } @@ -1503,7 +1492,7 @@ expandstr(const char *ps) n.narg.text = wordtext; n.narg.backquote = backquotelist; - expandarg(&n, NULL, 0); + expandarg(&n, NULL, EXP_QUOTED); return stackblock(); } diff --git a/src/parser.h b/src/parser.h index 76ec839..badbd07 100644 --- a/src/parser.h +++ b/src/parser.h @@ -40,8 +40,6 @@ #define CTLVAR -126 /* variable defn */ #define CTLENDVAR -125 #define CTLBACKQ -124 -#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ -/* CTLBACKQ | CTLQUOTE == -123 */ #define CTLARI -122 /* arithmetic expression */ #define CTLENDARI -121 #define CTLQUOTEMARK -120 @@ -50,7 +48,6 @@ /* variable substitution byte (follows CTLVAR) */ #define VSTYPE 0x0f /* type of variable substitution */ #define VSNUL 0x10 /* colon--treat the empty string as unset */ -#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ /* values of VSTYPE field */ #define VSNORMAL 0x1 /* normal variable: $var or ${var} */ diff --git a/src/show.c b/src/show.c index 1b58de1..14dbef3 100644 --- a/src/show.c +++ b/src/show.c @@ -222,7 +222,6 @@ sharg(union node *arg, FILE *fp) putc('}', fp); break; case CTLBACKQ: - case CTLBACKQ|CTLQUOTE: putc('$', fp); putc('(', fp); shtree(bqlist->n, -1, NULL, fp); @@ -314,9 +313,7 @@ trstring(char *s) case '\\': c = '\\'; goto backslash; case CTLESC: c = 'e'; goto backslash; case CTLVAR: c = 'v'; goto backslash; - case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; case CTLBACKQ: c = 'q'; goto backslash; - case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; backslash: putc('\\', tracefile); putc(c, tracefile); break; -- cgit 1.4.1