diff options
| author | Jari Aalto <jari.aalto@cante.net> | 2000-03-17 21:46:59 +0000 |
|---|---|---|
| committer | Jari Aalto <jari.aalto@cante.net> | 2009-09-12 16:46:53 +0000 |
| commit | bb70624e964126b7ac4ff085ba163a9c35ffa18f (patch) | |
| tree | ba2dd4add13ada94b1899c6d4aca80195b80b74b /parse.y | |
| parent | b72432fdcc59300c6fe7c9d6c8a31ad3447933f5 (diff) | |
| download | android_external_bash-bb70624e964126b7ac4ff085ba163a9c35ffa18f.tar.gz android_external_bash-bb70624e964126b7ac4ff085ba163a9c35ffa18f.tar.bz2 android_external_bash-bb70624e964126b7ac4ff085ba163a9c35ffa18f.zip | |
Imported from ../bash-2.04.tar.gz.
Diffstat (limited to 'parse.y')
| -rw-r--r-- | parse.y | 384 |
1 files changed, 340 insertions, 44 deletions
@@ -6,7 +6,7 @@ Bash is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free - Software Foundation; either version 1, or (at your option) any later + Software Foundation; either version 2, or (at your option) any later version. Bash is distributed in the hope that it will be useful, but WITHOUT ANY @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with Bash; see the file LICENSE. If not, write to the Free Software - Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ %{ #include "config.h" @@ -100,6 +100,7 @@ extern Function *last_shell_builtin, *this_shell_builtin; extern int bash_input_fd_changed; #endif +extern int errno; /* **************************************************************** */ /* */ /* "Forward" declarations */ @@ -177,6 +178,9 @@ static int function_dstart; /* The line number in a script on which a function body starts. */ static int function_bstart; +/* The line number in a script at which an arithmetic for command starts. */ +static int arith_for_lineno; + static REDIRECTEE redir; %} @@ -196,12 +200,12 @@ static REDIRECTEE redir; third group are recognized only under special circumstances. */ %token IF THEN ELSE ELIF FI CASE ESAC FOR SELECT WHILE UNTIL DO DONE FUNCTION %token COND_START COND_END COND_ERROR -%token IN BANG TIME TIMEOPT +%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 <word_list> ARITH_CMD ARITH_FOR_EXPRS %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 @@ -215,6 +219,7 @@ static REDIRECTEE redir; %type <command> for_command select_command case_command group_command %type <command> arith_command %type <command> cond_command +%type <command> arith_for_command %type <command> function_def if_command elif_clause subshell %type <redirect> redirection redirection_list %type <element> simple_command_element @@ -462,6 +467,14 @@ command: simple_command be attached to the function and performed when the function is executed, not as part of the function definition command. */ + /* XXX - I don't think it matters, but we might + want to change this in the future to avoid + problems differentiating between a function + definition with a redirection and a function + definition containing a single command with a + redirection. The two are semantically equivalent, + though -- the only difference is in how the + command printing code displays the redirections. */ if (tc->type == cm_function_def) { tc = tc->value.Function_def->command; @@ -501,6 +514,8 @@ shell_command: for_command { $$ = $1; } | cond_command { $$ = $1; } + | arith_for_command + { $$ = $1; } | function_def { $$ = $1; } ; @@ -519,6 +534,11 @@ for_command: FOR WORD newline_list DO compound_list DONE { $$ = make_for_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9); } ; +arith_for_command: FOR ARITH_FOR_EXPRS list_terminator newline_list DO compound_list DONE + { $$ = make_arith_for_command ($2, $6, arith_for_lineno); } + | FOR ARITH_FOR_EXPRS list_terminator newline_list '{' compound_list '}' + { $$ = make_arith_for_command ($2, $6, arith_for_lineno); } + ; select_command: SELECT WORD newline_list DO list DONE { $$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5); @@ -565,7 +585,10 @@ function_def: WORD '(' ')' newline_list group_command ; subshell: '(' compound_list ')' - { $2->flags |= CMD_WANT_SUBSHELL; $$ = $2; } + { + $$ = make_subshell_command ($2); + $$->flags |= CMD_WANT_SUBSHELL; + } ; if_command: IF compound_list THEN compound_list FI @@ -744,7 +767,7 @@ pipeline_command: pipeline } | timespec BANG pipeline { - $3->flags |= $1; + $3->flags |= $1|CMD_INVERT_RETURN; $$ = $3; } | BANG timespec pipeline @@ -779,6 +802,7 @@ timespec: TIME #define PST_CASESTMT 0x080 /* parsing a case statement */ #define PST_CONDCMD 0x100 /* parsing a [[...]] command */ #define PST_CONDEXPR 0x200 /* parsing the guts of [[...]] */ +#define PST_ARITHFOR 0x400 /* parsing an arithmetic for command */ /* Initial size to allocate for tokens, and the amount to grow them by. */ @@ -1068,20 +1092,21 @@ with_input_from_string (string, name) /* */ /* **************************************************************** */ +/* These two functions used to test the value of the HAVE_RESTARTABLE_SYSCALLS + define, and just use getc/ungetc if it was defined, but since bash + installs its signal handlers without the SA_RESTART flag, some signals + (like SIGCHLD, SIGWINCH, etc.) received during a read(2) will not cause + the read to be restarted. We need to restart it ourselves. */ + static int yy_stream_get () { - int result = EOF; + int result; + result = EOF; if (bash_input.location.file) - { -#if !defined (HAVE_RESTARTABLE_SYSCALLS) - result = getc_with_restart (bash_input.location.file); -#else /* HAVE_RESTARTABLE_SYSCALLS */ - result = getc (bash_input.location.file); - result = (feof (bash_input.location.file)) ? EOF : (unsigned char)result; -#endif /* HAVE_RESTARTABLE_SYSCALLS */ - } + result = getc_with_restart (bash_input.location.file); + return (result); } @@ -1089,11 +1114,7 @@ static int yy_stream_unget (c) int c; { -#if !defined (HAVE_RESTARTABLE_SYSCALLS) return (ungetc_with_restart (c, bash_input.location.file)); -#else /* HAVE_RESTARTABLE_SYSCALLS */ - return (ungetc (c, bash_input.location.file)); -#endif /* HAVE_RESTARTABLE_SYSCALLS */ } void @@ -1209,6 +1230,30 @@ stream_on_stack (type) return 0; } +/* Save the current token state and return it in a malloced array. */ +int * +save_token_state () +{ + int *ret; + + ret = (int *)xmalloc (3 * sizeof (int)); + ret[0] = last_read_token; + ret[1] = token_before_that; + ret[2] = two_tokens_ago; + return ret; +} + +void +restore_token_state (ts) + int *ts; +{ + if (ts == 0) + return; + last_read_token = ts[0]; + token_before_that = ts[1]; + two_tokens_ago = ts[2]; +} + /* * This is used to inhibit alias expansion and reserved word recognition * inside case statement pattern lists. A `case statement pattern list' is: @@ -2204,8 +2249,36 @@ read_token (command) case '|': return (OR_OR); -#if defined (DPAREN_ARITHMETIC) +#if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND) case '(': /* ) */ +# if defined (ARITH_FOR_COMMAND) + if (last_read_token == FOR) + { + int cmdtyp, len; + char *wval, *wv2; + WORD_DESC *wd; + + arith_for_lineno = line_number; + cmdtyp = parse_arith_cmd (&wval); + if (cmdtyp == 1) + { + /* parse_arith_cmd adds quotes at the beginning and end + of the string it returns; we need to take those out. */ + len = strlen (wval); + wv2 = xmalloc (len); + strncpy (wv2, wval + 1, len - 2); + wv2[len - 2] = '\0'; + wd = make_word (wv2); + yylval.word_list = make_word_list (wd, (WORD_LIST *)NULL); + free (wval); + free (wv2); + return (ARITH_FOR_EXPRS); + } + else + return -1; /* ERROR */ + } +# endif +# if defined (DPAREN_ARITHMETIC) if (reserved_word_acceptable (last_read_token)) { int cmdtyp, sline; @@ -2233,6 +2306,7 @@ read_token (command) return -1; } break; +# endif #endif } } @@ -2302,6 +2376,7 @@ read_token (command) correct error values if it reads EOF. */ #define P_FIRSTCLOSE 0x01 +#define P_ALLOWESC 0x02 static char matched_pair_error; static char * @@ -2324,7 +2399,7 @@ parse_matched_pair (qc, open, close, lenp, flags) start_lineno = line_number; while (count) { - ch = shell_getc (qc != '\'' && pass_next_character == 0); + ch = shell_getc ((qc != '\'' || (flags & P_ALLOWESC)) && pass_next_character == 0); if (ch == EOF) { free (ret); @@ -2362,6 +2437,11 @@ parse_matched_pair (qc, open, close, lenp, flags) } else if (ch == close) /* ending delimiter */ count--; +#if 1 + /* handle nested ${...} specially. */ + else if (open != close && was_dollar && open == '{' && ch == open) /* } */ + count++; +#endif else if (((flags & P_FIRSTCLOSE) == 0) && ch == open) /* nested begin */ count++; @@ -2370,7 +2450,11 @@ parse_matched_pair (qc, open, close, lenp, flags) ret[retind++] = ch; if (open == '\'') /* '' inside grouping construct */ - continue; + { + if ((flags & P_ALLOWESC) && ch == '\\') + pass_next_character++; + continue; + } if (ch == '\\') /* backslashes */ pass_next_character++; @@ -2449,7 +2533,7 @@ parse_matched_pair (qc, open, close, lenp, flags) return ret; } -#if defined (DPAREN_ARITHMETIC) +#if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND) /* We've seen a `(('. Look for the matching `))'. If we get it, return 1. If not, assume it's a nested subshell for backwards compatibility and return 0. In any case, put the characters we've consumed into a locally- @@ -2473,7 +2557,7 @@ parse_arith_cmd (ep) if ((c = shell_getc (0)) != ')') rval = 0; - token = xmalloc(ttoklen + 4); + token = xmalloc (ttoklen + 4); /* (( ... )) -> "..." */ token[0] = (rval == 1) ? '"' : '('; @@ -2493,7 +2577,7 @@ parse_arith_cmd (ep) FREE (ttok); return rval; } -#endif /* DPAREN_ARITHMETIC */ +#endif /* DPAREN_ARITHMETIC || ARITH_FOR_COMMAND */ #if defined (COND_COMMAND) static COND_COM *cond_term (); @@ -2604,7 +2688,7 @@ cond_term () (void)cond_skip_newlines (); } - else /* left argument to binary operator */ + else if (tok == WORD) /* left argument to binary operator */ { /* lhs */ tleft = make_cond_node (COND_TERM, yylval.word, (COND_COM *)NULL, (COND_COM *)NULL); @@ -2649,6 +2733,14 @@ cond_term () (void)cond_skip_newlines (); } + else + { + if (tok < 256) + parser_error (line_number, "unexpected token `%c' in conditional command", tok); + else + parser_error (line_number, "unexpected token %d in conditional command", tok); + COND_RETURN_ERROR (); + } return (term); } @@ -2809,7 +2901,7 @@ read_token_word (character) the command-oriented-history code. This way newlines appearing in the $(...) string get added to the history literally rather than causing a possibly- - incorrect `;' to be added. */ + incorrect `;' to be added. ) */ push_delimiter (dstack, peek_char); ttok = parse_matched_pair (cd, '(', ')', &ttoklen, 0); pop_delimiter (dstack); @@ -2836,21 +2928,46 @@ read_token_word (character) int first_line; first_line = line_number; - ttok = parse_matched_pair (peek_char, peek_char, peek_char, &ttoklen, 0); + push_delimiter (dstack, peek_char); + ttok = parse_matched_pair (peek_char, peek_char, peek_char, + &ttoklen, + (peek_char == '\'') ? P_ALLOWESC : 0); + pop_delimiter (dstack); if (ttok == &matched_pair_error) return -1; if (peek_char == '\'') - ttrans = ansiexpand (ttok, 0, ttoklen - 1, &ttranslen); + { + ttrans = ansiexpand (ttok, 0, ttoklen - 1, &ttranslen); + free (ttok); + /* Insert the single quotes and correctly quote any + embedded single quotes (allowed because P_ALLOWESC was + passed to parse_matched_pair). */ + ttok = single_quote (ttrans); + free (ttrans); + ttrans = ttok; + ttranslen = strlen (ttrans); + } else - ttrans = localeexpand (ttok, 0, ttoklen - 1, first_line, &ttranslen); - free (ttok); + { + /* Try to locale-expand the converted string. */ + ttrans = localeexpand (ttok, 0, ttoklen - 1, first_line, &ttranslen); + free (ttok); + + /* Add the double quotes back */ + ttok = xmalloc (ttranslen + 3); + ttok[0] = '"'; + strcpy (ttok + 1, ttrans); + ttok[ttranslen + 1] = '"'; + ttok[ttranslen += 2] = '\0'; + free (ttrans); + ttrans = ttok; + } + RESIZE_MALLOCED_BUFFER (token, token_index, ttranslen + 2, token_buffer_size, TOKEN_DEFAULT_GROW_SIZE); - token[token_index++] = peek_char; strcpy (token + token_index, ttrans); token_index += ttranslen; - token[token_index++] = peek_char; FREE (ttrans); quoted = 1; all_digits = 0; @@ -3036,7 +3153,7 @@ ansiexpand (string, start, end, lenp) if (*temp) { - t = ansicstr (temp, tlen, (int *)NULL, lenp); + t = ansicstr (temp, tlen, 0, (int *)NULL, lenp); free (temp); return (t); } @@ -3048,6 +3165,52 @@ ansiexpand (string, start, end, lenp) } } +/* Change a bash string into a string suitable for inclusion in a `po' file. + This backslash-escapes `"' and `\' and changes newlines into \\\n"\n". */ +static char * +mk_msgstr (string, foundnlp) + char *string; + int *foundnlp; +{ + register int c, len; + char *result, *r, *s; + + for (len = 0, s = string; s && *s; s++) + { + len++; + if (*s == '"' || *s == '\\') + len++; + else if (*s == '\n') + len += 5; + } + + r = result = xmalloc (len + 3); + *r++ = '"'; + + for (s = string; s && (c = *s); s++) + { + if (c == '\n') /* <NL> -> \n"<NL>" */ + { + *r++ = '\\'; + *r++ = 'n'; + *r++ = '"'; + *r++ = '\n'; + *r++ = '"'; + if (foundnlp) + *foundnlp = 1; + continue; + } + if (c == '"' || c == '\\') + *r++ = '\\'; + *r++ = c; + } + + *r++ = '"'; + *r++ = '\0'; + + return result; +} + /* $"..." -- Translate the portion of STRING between START and END according to current locale using gettext (if available) and return the result. The caller will take care of leaving the quotes intact. @@ -3060,22 +3223,35 @@ localeexpand (string, start, end, lineno, lenp) char *string; int start, end, lineno, *lenp; { - int len, tlen; - char *temp, *t; + int len, tlen, foundnl; + char *temp, *t, *t2; temp = xmalloc (end - start + 1); for (tlen = 0, len = start; len < end; ) temp[tlen++] = string[len++]; temp[tlen] = '\0'; - /* If we're just dumping translatable strings, don't do anything. */ + /* If we're just dumping translatable strings, don't do anything with the + string itself, but if we're dumping in `po' file format, convert it into a form more palatable to gettext(3) + and friends by quoting `"' and `\' with backslashes and converting <NL> + into `\n"<NL>"'. If we find a newline in TEMP, we first output a + `msgid ""' line and then the translated string; otherwise we output the + `msgid' and translated string all on one line. */ if (dump_translatable_strings) { if (dump_po_strings) - printf ("#: %s:%d\nmsgid \"%s\"\nmsgstr \"\"\n", - (bash_input.name ? bash_input.name : "stdin"), lineno, temp); + { + foundnl = 0; + t = mk_msgstr (temp, &foundnl); + t2 = foundnl ? "\"\"\n" : ""; + + printf ("#: %s:%d\nmsgid %s%s\nmsgstr \"\"\n", + (bash_input.name ? bash_input.name : "stdin"), lineno, t2, t); + free (t); + } else printf ("\"%s\"\n", temp); + if (lenp) *lenp = tlen; return (temp); @@ -3211,6 +3387,17 @@ history_delimiting_chars () else if (token_before_that == WORD && two_tokens_ago == FUNCTION) return " "; /* function def using `function name' without `()' */ + else if (token_before_that == WORD && two_tokens_ago == FOR) + { + /* Tricky. `for i\nin ...' should not have a semicolon, but + `for i\ndo ...' should. We do what we can. */ + for (i = shell_input_line_index; whitespace(shell_input_line[i]); i++) + ; + if (shell_input_line[i] && shell_input_line[i] == 'i' && shell_input_line[i+1] == 'n') + return " "; + return ";"; + } + for (i = 0; no_semi_successors[i]; i++) { if (token_before_that == no_semi_successors[i]) @@ -3264,6 +3451,20 @@ prompt_again () } } +int +get_current_prompt_level () +{ + return ((current_prompt_string && current_prompt_string == ps2_prompt) ? 2 : 1); +} + +void +set_current_prompt_level (x) + int x; +{ + prompt_string_pointer = (x == 2) ? &ps2_prompt : &ps1_prompt; + current_prompt_string = *prompt_string_pointer; +} + static void print_prompt () { @@ -3279,6 +3480,8 @@ print_prompt () \d the date in Day Mon Date format \h the hostname up to the first `.' \H the hostname + \j the number of active jobs + \l the basename of the shell's tty device name \n CRLF \s the name of the shell \t the time in 24-hour hh:mm:ss format @@ -3479,7 +3682,14 @@ decode_prompt_string (string) /* If we're going to be expanding the prompt string later, quote the directory name. */ if (promptvars || posixly_correct) +#if 0 temp = backslash_quote (t_string); +#else + /* Make sure that expand_prompt_string is called with a + second argument of Q_DOUBLE_QUOTE if we use this + function here. */ + temp = backslash_quote_for_double_quotes (t_string); +#endif else temp = savestring (t_string); @@ -3487,6 +3697,8 @@ decode_prompt_string (string) } case 'u': + if (current_user.user_name == 0) + get_current_user_info (); temp = savestring (current_user.user_name); goto add_string; @@ -3517,6 +3729,20 @@ decode_prompt_string (string) *t = '\0'; goto add_string; + case 'j': + temp = itos (count_all_jobs ()); + goto add_string; + + case 'l': +#if defined (HAVE_TTYNAME) + temp = (char *)ttyname (fileno (stdin)); + t = temp ? base_pathname (temp) : "tty"; + temp = savestring (t); +#else + temp = savestring ("tty"); +#endif /* !HAVE_TTYNAME */ + goto add_string; + #if defined (READLINE) case '[': case ']': @@ -3578,7 +3804,11 @@ decode_prompt_string (string) the prompt string. */ if (promptvars || posixly_correct) { +#if 0 list = expand_string_unsplit (result, Q_DOUBLE_QUOTES); +#else + list = expand_prompt_string (result, Q_DOUBLE_QUOTES); +#endif free (result); result = string_list (list); dispose_words (list); @@ -3653,11 +3883,7 @@ report_syntax_error (message) if (token_end || (i == 0 && token_end == 0)) { if (token_end) - { - msg = xmalloc (1 + (token_end - i)); - strncpy (msg, t + i, token_end - i); - msg[token_end - i] = '\0'; - } + msg = substring (t, i, token_end); else /* one-character token */ { msg2[0] = t[i]; @@ -3761,3 +3987,73 @@ handle_eof_input_unit () EOF_Reached = 1; } } + +static WORD_LIST parse_string_error; + +/* Take a string and run it through the shell parser, returning the + resultant word list. Used by compound array assignment. */ +WORD_LIST * +parse_string_to_word_list (s, whom) + char *s, *whom; +{ + WORD_LIST *wl; + int tok, orig_line_number, orig_input_terminator; +#if defined (HISTORY) + int old_remember_on_history, old_history_expansion_inhibited; +#endif + +#if defined (HISTORY) + old_remember_on_history = remember_on_history; +# if defined (BANG_HISTORY) + old_history_expansion_inhibited = history_expansion_inhibited; +# endif + bash_history_disable (); +#endif + + orig_line_number = line_number; + orig_input_terminator = shell_input_line_terminator; + + push_stream (1); + last_read_token = '\n'; + + with_input_from_string (s, whom); + wl = (WORD_LIST *)NULL; + while ((tok = read_token (READ)) != yacc_EOF) + { + if (tok == '\n' && *bash_input.location.string == '\0') + break; + if (tok != WORD && tok != ASSIGNMENT_WORD) + { + line_number = orig_line_number + line_number - 1; + yyerror (); /* does the right thing */ + if (wl) + dispose_words (wl); + wl = &parse_string_error; + break; + } + wl = make_word_list (yylval.word, wl); + } + + last_read_token = '\n'; + pop_stream (); + +#if defined (HISTORY) + remember_on_history = old_remember_on_history; +# if defined (BANG_HISTORY) + history_expansion_inhibited = old_history_expansion_inhibited; +# endif /* BANG_HISTORY */ +#endif /* HISTORY */ + + shell_input_line_terminator = orig_input_terminator; + + if (wl == &parse_string_error) + { + last_command_exit_value = EXECUTION_FAILURE; + if (interactive_shell == 0 && posixly_correct) + jump_to_top_level (FORCE_EOF); + else + jump_to_top_level (DISCARD); + } + + return (REVERSE_LIST (wl, WORD_LIST *)); +} |
