aboutsummaryrefslogtreecommitdiffstats
path: root/parse.y
diff options
context:
space:
mode:
authorJari Aalto <jari.aalto@cante.net>1998-04-17 19:52:44 +0000
committerJari Aalto <jari.aalto@cante.net>2009-09-12 16:46:51 +0000
commitcce855bc5b117cb7ae70064131120687bc69fac0 (patch)
tree39c7a4ec8f6d22ef03df74f2684e6a04fef10399 /parse.y
parente8ce775db824de329b81293b4e5d8fbd65624528 (diff)
downloadandroid_external_bash-cce855bc5b117cb7ae70064131120687bc69fac0.tar.gz
android_external_bash-cce855bc5b117cb7ae70064131120687bc69fac0.tar.bz2
android_external_bash-cce855bc5b117cb7ae70064131120687bc69fac0.zip
Imported from ../bash-2.02.tar.gz.
Diffstat (limited to 'parse.y')
-rw-r--r--parse.y400
1 files changed, 365 insertions, 35 deletions
diff --git a/parse.y b/parse.y
index a8d4780..eca168e 100644
--- a/parse.y
+++ b/parse.y
@@ -64,9 +64,11 @@
#endif /* ALIAS */
#if defined (PROMPT_STRING_DECODE)
-#include <sys/param.h>
-#include <time.h>
-#include "maxpath.h"
+# ifndef _MINIX
+# include <sys/param.h>
+# endif
+# include <time.h>
+# include "maxpath.h"
#endif /* PROMPT_STRING_DECODE */
#define RE_READ_TOKEN -99
@@ -74,6 +76,13 @@
#define YYDEBUG 0
+#if defined (EXTENDED_GLOB)
+#define PATTERN_CHAR(c) \
+ ((c) == '@' || (c) == '*' || (c) == '+' || (c) == '?' || (c) == '!')
+
+extern int extended_glob;
+#endif
+
extern int eof_encountered;
extern int no_line_editing, running_under_emacs;
extern int current_command_number;
@@ -85,7 +94,7 @@ extern int interrupt_immediately;
extern char *shell_name, *current_host_name;
extern char *dist_version;
extern int patch_level;
-extern int dump_translatable_strings;
+extern int dump_translatable_strings, dump_po_strings;
extern Function *last_shell_builtin, *this_shell_builtin;
#if defined (BUFFERED_INPUT)
extern int bash_input_fd_changed;
@@ -103,6 +112,9 @@ static int reserved_word_acceptable ();
static int read_token ();
static int yylex ();
static int parse_arith_cmd ();
+#if defined (COND_COMMAND)
+static COMMAND *parse_cond_command ();
+#endif
static int read_token_word ();
static void discard_parser_constructs ();
@@ -180,13 +192,17 @@ static REDIRECTEE redir;
/* Reserved words. Members of the first group are only recognized
in the case that they are preceded by a list_terminator. Members
- of the second group are recognized only under special circumstances. */
+ of the second group are for [[...]] commands. Members of the
+ third group are recognized only under special circumstances. */
%token IF THEN ELSE ELIF FI CASE ESAC FOR SELECT WHILE UNTIL DO DONE FUNCTION
-%token IN BANG TIME TIMEOPT
+%token COND_START COND_END COND_ERROR
+%token IN BANG TIME TIMEOPT
/* More general tokens. yylex () knows how to make these. */
%token <word> WORD ASSIGNMENT_WORD
%token <number> NUMBER
+%token <word_list> ARITH_CMD
+%token <command> COND_CMD
%token AND_AND OR_OR GREATER_GREATER LESS_LESS LESS_AND
%token GREATER_AND SEMI_SEMI LESS_LESS_MINUS AND_GREATER LESS_GREATER
%token GREATER_BAR
@@ -197,6 +213,8 @@ static REDIRECTEE redir;
%type <command> list list0 list1 compound_list simple_list simple_list1
%type <command> simple_command shell_command
%type <command> for_command select_command case_command group_command
+%type <command> arith_command
+%type <command> cond_command
%type <command> function_def if_command elif_clause subshell
%type <redirect> redirection redirection_list
%type <element> simple_command_element
@@ -479,6 +497,10 @@ shell_command: for_command
{ $$ = $1; }
| group_command
{ $$ = $1; }
+ | arith_command
+ { $$ = $1; }
+ | cond_command
+ { $$ = $1; }
| function_def
{ $$ = $1; }
;
@@ -559,6 +581,14 @@ group_command: '{' list '}'
{ $$ = make_group_command ($2); }
;
+arith_command: ARITH_CMD
+ { $$ = make_arith_command ($1); }
+ ;
+
+cond_command: COND_START COND_CMD COND_END
+ { $$ = $2; }
+ ;
+
elif_clause: ELIF compound_list THEN compound_list
{ $$ = make_if_command ($2, $4, (COMMAND *)NULL); }
| ELIF compound_list THEN compound_list ELSE compound_list
@@ -747,6 +777,8 @@ timespec: TIME
#define PST_SUBSHELL 0x020 /* ( ... ) subshell */
#define PST_CMDSUBST 0x040 /* $( ... ) command substitution */
#define PST_CASESTMT 0x080 /* parsing a case statement */
+#define PST_CONDCMD 0x100 /* parsing a [[...]] command */
+#define PST_CONDEXPR 0x200 /* parsing the guts of [[...]] */
/* Initial size to allocate for tokens, and the
amount to grow them by. */
@@ -1084,6 +1116,11 @@ typedef struct stream_saver {
/* The globally known line number. */
int line_number = 0;
+#if defined (COND_COMMAND)
+static int cond_lineno;
+static int cond_token;
+#endif
+
STREAM_SAVER *stream_list = (STREAM_SAVER *)NULL;
void
@@ -1098,10 +1135,8 @@ push_stream (reset_lineno)
saver->bstream = (BUFFERED_STREAM *)NULL;
/* If we have a buffered stream, clear out buffers[fd]. */
if (bash_input.type == st_bstream && bash_input.location.buffered_fd >= 0)
- {
- saver->bstream = buffers[bash_input.location.buffered_fd];
- buffers[bash_input.location.buffered_fd] = (BUFFERED_STREAM *)NULL;
- }
+ saver->bstream = set_buffered_stream (bash_input.location.buffered_fd,
+ (BUFFERED_STREAM *)NULL);
#endif /* BUFFERED_INPUT */
saver->line = line_number;
@@ -1147,7 +1182,7 @@ pop_stream ()
saver->bstream->b_fd = default_buffered_input;
}
}
- buffers[bash_input.location.buffered_fd] = saver->bstream;
+ set_buffered_stream (bash_input.location.buffered_fd, saver->bstream);
}
#endif /* BUFFERED_INPUT */
@@ -1180,7 +1215,11 @@ stream_on_stack (type)
* everything between a `;;' and the next `)' or `esac'
*/
-#if defined (ALIAS)
+#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
+
+#if !defined (ALIAS)
+typedef void *alias_t;
+#endif
#define END_OF_ALIAS 0
@@ -1197,7 +1236,9 @@ typedef struct string_saver {
struct string_saver *next;
int expand_alias; /* Value to set expand_alias to when string is popped. */
char *saved_line;
+#if defined (ALIAS)
alias_t *expander; /* alias that caused this line to be pushed. */
+#endif
int saved_line_size, saved_line_index, saved_line_terminator;
} STRING_SAVER;
@@ -1224,12 +1265,16 @@ push_string (s, expand, ap)
temp->saved_line_size = shell_input_line_size;
temp->saved_line_index = shell_input_line_index;
temp->saved_line_terminator = shell_input_line_terminator;
+#if defined (ALIAS)
temp->expander = ap;
+#endif
temp->next = pushed_string_list;
pushed_string_list = temp;
+#if defined (ALIAS)
if (ap)
ap->flags |= AL_BEINGEXPANDED;
+#endif
shell_input_line = s;
shell_input_line_size = strlen (s);
@@ -1263,8 +1308,10 @@ pop_string ()
t = pushed_string_list;
pushed_string_list = pushed_string_list->next;
+#if defined (ALIAS)
if (t->expander)
t->expander->flags &= ~AL_BEINGEXPANDED;
+#endif
free ((char *)t);
}
@@ -1278,14 +1325,17 @@ free_string_list ()
{
t1 = t->next;
FREE (t->saved_line);
- t->expander->flags &= ~AL_BEINGEXPANDED;
+#if defined (ALIAS)
+ if (t->expander)
+ t->expander->flags &= ~AL_BEINGEXPANDED;
+#endif
free ((char *)t);
t = t1;
}
pushed_string_list = (STRING_SAVER *)NULL;
}
-#endif /* ALIAS */
+#endif /* ALIAS || DPAREN_ARITHMETIC */
/* Return a line of text, taken from wherever yylex () reads input.
If there is no more input, then we return NULL. If REMOVE_QUOTED_NEWLINE
@@ -1410,6 +1460,10 @@ STRING_INT_ALIST word_token_alist[] = {
{ "{", '{' },
{ "}", '}' },
{ "!", BANG },
+#if defined (COND_COMMAND)
+ { "[[", COND_START },
+ { "]]", COND_END },
+#endif
{ (char *)NULL, 0}
};
@@ -1457,16 +1511,16 @@ shell_getc (remove_quoted_newline)
QUIT;
-#if defined (ALIAS)
+#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
/* If shell_input_line[shell_input_line_index] == 0, but there is
something on the pushed list of strings, then we don't want to go
off and get another line. We let the code down below handle it. */
if (!shell_input_line || ((!shell_input_line[shell_input_line_index]) &&
(pushed_string_list == (STRING_SAVER *)NULL)))
-#else /* !ALIAS */
+#else /* !ALIAS && !DPAREN_ARITHMETIC */
if (!shell_input_line || !shell_input_line[shell_input_line_index])
-#endif /* !ALIAS */
+#endif /* !ALIAS && !DPAREN_ARITHMETIC */
{
line_number++;
@@ -1617,7 +1671,7 @@ shell_getc (remove_quoted_newline)
goto restart_read;
}
-#if defined (ALIAS)
+#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
/* If C is NULL, we have reached the end of the current input string. If
pushed_string_list is non-empty, it's time to pop to the previous string
because we have fully consumed the result of the last alias expansion.
@@ -1639,7 +1693,7 @@ shell_getc (remove_quoted_newline)
c = ' ';
}
}
-#endif /* ALIAS */
+#endif /* ALIAS || DPAREN_ARITHMETIC */
if (!c && shell_input_line_terminator == EOF)
return ((shell_input_line_index != 0) ? '\n' : EOF);
@@ -1790,10 +1844,16 @@ static int open_brace_count;
{ \
if ((parser_state & PST_CASEPAT) && (word_token_alist[i].token != ESAC)) \
break; \
+ if (word_token_alist[i].token == TIME) \
+ break; \
if (word_token_alist[i].token == ESAC) \
parser_state &= ~(PST_CASEPAT|PST_CASESTMT); \
else if (word_token_alist[i].token == CASE) \
parser_state |= PST_CASESTMT; \
+ else if (word_token_alist[i].token == COND_END) \
+ parser_state &= ~(PST_CONDCMD|PST_CONDEXPR); \
+ else if (word_token_alist[i].token == COND_START) \
+ parser_state |= PST_CONDCMD; \
else if (word_token_alist[i].token == '{') \
open_brace_count++; \
else if (word_token_alist[i].token == '}' && open_brace_count) \
@@ -1847,6 +1907,27 @@ alias_expand_token (token)
}
#endif /* ALIAS */
+static int
+time_command_acceptable ()
+{
+#if defined (COMMAND_TIMING)
+ switch (last_read_token)
+ {
+ case 0:
+ case ';':
+ case '\n':
+ case AND_AND:
+ case OR_OR:
+ case '&':
+ return 1;
+ default:
+ return 0;
+ }
+#else
+ return 0;
+#endif /* COMMAND_TIMING */
+}
+
/* Handle special cases of token recognition:
IN is recognized if the last token was WORD and the token
before that was FOR or CASE or SELECT.
@@ -1861,6 +1942,14 @@ alias_expand_token (token)
before that was FUNCTION.
`}' is recognized if there is an unclosed `{' prsent.
+
+ `-p' is returned as TIMEOPT if the last read token was TIME.
+
+ ']]' is returned as COND_END if the parser is currently parsing
+ a conditional expression ((parser_state & PST_CONDEXPR) != 0)
+
+ `time' is returned as TIME if and only if it is immediately
+ preceded by one of `;', `\n', `||', `&&', or `&'.
*/
static int
@@ -1926,9 +2015,21 @@ special_case_tokens (token)
return ('}');
}
+#if defined (COMMAND_TIMING)
/* Handle -p after `time'. */
if (last_read_token == TIME && token[0] == '-' && token[1] == 'p' && !token[2])
return (TIMEOPT);
+#endif
+
+#if defined (COMMAND_TIMING)
+ if (STREQ (token, "time") && time_command_acceptable ())
+ return (TIME);
+#endif /* COMMAND_TIMING */
+
+#if defined (COND_COMMAND) /* [[ */
+ if ((parser_state & PST_CONDEXPR) && token[0] == ']' && token[1] == ']' && token[2] == '\0')
+ return (COND_END);
+#endif
return (-1);
}
@@ -1943,13 +2044,10 @@ reset_parser ()
parser_state = 0;
-#if defined (ALIAS)
+#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
if (pushed_string_list)
- {
- free_string_list ();
- pushed_string_list = (STRING_SAVER *)NULL;
- }
-#endif /* ALIAS */
+ free_string_list ();
+#endif /* ALIAS || DPAREN_ARITHMETIC */
if (shell_input_line)
{
@@ -1993,6 +2091,26 @@ read_token (command)
return (result);
}
+#if defined (COND_COMMAND)
+ if ((parser_state & (PST_CONDCMD|PST_CONDEXPR)) == PST_CONDCMD)
+ {
+ cond_lineno = line_number;
+ parser_state |= PST_CONDEXPR;
+ yylval.command = parse_cond_command ();
+ if (cond_token != COND_END)
+ {
+ if (EOF_Reached && cond_token != COND_ERROR) /* [[ */
+ parser_error (cond_lineno, "unexpected EOF while looking for `]]'");
+ else if (cond_token != COND_ERROR)
+ parser_error (cond_lineno, "syntax error in conditional expression");
+ return (-1);
+ }
+ token_to_read = COND_END;
+ parser_state &= ~(PST_CONDEXPR|PST_CONDCMD);
+ return (COND_CMD);
+ }
+#endif
+
#if defined (ALIAS)
/* This is a place to jump back to once we have successfully expanded a
token with an alias and pushed the string with push_string () */
@@ -2080,17 +2198,17 @@ read_token (command)
{
int cmdtyp, sline;
char *wval;
+ WORD_DESC *wd;
sline = line_number;
cmdtyp = parse_arith_cmd (&wval);
if (cmdtyp == 1) /* arithmetic command */
{
- word_desc_to_read = make_word (wval);
- word_desc_to_read->flags = W_QUOTED;
- token_to_read = WORD;
- free (wval);
- yylval.word = make_word ("let");
- return (WORD);
+ wd = make_word (wval);
+ wd->flags = W_QUOTED;
+ yylval.word_list = make_word_list (wd, (WORD_LIST *)NULL);
+ free (wval); /* make_word copies it */
+ return (ARITH_CMD);
}
else if (cmdtyp == 0) /* nested subshell */
{
@@ -2362,6 +2480,175 @@ parse_arith_cmd (ep)
}
#endif /* DPAREN_ARITHMETIC */
+#if defined (COND_COMMAND)
+static COND_COM *cond_term ();
+static COND_COM *cond_and ();
+static COND_COM *cond_or ();
+static COND_COM *cond_expr ();
+
+static COND_COM *
+cond_expr ()
+{
+ return (cond_or ());
+}
+
+static COND_COM *
+cond_or ()
+{
+ COND_COM *l, *r;
+
+ l = cond_and ();
+ if (cond_token == OR_OR)
+ {
+ r = cond_or ();
+ l = make_cond_node (COND_OR, (WORD_DESC *)NULL, l, r);
+ }
+ return l;
+}
+
+static COND_COM *
+cond_and ()
+{
+ COND_COM *l, *r;
+
+ l = cond_term ();
+ if (cond_token == AND_AND)
+ {
+ r = cond_and ();
+ l = make_cond_node (COND_AND, (WORD_DESC *)NULL, l, r);
+ }
+ return l;
+}
+
+static int
+cond_skip_newlines ()
+{
+ while ((cond_token = read_token (READ)) == '\n')
+ {
+ if (interactive && (bash_input.type == st_stdin || bash_input.type == st_stream))
+ prompt_again ();
+ }
+ return (cond_token);
+}
+
+#define COND_RETURN_ERROR() \
+ do { cond_token = COND_ERROR; return ((COND_COM *)NULL); } while (0)
+
+static COND_COM *
+cond_term ()
+{
+ WORD_DESC *op;
+ COND_COM *term, *tleft, *tright;
+ int tok, lineno;
+
+ /* Read a token. It can be a left paren, a `!', a unary operator, or a
+ word that should be the first argument of a binary operator. Start by
+ skipping newlines, since this is a compound command. */
+ tok = cond_skip_newlines ();
+ lineno = line_number;
+ if (tok == COND_END)
+ {
+ COND_RETURN_ERROR ();
+ }
+ else if (tok == '(')
+ {
+ term = cond_expr ();
+ if (cond_token != ')')
+ {
+ if (term)
+ dispose_cond_node (term); /* ( */
+ parser_error (lineno, "expected `)'");
+ COND_RETURN_ERROR ();
+ }
+ term = make_cond_node (COND_EXPR, (WORD_DESC *)NULL, term, (COND_COM *)NULL);
+ (void)cond_skip_newlines ();
+ }
+ else if (tok == BANG || (tok == WORD && (yylval.word->word[0] == '!' && yylval.word->word[1] == '\0')))
+ {
+ if (tok == WORD)
+ dispose_word (yylval.word); /* not needed */
+ term = cond_term ();
+ if (term)
+ term->flags |= CMD_INVERT_RETURN;
+ }
+ else if (tok == WORD && test_unop (yylval.word->word))
+ {
+ op = yylval.word;
+ tok = read_token (READ);
+ if (tok == WORD)
+ {
+ tleft = make_cond_node (COND_TERM, yylval.word, (COND_COM *)NULL, (COND_COM *)NULL);
+ term = make_cond_node (COND_UNARY, op, tleft, (COND_COM *)NULL);
+ }
+ else
+ {
+ dispose_word (op);
+ parser_error (line_number, "unexpected argument to conditional unary operator");
+ COND_RETURN_ERROR ();
+ }
+
+ (void)cond_skip_newlines ();
+ }
+ else /* left argument to binary operator */
+ {
+ /* lhs */
+ tleft = make_cond_node (COND_TERM, yylval.word, (COND_COM *)NULL, (COND_COM *)NULL);
+
+ /* binop */
+ tok = read_token (READ);
+ if (tok == WORD && test_binop (yylval.word->word))
+ op = yylval.word;
+ else if (tok == '<' || tok == '>')
+ op = make_word_from_token (tok);
+ else if (tok == COND_END || tok == AND_AND || tok == OR_OR)
+ {
+ /* Special case. [[ x ]] is equivalent to [[ -n x ]], just like
+ the test command. Similarly for [[ x && expr ]] or
+ [[ x || expr ]] */
+ op = make_word ("-n");
+ term = make_cond_node (COND_UNARY, op, tleft, (COND_COM *)NULL);
+ cond_token = tok;
+ return (term);
+ }
+ else
+ {
+ parser_error (line_number, "conditional binary operator expected");
+ dispose_cond_node (tleft);
+ COND_RETURN_ERROR ();
+ }
+
+ /* rhs */
+ tok = read_token (READ);
+ if (tok == WORD)
+ {
+ tright = make_cond_node (COND_TERM, yylval.word, (COND_COM *)NULL, (COND_COM *)NULL);
+ term = make_cond_node (COND_BINARY, op, tleft, tright);
+ }
+ else
+ {
+ parser_error (line_number, "unexpected argument to conditional binary operator");
+ dispose_cond_node (tleft);
+ dispose_word (op);
+ COND_RETURN_ERROR ();
+ }
+
+ (void)cond_skip_newlines ();
+ }
+ return (term);
+}
+
+/* This is kind of bogus -- we slip a mini recursive-descent parser in
+ here to handle the conditional statement syntax. */
+static COMMAND *
+parse_cond_command ()
+{
+ COND_COM *cexp;
+
+ cexp = cond_expr ();
+ return (make_cond_command (cexp));
+}
+#endif
+
static int
read_token_word (character)
int character;
@@ -2458,6 +2745,34 @@ read_token_word (character)
goto next_character;
}
+#ifdef EXTENDED_GLOB
+ /* Parse a ksh-style extended pattern matching specification. */
+ if (extended_glob && PATTERN_CHAR(character))
+ {
+ peek_char = shell_getc (1);
+ if (peek_char == '(') /* ) */
+ {
+ push_delimiter (dstack, peek_char);
+ ttok = parse_matched_pair (cd, '(', ')', &ttoklen, 0);
+ pop_delimiter (dstack);
+ if (ttok == &matched_pair_error)
+ return -1; /* Bail immediately. */
+ RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 2,
+ token_buffer_size,
+ TOKEN_DEFAULT_GROW_SIZE);
+ token[token_index++] = character;
+ token[token_index++] = peek_char;
+ strcpy (token + token_index, ttok);
+ token_index += ttoklen;
+ FREE (ttok);
+ dollar_present = all_digits = 0;
+ goto next_character;
+ }
+ else
+ shell_ungetc (peek_char);
+ }
+#endif /* EXTENDED_GLOB */
+
/* If the delimiter character is not single quote, parse some of
the shell expansions that must be read as a single word. */
#if defined (PROCESS_SUBSTITUTION)
@@ -2503,13 +2818,16 @@ read_token_word (character)
/* This handles $'...' and $"..." new-style quoted strings. */
else if (character == '$' && (peek_char == '\'' || peek_char == '"'))
{
+ int first_line;
+
+ first_line = line_number;
ttok = parse_matched_pair (peek_char, peek_char, peek_char, &ttoklen, 0);
if (ttok == &matched_pair_error)
return -1;
if (peek_char == '\'')
ttrans = ansiexpand (ttok, 0, ttoklen - 1, &ttranslen);
else
- ttrans = localeexpand (ttok, 0, ttoklen - 1, &ttranslen);
+ ttrans = localeexpand (ttok, 0, ttoklen - 1, first_line, &ttranslen);
free (ttok);
RESIZE_MALLOCED_BUFFER (token, token_index, ttranslen + 2,
token_buffer_size,
@@ -2701,9 +3019,9 @@ ansiexpand (string, start, end, lenp)
by the caller. The length of the translated string is returned in LENP,
if non-null. */
static char *
-localeexpand (string, start, end, lenp)
+localeexpand (string, start, end, lineno, lenp)
char *string;
- int start, end, *lenp;
+ int start, end, lineno, *lenp;
{
int len, tlen;
char *temp, *t;
@@ -2716,7 +3034,11 @@ localeexpand (string, start, end, lenp)
/* If we're just dumping translatable strings, don't do anything. */
if (dump_translatable_strings)
{
- printf ("\"%s\"\n", temp);
+ if (dump_po_strings)
+ printf ("#: %s:%d\nmsgid \"%s\"\nmsgstr \"\"\n",
+ (bash_input.name ? bash_input.name : "stdin"), lineno, temp);
+ else
+ printf ("\"%s\"\n", temp);
if (lenp)
*lenp = tlen;
return (temp);
@@ -2849,6 +3171,8 @@ history_delimiting_chars ()
else
return "; "; /* (...) subshell */
}
+ else if (token_before_that == WORD && two_tokens_ago == FUNCTION)
+ return " "; /* function def using `function name' without `()' */
for (i = 0; no_semi_successors[i]; i++)
{
@@ -3051,6 +3375,12 @@ decode_prompt_string (string)
}
goto add_string;
+ case 'r':
+ temp = xmalloc (2);
+ temp[0] = '\r';
+ temp[1] = '\0';
+ goto add_string;
+
case 'n':
temp = xmalloc (3);
temp[0] = no_line_editing ? '\n' : '\r';