sh: Allow backslash-newline continuation in more places:

* directly after a $
 * directly after ${
 * between the characters of a multi-character operator token
 * within a parameter name
This commit is contained in:
Jilles Tjoelker 2014-10-19 11:59:15 +00:00
parent 4040f4ecd6
commit 0b4b9c8158
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=273276
10 changed files with 156 additions and 32 deletions

View File

@ -125,6 +125,7 @@ static void consumetoken(int);
static void synexpect(int) __dead2;
static void synerror(const char *) __dead2;
static void setprompt(int);
static int pgetc_linecont(void);
static void *
@ -899,17 +900,17 @@ xxreadtoken(void)
case PEOF:
RETURN(TEOF);
case '&':
if (pgetc() == '&')
if (pgetc_linecont() == '&')
RETURN(TAND);
pungetc();
RETURN(TBACKGND);
case '|':
if (pgetc() == '|')
if (pgetc_linecont() == '|')
RETURN(TOR);
pungetc();
RETURN(TPIPE);
case ';':
c = pgetc();
c = pgetc_linecont();
if (c == ';')
RETURN(TENDCASE);
else if (c == '&')
@ -991,7 +992,7 @@ parseredir(char *out, int c)
np = (union node *)stalloc(sizeof (struct nfile));
if (c == '>') {
np->nfile.fd = 1;
c = pgetc();
c = pgetc_linecont();
if (c == '>')
np->type = NAPPEND;
else if (c == '&')
@ -1004,7 +1005,7 @@ parseredir(char *out, int c)
}
} else { /* c == '<' */
np->nfile.fd = 0;
c = pgetc();
c = pgetc_linecont();
if (c == '<') {
if (sizeof (struct nfile) != sizeof (struct nhere)) {
np = (union node *)stalloc(sizeof (struct nhere));
@ -1013,7 +1014,7 @@ parseredir(char *out, int c)
np->type = NHERE;
heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
heredoc->here = np;
if ((c = pgetc()) == '-') {
if ((c = pgetc_linecont()) == '-') {
heredoc->striptabs = 1;
} else {
heredoc->striptabs = 0;
@ -1094,25 +1095,12 @@ parsebackq(char *out, struct nodelist **pbqlist,
needprompt = 0;
}
CHECKSTRSPACE(2, oout);
c = pgetc();
c = pgetc_linecont();
if (c == '`')
break;
switch (c) {
case '\\':
if ((c = pgetc()) == '\n') {
plinno++;
if (doprompt)
setprompt(2);
else
setprompt(0);
/*
* If eating a newline, avoid putting
* the newline into the new character
* stream (via the USTPUTC after the
* switch).
*/
continue;
}
c = pgetc();
if (c != '\\' && c != '`' && c != '$'
&& (!dblquote || c != '"'))
USTPUTC('\\', oout);
@ -1507,7 +1495,7 @@ readtoken1(int firstc, char const *initialsyntax, const char *eofmark,
USTPUTC(c, out);
--state[level].parenlevel;
} else {
if (pgetc() == ')') {
if (pgetc_linecont() == ')') {
if (level > 0 &&
state[level].category == TSTATE_ARITH) {
level--;
@ -1593,9 +1581,9 @@ parsesub: {
int length;
int c1;
c = pgetc();
c = pgetc_linecont();
if (c == '(') { /* $(command) or $((arith)) */
if (pgetc() == '(') {
if (pgetc_linecont() == '(') {
PARSEARITH();
} else {
pungetc();
@ -1613,7 +1601,7 @@ parsesub: {
flags = 0;
if (c == '{') {
bracketed_name = 1;
c = pgetc();
c = pgetc_linecont();
subtype = 0;
}
varname:
@ -1621,7 +1609,7 @@ parsesub: {
length = 0;
do {
STPUTC(c, out);
c = pgetc();
c = pgetc_linecont();
length++;
} while (!is_eof(c) && is_in_name(c));
if (length == 6 &&
@ -1640,22 +1628,22 @@ parsesub: {
if (bracketed_name) {
do {
STPUTC(c, out);
c = pgetc();
c = pgetc_linecont();
} while (is_digit(c));
} else {
STPUTC(c, out);
c = pgetc();
c = pgetc_linecont();
}
} else if (is_special(c)) {
c1 = c;
c = pgetc();
c = pgetc_linecont();
if (subtype == 0 && c1 == '#') {
subtype = VSLENGTH;
if (strchr(types, c) == NULL && c != ':' &&
c != '#' && c != '%')
goto varname;
c1 = c;
c = pgetc();
c = pgetc_linecont();
if (c1 != '}' && c == '}') {
pungetc();
c = c1;
@ -1680,7 +1668,7 @@ parsesub: {
switch (c) {
case ':':
flags |= VSNUL;
c = pgetc();
c = pgetc_linecont();
/*FALLTHROUGH*/
default:
p = strchr(types, c);
@ -1700,7 +1688,7 @@ parsesub: {
int cc = c;
subtype = c == '#' ? VSTRIMLEFT :
VSTRIMRIGHT;
c = pgetc();
c = pgetc_linecont();
if (c == cc)
subtype++;
else
@ -1909,6 +1897,29 @@ setprompt(int which)
}
}
static int
pgetc_linecont(void)
{
int c;
while ((c = pgetc_macro()) == '\\') {
c = pgetc();
if (c == '\n') {
plinno++;
if (doprompt)
setprompt(2);
else
setprompt(0);
} else {
pungetc();
/* Allow the backslash to be pushed back. */
pushstring("\\", 1, NULL);
return (pgetc());
}
}
return (c);
}
/*
* called by editline -- any expansions to the prompt
* should be added here.

View File

@ -58,6 +58,14 @@ FILES+= heredoc12.0
FILES+= line-cont1.0
FILES+= line-cont2.0
FILES+= line-cont3.0
FILES+= line-cont4.0
FILES+= line-cont5.0
FILES+= line-cont6.0
FILES+= line-cont7.0
FILES+= line-cont8.0
FILES+= line-cont9.0
FILES+= line-cont10.0
FILES+= line-cont11.0
FILES+= no-space1.0
FILES+= no-space2.0
FILES+= only-redir1.0

View File

@ -0,0 +1,18 @@
# $FreeBSD$
v=XaaaXbbbX
[ "${v\
#\
*\
a}.${v\
#\
#\
*\
a}.${v\
%\
b\
*}.${v\
%\
%\
b\
*}" = aaXbbbX.XbbbX.XaaaXbb.XaaaX ]

View File

@ -0,0 +1,23 @@
# $FreeBSD$
T=$(mktemp "${TMPDIR:-/tmp}/sh-test.XXXXXXXX") || exit
trap 'rm -f -- "$T"' 0
w='#A'
# A naive pgetc_linecont() would push back two characters here, which
# fails if a new buffer is read between the two characters.
c='${w#\#}'
c=$c$c$c$c
c=$c$c$c$c
c=$c$c$c$c
c=$c$c$c$c
c=$c$c$c$c
c=$c$c$c$c
printf 'v=%s\n' "$c" >"$T"
. "$T"
if [ "${#v}" != 4096 ]; then
echo "Length is bad (${#v})"
exit 3
fi
case $v in
*[!A]*) echo "Content is bad"; exit 3 ;;
esac

View File

@ -0,0 +1,8 @@
# $FreeBSD$
v=abcd
[ "$\
v.$\
{v}.${\
v}.${v\
}" = abcd.abcd.abcd.abcd ]

View File

@ -0,0 +1,14 @@
# $FreeBSD$
bad=1
case x in
x\
) ;\
; *) exit 7
esac &\
& bad= &\
& : >\
>/dev/null
false |\
| [ -z "$bad" ]

View File

@ -0,0 +1,23 @@
# $FreeBSD$
v0\
=abc
v=$(cat <\
<\
E\
O\
F
${v0}d
EOF
)
w=$(cat <\
<\
-\
EOF
efgh
EOF
)
[ "$v.$w" = "abcd.efgh" ]

View File

@ -0,0 +1,7 @@
# $FreeBSD$
[ "$(\
(
1\
+ 1)\
)" = 2 ]

View File

@ -0,0 +1,6 @@
# $FreeBSD$
set -- a b c d e f g h i j
[ "${1\
0\
}" = j ]

View File

@ -0,0 +1,6 @@
# $FreeBSD$
[ "${$\
:\
+\
xyz}" = xyz ]