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)
This commit is contained in:
parent
0661e0348b
commit
048f26671a
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=214512
@ -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 @@ evalvar(char *p, int flag)
|
||||
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -285,6 +285,7 @@ init(void)
|
||||
syntax[base + CTLARI] = "CCTL";
|
||||
syntax[base + CTLENDARI] = "CCTL";
|
||||
syntax[base + CTLQUOTEMARK] = "CCTL";
|
||||
syntax[base + CTLQUOTEEND] = "CCTL";
|
||||
}
|
||||
|
||||
|
||||
|
@ -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++;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
34
tools/regression/bin/sh/expansion/plus-minus6.0
Normal file
34
tools/regression/bin/sh/expansion/plus-minus6.0
Normal file
@ -0,0 +1,34 @@
|
||||
# $FreeBSD$
|
||||
|
||||
failures=0
|
||||
unset LC_ALL
|
||||
export LC_CTYPE=en_US.ISO8859-1
|
||||
nl='
|
||||
'
|
||||
i=1
|
||||
set -f
|
||||
while [ "$i" -le 255 ]; do
|
||||
# A different byte still in the range 1..255.
|
||||
i2=$((i^2+(i==2)))
|
||||
# Add a character to work around command substitution's removal of
|
||||
# final newlines, then remove it again.
|
||||
c=$(printf \\"$(printf %o@ "$i")")
|
||||
c=${c%@}
|
||||
c2=$(printf \\"$(printf %o@ "$i2")")
|
||||
c2=${c2%@}
|
||||
case $c in
|
||||
[\'$nl'$}();&|\"`']) c=M
|
||||
esac
|
||||
case $c2 in
|
||||
[\'$nl'$}();&|\"`']) c2=N
|
||||
esac
|
||||
IFS=$c
|
||||
command eval "set -- \${\$+$c2$c$c2$c$c2}"
|
||||
if [ "$#" -ne 3 ] || [ "$1" != "$c2" ] || [ "$2" != "$c2" ] ||
|
||||
[ "$3" != "$c2" ]; then
|
||||
echo "Bad results for separator $i (word $i2)" >&2
|
||||
: $((failures += 1))
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
exit $((failures > 0))
|
Loading…
Reference in New Issue
Block a user