diff options
author | jilles <jilles@FreeBSD.org> | 2010-10-29 13:42:18 +0000 |
---|---|---|
committer | jilles <jilles@FreeBSD.org> | 2010-10-29 13:42:18 +0000 |
commit | 28ad180ab428c06679f4d2e8422bfb77ae2a926c (patch) | |
tree | a904801ee278e1d897c3118d8e675fd52f1a7856 /bin | |
parent | b6cd17990d125d91ed9a1a32e708c186c2fd3945 (diff) | |
download | FreeBSD-src-28ad180ab428c06679f4d2e8422bfb77ae2a926c.zip FreeBSD-src-28ad180ab428c06679f4d2e8422bfb77ae2a926c.tar.gz |
sh: Do IFS splitting on word in ${v+word} and ${v-word}.
The code is inspired by NetBSD sh somewhat, but different because we
preserve the old Almquist/Bourne/Korn ability to have an unquoted part in a
quoted ${v+word}. For example, "${v-"*"}" expands to $v as a single field if
v is set, but generates filenames otherwise.
Note that this is the only place where we split text literally from the
script (the similar ${v=word} assigns to v and then expands $v). The parser
must now add additional markers to allow the expansion code to know whether
arbitrary characters in substitutions are quoted.
Example:
for i in ${$+a b c}; do echo $i; done
Exp-run done by: pav (with some other sh(1) changes)
Diffstat (limited to 'bin')
-rw-r--r-- | bin/sh/expand.c | 37 | ||||
-rw-r--r-- | bin/sh/expand.h | 2 | ||||
-rw-r--r-- | bin/sh/mksyntax.c | 1 | ||||
-rw-r--r-- | bin/sh/parser.c | 16 | ||||
-rw-r--r-- | bin/sh/parser.h | 1 |
5 files changed, 43 insertions, 14 deletions
diff --git a/bin/sh/expand.c b/bin/sh/expand.c index d33f0ac..13dc38a 100644 --- a/bin/sh/expand.c +++ b/bin/sh/expand.c @@ -216,7 +216,12 @@ argstr(char *p, int flag) char c; int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ int firsteq = 1; + int split_lit; + int lit_quoted; + split_lit = flag & EXP_SPLIT_LIT; + lit_quoted = flag & EXP_LIT_QUOTED; + flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED); if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) p = exptilde(p, flag); for (;;) { @@ -225,17 +230,25 @@ argstr(char *p, int flag) case CTLENDVAR: goto breakloop; case CTLQUOTEMARK: + lit_quoted = 1; /* "$@" syntax adherence hack */ if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') break; if ((flag & EXP_FULL) != 0) STPUTC(c, expdest); break; + case CTLQUOTEEND: + lit_quoted = 0; + break; case CTLESC: if (quotes) STPUTC(c, expdest); c = *p++; STPUTC(c, expdest); + if (split_lit && !lit_quoted) + recordregion(expdest - stackblock() - + (quotes ? 2 : 1), + expdest - stackblock(), 0); break; case CTLVAR: p = evalvar(p, flag); @@ -255,18 +268,21 @@ argstr(char *p, int flag) * assignments (after the first '=' and after ':'s). */ STPUTC(c, expdest); - if (flag & EXP_VARTILDE && *p == '~') { - if (c == '=') { - if (firsteq) - firsteq = 0; - else - break; - } + if (split_lit && !lit_quoted) + recordregion(expdest - stackblock() - 1, + expdest - stackblock(), 0); + if (flag & EXP_VARTILDE && *p == '~' && + (c != '=' || firsteq)) { + if (c == '=') + firsteq = 0; p = exptilde(p, flag); } break; default: STPUTC(c, expdest); + if (split_lit && !lit_quoted) + recordregion(expdest - stackblock() - 1, + expdest - stackblock(), 0); } } breakloop:; @@ -742,7 +758,8 @@ record: case VSPLUS: case VSMINUS: if (!set) { - argstr(p, flag); + argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) | + (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0)); break; } if (easy) @@ -1495,13 +1512,13 @@ rmescapes(char *str) char *p, *q; p = str; - while (*p != CTLESC && *p != CTLQUOTEMARK) { + while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) { if (*p++ == '\0') return; } q = p; while (*p) { - if (*p == CTLQUOTEMARK) { + if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) { p++; continue; } diff --git a/bin/sh/expand.h b/bin/sh/expand.h index a3e079a..a4bb198 100644 --- a/bin/sh/expand.h +++ b/bin/sh/expand.h @@ -52,6 +52,8 @@ 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_SPLIT_LIT 0x20 /* IFS split literal text ${v+-a b c} */ +#define EXP_LIT_QUOTED 0x40 /* for EXP_SPLIT_LIT, start off quoted */ union node; diff --git a/bin/sh/mksyntax.c b/bin/sh/mksyntax.c index e4c4d14..07e8eb7 100644 --- a/bin/sh/mksyntax.c +++ b/bin/sh/mksyntax.c @@ -285,6 +285,7 @@ init(void) syntax[base + CTLARI] = "CCTL"; syntax[base + CTLENDARI] = "CCTL"; syntax[base + CTLQUOTEMARK] = "CCTL"; + syntax[base + CTLQUOTEEND] = "CCTL"; } diff --git a/bin/sh/parser.c b/bin/sh/parser.c index 07e6e72..64dcb40 100644 --- a/bin/sh/parser.c +++ b/bin/sh/parser.c @@ -1161,7 +1161,7 @@ readtoken1(int firstc, char const *initialsyntax, char *eofmark, int striptabs) loop: { /* for each line, until end of word */ CHECKEND(); /* set c to PEOF if at end of here document */ for (;;) { /* until end of line or end of word */ - CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */ + CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ synentry = state[level].syntax[c]; @@ -1203,12 +1203,18 @@ readtoken1(int firstc, char const *initialsyntax, char *eofmark, int striptabs) newvarnest == 0)) && (c != '}' || state[level].category != TSTATE_VAR_OLD)) USTPUTC('\\', out); + if ((eofmark == NULL || + newvarnest > 0) && + state[level].syntax == BASESYNTAX) + USTPUTC(CTLQUOTEMARK, out); if (SQSYNTAX[c] == CCTL) USTPUTC(CTLESC, out); - else if (eofmark == NULL || - newvarnest > 0) - USTPUTC(CTLQUOTEMARK, out); USTPUTC(c, out); + if ((eofmark == NULL || + newvarnest > 0) && + state[level].syntax == BASESYNTAX && + state[level].category == TSTATE_VAR_OLD) + USTPUTC(CTLQUOTEEND, out); quotef++; } break; @@ -1224,6 +1230,8 @@ readtoken1(int firstc, char const *initialsyntax, char *eofmark, int striptabs) if (eofmark != NULL && newvarnest == 0) USTPUTC(c, out); else { + if (state[level].category == TSTATE_VAR_OLD) + USTPUTC(CTLQUOTEEND, out); state[level].syntax = BASESYNTAX; quotef++; } diff --git a/bin/sh/parser.h b/bin/sh/parser.h index 453711f..64ec97f 100644 --- a/bin/sh/parser.h +++ b/bin/sh/parser.h @@ -43,6 +43,7 @@ #define CTLARI '\206' #define CTLENDARI '\207' #define CTLQUOTEMARK '\210' +#define CTLQUOTEEND '\211' /* only for ${v+-...} */ /* variable substitution byte (follows CTLVAR) */ #define VSTYPE 0x0f /* type of variable substitution */ |