better parsing for x=(…) – bug noted by Frank Terbeck

This commit is contained in:
tg 2011-06-04 16:11:20 +00:00
parent e0a913181b
commit b3d38f9cdf
4 changed files with 62 additions and 75 deletions

33
check.t
View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.t,v 1.457 2011/05/29 16:38:58 tg Exp $ # $MirOS: src/bin/mksh/check.t,v 1.458 2011/06/04 16:11:16 tg Exp $
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $ # $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $ # $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $ # $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
@ -6132,7 +6132,7 @@ stdin:
expected-stdout: expected-stdout:
5|a|$v|c d|$v|b| 5|a|$v|c d|$v|b|
--- ---
name: arrays-2 name: arrays-2a
description: description:
Check if bash-style arrays work as expected Check if bash-style arrays work as expected
category: !smksh category: !smksh
@ -6143,6 +6143,33 @@ stdin:
expected-stdout: expected-stdout:
5|a|$v|c d|$v|b| 5|a|$v|c d|$v|b|
--- ---
name: arrays-2b
description:
Check if bash-style arrays work as expected, with newlines
category: !smksh
stdin:
test -n "$ZSH_VERSION" && setopt KSH_ARRAYS
v="e f"
foo=(a
bc
d \$v "$v" '$v' g
)
printf '%s|' "${#foo[*]}" "${foo[0]}" "${foo[1]}" "${foo[2]}" "${foo[3]}" "${foo[4]}" "${foo[5]}" "${foo[6]}"; echo
foo=(a\
bc
d \$v "$v" '$v' g
)
printf '%s|' "${#foo[*]}" "${foo[0]}" "${foo[1]}" "${foo[2]}" "${foo[3]}" "${foo[4]}" "${foo[5]}" "${foo[6]}"; echo
foo=(a\
bc\\
d \$v "$v" '$v'
g)
printf '%s|' "${#foo[*]}" "${foo[0]}" "${foo[1]}" "${foo[2]}" "${foo[3]}" "${foo[4]}" "${foo[5]}" "${foo[6]}"; echo
expected-stdout:
7|a|bc|d|$v|e f|$v|g|
7|a|bc|d|$v|e f|$v|g|
6|abc\|d|$v|e f|$v|g||
---
name: arrays-3 name: arrays-3
description: description:
Check if array bounds are uint32_t Check if array bounds are uint32_t
@ -7675,7 +7702,7 @@ expected-stdout:
a+=b a+=b
;| ;|
(*) (*)
eval set -A c+ -- d e set -A c+ -- d e
;; ;;
esac esac
} }

50
lex.c
View File

@ -22,7 +22,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.152 2011/05/29 06:19:27 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/lex.c,v 1.153 2011/06/04 16:11:18 tg Exp $");
/* /*
* states while lexing word * states while lexing word
@ -41,9 +41,8 @@ __RCSID("$MirOS: src/bin/mksh/lex.c,v 1.152 2011/05/29 06:19:27 tg Exp $");
#define SHEREDQUOTE 11 /* parsing " in <<,<<- delimiter */ #define SHEREDQUOTE 11 /* parsing " in <<,<<- delimiter */
#define SPATTERN 12 /* parsing *(...|...) pattern (*+?@!) */ #define SPATTERN 12 /* parsing *(...|...) pattern (*+?@!) */
#define STBRACE 13 /* parsing ${...[#%]...} */ #define STBRACE 13 /* parsing ${...[#%]...} */
#define SLETARRAY 14 /* inside =( ), just copy */ #define SADELIM 14 /* like SBASE, looking for delimiter */
#define SADELIM 15 /* like SBASE, looking for delimiter */ #define SHERESTRING 15 /* parsing <<< string */
#define SHERESTRING 16 /* parsing <<< string */
#define SINVALID 255 /* invalid state */ #define SINVALID 255 /* invalid state */
struct sretrace_info { struct sretrace_info {
@ -220,12 +219,6 @@ yylex(int cf)
*wp++ = OQUOTE; *wp++ = OQUOTE;
state = SLETPAREN; state = SLETPAREN;
statep->nparen = 0; statep->nparen = 0;
#ifndef MKSH_SMALL
} else if (cf & LETARRAY) {
state = SLETARRAY;
statep->nparen = 0;
PUSH_SRETRACE();
#endif
} else { } else {
/* normal lexing */ /* normal lexing */
state = (cf & HEREDELIM) ? SHEREDELIM : SBASE; state = (cf & HEREDELIM) ? SHEREDELIM : SBASE;
@ -245,7 +238,7 @@ yylex(int cf)
cf |= ALIAS; cf |= ALIAS;
} }
/* Initial state: one of SWORD SLETPAREN SLETARRAY SHEREDELIM SBASE */ /* Initial state: one of SWORD SLETPAREN SHEREDELIM SBASE */
statep->type = state; statep->type = state;
/* check for here string */ /* check for here string */
@ -779,30 +772,6 @@ yylex(int cf)
++statep->nparen; ++statep->nparen;
goto Sbase2; goto Sbase2;
#ifndef MKSH_SMALL
/* LETARRAY: =( ... ) */
case SLETARRAY:
if (c == '('/*)*/)
++statep->nparen;
else if (c == /*(*/')') {
if (statep->nparen-- == 0) {
POP_SRETRACE();
/* drop trailing paren */
c = strlen(dp = sp) - 1;
XcheckN(ws, wp, c * 2);
while (c--) {
*wp++ = CHAR;
*wp++ = *dp++;
}
afree(sp, ATEMP);
/* assert: c == 0 */
goto Done;
}
}
/* reuse existing state machine */
goto Sbase2;
#endif
/* <<< delimiter */ /* <<< delimiter */
case SHERESTRING: case SHERESTRING:
if (c == '\\') { if (c == '\\') {
@ -934,11 +903,6 @@ yylex(int cf)
/* XXX figure out what is missing */ /* XXX figure out what is missing */
yyerror("no closing quote\n"); yyerror("no closing quote\n");
#ifndef MKSH_SMALL
if (state == SLETARRAY && statep->nparen != -1)
yyerror("%s: %s\n", T_synerr, "missing )");
#endif
/* This done to avoid tests for SHEREDELIM wherever SBASE tested */ /* This done to avoid tests for SHEREDELIM wherever SBASE tested */
if (state == SHEREDELIM || state == SHERESTRING) if (state == SHEREDELIM || state == SHERESTRING)
state = SBASE; state = SBASE;
@ -1046,11 +1010,7 @@ yylex(int cf)
*wp++ = EOS; *wp++ = EOS;
yylval.cp = Xclose(ws, wp); yylval.cp = Xclose(ws, wp);
if (state == SWORD || state == SLETPAREN if (state == SWORD || state == SLETPAREN
/* XXX ONEWORD? */ /* XXX ONEWORD? */)
#ifndef MKSH_SMALL
|| state == SLETARRAY
#endif
)
return (LWORD); return (LWORD);
/* unget terminator */ /* unget terminator */

5
sh.h
View File

@ -151,7 +151,7 @@
#endif #endif
#ifdef EXTERN #ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.471 2011/05/29 16:31:42 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/sh.h,v 1.472 2011/06/04 16:11:19 tg Exp $");
#endif #endif
#define MKSH_VERSION "R40 2011/05/29" #define MKSH_VERSION "R40 2011/05/29"
@ -1413,9 +1413,8 @@ typedef union {
#define HEREDELIM BIT(9) /* parsing <<,<<- delimiter */ #define HEREDELIM BIT(9) /* parsing <<,<<- delimiter */
#define LQCHAR BIT(10) /* source string contains QCHAR */ #define LQCHAR BIT(10) /* source string contains QCHAR */
#define HEREDOC BIT(11) /* parsing a here document */ #define HEREDOC BIT(11) /* parsing a here document */
#define LETARRAY BIT(12) /* copy expression inside =( ) */
#define HERES 10 /* max << in line */ #define HERES 10 /* max number of << in line */
#undef CTRL #undef CTRL
#define CTRL(x) ((x) == '?' ? 0x7F : (x) & 0x1F) /* ASCII */ #define CTRL(x) ((x) == '?' ? 0x7F : (x) & 0x1F) /* ASCII */

49
syn.c
View File

@ -22,7 +22,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.65 2011/05/29 02:18:57 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/syn.c,v 1.66 2011/06/04 16:11:20 tg Exp $");
extern short subshell_nesting_level; extern short subshell_nesting_level;
@ -292,6 +292,11 @@ get_command(int cf)
break; break;
case '(': case '(':
#ifndef MKSH_SMALL
if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
XPsize(vars) == 1 && is_wdvarassign(yylval.cp))
goto is_wdarrassign;
#endif
/* /*
* Check for "> foo (echo hi)" which AT&T ksh * Check for "> foo (echo hi)" which AT&T ksh
* allows (not POSIX, but not disallowed) * allows (not POSIX, but not disallowed)
@ -301,11 +306,7 @@ get_command(int cf)
ACCEPT; ACCEPT;
goto Subshell; goto Subshell;
} }
#ifndef MKSH_SMALL
if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
XPsize(vars) == 1 && is_wdvarassign(yylval.cp))
goto is_wdarrassign;
#endif
/* must be a function */ /* must be a function */
if (iopn != 0 || XPsize(args) != 1 || if (iopn != 0 || XPsize(args) != 1 ||
XPsize(vars) != 0) XPsize(vars) != 0)
@ -318,37 +319,37 @@ get_command(int cf)
is_wdarrassign: is_wdarrassign:
{ {
static const char set_cmd0[] = { static const char set_cmd0[] = {
CHAR, 'e', CHAR, 'v', CHAR, 's', CHAR, 'e',
CHAR, 'a', CHAR, 'l', EOS CHAR, 't', EOS
}; };
static const char set_cmd1[] = { static const char set_cmd1[] = {
CHAR, 's', CHAR, 'e',
CHAR, 't', CHAR, ' ',
CHAR, '-', CHAR, 'A', EOS CHAR, '-', CHAR, 'A', EOS
}; };
static const char set_cmd2[] = { static const char set_cmd2[] = {
CHAR, '-', CHAR, '-', EOS CHAR, '-', CHAR, '-', EOS
}; };
char *tcp; char *tcp;
XPfree(vars);
XPinit(vars, 16);
/*
* we know (or rather hope) that yylval.cp
* contains a string "varname="
*/
tcp = wdcopy(yylval.cp, ATEMP);
tcp[wdscan(tcp, EOS) - tcp - 3] = EOS;
/* now make an array assignment command */
t = newtp(TCOM);
t->lineno = source->line;
ACCEPT; ACCEPT;
/* manipulate the vars string */
tcp = *(--vars.cur);
/* 'varname=' -> 'varname' */
tcp[wdscan(tcp, EOS) - tcp - 3] = EOS;
/* construct new args strings */
XPput(args, wdcopy(set_cmd0, ATEMP)); XPput(args, wdcopy(set_cmd0, ATEMP));
XPput(args, wdcopy(set_cmd1, ATEMP)); XPput(args, wdcopy(set_cmd1, ATEMP));
XPput(args, tcp); XPput(args, tcp);
XPput(args, wdcopy(set_cmd2, ATEMP)); XPput(args, wdcopy(set_cmd2, ATEMP));
musthave(LWORD,LETARRAY);
XPput(args, yylval.cp); /* slurp in words till closing paren */
break; while (token(CONTIN) == LWORD)
XPput(args, yylval.cp);
if (symbol != /*(*/ ')')
syntaxerr(NULL);
goto Leave;
} }
#endif #endif