From 289b14113b1675b7b8d844b1f41aff1e2f3a5146 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 5 Oct 2006 21:53:26 +0000 Subject: 22819: improved internal use of string quotation, plus completion bug fix with RCQUOTES --- Src/Zle/compcore.c | 163 +++++++++++++++++++++++++++++++++++++++------------ Src/Zle/compctl.c | 95 +++++++++++++++++++++--------- Src/Zle/complete.c | 24 +++++++- Src/Zle/computil.c | 7 +-- Src/Zle/zle_tricky.c | 98 +++++++++++++++++++++++-------- Src/builtin.c | 3 +- Src/subst.c | 36 ++++++------ Src/text.c | 2 +- Src/utils.c | 57 ++++++++++-------- Src/zsh.h | 35 +++++++++++ 10 files changed, 377 insertions(+), 143 deletions(-) (limited to 'Src') diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index 8aa44d319..e397e776b 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -303,11 +303,13 @@ do_completion(UNUSED(Hookdef dummy), Compldat dat) matchers = newlinklist(); zsfree(compqstack); - compqstack = ztrdup("\\"); - if (instring == 2) - compqstack[0] = '"'; - else if (instring) - compqstack[0] = '\''; + compqstack = zalloc(2); + /* + * It looks like we may need to do stuff with backslashes even + * if instring is QT_NONE. + */ + *compqstack = (instring == QT_NONE) ? QT_BACKSLASH : (char)instring; + compqstack[1] = '\0'; hasunqu = 0; useline = (wouldinstab ? -1 : (lst != COMP_LIST_COMPLETE)); @@ -650,13 +652,22 @@ callcompfunc(char *s, char *fn) compredirect = ztrdup(compredirect); zsfree(compquote); zsfree(compquoting); - if (instring) { - if (instring == 1) { + if (instring > QT_BACKSLASH) { + switch (instring) { + case QT_SINGLE: compquote = ztrdup("\'"); compquoting = ztrdup("single"); - } else { + break; + + case QT_DOUBLE: compquote = ztrdup("\""); compquoting = ztrdup("double"); + break; + + case QT_DOLLARS: + compquote = ztrdup("$'"); + compquoting = ztrdup("dollars"); + break; } kset |= CP_QUOTE | CP_QUOTING; } else if (inbackt) { @@ -1026,8 +1037,7 @@ multiquote(char *s, int ign) p += ign; while (*p) { if (ign >= 0 || p[1]) - s = bslashquote(s, NULL, - (*p == '\'' ? 1 : (*p == '"' ? 2 : 0))); + s = quotestring(s, NULL, *p); p++; } } @@ -1290,9 +1300,29 @@ comp_str(int *ipl, int *pl, int untok) return str; } +/**/ +mod_export char * +comp_quoting_string(int stype) +{ + switch (stype) + { + case QT_SINGLE: + return "'"; + case QT_DOUBLE: + return "\""; + case QT_DOLLARS: + return "$'"; + default: /* shuts up compiler */ + return "\\"; + } +} + /* * This is the code behind compset -q, which splits the * the current word as if it were a command line. + * + * This is one of those completion functions that merits the + * coveted title "not just ordinarily horrific". */ /**/ @@ -1307,7 +1337,7 @@ set_comp_sep(void) int tl, got = 0, i = 0, j, cur = -1, oll, sl, css = 0; int remq = 0, dq = 0, odq, sq = 0, osq, issq = 0, sqq = 0, lsq = 0, qa = 0; int ois = instring, oib = inbackt, noffs = lp, ona = noaliases; - char *tmp, *p, *ns, *ol, sav, *qp, *qs, *ts, qc = '\0'; + char *tmp, *p, *ns, *ol, sav, *qp, *qs, *ts; METACHECK(); @@ -1334,21 +1364,27 @@ set_comp_sep(void) strcpy(tmp + 2 + noffs, s + noffs); switch (*compqstack) { - case '\\': + case QT_NONE: +#ifdef DEBUG + dputs("BUG: head of compstack is NULL"); +#endif + break; + + case QT_BACKSLASH: remq = 1; tmp = rembslash(tmp); break; - case '\'': + + case QT_SINGLE: issq = 1; if (isset(RCQUOTES)) qa = 1; else qa = 3; - sq = remsquote(tmp); - break; - case '"': + + case QT_DOUBLE: for (j = 0, p = tmp; *p; p++, j++) if (*p == '\\' && p[1] == '\\') { dq++; @@ -1360,6 +1396,11 @@ set_comp_sep(void) if (!*p) break; } + break; + + case QT_DOLLARS: + /* TODO */ + break; } odq = dq; osq = sq; @@ -1450,19 +1491,36 @@ set_comp_sep(void) untokenize(ts = dupstring(ns)); - if (*ns == Snull || *ns == Dnull) { - instring = (*ns == Snull ? 1 : 2); + if (*ns == Snull || *ns == Dnull || + ((*ns == String || *ns == Qstring) && ns[1] == Snull)) { + char *tsptr = ts, *nsptr = ns, sav; + switch (*ns) { + case Snull: + instring = QT_SINGLE; + break; + + case Dnull: + instring = QT_DOUBLE; + break; + + default: + instring = QT_DOLLARS; + nsptr++; + tsptr++; + break; + } + inbackt = 0; swb++; - if (ns[strlen(ns) - 1] == *ns && ns[1]) + if (nsptr[strlen(nsptr) - 1] == *nsptr && nsptr[1]) swe--; zsfree(autoq); - autoq = ztrdup(compqstack[1] ? "" : - multiquote(*ns == Snull ? "'" : "\"", 1)); - qc = (*ns == Snull ? '\'' : '"'); - ts++; + sav = *++tsptr; + *tsptr = '\0'; + autoq = ztrdup(compqstack[1] ? "" : multiquote(ts, 1)); + *(ts = tsptr) = sav; } else { - instring = 0; + instring = QT_NONE; zsfree(autoq); autoq = NULL; } @@ -1496,7 +1554,7 @@ set_comp_sep(void) } ns = ts; - if (instring && strchr(compqstack, '\\')) { + if (instring && strchr(compqstack, QT_BACKSLASH)) { int rl = strlen(ns), ql = strlen(multiquote(ns, !!compqstack[1])); if (ql > rl) @@ -1525,24 +1583,38 @@ set_comp_sep(void) } { int set = CP_QUOTE | CP_QUOTING, unset = 0; + char compnewchars[2]; - p = tricat((instring ? (instring == 1 ? "'" : "\"") : "\\"), - compqstack, ""); + compnewchars[0] = + (char)(instring == QT_NONE ? QT_BACKSLASH : instring); + compnewchars[1] = '\0'; + p = tricat(compnewchars, compqstack, ""); zsfree(compqstack); compqstack = p; zsfree(compquote); zsfree(compquoting); - if (instring == 2) { + switch (instring) { + case QT_DOUBLE: compquote = "\""; compquoting = "double"; - } else if (instring == 1) { + break; + + case QT_SINGLE: compquote = "'"; compquoting = "single"; - } else { + break; + + case QT_DOLLARS: + compquote = "$'"; + compquoting = "dollars"; + break; + + default: compquote = compquoting = ""; unset = set; set = 0; + break; } compquote = ztrdup(compquote); compquoting = ztrdup(compquoting); @@ -1804,20 +1876,33 @@ addmatches(Cadata dat, char **argv) dat->flags |= parflags; if (compquote && (qc = *compquote)) { if (qc == '`') { - instring = 0; + instring = QT_NONE; + /* + * Yes, inbackt has always been set to zero here. I'm + * sure there's a simple explanation. + */ inbackt = 0; autoq = ""; } else { - char buf[2]; + switch (qc) { + case '\'': + instring = QT_SINGLE; + break; - instring = (qc == '\'' ? 1 : 2); + case '"': + instring = QT_DOUBLE; + break; + + case '$': + instring = QT_DOLLARS; + break; + } inbackt = 0; - buf[0] = qc; - buf[1] = '\0'; - autoq = multiquote(buf, 1); + autoq = multiquote(compquote, 1); } } else { - instring = inbackt = 0; + instring = QT_NONE; + inbackt = 0; autoq = NULL; } qipre = ztrdup(compqiprefix ? compqiprefix : ""); @@ -2549,8 +2634,8 @@ add_match_data(int alt, char *str, char *orig, Cline line, cm->modec = '\0'; } } - if ((*compqstack == '\\' && compqstack[1]) || - (autoq && *compqstack && compqstack[1] == '\\')) + if ((*compqstack == QT_BACKSLASH && compqstack[1]) || + (autoq && *compqstack && compqstack[1] == QT_BACKSLASH)) cm->flags |= CMF_NOSPACE; if (nbrbeg) { int *p; diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index b8ed66260..4cd9d9c65 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -1401,7 +1401,7 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat) untokenize(p); quotedzputs(p, stdout); } else - quotedzputs(bslashquote(s, NULL, 0), stdout); + quotedzputs(quotestring(s, NULL, QT_BACKSLASH), stdout); } /* loop through flags w/o args that are set, printing them if so */ @@ -1537,7 +1537,7 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat) char *p = dupstring(s); untokenize(p); - quotedzputs(bslashquote(p, NULL, 0), stdout); + quotedzputs(quotestring(p, NULL, QT_BACKSLASH), stdout); } } putchar('\n'); @@ -1735,10 +1735,13 @@ static char ic; static int addwhat; -/* Convenience macro for calling bslashquote() (formerly quotename()). * - * This uses the instring variable above. */ +/* + * Convenience macro for calling quotestring (formerly bslashquote() + * (formerly quotename())). + * This uses the instring variable exported from zle_tricky.c. + */ -#define quotename(s, e) bslashquote(s, e, instring) +#define quotename(s, e) quotestring(s, e, instring) /* Hook functions */ @@ -2279,22 +2282,38 @@ makecomplistctl(int flags) char *os = cmdstr, **ow = clwords, **p, **q, qc; int on = clwnum, op = clwpos, ois = instring, oib = inbackt; char *oisuf = isuf, *oqp = qipre, *oqs = qisuf, *oaq = autoq; - char buf[2]; + char buf[3]; if (compquote && (qc = *compquote)) { if (qc == '`') { - instring = 0; + instring = QT_NONE; + /* + * Yes, inbackt has always been set to zero here. I'm + * sure there's a simple explanation. + */ inbackt = 0; autoq = ""; } else { - buf[0] = qc; - buf[1] = '\0'; - instring = (qc == '\'' ? 1 : 2); + switch (qc) { + case '\'': + instring = QT_SINGLE; + break; + + case '"': + instring = QT_DOUBLE; + break; + + case '$': + instring = QT_DOLLARS; + break; + } inbackt = 0; + strcpy(buf, compquote); autoq = buf; } } else { - instring = inbackt = 0; + instring = QT_NONE; + inbackt = 0; autoq = ""; } qipre = ztrdup(compqiprefix ? compqiprefix : ""); @@ -2589,7 +2608,7 @@ makecomplistext(Compctl occ, char *os, int incmd) int compadd, m = 0, d = 0, t, tt, i, j, a, b, ins; char *sc = NULL, *s, *ss; - ins = (instring ? instring : (inbackt ? 3 : 0)); + ins = (instring != QT_NONE ? instring : (inbackt ? QT_BACKTICK : 0)); /* This loops over the patterns separated by `-'s. */ for (compc = occ->ext; compc; compc = compc->next) { @@ -2607,9 +2626,9 @@ makecomplistext(Compctl occ, char *os, int incmd) erange = clwnum - 1; switch (cc->type) { case CCT_QUOTE: - t = ((cc->u.s.s[i][0] == 's' && ins == 1) || - (cc->u.s.s[i][0] == 'd' && ins == 2) || - (cc->u.s.s[i][0] == 'b' && ins == 3)); + t = ((cc->u.s.s[i][0] == 's' && ins == QT_SINGLE) || + (cc->u.s.s[i][0] == 'd' && ins == QT_DOUBLE) || + (cc->u.s.s[i][0] == 'b' && ins == QT_BACKTICK)); break; case CCT_POS: tt = clwpos; @@ -2755,7 +2774,7 @@ sep_comp_string(char *ss, char *s, int noffs) int sl = strlen(ss), tl, got = 0, i = 0, cur = -1, oll = zlemetall, remq; int ois = instring, oib = inbackt, ona = noaliases; char *tmp, *p, *ns, *ol = zlemetaline, sav, *oaq = autoq; - char *qp, *qs, *ts, qc = '\0'; + char *qp, *qs, *ts; swb = swe = soffs = 0; ns = NULL; @@ -2774,7 +2793,7 @@ sep_comp_string(char *ss, char *s, int noffs) memcpy(tmp + sl + 1, s, noffs); tmp[(scs = zlemetacs = sl + 1 + noffs)] = 'x'; strcpy(tmp + sl + 2 + noffs, s + noffs); - if ((remq = (*compqstack == '\\'))) + if ((remq = (*compqstack == QT_BACKSLASH))) tmp = rembslash(tmp); inpush(dupstrspace(tmp), 0, NULL); zlemetaline = tmp; @@ -2841,17 +2860,35 @@ sep_comp_string(char *ss, char *s, int noffs) untokenize(ts = dupstring(ns)); - if (*ns == Snull || *ns == Dnull) { - instring = (*ns == Snull ? 1 : 2); + if (*ns == Snull || *ns == Dnull || + ((*ns == String || *ns == Qstring) && ns[1] == Snull)) { + char *tsptr = ts, *nsptr = ns, sav; + switch (*ns) { + case Snull: + instring = QT_SINGLE; + break; + + case Dnull: + instring = QT_DOUBLE; + break; + + default: + instring = QT_DOLLARS; + nsptr++; + tsptr++; + break; + } + inbackt = 0; swb++; - if (ns[strlen(ns) - 1] == *ns && ns[1]) + if (nsptr[strlen(nsptr) - 1] == *nsptr && nsptr[1]) swe--; - autoq = compqstack[1] ? "" : multiquote(*ns == Snull ? "'" : "\"", 1); - qc = (*ns == Snull ? '\'' : '"'); - ts++; + sav = *++tsptr; + *tsptr = '\0'; + autoq = compqstack[1] ? "" : multiquote(ts, 1); + *(ts = tsptr) = sav; } else { - instring = 0; + instring = QT_NONE; autoq = ""; } for (p = ns, i = swb; *p; p++, i++) { @@ -2878,7 +2915,7 @@ sep_comp_string(char *ss, char *s, int noffs) } ns = ts; - if (instring && strchr(compqstack, '\\')) { + if (instring != QT_NONE && strchr(compqstack, QT_BACKSLASH)) { int rl = strlen(ns), ql = strlen(multiquote(ns, !!compqstack[1])); if (ql > rl) @@ -2904,13 +2941,15 @@ sep_comp_string(char *ss, char *s, int noffs) { char **ow = clwords, *os = cmdstr, *oqp = qipre, *oqs = qisuf; - char *oqst = compqstack; + char *oqst = compqstack, compnewchar[2]; int olws = clwsize, olwn = clwnum, olwp = clwpos; int obr = brange, oer = erange, oof = offs; unsigned long occ = ccont; - compqstack = tricat((instring ? (instring == 1 ? "'" : "\"") : "\\"), - compqstack, ""); + compnewchar[0] = (char)(instring != QT_NONE ? (char)instring : + QT_BACKSLASH); + compnewchar[1] = '\0'; + compqstack = tricat(compnewchar, compqstack, ""); clwsize = clwnum = countlinknodes(foo); clwords = (char **) zalloc((clwnum + 1) * sizeof(char *)); diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c index 46e9a06d8..a73d3cf14 100644 --- a/Src/Zle/complete.c +++ b/Src/Zle/complete.c @@ -992,6 +992,8 @@ static const struct gsu_scalar unambig_pos_gsu = { get_unambig_pos, nullstrsetfn, compunsetfn }; static const struct gsu_scalar insert_pos_gsu = { get_insert_pos, nullstrsetfn, compunsetfn }; +static const struct gsu_scalar compqstack_gsu = +{ get_compqstack, nullstrsetfn, compunsetfn }; static const struct gsu_integer compvarinteger_gsu = { intvargetfn, intvarsetfn, compunsetfn }; @@ -1047,7 +1049,7 @@ static struct compparam compkparams[] = { { "old_insert", PM_SCALAR, VAL(compoldins), NULL }, { "vared", PM_SCALAR, VAL(compvared), NULL }, { "list_lines", PM_INTEGER | PM_READONLY, NULL, GSU(listlines_gsu) }, - { "all_quotes", PM_SCALAR | PM_READONLY, VAL(compqstack), NULL }, + { "all_quotes", PM_SCALAR | PM_READONLY, NULL, GSU(compqstack_gsu) }, { "ignored", PM_INTEGER | PM_READONLY, VAL(compignored), NULL }, { NULL, 0, NULL, NULL } }; @@ -1222,6 +1224,26 @@ get_insert_pos(UNUSED(Param pm)) return p; } +/**/ +static char * +get_compqstack(UNUSED(Param pm)) +{ + char *p, *ptr, *cqp; + + if (!compqstack) /* TODO: don't think this can happen... */ + return ""; + + ptr = p = zhalloc(2*strlen(compqstack)+1); + + for (cqp = compqstack; *cqp; cqp++) { + char *str = comp_quoting_string(*cqp); + *ptr++ = *str; + } + *ptr = '\0'; + + return p; +} + /**/ static void compunsetfn(Param pm, int exp) diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index bde1f79af..f13397e5e 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -3441,8 +3441,7 @@ comp_quote(char *str, int prefix) if ((x = (prefix && *str == '='))) *str = 'x'; - ret = bslashquote(str, NULL, (*compqstack == '\'' ? 1 : - (*compqstack == '"' ? 2 : 0))); + ret = quotestring(str, NULL, *compqstack); if (x) *str = *ret = '='; @@ -4344,7 +4343,7 @@ cf_ignore(char **names, LinkList ign, char *style, char *path) for (; (n = *names); names++) { if (!ztat(n, &nst, 0) && S_ISDIR(nst.st_mode)) { if (tpwd && nst.st_dev == est.st_dev && nst.st_ino == est.st_ino) { - addlinknode(ign, bslashquote(n, NULL, 0)); + addlinknode(ign, quotestring(n, NULL, QT_BACKSLASH)); continue; } if (tpar && !strncmp((c = dupstring(n)), path, pl)) { @@ -4360,7 +4359,7 @@ cf_ignore(char **names, LinkList ign, char *style, char *path) if (found || ((e = strrchr(c, '/')) && e > c + pl && !ztat(c, &st, 0) && st.st_dev == nst.st_dev && st.st_ino == nst.st_ino)) - addlinknode(ign, bslashquote(n, NULL, 0)); + addlinknode(ign, quotestring(n, NULL, QT_BACKSLASH)); } } } diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 89277a9e1..09a0be5d8 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -402,15 +402,22 @@ mod_export int insubscr; /**/ mod_export Param keypm; -/* 1 if we are completing in a quoted string (or inside `...`) */ +/* + * instring takes one of the QT_* values defined in zsh.h. + * It's never QT_TICK, instead we use inbackt. + * TODO: can we combine the two? + */ /**/ mod_export int instring, inbackt; -/* Convenience macro for calling bslashquote() (formerly quotename()). * - * This uses the instring variable above. */ +/* + * Convenience macro for calling quotestring (formerly bslashquote() (formerly + * quotename())). + * This uses the instring variable above. + */ -#define quotename(s, e) bslashquote(s, e, instring) +#define quotename(s, e) quotestring(s, e, instring) /* Check if the given string is the name of a parameter and if this * * parameter is one worth expanding. */ @@ -891,7 +898,7 @@ addx(char **ptmp) zlemetaline[zlemetacs] == ';' || zlemetaline[zlemetacs] == '|' || zlemetaline[zlemetacs] == '&' || zlemetaline[zlemetacs] == '>' || zlemetaline[zlemetacs] == '<' || - (instring && (zlemetaline[zlemetacs] == '"' || + (instring != QT_NONE && (zlemetaline[zlemetacs] == '"' || zlemetaline[zlemetacs] == '\'')) || (addspace = (comppref && !iblank(zlemetaline[zlemetacs])))) { *ptmp = zlemetaline; @@ -1032,7 +1039,18 @@ static char * get_comp_string(void) { int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins, ia, parct, varq = 0; - int ona = noaliases, qsub; + int ona = noaliases; + /* + * qsub fixes up the offset into the current completion word + * for changes made by the lexer. That currently means the + * effect of RCQUOTES on embedded pairs of single quotes. + * zlemetacs_qsub takes account of the effect of this offset + * on the cursor position; it's only needed when using the + * word we got from the lexer, which we only do sometimes because + * otherwise it would be too easy. If looking at zlemetaline we + * still use zlemetacs. + */ + int qsub, zlemetacs_qsub = 0; char *s = NULL, *tmp, *p, *tt = NULL, rdop[20]; char *linptr, *u; @@ -1070,7 +1088,7 @@ get_comp_string(void) u++; } inbackt = (i & 1); - instring = 0; + instring = QT_NONE; addx(&tmp); linptr = zlemetaline; pushheap(); @@ -1235,9 +1253,11 @@ get_comp_string(void) clwords[i][--sl] = '\0'; /* If this is the word the cursor is in and we added a `x', * * remove it. */ - if (clwpos == i++ && addedx) - chuck(&clwords[i - 1][((zlemetacs - wb - qsub) >= sl) ? - (sl - 1) : (zlemetacs - wb - qsub)]); + if (clwpos == i++ && addedx) { + zlemetacs_qsub = zlemetacs - qsub; + chuck(&clwords[i - 1][((zlemetacs_qsub - wb) >= sl) ? + (sl - 1) : (zlemetacs_qsub - wb)]); + } } while (tok != LEXERR && tok != ENDINPUT && (tok != SEPER || (zleparse && !tt0))); /* Calculate the number of words stored in the clwords array. */ @@ -1299,7 +1319,8 @@ get_comp_string(void) *s = sav; if (*s == '+') s++; - if (skipparens(Inbrack, Outbrack, &s) > 0 || s > tt + zlemetacs - wb) { + if (skipparens(Inbrack, Outbrack, &s) > 0 || s > tt + + zlemetacs_qsub - wb) { s = NULL; inwhat = IN_MATH; if ((keypm = (Param) paramtab->getnode(paramtab, varname)) && @@ -1308,7 +1329,7 @@ get_comp_string(void) else insubscr = 1; } else if (*s == '=') { - if (zlemetacs > wb + (s - tt)) { + if (zlemetacs_qsub > wb + (s - tt)) { s++; wb += s - tt; s = ztrdup(s); @@ -1365,7 +1386,7 @@ get_comp_string(void) nnb = s + MB_METACHARLEN(s); else nnb = s; - for (tt = s; tt < s + zlemetacs - wb;) { + for (tt = s; tt < s + zlemetacs_qsub - wb;) { if (*tt == Inbrack) { i++; nb = nnb; @@ -1504,21 +1525,46 @@ get_comp_string(void) level--; } } - if ((*s == Snull || *s == Dnull) && !has_real_token(s + 1)) { - char *q = (*s == Snull ? "'" : "\""), *n = tricat(qipre, q, ""); + if ((*s == Snull || *s == Dnull || + ((*s == String || *s == Qstring) && s[1] == Snull)) + && !has_real_token(s + 1)) { int sl = strlen(s); + char *q, *qtptr = s, *n; + + switch (*s) { + case Snull: + q = "'"; + instring = QT_SINGLE; + break; + + case Dnull: + q = "\""; + instring = QT_DOUBLE; + break; + + default: + q = "$'"; + instring = QT_DOLLARS; + qtptr++; + sl--; + break; + } - instring = (*s == Snull ? 1 : 2); + n = tricat(qipre, q, ""); zsfree(qipre); qipre = n; - if (sl > 1 && s[sl - 1] == *s) { + if (sl > 1 && qtptr[sl - 1] == *qtptr) { n = tricat(q, qisuf, ""); zsfree(qisuf); qisuf = n; } autoq = ztrdup(q); - if (instring == 2) { + /* + * \! in double quotes is extracted by the history code before normal + * parsing, so sanitize it here, too. + */ + if (instring == QT_DOUBLE) { for (q = s; *q; q++) if (*q == '\\' && q[1] == '!') *q = Bnull; @@ -1651,11 +1697,11 @@ get_comp_string(void) new->next = NULL; new->str = dupstrpfx(bbeg, len); - new->str = ztrdup(bslashquote(new->str, NULL, instring)); + new->str = ztrdup(quotename(new->str, NULL)); untokenize(new->str); new->pos = begi; *dbeg = '\0'; - new->qpos = strlen(bslashquote(predup, NULL, instring)); + new->qpos = strlen(quotename(predup, NULL)); *dbeg = '{'; i -= len; boffs -= len; @@ -1700,11 +1746,11 @@ get_comp_string(void) lastbrbeg = new; new->str = dupstrpfx(bbeg, len); - new->str = ztrdup(bslashquote(new->str, NULL, instring)); + new->str = ztrdup(quotename(new->str, NULL)); untokenize(new->str); new->pos = begi; *dbeg = '\0'; - new->qpos = strlen(bslashquote(predup, NULL, instring)); + new->qpos = strlen(quotename(predup, NULL)); *dbeg = '{'; i -= len; boffs -= len; @@ -1737,7 +1783,7 @@ get_comp_string(void) brend = new; new->str = dupstrpfx(bbeg, len); - new->str = ztrdup(bslashquote(new->str, NULL, instring)); + new->str = ztrdup(quotename(new->str, NULL)); untokenize(new->str); new->pos = dp - predup - len + 1; new->qpos = len; @@ -1766,11 +1812,11 @@ get_comp_string(void) lastbrbeg = new; new->str = dupstrpfx(bbeg, len); - new->str = ztrdup(bslashquote(new->str, NULL, instring)); + new->str = ztrdup(quotename(new->str, NULL)); untokenize(new->str); new->pos = begi; *dbeg = '\0'; - new->qpos = strlen(bslashquote(predup, NULL, instring)); + new->qpos = strlen(quotename(predup, NULL)); *dbeg = '{'; boffs -= len; strcpy(dbeg, dbeg + len); @@ -1785,7 +1831,7 @@ get_comp_string(void) p = bp->pos; l = bp->qpos; bp->pos = strlen(predup + p + l); - bp->qpos = strlen(bslashquote(predup + p + l, NULL, instring)); + bp->qpos = strlen(quotename(predup + p + l, NULL)); strcpy(predup + p, predup + p + l); } } diff --git a/Src/builtin.c b/Src/builtin.c index 713e41a98..185365644 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -3964,7 +3964,8 @@ bin_print(char *name, char **args, Options ops, int func) count += fprintf(fout, "%*c", width, ' '); break; case 'q': - stringval = curarg ? bslashquote(curarg, NULL, 0) : &nullstr; + stringval = curarg ? + quotestring(curarg, NULL, QT_BACKSLASH) : &nullstr; *d = 's'; print_val(stringval); break; diff --git a/Src/subst.c b/Src/subst.c index 0f351df4c..8ef8d446e 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1394,7 +1394,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) * quoterr is simply (X) but gets passed around a lot because the * combination (eX) needs it. */ - int quotemod = 0, quotetype = 0, quoteerr = 0; + int quotemod = 0, quotetype = QT_NONE, quoteerr = 0; /* * (V) flag: fairly straightforward, except that as with so * many flags it's not easy to decide where to put it in the order. @@ -2835,8 +2835,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) * the repetitions of the (q) flag. */ if (quotemod) { - if (--quotetype > 3) - quotetype = 3; + if (quotetype > QT_DOLLARS) + quotetype = QT_DOLLARS; if (isarr) { char **ap; @@ -2845,24 +2845,25 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) ap = aval; if (quotemod > 0) { - if (quotetype) { + if (quotetype > QT_BACKSLASH) { int sl; char *tmp; for (; *ap; ap++) { - int pre = quotetype != 3 ? 1 : 2; - tmp = bslashquote(*ap, NULL, quotetype); + int pre = quotetype != QT_DOLLARS ? 1 : 2; + tmp = quotestring(*ap, NULL, quotetype); sl = strlen(tmp); *ap = (char *) zhalloc(pre + sl + 2); strcpy((*ap) + pre, tmp); - ap[0][pre - 1] = ap[0][pre + sl] = (quotetype != 2 ? '\'' : '"'); + ap[0][pre - 1] = ap[0][pre + sl] = + (quotetype != QT_DOUBLE ? '\'' : '"'); ap[0][pre + sl + 1] = '\0'; - if (quotetype == 3) + if (quotetype == QT_DOLLARS) ap[0][0] = '$'; } } else for (; *ap; ap++) - *ap = bslashquote(*ap, NULL, 0); + *ap = quotestring(*ap, NULL, QT_BACKSLASH); } else { int one = noerrs, oef = errflag, haserr = 0; @@ -2885,20 +2886,21 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) if (!copied) val = dupstring(val), copied = 1; if (quotemod > 0) { - if (quotetype) { - int pre = quotetype != 3 ? 1 : 2; + if (quotetype > QT_BACKSLASH) { + int pre = quotetype != QT_DOLLARS ? 1 : 2; int sl; char *tmp; - tmp = bslashquote(val, NULL, quotetype); + tmp = quotestring(val, NULL, quotetype); sl = strlen(tmp); val = (char *) zhalloc(pre + sl + 2); strcpy(val + pre, tmp); - val[pre - 1] = val[pre + sl] = (quotetype != 2 ? '\'' : '"'); + val[pre - 1] = val[pre + sl] = + (quotetype != QT_DOUBLE ? '\'' : '"'); val[pre + sl + 1] = '\0'; - if (quotetype == 3) + if (quotetype == QT_DOLLARS) val[0] = '$'; } else - val = bslashquote(val, NULL, 0); + val = quotestring(val, NULL, QT_BACKSLASH); } else { int one = noerrs, oef = errflag, haserr; @@ -3387,7 +3389,7 @@ modify(char **str, char **ptr) subst(©, hsubl, hsubr, gbal); break; case 'q': - copy = bslashquote(copy, NULL, 0); + copy = quotestring(copy, NULL, QT_BACKSLASH); break; case 'Q': { @@ -3453,7 +3455,7 @@ modify(char **str, char **ptr) } break; case 'q': - *str = bslashquote(*str, NULL, 0); + *str = quotestring(*str, NULL, QT_BACKSLASH); break; case 'Q': { diff --git a/Src/text.c b/Src/text.c index db24d8c9e..0079e9fea 100644 --- a/Src/text.c +++ b/Src/text.c @@ -807,7 +807,7 @@ getredirs(LinkList redirs) * quotes certainly isn't right in that case). */ taddchr('\''); - taddstr(bslashquote(f->name, NULL, 1)); + taddstr(quotestring(f->name, NULL, QT_SINGLE)); taddchr('\''); } else taddstr(f->name); diff --git a/Src/utils.c b/Src/utils.c index 99b0eb743..0aa0dfe2f 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1357,7 +1357,7 @@ settyinfo(struct ttyinfo *ti) } /* the default tty state */ - + /**/ mod_export struct ttyinfo shttyinfo; @@ -1576,13 +1576,13 @@ extern char *_mktemp(char *); * NULL, the name is relative to $TMPPREFIX; If it is non-NULL, the * unique suffix includes a prefixed '.' for improved readability. If * "use_heap" is true, we allocate the returned name on the heap. */ - + /**/ mod_export char * gettempname(const char *prefix, int use_heap) { char *ret, *suffix = prefix ? ".XXXXXX" : "XXXXXX"; - + queue_signals(); if (!prefix && !(prefix = getsparam("TMPPREFIX"))) prefix = DEFAULT_TMPPREFIX; @@ -1642,7 +1642,7 @@ gettempfile(const char *prefix, int use_heap, char **tempname) *tempname = fn; return fd; } - + /* Check if a string contains a token */ /**/ @@ -1656,7 +1656,7 @@ has_token(const char *s) } /* Delete a character in a string */ - + /**/ mod_export void chuck(char *str) @@ -3082,7 +3082,7 @@ itype_end(const char *ptr, int itype, int once) */ switch (itype) { case IWORD: - if (!iswalnum(wc) && + if (!iswalnum(wc) && !wmemchr(wordchars_wide.chars, wc, wordchars_wide.len)) return (char *)ptr; @@ -3820,7 +3820,7 @@ nicezputs(char const *s, FILE *stream) if (itok(c)) { if (c <= Comma) c = ztokens[c - Pound]; - else + else continue; } if (c == Meta) @@ -3845,7 +3845,7 @@ niceztrlen(char const *s) if (itok(c)) { if (c <= Comma) c = ztokens[c - Pound]; - else + else continue; } if (c == Meta) @@ -4134,27 +4134,32 @@ hasspecial(char const *s) return 0; } -/* Quote the string s and return the result. If e is non-zero, the * - * pointer it points to may point to a position in s and in e the position * - * of the corresponding character in the quoted string is returned. * - * The last argument should be zero if this is to be used outside a string, * - * one if it is to be quoted for the inside of a single quoted string, * - * two if it is for the inside of a double quoted string, and * - * three if it is for the inside of a $'...' quoted string. * - * The string may be metafied and contain tokens. */ +/* + * Quote the string s and return the result. + * + * If e is non-zero, the + * pointer it points to may point to a position in s and in e the position + * of the corresponding character in the quoted string is returned. + * + * The last argument is a QT_ value defined in zsh.h other than QT_NONE. + * + * The string may be metafied and contain tokens. + */ /**/ mod_export char * -bslashquote(const char *s, char **e, int instring) +quotestring(const char *s, char **e, int instring) { const char *u, *tt; char *v; char *buf = hcalloc(4 * strlen(s) + 1); int sf = 0; + DPUTS(instring < QT_BACKSLASH || instring > QT_DOLLARS, + "BUG: bad quote type in quotestring"); tt = v = buf; u = s; - if (instring == 3) { + if (instring == QT_DOLLARS) { /* * As we test for printability here we need to be able * to look for multibyte characters. @@ -4170,7 +4175,7 @@ bslashquote(const char *s, char **e, int instring) } if ( #ifdef MULTIBYTE_SUPPORT - cc != WEOF && + cc != WEOF && #endif WC_ISPRINT(cc)) { switch (cc) { @@ -4276,13 +4281,13 @@ bslashquote(const char *s, char **e, int instring) (isset(MAGICEQUALSUBST) && (u[-1] == '=' || u[-1] == ':')) || (*u == '~' && isset(EXTENDEDGLOB))) && - (!instring || + (instring == QT_BACKSLASH || (isset(BANGHIST) && *u == (char)bangchar && - instring != 1) || - (instring == 2 && + instring != QT_SINGLE) || + (instring == QT_DOUBLE && (*u == '$' || *u == '`' || *u == '\"' || *u == '\\')) || - (instring == 1 && *u == '\''))) { - if (*u == '\n' || (instring == 1 && *u == '\'')) { + (instring == QT_SINGLE && *u == '\''))) { + if (*u == '\n' || (instring == QT_SINGLE && *u == '\'')) { if (unset(RCQUOTES)) { *v++ = '\''; if (*u == '\'') @@ -4306,7 +4311,7 @@ bslashquote(const char *s, char **e, int instring) if (e && *e == u) *e = v, sf = 1; - DPUTS(e && !sf, "BUG: Wild pointer *e in bslashquote()"); + DPUTS(e && !sf, "BUG: Wild pointer *e in quotestring()"); return buf; } @@ -4654,7 +4659,7 @@ getkeystring(char *s, int *len, int how, int *misc) *len = t - buf; return buf; } - t += count; + t += count; continue; # else # if defined(HAVE_NL_LANGINFO) && defined(CODESET) diff --git a/Src/zsh.h b/Src/zsh.h index 9a76bf9a5..c73ae3b9a 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -178,6 +178,41 @@ struct mathfunc { #define SPECCHARS "#$^*()=|{}[]`<>?~;&\n\t \\\'\"" +/* + * Types of quote. This is used in various places, so care needs + * to be taken when changing them. (Oooh, don't you look surprised.) + * - Passed to quotestring() to indicate style. This is the ultimate + * destiny of most of the other uses of members of the enum. + * - In paramsubst(), to count q's in parameter substitution. + * - In the completion code, where we maintain a stack of quotation types. + */ +enum { + /* + * No quote. Not a valid quote, but useful in the substitution + * and completion code to indicate we're not doing any quoting. + */ + QT_NONE, + /* Backslash: \ */ + QT_BACKSLASH, + /* Single quote: ' */ + QT_SINGLE, + /* Double quote: " */ + QT_DOUBLE, + /* Print-style quote: $' */ + QT_DOLLARS, + /* + * Backtick: ` + * Not understood by many parts of the code; here for a convenience + * in those cases where we need to represent a complete set. + */ + QT_BACKTICK, +}; + +/* + * Lexical tokens: unlike the character tokens above, these never + * appear in strings and don't necessarily represent a single character. + */ + enum { NULLTOK, /* 0 */ SEPER, -- cgit 1.4.1