X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=parse.y;h=eca168e25fb2a00e5ad4cdb6a1dcdf836078c8c5;hb=cce855bc5b117cb7ae70064131120687bc69fac0;hp=a8d478055b64432c930ae45fc9a903d4b89e2721;hpb=e8ce775db824de329b81293b4e5d8fbd65624528;p=platform%2Fupstream%2Fbash.git 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 -#include -#include "maxpath.h" +# ifndef _MINIX +# include +# endif +# include +# 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 ASSIGNMENT_WORD %token NUMBER +%token ARITH_CMD +%token 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 list list0 list1 compound_list simple_list simple_list1 %type simple_command shell_command %type for_command select_command case_command group_command +%type arith_command +%type cond_command %type function_def if_command elif_clause subshell %type redirection redirection_list %type 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';