X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=y.tab.c;h=367348a8ce3b58831ac6c0ff27e4c96285aabfee;hb=95732b497d12c98613bb3c5db16b61f377501a59;hp=03f1af9d5d3ea788ccb883ea86382a860e12e0f3;hpb=28ef6c316f1aff914bb95ac09787a3c83c1815fd;p=platform%2Fupstream%2Fbash.git diff --git a/y.tab.c b/y.tab.c index 03f1af9..367348a 100644 --- a/y.tab.c +++ b/y.tab.c @@ -1,19 +1,52 @@ -#ifndef lint -static char const -yyrcsid[] = "$FreeBSD: src/usr.bin/yacc/skeleton.c,v 1.28 2000/01/17 02:04:06 bde Exp $"; -#endif -#include -#define YYBYACC 1 -#define YYMAJOR 1 -#define YYMINOR 9 -#define YYLEX yylex() -#define YYEMPTY -1 -#define yyclearin (yychar=(YYEMPTY)) -#define yyerrok (yyerrflag=0) -#define YYRECOVERING() (yyerrflag!=0) -static int yygrowstack(); -#define YYPREFIX "yy" -#line 22 "/usr/homes/chet/src/bash/src/parse.y" + +/* A Bison parser, made from /usr/src/local/chet/src/bash/src/parse.y + by GNU Bison version 1.28 */ + +#define YYBISON 1 /* Identify Bison output. */ + +#define IF 257 +#define THEN 258 +#define ELSE 259 +#define ELIF 260 +#define FI 261 +#define CASE 262 +#define ESAC 263 +#define FOR 264 +#define SELECT 265 +#define WHILE 266 +#define UNTIL 267 +#define DO 268 +#define DONE 269 +#define FUNCTION 270 +#define COND_START 271 +#define COND_END 272 +#define COND_ERROR 273 +#define IN 274 +#define BANG 275 +#define TIME 276 +#define TIMEOPT 277 +#define WORD 278 +#define ASSIGNMENT_WORD 279 +#define NUMBER 280 +#define ARITH_CMD 281 +#define ARITH_FOR_EXPRS 282 +#define COND_CMD 283 +#define AND_AND 284 +#define OR_OR 285 +#define GREATER_GREATER 286 +#define LESS_LESS 287 +#define LESS_AND 288 +#define LESS_LESS_LESS 289 +#define GREATER_AND 290 +#define SEMI_SEMI 291 +#define LESS_LESS_MINUS 292 +#define AND_GREATER 293 +#define LESS_GREATER 294 +#define GREATER_BAR 295 +#define yacc_EOF 296 + +#line 21 "/usr/src/local/chet/src/bash/src/parse.y" + #include "config.h" #include "bashtypes.h" @@ -30,18 +63,27 @@ static int yygrowstack(); #endif #include +#include "chartypes.h" #include #include "memalloc.h" +#include "bashintl.h" + +#define NEED_STRFTIME_DECL /* used in externs.h */ + #include "shell.h" #include "trap.h" #include "flags.h" #include "parser.h" #include "mailcheck.h" +#include "test.h" +#include "builtins.h" #include "builtins/common.h" #include "builtins/builtext.h" +#include "shmbutil.h" + #if defined (READLINE) # include "bashline.h" # include @@ -58,6 +100,8 @@ static int yygrowstack(); #if defined (ALIAS) # include "alias.h" +#else +typedef void *alias_t; #endif /* ALIAS */ #if defined (PROMPT_STRING_DECODE) @@ -65,13 +109,32 @@ static int yygrowstack(); # include # endif # include +# if defined (TM_IN_SYS_TIME) +# include +# include +# endif /* TM_IN_SYS_TIME */ # include "maxpath.h" #endif /* PROMPT_STRING_DECODE */ #define RE_READ_TOKEN -99 #define NO_EXPANSION -100 -#define YYDEBUG 0 +#ifdef DEBUG +# define YYDEBUG 1 +#else +# define YYDEBUG 0 +#endif + +#if defined (HANDLE_MULTIBYTE) +# define last_shell_getc_is_singlebyte \ + ((shell_input_line_index > 1) \ + ? shell_input_line_property[shell_input_line_index - 1] \ + : 1) +# define MBTEST(x) ((x) && last_shell_getc_is_singlebyte) +#else +# define last_shell_getc_is_singlebyte 1 +# define MBTEST(x) ((x)) +#endif #if defined (EXTENDED_GLOB) extern int extended_glob; @@ -80,7 +143,6 @@ extern int extended_glob; extern int eof_encountered; extern int no_line_editing, running_under_emacs; extern int current_command_number; -extern int interactive, interactive_shell, login_shell; extern int sourcelevel; extern int posixly_correct; extern int last_command_exit_value; @@ -89,7 +151,7 @@ extern char *shell_name, *current_host_name; extern char *dist_version; extern int patch_level; extern int dump_translatable_strings, dump_po_strings; -extern Function *last_shell_builtin, *this_shell_builtin; +extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin; #if defined (BUFFERED_INPUT) extern int bash_input_fd_changed; #endif @@ -101,31 +163,93 @@ extern int errno; /* */ /* **************************************************************** */ -static char *ansiexpand (); -static char *localeexpand (); -static int reserved_word_acceptable (); -static int read_token (); -static int yylex (); -static int parse_arith_cmd (); +#ifdef DEBUG +static void debug_parser __P((int)); +#endif + +static int yy_getc __P((void)); +static int yy_ungetc __P((int)); + +#if defined (READLINE) +static int yy_readline_get __P((void)); +static int yy_readline_unget __P((int)); +#endif + +static int yy_string_get __P((void)); +static int yy_string_unget __P((int)); +static int yy_stream_get __P((void)); +static int yy_stream_unget __P((int)); + +static int shell_getc __P((int)); +static void shell_ungetc __P((int)); +static void discard_until __P((int)); + +#if defined (ALIAS) || defined (DPAREN_ARITHMETIC) +static void push_string __P((char *, int, alias_t *)); +static void pop_string __P((void)); +static void free_string_list __P((void)); +#endif + +static char *read_a_line __P((int)); + +static int reserved_word_acceptable __P((int)); +static int yylex __P((void)); +static int alias_expand_token __P((char *)); +static int time_command_acceptable __P((void)); +static int special_case_tokens __P((char *)); +static int read_token __P((int)); +static char *parse_matched_pair __P((int, int, int, int *, int)); +#if defined (ARRAY_VARS) +static char *parse_compound_assignment __P((int *)); +#endif +#if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND) +static int parse_dparen __P((int)); +static int parse_arith_cmd __P((char **, int)); +#endif #if defined (COND_COMMAND) -static COMMAND *parse_cond_command (); +static void cond_error __P((void)); +static COND_COM *cond_expr __P((void)); +static COND_COM *cond_or __P((void)); +static COND_COM *cond_and __P((void)); +static COND_COM *cond_term __P((void)); +static int cond_skip_newlines __P((void)); +static COMMAND *parse_cond_command __P((void)); +#endif +#if defined (ARRAY_VARS) +static int token_is_assignment __P((char *, int)); +static int token_is_ident __P((char *, int)); #endif -static int read_token_word (); -static void discard_parser_constructs (); +static int read_token_word __P((int)); +static void discard_parser_constructs __P((int)); + +static char *error_token_from_token __P((int)); +static char *error_token_from_text __P((void)); +static void print_offending_line __P((void)); +static void report_syntax_error __P((char *)); -static void report_syntax_error (); -static void handle_eof_input_unit (); -static void prompt_again (); +static void handle_eof_input_unit __P((void)); +static void prompt_again __P((void)); #if 0 -static void reset_readline_prompt (); +static void reset_readline_prompt __P((void)); #endif -static void print_prompt (); +static void print_prompt __P((void)); #if defined (HISTORY) -char *history_delimiting_chars (); +char *history_delimiting_chars __P((void)); #endif -extern int yyerror (); +#if defined (HANDLE_MULTIBYTE) +static void set_line_mbstate __P((void)); +static char *shell_input_line_property = NULL; +#else +# define set_line_mbstate() +#endif + +extern int yyerror __P((const char *)); + +#ifdef DEBUG +extern int yydebug; +#endif /* Default prompt strings */ char *primary_prompt = PPROMPT; @@ -148,6 +272,11 @@ int expand_aliases = 0; decode_prompt_string. */ int promptvars = 1; +/* If non-zero, $'...' and $"..." are expanded when they appear within + a ${...} expansion, even when the expansion appears within double + quotes. */ +int extended_quote = 1; + /* The decoded prompt string. Used if READLINE is not defined or if editing is turned off. Analogous to current_readline_prompt. */ static char *current_decoded_prompt; @@ -179,8 +308,23 @@ static int function_bstart; /* The line number in a script at which an arithmetic for command starts. */ static int arith_for_lineno; +/* The line number in a script where the word in a `case WORD', `select WORD' + or `for WORD' begins. This is a nested command maximum, since the array + index is decremented after a case, select, or for command is parsed. */ +#define MAX_CASE_NEST 128 +static int word_lineno[MAX_CASE_NEST]; +static int word_top = -1; + +/* If non-zero, it is the token that we want read_token to return + regardless of what text is (or isn't) present to be read. This + is reset by read_token. If token_to_read == WORD or + ASSIGNMENT_WORD, yylval.word should be set to word_desc_to_read. */ +static int token_to_read; +static WORD_DESC *word_desc_to_read; + static REDIRECTEE redir; -#line 190 "/usr/homes/chet/src/bash/src/parse.y" + +#line 300 "/usr/src/local/chet/src/bash/src/parse.y" typedef union { WORD_DESC *word; /* the word that we read. */ int number; /* the number that we read. */ @@ -190,4983 +334,5914 @@ typedef union { ELEMENT element; PATTERN_LIST *pattern; } YYSTYPE; -#line 194 "y.tab.c" -#define YYERRCODE 256 -#define IF 257 -#define THEN 258 -#define ELSE 259 -#define ELIF 260 -#define FI 261 -#define CASE 262 -#define ESAC 263 -#define FOR 264 -#define SELECT 265 -#define WHILE 266 -#define UNTIL 267 -#define DO 268 -#define DONE 269 -#define FUNCTION 270 -#define COND_START 271 -#define COND_END 272 -#define COND_ERROR 273 -#define IN 274 -#define BANG 275 -#define TIME 276 -#define TIMEOPT 277 -#define WORD 278 -#define ASSIGNMENT_WORD 279 -#define NUMBER 280 -#define ARITH_CMD 281 -#define ARITH_FOR_EXPRS 282 -#define COND_CMD 283 -#define AND_AND 284 -#define OR_OR 285 -#define GREATER_GREATER 286 -#define LESS_LESS 287 -#define LESS_AND 288 -#define GREATER_AND 289 -#define SEMI_SEMI 290 -#define LESS_LESS_MINUS 291 -#define AND_GREATER 292 -#define LESS_GREATER 293 -#define GREATER_BAR 294 -#define yacc_EOF 295 -const short yylhs[] = { -1, - 0, 0, 0, 0, 27, 27, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 26, 26, 26, 25, 25, 10, 10, - 1, 1, 1, 1, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, - 12, 18, 18, 18, 18, 13, 13, 13, 13, 13, - 13, 14, 14, 14, 19, 19, 19, 20, 20, 23, - 21, 21, 21, 15, 16, 17, 22, 22, 22, 31, - 31, 29, 29, 29, 29, 30, 30, 28, 28, 4, - 7, 7, 5, 5, 5, 6, 6, 6, 6, 6, - 6, 34, 34, 34, 33, 33, 8, 8, 8, 9, - 9, 9, 9, 9, 3, 3, 3, 3, 3, 2, - 2, 32, 32, +#include + +#ifndef __cplusplus +#ifndef __STDC__ +#define const +#endif +#endif + + + +#define YYFINAL 302 +#define YYFLAG -32768 +#define YYNTBASE 54 + +#define YYTRANSLATE(x) ((unsigned)(x) <= 296 ? yytranslate[x] : 90) + +static const char yytranslate[] = { 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 44, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 42, 2, 52, + 53, 2, 2, 2, 49, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 43, 48, + 2, 47, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 50, 46, 51, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 1, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 45 +}; + +#if YYDEBUG != 0 +static const short yyprhs[] = { 0, + 0, 3, 5, 8, 10, 12, 15, 18, 21, 25, + 29, 32, 36, 39, 43, 46, 50, 53, 57, 60, + 64, 67, 71, 74, 78, 81, 85, 88, 92, 95, + 99, 102, 106, 109, 112, 116, 118, 120, 122, 124, + 127, 129, 132, 134, 136, 139, 141, 143, 145, 151, + 157, 159, 161, 163, 165, 167, 169, 171, 178, 185, + 193, 201, 212, 223, 233, 243, 251, 259, 265, 271, + 278, 285, 293, 301, 312, 323, 330, 338, 345, 351, + 358, 363, 365, 368, 372, 378, 386, 393, 397, 399, + 403, 408, 415, 421, 423, 426, 431, 436, 442, 448, + 451, 455, 457, 461, 464, 466, 469, 473, 477, 481, + 486, 491, 496, 501, 506, 508, 510, 512, 514, 516, + 518, 519, 522, 524, 527, 530, 535, 540, 544, 548, + 550, 552, 555, 558, 562, 566, 569, 574, 576, 578 +}; + +static const short yyrhs[] = { 85, + 82, 0, 44, 0, 1, 44, 0, 45, 0, 24, + 0, 55, 24, 0, 47, 24, 0, 48, 24, 0, + 26, 47, 24, 0, 26, 48, 24, 0, 32, 24, + 0, 26, 32, 24, 0, 33, 24, 0, 26, 33, + 24, 0, 35, 24, 0, 26, 35, 24, 0, 34, + 26, 0, 26, 34, 26, 0, 36, 26, 0, 26, + 36, 26, 0, 34, 24, 0, 26, 34, 24, 0, + 36, 24, 0, 26, 36, 24, 0, 38, 24, 0, + 26, 38, 24, 0, 36, 49, 0, 26, 36, 49, + 0, 34, 49, 0, 26, 34, 49, 0, 39, 24, + 0, 26, 40, 24, 0, 40, 24, 0, 41, 24, + 0, 26, 41, 24, 0, 24, 0, 25, 0, 56, + 0, 56, 0, 58, 56, 0, 57, 0, 59, 57, + 0, 59, 0, 61, 0, 61, 58, 0, 66, 0, + 62, 0, 65, 0, 12, 79, 14, 79, 15, 0, + 13, 79, 14, 79, 15, 0, 64, 0, 69, 0, + 68, 0, 70, 0, 71, 0, 72, 0, 63, 0, + 10, 24, 84, 14, 79, 15, 0, 10, 24, 84, + 50, 79, 51, 0, 10, 24, 43, 84, 14, 79, + 15, 0, 10, 24, 43, 84, 50, 79, 51, 0, + 10, 24, 84, 20, 55, 83, 84, 14, 79, 15, + 0, 10, 24, 84, 20, 55, 83, 84, 50, 79, + 51, 0, 10, 24, 84, 20, 83, 84, 14, 79, + 15, 0, 10, 24, 84, 20, 83, 84, 50, 79, + 51, 0, 10, 28, 83, 84, 14, 79, 15, 0, + 10, 28, 83, 84, 50, 79, 51, 0, 10, 28, + 14, 79, 15, 0, 10, 28, 50, 79, 51, 0, + 11, 24, 84, 14, 78, 15, 0, 11, 24, 84, + 50, 78, 51, 0, 11, 24, 43, 84, 14, 78, + 15, 0, 11, 24, 43, 84, 50, 78, 51, 0, + 11, 24, 84, 20, 55, 83, 84, 14, 78, 15, + 0, 11, 24, 84, 20, 55, 83, 84, 50, 78, + 51, 0, 8, 24, 84, 20, 84, 9, 0, 8, + 24, 84, 20, 76, 84, 9, 0, 8, 24, 84, + 20, 74, 9, 0, 24, 52, 53, 84, 67, 0, + 16, 24, 52, 53, 84, 67, 0, 16, 24, 84, + 67, 0, 61, 0, 61, 58, 0, 52, 79, 53, + 0, 3, 79, 4, 79, 7, 0, 3, 79, 4, + 79, 5, 79, 7, 0, 3, 79, 4, 79, 73, + 7, 0, 50, 79, 51, 0, 27, 0, 17, 29, + 18, 0, 6, 79, 4, 79, 0, 6, 79, 4, + 79, 5, 79, 0, 6, 79, 4, 79, 73, 0, + 75, 0, 76, 75, 0, 84, 77, 53, 79, 0, + 84, 77, 53, 84, 0, 84, 52, 77, 53, 79, + 0, 84, 52, 77, 53, 84, 0, 75, 37, 0, + 76, 75, 37, 0, 24, 0, 77, 46, 24, 0, + 84, 80, 0, 78, 0, 84, 81, 0, 81, 44, + 84, 0, 81, 42, 84, 0, 81, 43, 84, 0, + 81, 30, 84, 81, 0, 81, 31, 84, 81, 0, + 81, 42, 84, 81, 0, 81, 43, 84, 81, 0, + 81, 44, 84, 81, 0, 87, 0, 44, 0, 45, + 0, 44, 0, 43, 0, 45, 0, 0, 84, 44, + 0, 86, 0, 86, 42, 0, 86, 43, 0, 86, + 30, 84, 86, 0, 86, 31, 84, 86, 0, 86, + 42, 86, 0, 86, 43, 86, 0, 87, 0, 88, + 0, 21, 88, 0, 89, 88, 0, 89, 21, 88, + 0, 21, 89, 88, 0, 89, 83, 0, 88, 46, + 84, 88, 0, 60, 0, 22, 0, 22, 23, 0 }; -const short yylen[] = { 2, - 2, 1, 2, 1, 1, 2, 2, 2, 3, 3, - 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, - 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, - 2, 2, 3, 1, 1, 1, 1, 2, 1, 2, - 1, 1, 2, 1, 1, 1, 5, 5, 1, 1, - 1, 1, 1, 1, 1, 6, 6, 7, 7, 10, - 10, 7, 7, 5, 5, 6, 6, 7, 7, 10, - 10, 6, 7, 6, 5, 6, 4, 1, 2, 3, - 5, 7, 6, 3, 1, 3, 4, 6, 5, 1, - 2, 4, 4, 5, 5, 2, 3, 1, 3, 2, - 1, 2, 3, 3, 3, 4, 4, 4, 4, 4, - 1, 1, 1, 1, 0, 2, 1, 2, 2, 4, - 4, 3, 3, 1, 1, 2, 2, 3, 3, 4, - 1, 1, 2, + +#endif + +#if YYDEBUG != 0 +static const short yyrline[] = { 0, + 351, 360, 367, 382, 392, 394, 398, 403, 408, 413, + 418, 423, 428, 434, 440, 445, 450, 455, 460, 465, + 470, 475, 480, 485, 490, 497, 504, 509, 514, 519, + 524, 529, 534, 539, 544, 551, 553, 555, 559, 563, + 574, 576, 580, 582, 584, 600, 604, 606, 608, 610, + 612, 614, 616, 618, 620, 622, 624, 628, 633, 638, + 643, 648, 653, 658, 663, 670, 675, 680, 685, 692, + 697, 702, 707, 712, 717, 724, 729, 734, 741, 744, + 747, 752, 754, 785, 792, 794, 796, 801, 805, 809, + 813, 815, 817, 821, 822, 826, 828, 830, 832, 836, + 837, 841, 843, 852, 860, 861, 867, 868, 875, 879, + 881, 883, 890, 892, 894, 898, 899, 902, 904, 906, + 910, 911, 920, 926, 935, 943, 945, 947, 954, 957, + 961, 963, 969, 975, 981, 987, 1007, 1010, 1014, 1016 }; -const short yydefred[] = { 0, - 0, 115, 0, 0, 0, 115, 115, 0, 0, 0, - 0, 0, 35, 0, 85, 0, 0, 0, 0, 0, - 0, 0, 0, 2, 4, 0, 0, 115, 115, 0, - 131, 0, 124, 0, 0, 0, 0, 45, 49, 46, - 52, 53, 54, 55, 44, 50, 51, 36, 39, 0, - 3, 101, 0, 0, 115, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 133, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 11, 13, 19, 15, 27, - 21, 17, 25, 23, 29, 31, 32, 7, 8, 0, - 0, 115, 1, 115, 115, 0, 0, 34, 40, 37, - 0, 0, 0, 115, 116, 111, 100, 0, 0, 115, - 0, 115, 113, 112, 114, 115, 115, 115, 0, 115, - 115, 0, 0, 86, 0, 115, 12, 14, 20, 16, - 28, 22, 18, 26, 24, 30, 33, 9, 10, 84, - 80, 0, 0, 0, 0, 0, 38, 0, 0, 115, - 115, 115, 115, 115, 115, 0, 115, 0, 115, 0, - 0, 0, 0, 115, 0, 115, 0, 0, 115, 0, - 77, 0, 0, 120, 121, 0, 0, 115, 115, 81, - 0, 0, 0, 0, 0, 0, 0, 115, 0, 0, - 115, 115, 0, 5, 0, 0, 64, 65, 115, 115, - 115, 115, 0, 0, 0, 0, 47, 48, 0, 0, - 75, 0, 0, 83, 106, 107, 0, 0, 0, 96, - 0, 0, 74, 72, 98, 0, 0, 0, 0, 56, - 6, 115, 57, 0, 0, 0, 0, 66, 0, 115, - 67, 76, 82, 115, 115, 115, 115, 97, 73, 0, - 0, 115, 58, 59, 0, 62, 63, 68, 69, 0, - 0, 0, 0, 0, 115, 99, 92, 0, 115, 115, - 115, 115, 115, 89, 94, 0, 0, 0, 0, 0, - 88, 60, 61, 70, 71, +#endif + + +#if YYDEBUG != 0 || defined (YYERROR_VERBOSE) + +static const char * const yytname[] = { "$","error","$undefined.","IF","THEN", +"ELSE","ELIF","FI","CASE","ESAC","FOR","SELECT","WHILE","UNTIL","DO","DONE", +"FUNCTION","COND_START","COND_END","COND_ERROR","IN","BANG","TIME","TIMEOPT", +"WORD","ASSIGNMENT_WORD","NUMBER","ARITH_CMD","ARITH_FOR_EXPRS","COND_CMD","AND_AND", +"OR_OR","GREATER_GREATER","LESS_LESS","LESS_AND","LESS_LESS_LESS","GREATER_AND", +"SEMI_SEMI","LESS_LESS_MINUS","AND_GREATER","LESS_GREATER","GREATER_BAR","'&'", +"';'","'\\n'","yacc_EOF","'|'","'>'","'<'","'-'","'{'","'}'","'('","')'","inputunit", +"word_list","redirection","simple_command_element","redirection_list","simple_command", +"command","shell_command","for_command","arith_for_command","select_command", +"case_command","function_def","function_body","subshell","if_command","group_command", +"arith_command","cond_command","elif_clause","case_clause","pattern_list","case_clause_sequence", +"pattern","list","compound_list","list0","list1","simple_list_terminator","list_terminator", +"newline_list","simple_list","simple_list1","pipeline_command","pipeline","timespec", NULL }; -const short yydgoto[] = { 30, - 31, 32, 106, 52, 107, 108, 53, 34, 145, 36, - 37, 38, 39, 40, 41, 42, 43, 44, 45, 171, - 46, 181, 47, 48, 101, 49, 195, 227, 187, 188, - 189, 50, 54, 117, +#endif + +static const short yyr1[] = { 0, + 54, 54, 54, 54, 55, 55, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 57, 57, 57, 58, 58, + 59, 59, 60, 60, 60, 60, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 62, 62, 62, + 62, 62, 62, 62, 62, 63, 63, 63, 63, 64, + 64, 64, 64, 64, 64, 65, 65, 65, 66, 66, + 66, 67, 67, 68, 69, 69, 69, 70, 71, 72, + 73, 73, 73, 74, 74, 75, 75, 75, 75, 76, + 76, 77, 77, 78, 79, 79, 80, 80, 80, 81, + 81, 81, 81, 81, 81, 82, 82, 83, 83, 83, + 84, 84, 85, 85, 85, 86, 86, 86, 86, 86, + 87, 87, 87, 87, 87, 87, 88, 88, 89, 89 }; -const short yysindex[] = { -10, - 2, 0, -258, -255, -246, 0, 0, -237, -249, 846, - -226, 15, 0, 123, 0, -211, -203, -44, -43, -199, - -198, -194, -193, 0, 0, -192, -191, 0, 0, 0, - 0, -41, 0, 78, 23, 956, 975, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 721, - 0, 0, -169, 71, 0, 35, -6, 39, -177, -172, - 61, -168, -41, 933, 0, 62, -176, -173, -30, -27, - -166, -163, -162, -160, -159, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, -17, - 69, 0, 0, 0, 0, 813, 813, 0, 0, 0, - 975, 933, -41, 0, 0, 0, 0, 19, -7, 0, - 66, 0, 0, 0, 0, 0, 0, 0, 99, 0, - 0, 79, 305, 0, -41, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 104, 71, 71, -247, -247, 0, -41, -195, 0, - 0, 0, 0, 0, 0, 4, 0, -156, 0, -145, - 1, 7, 12, 0, -156, 0, -144, -140, 0, 975, - 0, 305, -41, 0, 0, 813, 813, 0, 0, 0, - -138, 71, 71, 71, 71, 71, -158, 0, -129, -4, - 0, 0, -132, 0, -5, 16, 0, 0, 0, 0, - 0, 0, -126, 71, -5, 20, 0, 0, 305, 975, - 0, -115, -111, 0, 0, 0, -240, -240, -240, 0, - -142, 32, 0, 0, 0, -128, -34, -120, 26, 0, - 0, 0, 0, -114, 36, -107, 38, 0, 19, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, -25, - -113, 0, 0, 0, 29, 0, 0, 0, 0, 37, - -201, 71, 71, 71, 0, 0, 0, 71, 0, 0, - 0, 0, 0, 0, 0, 71, -100, 45, -98, 48, - 0, 0, 0, 0, 0, + +static const short yyr2[] = { 0, + 2, 1, 2, 1, 1, 2, 2, 2, 3, 3, + 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, + 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, + 2, 3, 2, 2, 3, 1, 1, 1, 1, 2, + 1, 2, 1, 1, 2, 1, 1, 1, 5, 5, + 1, 1, 1, 1, 1, 1, 1, 6, 6, 7, + 7, 10, 10, 9, 9, 7, 7, 5, 5, 6, + 6, 7, 7, 10, 10, 6, 7, 6, 5, 6, + 4, 1, 2, 3, 5, 7, 6, 3, 1, 3, + 4, 6, 5, 1, 2, 4, 4, 5, 5, 2, + 3, 1, 3, 2, 1, 2, 3, 3, 3, 4, + 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, + 0, 2, 1, 2, 2, 4, 4, 3, 3, 1, + 1, 2, 2, 3, 3, 2, 4, 1, 1, 2 }; -const short yyrindex[] = { 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 888, 33, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 450, 0, 0, 164, 118, 162, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 126, 0, 126, 0, 0, - 216, 0, 462, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 165, 166, 0, 0, 0, - 174, 0, 495, 0, 0, 0, 0, 436, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 507, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 11, 18, 0, 540, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 403, - 0, 0, 553, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 573, 662, 765, -85, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, - 0, 0, 0, 0, 0, 0, 587, 599, 611, 0, - -84, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - -81, 0, 0, 0, 0, 0, 0, -250, 0, 0, - 0, 0, 0, 0, 0, -244, 0, 0, 0, 0, - 0, 0, 0, 0, 0, + +static const short yydefact[] = { 0, + 0, 121, 0, 0, 0, 121, 121, 0, 0, 0, + 139, 36, 37, 0, 89, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 4, 0, 0, 121, 121, + 38, 41, 43, 138, 44, 47, 57, 51, 48, 46, + 53, 52, 54, 55, 56, 0, 123, 130, 131, 0, + 3, 105, 0, 0, 121, 121, 0, 121, 0, 0, + 121, 0, 132, 0, 140, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 11, 13, 21, 17, + 29, 15, 23, 19, 27, 25, 31, 33, 34, 7, + 8, 0, 0, 36, 42, 39, 45, 116, 117, 1, + 121, 121, 124, 125, 121, 0, 119, 118, 120, 136, + 133, 121, 122, 104, 106, 115, 0, 121, 0, 121, + 121, 121, 121, 0, 121, 121, 0, 0, 90, 135, + 121, 12, 14, 22, 18, 30, 16, 24, 20, 28, + 26, 32, 35, 9, 10, 88, 84, 40, 0, 0, + 128, 129, 0, 134, 0, 121, 121, 121, 121, 121, + 121, 0, 121, 0, 121, 0, 0, 0, 0, 121, + 0, 121, 0, 0, 121, 82, 81, 0, 126, 127, + 0, 0, 137, 121, 121, 85, 0, 0, 0, 108, + 109, 107, 0, 94, 121, 0, 121, 121, 0, 5, + 0, 121, 0, 68, 69, 121, 121, 121, 121, 0, + 0, 0, 0, 49, 50, 0, 83, 79, 0, 0, + 87, 110, 111, 112, 113, 114, 78, 100, 95, 0, + 76, 102, 0, 0, 0, 0, 58, 6, 121, 0, + 59, 0, 0, 0, 0, 70, 0, 121, 71, 80, + 86, 121, 121, 121, 121, 101, 77, 0, 0, 121, + 60, 61, 0, 121, 121, 66, 67, 72, 73, 0, + 91, 0, 0, 0, 121, 103, 96, 97, 121, 121, + 0, 0, 121, 121, 121, 93, 98, 99, 0, 0, + 64, 65, 0, 0, 92, 62, 63, 74, 75, 0, + 0, 0 }; -const short yygindex[] = { 0, - 0, -2, 10, -133, 0, 340, 798, 0, 24, 0, - -112, 0, 0, 0, 0, 0, 0, 0, 0, -146, - 0, -80, 0, -28, 21, 152, 25, -33, 8, 0, - 0, 182, 1196, -170, + +static const short yydefgoto[] = { 300, + 201, 31, 32, 97, 33, 34, 35, 36, 37, 38, + 39, 40, 177, 41, 42, 43, 44, 45, 187, 193, + 194, 195, 234, 52, 53, 114, 115, 100, 110, 54, + 46, 151, 116, 49, 50 }; -#define YYTABLESIZE 1468 -const short yytable[] = { 24, - 80, 83, 105, 114, 114, 105, 252, 63, 100, 33, - 170, 51, 93, 105, 131, 265, 105, 134, 95, 55, - 122, 105, 56, 35, 232, 211, 57, 123, 154, 29, - 203, 58, 206, 62, 240, 226, 94, 95, 105, 93, - 61, 105, 34, 150, 151, 95, 105, 103, 122, 27, - 65, 26, 113, 113, 66, 123, 152, 273, 179, 170, - 96, 125, 242, 178, 179, 180, 76, 236, 237, 122, - 34, 226, 147, 34, 77, 105, 123, 153, 84, 85, - 105, 97, 92, 86, 87, 88, 89, 93, 104, 251, - 120, 34, 34, 110, 34, 121, 170, 118, 251, 148, - 122, 127, 126, 124, 128, 33, 33, 140, 105, 141, - 29, 135, 28, 105, 136, 137, 116, 138, 139, 169, - 146, 194, 214, 197, 207, 198, 192, 41, 208, 200, - 27, 220, 26, 223, 202, 115, 230, 279, 280, 173, - 233, 100, 238, 29, 241, 243, 244, 248, 253, 225, - 254, 270, 33, 33, 256, 41, 34, 34, 41, 272, - 257, 258, 259, 27, 266, 26, 174, 175, 282, 283, - 284, 42, 285, 117, 118, 119, 41, 90, 91, 87, - 274, 147, 75, 43, 74, 33, 33, 99, 159, 205, - 210, 64, 250, 28, 0, 221, 0, 0, 0, 42, - 146, 0, 42, 0, 0, 0, 0, 0, 0, 0, - 0, 43, 0, 0, 43, 0, 0, 0, 0, 0, - 42, 166, 0, 0, 0, 115, 28, 0, 0, 0, - 0, 0, 43, 78, 81, 79, 82, 0, 0, 0, - 0, 41, 41, 0, 0, 1, 2, 129, 115, 130, - 132, 3, 133, 4, 5, 6, 7, 0, 224, 8, - 9, 112, 0, 0, 10, 11, 155, 12, 13, 14, - 15, 191, 231, 225, 199, 16, 17, 18, 19, 201, - 20, 21, 22, 23, 25, 42, 42, 0, 115, 115, - 34, 34, 34, 34, 249, 34, 269, 43, 43, 0, - 34, 34, 150, 151, 271, 0, 94, 95, 0, 225, - 34, 34, 34, 0, 105, 0, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 2, 0, 0, - 0, 0, 3, 157, 4, 5, 6, 7, 115, 158, - 8, 9, 0, 0, 29, 10, 11, 0, 12, 13, - 14, 15, 0, 0, 0, 0, 16, 17, 18, 19, - 2, 20, 21, 22, 23, 3, 164, 4, 5, 6, - 7, 0, 165, 8, 9, 41, 41, 41, 41, 0, - 41, 12, 13, 14, 15, 41, 41, 0, 0, 16, - 17, 18, 19, 115, 20, 21, 22, 23, 0, 115, - 0, 41, 41, 0, 0, 0, 0, 41, 67, 68, - 69, 70, 78, 71, 0, 72, 73, 0, 0, 42, - 42, 42, 42, 0, 42, 79, 0, 28, 0, 42, - 42, 43, 43, 43, 43, 0, 43, 0, 0, 0, - 78, 43, 43, 78, 0, 42, 42, 0, 0, 0, - 0, 42, 0, 79, 0, 0, 79, 43, 43, 125, - 0, 78, 0, 43, 0, 0, 0, 0, 0, 0, - 0, 126, 115, 0, 79, 0, 102, 115, 0, 115, - 115, 115, 115, 0, 0, 0, 115, 125, 0, 0, - 125, 0, 0, 0, 0, 0, 115, 0, 0, 126, - 0, 0, 126, 0, 127, 0, 0, 0, 125, 0, - 0, 0, 0, 0, 0, 0, 129, 0, 0, 0, - 126, 215, 216, 217, 218, 219, 78, 78, 0, 0, - 0, 0, 127, 0, 0, 127, 0, 0, 0, 79, - 79, 0, 0, 239, 129, 0, 0, 129, 0, 128, - 0, 0, 0, 127, 0, 0, 0, 0, 0, 0, - 102, 2, 130, 0, 0, 129, 3, 0, 4, 5, - 6, 7, 0, 0, 125, 9, 0, 128, 0, 0, - 128, 0, 0, 0, 0, 15, 126, 0, 0, 0, - 130, 0, 0, 130, 0, 0, 108, 0, 128, 0, - 0, 217, 218, 219, 0, 0, 0, 0, 109, 0, - 0, 130, 0, 104, 0, 0, 0, 0, 0, 127, - 110, 0, 0, 0, 108, 0, 0, 108, 0, 0, - 0, 129, 0, 0, 0, 0, 109, 0, 0, 109, - 0, 0, 0, 0, 0, 108, 0, 0, 110, 0, - 0, 110, 0, 0, 0, 0, 0, 109, 0, 0, - 78, 78, 78, 78, 128, 78, 0, 0, 0, 110, - 78, 78, 0, 79, 79, 79, 79, 130, 79, 0, - 0, 0, 0, 79, 79, 0, 78, 78, 0, 0, - 0, 0, 78, 102, 102, 102, 102, 104, 102, 79, - 79, 0, 105, 102, 102, 79, 0, 125, 125, 125, - 125, 108, 125, 0, 0, 0, 0, 125, 125, 126, - 126, 126, 126, 109, 126, 102, 0, 0, 0, 126, - 126, 0, 0, 125, 125, 110, 0, 0, 0, 125, - 0, 0, 0, 0, 0, 126, 126, 0, 0, 0, - 0, 126, 127, 127, 127, 127, 0, 127, 0, 0, - 29, 0, 127, 127, 129, 129, 129, 129, 0, 129, - 0, 0, 0, 0, 129, 129, 0, 0, 127, 127, - 27, 0, 26, 0, 127, 0, 105, 0, 0, 0, - 129, 129, 0, 0, 0, 0, 129, 128, 128, 128, - 128, 0, 128, 59, 60, 103, 0, 128, 128, 0, - 130, 130, 130, 130, 0, 130, 0, 0, 0, 0, - 130, 130, 0, 128, 128, 90, 91, 0, 0, 128, - 104, 104, 104, 104, 0, 104, 130, 130, 0, 0, - 104, 104, 130, 28, 108, 108, 108, 108, 0, 108, - 0, 0, 29, 0, 108, 108, 109, 109, 109, 109, - 0, 109, 104, 0, 0, 0, 109, 109, 110, 110, - 110, 110, 27, 110, 26, 0, 108, 0, 110, 110, - 0, 0, 0, 0, 0, 29, 0, 0, 109, 103, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 110, 149, 0, 0, 0, 27, 0, 26, 0, 160, - 0, 0, 0, 161, 0, 0, 0, 167, 168, 105, - 105, 105, 105, 0, 105, 0, 0, 132, 0, 105, - 105, 0, 0, 0, 0, 28, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 132, 0, 132, - 0, 105, 0, 0, 193, 0, 196, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, - 0, 0, 29, 0, 0, 212, 213, 2, 0, 0, - 0, 0, 3, 0, 4, 5, 6, 7, 228, 229, - 8, 9, 27, 0, 26, 102, 234, 235, 12, 13, - 14, 15, 0, 0, 0, 0, 16, 17, 18, 19, - 132, 20, 21, 22, 23, 27, 0, 26, 0, 0, - 0, 0, 103, 103, 103, 103, 0, 103, 0, 0, - 0, 0, 103, 103, 27, 0, 26, 0, 0, 0, - 0, 261, 0, 0, 0, 0, 0, 0, 0, 267, - 0, 0, 0, 0, 103, 28, 0, 0, 0, 0, - 0, 0, 275, 0, 0, 0, 277, 278, 0, 2, - 281, 0, 0, 0, 3, 0, 4, 5, 6, 7, - 0, 0, 8, 9, 0, 0, 0, 10, 11, 0, - 12, 13, 14, 15, 0, 0, 0, 0, 16, 17, - 18, 19, 2, 20, 21, 22, 23, 3, 0, 4, - 5, 6, 7, 0, 0, 8, 9, 0, 0, 0, - 0, 11, 0, 12, 13, 14, 15, 0, 0, 0, - 0, 16, 17, 18, 19, 0, 20, 21, 22, 23, - 0, 0, 0, 0, 132, 0, 0, 0, 0, 132, - 0, 132, 132, 132, 132, 0, 0, 132, 132, 0, - 0, 0, 132, 0, 0, 132, 132, 132, 132, 0, - 0, 0, 0, 132, 132, 132, 132, 0, 132, 132, - 132, 132, 0, 0, 0, 0, 0, 0, 0, 2, - 0, 0, 0, 0, 3, 0, 4, 5, 6, 7, - 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, - 12, 13, 14, 15, 0, 0, 0, 0, 16, 17, - 18, 19, 0, 20, 21, 22, 23, 0, 0, 0, - 0, 0, 0, 98, 13, 14, 0, 0, 0, 0, - 0, 16, 17, 18, 19, 0, 20, 21, 22, 23, - 109, 111, 0, 119, 14, 0, 123, 0, 0, 0, - 16, 17, 18, 19, 0, 20, 21, 22, 23, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 142, 0, 143, - 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 156, 0, 0, 0, 0, - 0, 0, 162, 163, 0, 0, 0, 0, 0, 0, - 0, 172, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 182, 183, 184, 185, 186, - 190, 0, 0, 0, 0, 0, 0, 0, 0, 204, - 0, 204, 0, 0, 209, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 222, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 204, 204, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, - 0, 0, 0, 0, 0, 260, 0, 0, 0, 0, - 262, 263, 264, 0, 0, 0, 0, 268, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 276, 0, 0, 0, 0, 0, 204, 204, + +static const short yypact[] = { 274, + -23,-32768, 5, 55, 10,-32768,-32768, 21, 31, 446, + 32, 19,-32768, 553,-32768, 56, 72, -12, 89, -11, + 92, 102, 111, 113,-32768,-32768, 117, 124,-32768,-32768, +-32768,-32768, 183,-32768, 202,-32768,-32768,-32768,-32768,-32768, +-32768,-32768,-32768,-32768,-32768, -13, 134,-32768, 52, 317, +-32768,-32768, 148, 360,-32768, 115, 22, 120, 154, 173, + 114, 155, 52, 532,-32768, 140, 180, 181, 101, 182, + 112, 186, 188, 189, 190, 205,-32768,-32768,-32768,-32768, +-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, +-32768, 193, 195,-32768,-32768,-32768, 202,-32768,-32768,-32768, +-32768,-32768, 403, 403,-32768, 532,-32768,-32768,-32768,-32768, + 52,-32768,-32768,-32768, 221,-32768, -9,-32768, 42,-32768, +-32768,-32768,-32768, 61,-32768,-32768, 200, 41,-32768, 52, +-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, +-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, 360, 360, + 152, 152, 489, 52, 192,-32768,-32768,-32768,-32768,-32768, +-32768, 26,-32768, 127,-32768, 218, 194, 34, 73,-32768, + 215,-32768, 242, 245,-32768, 202,-32768, 41,-32768,-32768, + 403, 403, 52,-32768,-32768,-32768, 254, 360, 360, 360, + 360, 360, 253, 229,-32768, -2,-32768,-32768, 252,-32768, + 151,-32768, 217,-32768,-32768,-32768,-32768,-32768,-32768, 255, + 360, 151, 220,-32768,-32768, 41, 202,-32768, 265, 272, +-32768,-32768,-32768, 159, 159, 159,-32768,-32768, 241, 17, +-32768,-32768, 256, 36, 266, 232,-32768,-32768,-32768, 74, +-32768, 273, 238, 277, 243,-32768, 221,-32768,-32768,-32768, +-32768,-32768,-32768,-32768,-32768,-32768,-32768, 116, 269,-32768, +-32768,-32768, 90,-32768,-32768,-32768,-32768,-32768,-32768, 95, + 197, 360, 360, 360,-32768,-32768,-32768, 360,-32768,-32768, + 282, 251,-32768,-32768,-32768,-32768,-32768, 360, 288, 260, +-32768,-32768, 289, 280,-32768,-32768,-32768,-32768,-32768, 305, + 316,-32768 }; -const short yycheck[] = { 10, - 45, 45, 10, 10, 10, 10, 41, 10, 37, 0, - 123, 10, 263, 10, 45, 41, 10, 45, 263, 278, - 10, 10, 278, 0, 195, 172, 282, 10, 10, 40, - 164, 278, 166, 283, 205, 40, 284, 285, 10, 290, - 278, 10, 10, 284, 285, 290, 10, 50, 38, 60, - 277, 62, 59, 59, 40, 38, 38, 259, 260, 172, - 38, 64, 209, 259, 260, 261, 278, 201, 202, 59, - 38, 40, 101, 41, 278, 10, 59, 59, 278, 278, - 10, 59, 124, 278, 278, 278, 278, 10, 258, 124, - 268, 59, 60, 59, 62, 268, 209, 59, 124, 102, - 40, 278, 41, 272, 278, 96, 97, 125, 10, 41, - 40, 278, 123, 10, 278, 278, 123, 278, 278, 41, - 97, 278, 261, 269, 269, 125, 123, 10, 269, 123, - 60, 290, 62, 263, 123, 10, 269, 271, 272, 142, - 125, 170, 269, 40, 125, 261, 258, 290, 269, 278, - 125, 123, 143, 144, 269, 38, 124, 125, 41, 123, - 125, 269, 125, 60, 278, 62, 143, 144, 269, 125, - 269, 10, 125, 10, 10, 10, 59, 263, 263, 261, - 261, 210, 60, 10, 62, 176, 177, 36, 123, 165, - 170, 10, 226, 123, -1, 188, -1, -1, -1, 38, - 177, -1, 41, -1, -1, -1, -1, -1, -1, -1, - -1, 38, -1, -1, 41, -1, -1, -1, -1, -1, - 59, 123, -1, -1, -1, 10, 123, -1, -1, -1, - -1, -1, 59, 278, 278, 280, 280, -1, -1, -1, - -1, 124, 125, -1, -1, 256, 257, 278, 123, 280, - 278, 262, 280, 264, 265, 266, 267, -1, 263, 270, - 271, 268, -1, -1, 275, 276, 274, 278, 279, 280, - 281, 268, 278, 278, 268, 286, 287, 288, 289, 268, - 291, 292, 293, 294, 295, 124, 125, -1, 295, 295, - 258, 259, 260, 261, 263, 263, 268, 124, 125, -1, - 268, 269, 284, 285, 268, -1, 284, 285, -1, 278, - 278, 279, 280, -1, 10, -1, 284, 285, 286, 287, - 288, 289, 290, 291, 292, 293, 294, 257, -1, -1, - -1, -1, 262, 268, 264, 265, 266, 267, 123, 274, - 270, 271, -1, -1, 40, 275, 276, -1, 278, 279, - 280, 281, -1, -1, -1, -1, 286, 287, 288, 289, - 257, 291, 292, 293, 294, 262, 268, 264, 265, 266, - 267, -1, 274, 270, 271, 258, 259, 260, 261, -1, - 263, 278, 279, 280, 281, 268, 269, -1, -1, 286, - 287, 288, 289, 268, 291, 292, 293, 294, -1, 274, - -1, 284, 285, -1, -1, -1, -1, 290, 286, 287, - 288, 289, 10, 291, -1, 293, 294, -1, -1, 258, - 259, 260, 261, -1, 263, 10, -1, 123, -1, 268, - 269, 258, 259, 260, 261, -1, 263, -1, -1, -1, - 38, 268, 269, 41, -1, 284, 285, -1, -1, -1, - -1, 290, -1, 38, -1, -1, 41, 284, 285, 10, - -1, 59, -1, 290, -1, -1, -1, -1, -1, -1, - -1, 10, 257, -1, 59, -1, 41, 262, -1, 264, - 265, 266, 267, -1, -1, -1, 271, 38, -1, -1, - 41, -1, -1, -1, -1, -1, 281, -1, -1, 38, - -1, -1, 41, -1, 10, -1, -1, -1, 59, -1, - -1, -1, -1, -1, -1, -1, 10, -1, -1, -1, - 59, 182, 183, 184, 185, 186, 124, 125, -1, -1, - -1, -1, 38, -1, -1, 41, -1, -1, -1, 124, - 125, -1, -1, 204, 38, -1, -1, 41, -1, 10, - -1, -1, -1, 59, -1, -1, -1, -1, -1, -1, - 125, 257, 10, -1, -1, 59, 262, -1, 264, 265, - 266, 267, -1, -1, 125, 271, -1, 38, -1, -1, - 41, -1, -1, -1, -1, 281, 125, -1, -1, -1, - 38, -1, -1, 41, -1, -1, 10, -1, 59, -1, - -1, 262, 263, 264, -1, -1, -1, -1, 10, -1, - -1, 59, -1, 41, -1, -1, -1, -1, -1, 125, - 10, -1, -1, -1, 38, -1, -1, 41, -1, -1, - -1, 125, -1, -1, -1, -1, 38, -1, -1, 41, - -1, -1, -1, -1, -1, 59, -1, -1, 38, -1, - -1, 41, -1, -1, -1, -1, -1, 59, -1, -1, - 258, 259, 260, 261, 125, 263, -1, -1, -1, 59, - 268, 269, -1, 258, 259, 260, 261, 125, 263, -1, - -1, -1, -1, 268, 269, -1, 284, 285, -1, -1, - -1, -1, 290, 258, 259, 260, 261, 125, 263, 284, - 285, -1, 41, 268, 269, 290, -1, 258, 259, 260, - 261, 125, 263, -1, -1, -1, -1, 268, 269, 258, - 259, 260, 261, 125, 263, 290, -1, -1, -1, 268, - 269, -1, -1, 284, 285, 125, -1, -1, -1, 290, - -1, -1, -1, -1, -1, 284, 285, -1, -1, -1, - -1, 290, 258, 259, 260, 261, -1, 263, -1, -1, - 40, -1, 268, 269, 258, 259, 260, 261, -1, 263, - -1, -1, -1, -1, 268, 269, -1, -1, 284, 285, - 60, -1, 62, -1, 290, -1, 125, -1, -1, -1, - 284, 285, -1, -1, -1, -1, 290, 258, 259, 260, - 261, -1, 263, 6, 7, 41, -1, 268, 269, -1, - 258, 259, 260, 261, -1, 263, -1, -1, -1, -1, - 268, 269, -1, 284, 285, 28, 29, -1, -1, 290, - 258, 259, 260, 261, -1, 263, 284, 285, -1, -1, - 268, 269, 290, 123, 258, 259, 260, 261, -1, 263, - -1, -1, 40, -1, 268, 269, 258, 259, 260, 261, - -1, 263, 290, -1, -1, -1, 268, 269, 258, 259, - 260, 261, 60, 263, 62, -1, 290, -1, 268, 269, - -1, -1, -1, -1, -1, 40, -1, -1, 290, 125, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 290, 104, -1, -1, -1, 60, -1, 62, -1, 112, - -1, -1, -1, 116, -1, -1, -1, 120, 121, 258, - 259, 260, 261, -1, 263, -1, -1, 40, -1, 268, - 269, -1, -1, -1, -1, 123, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 60, -1, 62, - -1, 290, -1, -1, 157, -1, 159, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 123, -1, - -1, -1, 40, -1, -1, 178, 179, 257, -1, -1, - -1, -1, 262, -1, 264, 265, 266, 267, 191, 192, - 270, 271, 60, -1, 62, 275, 199, 200, 278, 279, - 280, 281, -1, -1, -1, -1, 286, 287, 288, 289, - 123, 291, 292, 293, 294, 60, -1, 62, -1, -1, - -1, -1, 258, 259, 260, 261, -1, 263, -1, -1, - -1, -1, 268, 269, 60, -1, 62, -1, -1, -1, - -1, 244, -1, -1, -1, -1, -1, -1, -1, 252, - -1, -1, -1, -1, 290, 123, -1, -1, -1, -1, - -1, -1, 265, -1, -1, -1, 269, 270, -1, 257, - 273, -1, -1, -1, 262, -1, 264, 265, 266, 267, - -1, -1, 270, 271, -1, -1, -1, 275, 276, -1, - 278, 279, 280, 281, -1, -1, -1, -1, 286, 287, - 288, 289, 257, 291, 292, 293, 294, 262, -1, 264, - 265, 266, 267, -1, -1, 270, 271, -1, -1, -1, - -1, 276, -1, 278, 279, 280, 281, -1, -1, -1, - -1, 286, 287, 288, 289, -1, 291, 292, 293, 294, - -1, -1, -1, -1, 257, -1, -1, -1, -1, 262, - -1, 264, 265, 266, 267, -1, -1, 270, 271, -1, - -1, -1, 275, -1, -1, 278, 279, 280, 281, -1, - -1, -1, -1, 286, 287, 288, 289, -1, 291, 292, - 293, 294, -1, -1, -1, -1, -1, -1, -1, 257, - -1, -1, -1, -1, 262, -1, 264, 265, 266, 267, - -1, -1, 270, 271, -1, -1, -1, -1, -1, -1, - 278, 279, 280, 281, -1, -1, -1, -1, 286, 287, - 288, 289, -1, 291, 292, 293, 294, -1, -1, -1, - -1, -1, -1, 278, 279, 280, -1, -1, -1, -1, - -1, 286, 287, 288, 289, -1, 291, 292, 293, 294, - 55, 56, -1, 58, 280, -1, 61, -1, -1, -1, - 286, 287, 288, 289, -1, 291, 292, 293, 294, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 92, -1, 94, - 95, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 110, -1, -1, -1, -1, - -1, -1, 117, 118, -1, -1, -1, -1, -1, -1, - -1, 126, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 150, 151, 152, 153, 154, - 155, -1, -1, -1, -1, -1, -1, -1, -1, 164, - -1, 166, -1, -1, 169, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 188, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 201, 202, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 232, -1, -1, - -1, -1, -1, -1, -1, 240, -1, -1, -1, -1, - 245, 246, 247, -1, -1, -1, -1, 252, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 265, -1, -1, -1, -1, -1, 271, 272, + +static const short yypgoto[] = {-32768, + 146, -33, 290, 156,-32768,-32768, -119,-32768,-32768,-32768, +-32768,-32768, -153,-32768,-32768,-32768,-32768,-32768, 64,-32768, + 141,-32768, 104, -162, -6,-32768, -172,-32768, -52, -28, +-32768, 6, 4, -7, 329 }; -#define YYFINAL 30 -#ifndef YYDEBUG -#define YYDEBUG 0 -#endif -#define YYMAXTOKEN 295 -#if YYDEBUG -const char * const yyname[] = { -"end-of-file",0,0,0,0,0,0,0,0,0,"'\\n'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,"'&'",0,"'('","')'",0,0,0,"'-'",0,0,0,0,0,0,0,0,0,0,0,0,0,"';'", -"'<'",0,"'>'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'{'","'|'","'}'",0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,"IF","THEN","ELSE","ELIF","FI","CASE","ESAC","FOR","SELECT", -"WHILE","UNTIL","DO","DONE","FUNCTION","COND_START","COND_END","COND_ERROR", -"IN","BANG","TIME","TIMEOPT","WORD","ASSIGNMENT_WORD","NUMBER","ARITH_CMD", -"ARITH_FOR_EXPRS","COND_CMD","AND_AND","OR_OR","GREATER_GREATER","LESS_LESS", -"LESS_AND","GREATER_AND","SEMI_SEMI","LESS_LESS_MINUS","AND_GREATER", -"LESS_GREATER","GREATER_BAR","yacc_EOF", + + +#define YYLAST 601 + + +static const short yytable[] = { 59, + 60, 96, 63, 48, 122, 47, 231, 210, 176, 213, + 161, 79, 83, 80, 84, 222, 223, 224, 225, 226, + 51, 232, 92, 93, 218, 257, 117, 119, 55, 124, + 98, 99, 128, 58, 113, 120, 81, 85, 247, 197, + 232, 113, 111, 2, 61, 244, 245, 206, 3, 233, + 4, 5, 6, 7, 65, 163, 130, 9, 176, 62, + 113, 164, 250, 148, 107, 108, 109, 15, 233, 113, + 66, 121, 149, 150, 170, 198, 153, 113, 56, 77, + 171, 259, 57, 207, 113, 113, 208, 264, 260, 162, + 29, 165, 30, 168, 169, 78, 176, 105, 154, 224, + 225, 226, 178, 279, 113, 155, 48, 48, 283, 152, + 172, 202, 82, 166, 167, 86, 113, 113, 173, 174, + 293, 294, 209, 265, 134, 87, 135, 188, 189, 190, + 191, 192, 196, 113, 88, 138, 89, 139, 113, 280, + 90, 211, 96, 211, 284, 183, 216, 91, 239, 136, + 200, 112, 48, 48, 179, 180, 199, 118, 203, 248, + 140, 259, 123, 101, 102, 127, 230, 125, 275, 107, + 108, 109, 129, 240, 238, 103, 104, 219, 220, 211, + 211, 101, 102, 148, 48, 48, 126, 152, 156, 157, + 235, 236, 131, 107, 108, 109, 184, 185, 186, 242, + 243, 285, 185, 132, 133, 137, 94, 13, 14, 141, + 263, 142, 143, 144, 16, 17, 18, 19, 20, 270, + 21, 22, 23, 24, 272, 273, 274, 14, 145, 27, + 28, 278, 204, 16, 17, 18, 19, 20, 200, 21, + 22, 23, 24, 146, 205, 271, 288, 147, 27, 28, + 156, 157, 175, 277, 211, 211, 214, 281, 282, 215, + 221, 227, 158, 159, 160, 228, 237, 241, 287, 246, + 249, 251, 289, 290, 1, 252, 2, 256, 295, 232, + 261, 3, 262, 4, 5, 6, 7, 266, 267, 8, + 9, 268, 276, 269, 10, 11, 291, 12, 13, 14, + 15, 292, 296, 298, 301, 16, 17, 18, 19, 20, + 297, 21, 22, 23, 24, 302, 212, 25, 26, 2, + 27, 28, 95, 29, 3, 30, 4, 5, 6, 7, + 299, 217, 8, 9, 286, 229, 258, 106, 64, 0, + 12, 13, 14, 15, 0, 0, 0, 0, 16, 17, + 18, 19, 20, 0, 21, 22, 23, 24, 0, 107, + 108, 109, 2, 27, 28, 0, 29, 3, 30, 4, + 5, 6, 7, 0, 0, 8, 9, 0, 0, 0, + 10, 11, 0, 12, 13, 14, 15, 0, 0, 0, + 0, 16, 17, 18, 19, 20, 0, 21, 22, 23, + 24, 0, 0, 113, 0, 2, 27, 28, 0, 29, + 3, 30, 4, 5, 6, 7, 0, 0, 8, 9, + 0, 0, 0, 10, 11, 0, 12, 13, 14, 15, + 0, 0, 0, 0, 16, 17, 18, 19, 20, 0, + 21, 22, 23, 24, 0, 0, 0, 0, 2, 27, + 28, 0, 29, 3, 30, 4, 5, 6, 7, 0, + 0, 8, 9, 0, 0, 0, 0, 11, 0, 12, + 13, 14, 15, 0, 0, 0, 0, 16, 17, 18, + 19, 20, 0, 21, 22, 23, 24, 0, 0, 0, + 0, 2, 27, 28, 0, 29, 3, 30, 4, 5, + 6, 7, 0, 0, 8, 9, 0, 0, 0, 0, + 0, 0, 12, 13, 14, 15, 0, 0, 0, 0, + 16, 17, 18, 19, 20, 0, 21, 22, 23, 24, + 0, 0, 113, 0, 2, 27, 28, 0, 29, 3, + 30, 4, 5, 6, 7, 0, 0, 8, 9, 0, + 0, 0, 0, 0, 0, 12, 13, 14, 15, 0, + 0, 0, 0, 16, 17, 18, 19, 20, 0, 21, + 22, 23, 24, 0, 0, 0, 0, 0, 27, 28, + 0, 29, 0, 30, 67, 68, 69, 70, 71, 0, + 72, 0, 73, 74, 0, 0, 0, 0, 0, 75, + 76 }; -const char * const yyrule[] = { -"$accept : inputunit", -"inputunit : simple_list '\\n'", -"inputunit : '\\n'", -"inputunit : error '\\n'", -"inputunit : yacc_EOF", -"word_list : WORD", -"word_list : word_list WORD", -"redirection : '>' WORD", -"redirection : '<' WORD", -"redirection : NUMBER '>' WORD", -"redirection : NUMBER '<' WORD", -"redirection : GREATER_GREATER WORD", -"redirection : NUMBER GREATER_GREATER WORD", -"redirection : LESS_LESS WORD", -"redirection : NUMBER LESS_LESS WORD", -"redirection : LESS_AND NUMBER", -"redirection : NUMBER LESS_AND NUMBER", -"redirection : GREATER_AND NUMBER", -"redirection : NUMBER GREATER_AND NUMBER", -"redirection : LESS_AND WORD", -"redirection : NUMBER LESS_AND WORD", -"redirection : GREATER_AND WORD", -"redirection : NUMBER GREATER_AND WORD", -"redirection : LESS_LESS_MINUS WORD", -"redirection : NUMBER LESS_LESS_MINUS WORD", -"redirection : GREATER_AND '-'", -"redirection : NUMBER GREATER_AND '-'", -"redirection : LESS_AND '-'", -"redirection : NUMBER LESS_AND '-'", -"redirection : AND_GREATER WORD", -"redirection : NUMBER LESS_GREATER WORD", -"redirection : LESS_GREATER WORD", -"redirection : GREATER_BAR WORD", -"redirection : NUMBER GREATER_BAR WORD", -"simple_command_element : WORD", -"simple_command_element : ASSIGNMENT_WORD", -"simple_command_element : redirection", -"redirection_list : redirection", -"redirection_list : redirection_list redirection", -"simple_command : simple_command_element", -"simple_command : simple_command simple_command_element", -"command : simple_command", -"command : shell_command", -"command : shell_command redirection_list", -"command : function_def", -"shell_command : for_command", -"shell_command : case_command", -"shell_command : WHILE compound_list DO compound_list DONE", -"shell_command : UNTIL compound_list DO compound_list DONE", -"shell_command : select_command", -"shell_command : if_command", -"shell_command : subshell", -"shell_command : group_command", -"shell_command : arith_command", -"shell_command : cond_command", -"shell_command : arith_for_command", -"for_command : FOR WORD newline_list DO compound_list DONE", -"for_command : FOR WORD newline_list '{' compound_list '}'", -"for_command : FOR WORD ';' newline_list DO compound_list DONE", -"for_command : FOR WORD ';' newline_list '{' compound_list '}'", -"for_command : FOR WORD newline_list IN word_list list_terminator newline_list DO compound_list DONE", -"for_command : FOR WORD newline_list IN word_list list_terminator newline_list '{' compound_list '}'", -"arith_for_command : FOR ARITH_FOR_EXPRS list_terminator newline_list DO compound_list DONE", -"arith_for_command : FOR ARITH_FOR_EXPRS list_terminator newline_list '{' compound_list '}'", -"arith_for_command : FOR ARITH_FOR_EXPRS DO compound_list DONE", -"arith_for_command : FOR ARITH_FOR_EXPRS '{' compound_list '}'", -"select_command : SELECT WORD newline_list DO list DONE", -"select_command : SELECT WORD newline_list '{' list '}'", -"select_command : SELECT WORD ';' newline_list DO list DONE", -"select_command : SELECT WORD ';' newline_list '{' list '}'", -"select_command : SELECT WORD newline_list IN word_list list_terminator newline_list DO list DONE", -"select_command : SELECT WORD newline_list IN word_list list_terminator newline_list '{' list '}'", -"case_command : CASE WORD newline_list IN newline_list ESAC", -"case_command : CASE WORD newline_list IN case_clause_sequence newline_list ESAC", -"case_command : CASE WORD newline_list IN case_clause ESAC", -"function_def : WORD '(' ')' newline_list function_body", -"function_def : FUNCTION WORD '(' ')' newline_list function_body", -"function_def : FUNCTION WORD newline_list function_body", -"function_body : shell_command", -"function_body : shell_command redirection_list", -"subshell : '(' compound_list ')'", -"if_command : IF compound_list THEN compound_list FI", -"if_command : IF compound_list THEN compound_list ELSE compound_list FI", -"if_command : IF compound_list THEN compound_list elif_clause FI", -"group_command : '{' compound_list '}'", -"arith_command : ARITH_CMD", -"cond_command : COND_START COND_CMD COND_END", -"elif_clause : ELIF compound_list THEN compound_list", -"elif_clause : ELIF compound_list THEN compound_list ELSE compound_list", -"elif_clause : ELIF compound_list THEN compound_list elif_clause", -"case_clause : pattern_list", -"case_clause : case_clause_sequence pattern_list", -"pattern_list : newline_list pattern ')' compound_list", -"pattern_list : newline_list pattern ')' newline_list", -"pattern_list : newline_list '(' pattern ')' compound_list", -"pattern_list : newline_list '(' pattern ')' newline_list", -"case_clause_sequence : pattern_list SEMI_SEMI", -"case_clause_sequence : case_clause_sequence pattern_list SEMI_SEMI", -"pattern : WORD", -"pattern : pattern '|' WORD", -"list : newline_list list0", -"compound_list : list", -"compound_list : newline_list list1", -"list0 : list1 '\\n' newline_list", -"list0 : list1 '&' newline_list", -"list0 : list1 ';' newline_list", -"list1 : list1 AND_AND newline_list list1", -"list1 : list1 OR_OR newline_list list1", -"list1 : list1 '&' newline_list list1", -"list1 : list1 ';' newline_list list1", -"list1 : list1 '\\n' newline_list list1", -"list1 : pipeline_command", -"list_terminator : '\\n'", -"list_terminator : ';'", -"list_terminator : yacc_EOF", -"newline_list :", -"newline_list : newline_list '\\n'", -"simple_list : simple_list1", -"simple_list : simple_list1 '&'", -"simple_list : simple_list1 ';'", -"simple_list1 : simple_list1 AND_AND newline_list simple_list1", -"simple_list1 : simple_list1 OR_OR newline_list simple_list1", -"simple_list1 : simple_list1 '&' simple_list1", -"simple_list1 : simple_list1 ';' simple_list1", -"simple_list1 : pipeline_command", -"pipeline_command : pipeline", -"pipeline_command : BANG pipeline", -"pipeline_command : timespec pipeline", -"pipeline_command : timespec BANG pipeline", -"pipeline_command : BANG timespec pipeline", -"pipeline : pipeline '|' newline_list pipeline", -"pipeline : command", -"timespec : TIME", -"timespec : TIME TIMEOPT", + +static const short yycheck[] = { 6, + 7, 35, 10, 0, 57, 0, 9, 170, 128, 172, + 20, 24, 24, 26, 26, 188, 189, 190, 191, 192, + 44, 24, 29, 30, 178, 9, 55, 56, 24, 58, + 44, 45, 61, 24, 44, 14, 49, 49, 211, 14, + 24, 44, 50, 3, 24, 208, 209, 14, 8, 52, + 10, 11, 12, 13, 23, 14, 64, 17, 178, 29, + 44, 20, 216, 97, 43, 44, 45, 27, 52, 44, + 52, 50, 101, 102, 14, 50, 105, 44, 24, 24, + 20, 46, 28, 50, 44, 44, 14, 14, 53, 118, + 50, 50, 52, 122, 123, 24, 216, 46, 106, 272, + 273, 274, 131, 14, 44, 112, 103, 104, 14, 104, + 50, 164, 24, 120, 121, 24, 44, 44, 125, 126, + 283, 284, 50, 50, 24, 24, 26, 156, 157, 158, + 159, 160, 161, 44, 24, 24, 24, 26, 44, 50, + 24, 170, 176, 172, 50, 153, 175, 24, 201, 49, + 24, 4, 149, 150, 149, 150, 163, 43, 165, 212, + 49, 46, 43, 30, 31, 52, 195, 14, 53, 43, + 44, 45, 18, 202, 24, 42, 43, 184, 185, 208, + 209, 30, 31, 217, 181, 182, 14, 182, 30, 31, + 197, 198, 53, 43, 44, 45, 5, 6, 7, 206, + 207, 5, 6, 24, 24, 24, 24, 25, 26, 24, + 239, 24, 24, 24, 32, 33, 34, 35, 36, 248, + 38, 39, 40, 41, 253, 254, 255, 26, 24, 47, + 48, 260, 15, 32, 33, 34, 35, 36, 24, 38, + 39, 40, 41, 51, 51, 252, 275, 53, 47, 48, + 30, 31, 53, 260, 283, 284, 15, 264, 265, 15, + 7, 9, 42, 43, 44, 37, 15, 51, 275, 15, + 51, 7, 279, 280, 1, 4, 3, 37, 285, 24, + 15, 8, 51, 10, 11, 12, 13, 15, 51, 16, + 17, 15, 24, 51, 21, 22, 15, 24, 25, 26, + 27, 51, 15, 15, 0, 32, 33, 34, 35, 36, + 51, 38, 39, 40, 41, 0, 171, 44, 45, 3, + 47, 48, 33, 50, 8, 52, 10, 11, 12, 13, + 51, 176, 16, 17, 271, 195, 233, 21, 10, -1, + 24, 25, 26, 27, -1, -1, -1, -1, 32, 33, + 34, 35, 36, -1, 38, 39, 40, 41, -1, 43, + 44, 45, 3, 47, 48, -1, 50, 8, 52, 10, + 11, 12, 13, -1, -1, 16, 17, -1, -1, -1, + 21, 22, -1, 24, 25, 26, 27, -1, -1, -1, + -1, 32, 33, 34, 35, 36, -1, 38, 39, 40, + 41, -1, -1, 44, -1, 3, 47, 48, -1, 50, + 8, 52, 10, 11, 12, 13, -1, -1, 16, 17, + -1, -1, -1, 21, 22, -1, 24, 25, 26, 27, + -1, -1, -1, -1, 32, 33, 34, 35, 36, -1, + 38, 39, 40, 41, -1, -1, -1, -1, 3, 47, + 48, -1, 50, 8, 52, 10, 11, 12, 13, -1, + -1, 16, 17, -1, -1, -1, -1, 22, -1, 24, + 25, 26, 27, -1, -1, -1, -1, 32, 33, 34, + 35, 36, -1, 38, 39, 40, 41, -1, -1, -1, + -1, 3, 47, 48, -1, 50, 8, 52, 10, 11, + 12, 13, -1, -1, 16, 17, -1, -1, -1, -1, + -1, -1, 24, 25, 26, 27, -1, -1, -1, -1, + 32, 33, 34, 35, 36, -1, 38, 39, 40, 41, + -1, -1, 44, -1, 3, 47, 48, -1, 50, 8, + 52, 10, 11, 12, 13, -1, -1, 16, 17, -1, + -1, -1, -1, -1, -1, 24, 25, 26, 27, -1, + -1, -1, -1, 32, 33, 34, 35, 36, -1, 38, + 39, 40, 41, -1, -1, -1, -1, -1, 47, 48, + -1, 50, -1, 52, 32, 33, 34, 35, 36, -1, + 38, -1, 40, 41, -1, -1, -1, -1, -1, 47, + 48 }; +/* -*-C-*- Note some compilers choke on comments on `#line' lines. */ +#line 3 "/usr/share/bison.simple" +/* This file comes from bison-1.28. */ + +/* Skeleton output parser for bison, + Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc. + + This program 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 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* This is the parser code that is written into each bison parser + when the %semantic_parser declaration is not specified in the grammar. + It was written by Richard Stallman by simplifying the hairy parser + used when %semantic_parser is specified. */ + +#ifndef YYSTACK_USE_ALLOCA +#ifdef alloca +#define YYSTACK_USE_ALLOCA +#else /* alloca not defined */ +#ifdef __GNUC__ +#define YYSTACK_USE_ALLOCA +#define alloca __builtin_alloca +#else /* not GNU C. */ +#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) || (defined (__sun) && defined (__i386)) +#define YYSTACK_USE_ALLOCA +#include +#else /* not sparc */ +/* We think this test detects Watcom and Microsoft C. */ +/* This used to test MSDOS, but that is a bad idea + since that symbol is in the user namespace. */ +#if (defined (_MSDOS) || defined (_MSDOS_)) && !defined (__TURBOC__) +#if 0 /* No need for malloc.h, which pollutes the namespace; + instead, just don't use alloca. */ +#include #endif -#if YYDEBUG -#include +#else /* not MSDOS, or __TURBOC__ */ +#if defined(_AIX) +/* I don't know what this was needed for, but it pollutes the namespace. + So I turned it off. rms, 2 May 1997. */ +/* #include */ + #pragma alloca +#define YYSTACK_USE_ALLOCA +#else /* not MSDOS, or __TURBOC__, or _AIX */ +#if 0 +#ifdef __hpux /* haible@ilog.fr says this works for HPUX 9.05 and up, + and on HPUX 10. Eventually we can turn this on. */ +#define YYSTACK_USE_ALLOCA +#define alloca __builtin_alloca +#endif /* __hpux */ #endif -#ifdef YYSTACKSIZE -#undef YYMAXDEPTH -#define YYMAXDEPTH YYSTACKSIZE +#endif /* not _AIX */ +#endif /* not MSDOS, or __TURBOC__ */ +#endif /* not sparc */ +#endif /* not GNU C */ +#endif /* alloca not defined */ +#endif /* YYSTACK_USE_ALLOCA not defined */ + +#ifdef YYSTACK_USE_ALLOCA +#define YYSTACK_ALLOC alloca #else -#ifdef YYMAXDEPTH -#define YYSTACKSIZE YYMAXDEPTH +#define YYSTACK_ALLOC malloc +#endif + +/* Note: there must be only one dollar sign in this file. + It is replaced by the list of actions, each action + as one case of the switch. */ + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY -2 +#define YYEOF 0 +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrlab1 +/* Like YYERROR except do call yyerror. + This remains here temporarily to ease the + transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ +#define YYFAIL goto yyerrlab +#define YYRECOVERING() (!!yyerrstatus) +#define YYBACKUP(token, value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { yychar = (token), yylval = (value); \ + yychar1 = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { yyerror ("syntax error: cannot back up"); YYERROR; } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + +#ifndef YYPURE +#define YYLEX yylex() +#endif + +#ifdef YYPURE +#ifdef YYLSP_NEEDED +#ifdef YYLEX_PARAM +#define YYLEX yylex(&yylval, &yylloc, YYLEX_PARAM) #else -#define YYSTACKSIZE 10000 -#define YYMAXDEPTH 10000 +#define YYLEX yylex(&yylval, &yylloc) +#endif +#else /* not YYLSP_NEEDED */ +#ifdef YYLEX_PARAM +#define YYLEX yylex(&yylval, YYLEX_PARAM) +#else +#define YYLEX yylex(&yylval) #endif +#endif /* not YYLSP_NEEDED */ #endif -#define YYINITSTACKSIZE 200 -int yydebug; -int yynerrs; -int yyerrflag; -int yychar; -short *yyssp; -YYSTYPE *yyvsp; -YYSTYPE yyval; -YYSTYPE yylval; -short *yyss; -short *yysslim; -YYSTYPE *yyvs; -int yystacksize; -#line 815 "/usr/homes/chet/src/bash/src/parse.y" -/* Possible states for the parser that require it to do special things. */ -#define PST_CASEPAT 0x001 /* in a case pattern list */ -#define PST_ALEXPNEXT 0x002 /* expand next word for aliases */ -#define PST_ALLOWOPNBRC 0x004 /* allow open brace for function def */ -#define PST_NEEDCLOSBRC 0x008 /* need close brace */ -#define PST_DBLPAREN 0x010 /* double-paren parsing */ -#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 [[...]] */ -#define PST_ARITHFOR 0x400 /* parsing an arithmetic for command */ +/* If nonreentrant, generate the variables here */ -/* Initial size to allocate for tokens, and the - amount to grow them by. */ -#define TOKEN_DEFAULT_INITIAL_SIZE 496 -#define TOKEN_DEFAULT_GROW_SIZE 512 +#ifndef YYPURE -/* The token currently being read. */ -static int current_token; +int yychar; /* the lookahead symbol */ +YYSTYPE yylval; /* the semantic value of the */ + /* lookahead symbol */ -/* The last read token, or NULL. read_token () uses this for context - checking. */ -static int last_read_token; +#ifdef YYLSP_NEEDED +YYLTYPE yylloc; /* location data for the lookahead */ + /* symbol */ +#endif -/* The token read prior to last_read_token. */ -static int token_before_that; +int yynerrs; /* number of parse errors so far */ +#endif /* not YYPURE */ -/* The token read prior to token_before_that. */ -static int two_tokens_ago; +#if YYDEBUG != 0 +int yydebug; /* nonzero means print parse trace */ +/* Since this is uninitialized, it does not stop multiple parsers + from coexisting. */ +#endif -/* If non-zero, it is the token that we want read_token to return - regardless of what text is (or isn't) present to be read. This - is reset by read_token. If token_to_read == WORD or - ASSIGNMENT_WORD, yylval.word should be set to word_desc_to_read. */ -static int token_to_read; -static WORD_DESC *word_desc_to_read; +/* YYINITDEPTH indicates the initial size of the parser's stacks */ -/* The current parser state. */ -static int parser_state; +#ifndef YYINITDEPTH +#define YYINITDEPTH 200 +#endif -/* Global var is non-zero when end of file has been reached. */ -int EOF_Reached = 0; +/* YYMAXDEPTH is the maximum size the stacks can grow to + (effective only if the built-in stack extension method is used). */ -void -debug_parser (i) - int i; -{ -#if YYDEBUG != 0 - yydebug = i; +#if YYMAXDEPTH == 0 +#undef YYMAXDEPTH #endif -} - -/* yy_getc () returns the next available character from input or EOF. - yy_ungetc (c) makes `c' the next character to read. - init_yy_io (get, unget, type, location) makes the function GET the - installed function for getting the next character, makes UNGET the - installed function for un-getting a character, sets the type of stream - (either string or file) from TYPE, and makes LOCATION point to where - the input is coming from. */ -/* Unconditionally returns end-of-file. */ -int -return_EOF () +#ifndef YYMAXDEPTH +#define YYMAXDEPTH 10000 +#endif + +/* Define __yy_memcpy. Note that the size argument + should be passed with type unsigned int, because that is what the non-GCC + definitions require. With GCC, __builtin_memcpy takes an arg + of type size_t, but it can handle unsigned int. */ + +#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */ +#define __yy_memcpy(TO,FROM,COUNT) __builtin_memcpy(TO,FROM,COUNT) +#else /* not GNU C or C++ */ +#ifndef __cplusplus + +/* This is the most reliable way to avoid incompatibilities + in available built-in functions on various systems. */ +static void +__yy_memcpy (to, from, count) + char *to; + char *from; + unsigned int count; { - return (EOF); + register char *f = from; + register char *t = to; + register int i = count; + + while (i-- > 0) + *t++ = *f++; } -/* Variable containing the current get and unget functions. - See ./input.h for a clearer description. */ -BASH_INPUT bash_input; +#else /* __cplusplus */ -/* Set all of the fields in BASH_INPUT to NULL. Free bash_input.name if it - is non-null, avoiding a memory leak. */ -void -initialize_bash_input () +/* This is the most reliable way to avoid incompatibilities + in available built-in functions on various systems. */ +static void +__yy_memcpy (char *to, char *from, unsigned int count) { - bash_input.type = st_none; - FREE (bash_input.name); - bash_input.name = (char *)NULL; - bash_input.location.file = (FILE *)NULL; - bash_input.location.string = (char *)NULL; - bash_input.getter = (Function *)NULL; - bash_input.ungetter = (Function *)NULL; + register char *t = to; + register char *f = from; + register int i = count; + + while (i-- > 0) + *t++ = *f++; } -/* Set the contents of the current bash input stream from - GET, UNGET, TYPE, NAME, and LOCATION. */ -void -init_yy_io (get, unget, type, name, location) - Function *get, *unget; - enum stream_type type; - char *name; - INPUT_STREAM location; -{ - bash_input.type = type; - FREE (bash_input.name); - bash_input.name = name ? savestring (name) : (char *)NULL; +#endif +#endif + +#line 217 "/usr/share/bison.simple" + +/* The user can define YYPARSE_PARAM as the name of an argument to be passed + into yyparse. The argument should have type void *. + It should actually point to an object. + Grammar actions can access the variable by casting it + to the proper pointer type. */ + +#ifdef YYPARSE_PARAM +#ifdef __cplusplus +#define YYPARSE_PARAM_ARG void *YYPARSE_PARAM +#define YYPARSE_PARAM_DECL +#else /* not __cplusplus */ +#define YYPARSE_PARAM_ARG YYPARSE_PARAM +#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM; +#endif /* not __cplusplus */ +#else /* not YYPARSE_PARAM */ +#define YYPARSE_PARAM_ARG +#define YYPARSE_PARAM_DECL +#endif /* not YYPARSE_PARAM */ - /* XXX */ -#if defined (CRAY) - memcpy((char *)&bash_input.location.string, (char *)&location.string, sizeof(location)); +/* Prevent warning if -Wstrict-prototypes. */ +#ifdef __GNUC__ +#ifdef YYPARSE_PARAM +int yyparse (void *); #else - bash_input.location = location; +int yyparse (void); +#endif #endif - bash_input.getter = get; - bash_input.ungetter = unget; -} -/* Call this to get the next character of input. */ int -yy_getc () +yyparse(YYPARSE_PARAM_ARG) + YYPARSE_PARAM_DECL { - return (*(bash_input.getter)) (); -} + register int yystate; + register int yyn; + register short *yyssp; + register YYSTYPE *yyvsp; + int yyerrstatus; /* number of tokens to shift before error messages enabled */ + int yychar1 = 0; /* lookahead token as an internal (translated) token number */ -/* Call this to unget C. That is, to make C the next character - to be read. */ -int -yy_ungetc (c) - int c; -{ - return (*(bash_input.ungetter)) (c); -} + short yyssa[YYINITDEPTH]; /* the state stack */ + YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */ -#if defined (BUFFERED_INPUT) -int -input_file_descriptor () -{ - switch (bash_input.type) - { - case st_stream: - return (fileno (bash_input.location.file)); - case st_bstream: - return (bash_input.location.buffered_fd); - case st_stdin: - default: - return (fileno (stdin)); - } -} -#endif /* BUFFERED_INPUT */ + short *yyss = yyssa; /* refer to the stacks thru separate pointers */ + YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */ -/* **************************************************************** */ -/* */ -/* Let input be read from readline (). */ -/* */ -/* **************************************************************** */ +#ifdef YYLSP_NEEDED + YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */ + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp; -#if defined (READLINE) -char *current_readline_prompt = (char *)NULL; -char *current_readline_line = (char *)NULL; -int current_readline_line_index = 0; +#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--) +#else +#define YYPOPSTACK (yyvsp--, yyssp--) +#endif -static int -yy_readline_get () -{ - SigHandler *old_sigint; - int line_len, c; + int yystacksize = YYINITDEPTH; + int yyfree_stacks = 0; - if (!current_readline_line) - { - if (!bash_readline_initialized) - initialize_readline (); +#ifdef YYPURE + int yychar; + YYSTYPE yylval; + int yynerrs; +#ifdef YYLSP_NEEDED + YYLTYPE yylloc; +#endif +#endif -#if defined (JOB_CONTROL) - if (job_control) - give_terminal_to (shell_pgrp, 0); -#endif /* JOB_CONTROL */ + YYSTYPE yyval; /* the variable used to return */ + /* semantic values from the action */ + /* routines */ - if (signal_is_ignored (SIGINT) == 0) - { - old_sigint = (SigHandler *)set_signal_handler (SIGINT, sigint_sighandler); - interrupt_immediately++; - } + int yylen; - current_readline_line = readline (current_readline_prompt ? - current_readline_prompt : ""); +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Starting parse\n"); +#endif - if (signal_is_ignored (SIGINT) == 0) - { - interrupt_immediately--; - set_signal_handler (SIGINT, old_sigint); - } + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ -#if 0 - /* Reset the prompt to the decoded value of prompt_string_pointer. */ - reset_readline_prompt (); -#endif + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ - if (current_readline_line == 0) - return (EOF); + yyssp = yyss - 1; + yyvsp = yyvs; +#ifdef YYLSP_NEEDED + yylsp = yyls; +#endif - current_readline_line_index = 0; - line_len = strlen (current_readline_line); +/* Push a new state, which is found in yystate . */ +/* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. */ +yynewstate: - current_readline_line = xrealloc (current_readline_line, 2 + line_len); - current_readline_line[line_len++] = '\n'; - current_readline_line[line_len] = '\0'; - } + *++yyssp = yystate; - if (current_readline_line[current_readline_line_index] == 0) - { - free (current_readline_line); - current_readline_line = (char *)NULL; - return (yy_readline_get ()); - } - else + if (yyssp >= yyss + yystacksize - 1) { - c = (unsigned char)current_readline_line[current_readline_line_index++]; - return (c); - } -} + /* Give user a chance to reallocate the stack */ + /* Use copies of these so that the &'s don't force the real ones into memory. */ + YYSTYPE *yyvs1 = yyvs; + short *yyss1 = yyss; +#ifdef YYLSP_NEEDED + YYLTYPE *yyls1 = yyls; +#endif -static int -yy_readline_unget (c) - int c; -{ - if (current_readline_line_index && current_readline_line) - current_readline_line[--current_readline_line_index] = c; - return (c); -} + /* Get the current used size of the three stacks, in elements. */ + int size = yyssp - yyss + 1; + +#ifdef yyoverflow + /* Each stack pointer address is followed by the size of + the data in use in that stack, in bytes. */ +#ifdef YYLSP_NEEDED + /* This used to be a conditional around just the two extra args, + but that might be undefined if yyoverflow is a macro. */ + yyoverflow("parser stack overflow", + &yyss1, size * sizeof (*yyssp), + &yyvs1, size * sizeof (*yyvsp), + &yyls1, size * sizeof (*yylsp), + &yystacksize); +#else + yyoverflow("parser stack overflow", + &yyss1, size * sizeof (*yyssp), + &yyvs1, size * sizeof (*yyvsp), + &yystacksize); +#endif -void -with_input_from_stdin () -{ - INPUT_STREAM location; + yyss = yyss1; yyvs = yyvs1; +#ifdef YYLSP_NEEDED + yyls = yyls1; +#endif +#else /* no yyoverflow */ + /* Extend the stack our own way. */ + if (yystacksize >= YYMAXDEPTH) + { + yyerror("parser stack overflow"); + if (yyfree_stacks) + { + free (yyss); + free (yyvs); +#ifdef YYLSP_NEEDED + free (yyls); +#endif + } + return 2; + } + yystacksize *= 2; + if (yystacksize > YYMAXDEPTH) + yystacksize = YYMAXDEPTH; +#ifndef YYSTACK_USE_ALLOCA + yyfree_stacks = 1; +#endif + yyss = (short *) YYSTACK_ALLOC (yystacksize * sizeof (*yyssp)); + __yy_memcpy ((char *)yyss, (char *)yyss1, + size * (unsigned int) sizeof (*yyssp)); + yyvs = (YYSTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yyvsp)); + __yy_memcpy ((char *)yyvs, (char *)yyvs1, + size * (unsigned int) sizeof (*yyvsp)); +#ifdef YYLSP_NEEDED + yyls = (YYLTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yylsp)); + __yy_memcpy ((char *)yyls, (char *)yyls1, + size * (unsigned int) sizeof (*yylsp)); +#endif +#endif /* no yyoverflow */ - if (bash_input.type != st_stdin && stream_on_stack (st_stdin) == 0) - { - location.string = current_readline_line; - init_yy_io (yy_readline_get, yy_readline_unget, - st_stdin, "readline stdin", location); + yyssp = yyss + size - 1; + yyvsp = yyvs + size - 1; +#ifdef YYLSP_NEEDED + yylsp = yyls + size - 1; +#endif + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Stack size increased to %d\n", yystacksize); +#endif + + if (yyssp >= yyss + yystacksize - 1) + YYABORT; } -} -#else /* !READLINE */ +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Entering state %d\n", yystate); +#endif -void -with_input_from_stdin () -{ - with_input_from_stream (stdin, "stdin"); -} -#endif /* !READLINE */ + goto yybackup; + yybackup: -/* **************************************************************** */ -/* */ -/* Let input come from STRING. STRING is zero terminated. */ -/* */ -/* **************************************************************** */ +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* yyresume: */ -static int -yy_string_get () -{ - register char *string; - register int c; + /* First try to decide what to do without reference to lookahead token. */ - string = bash_input.location.string; - c = EOF; + yyn = yypact[yystate]; + if (yyn == YYFLAG) + goto yydefault; - /* If the string doesn't exist, or is empty, EOF found. */ - if (string && *string) + /* Not known => get a lookahead token if don't already have one. */ + + /* yychar is either YYEMPTY or YYEOF + or a valid token in external form. */ + + if (yychar == YYEMPTY) { - c = *(unsigned char *)string++; - bash_input.location.string = string; +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Reading a token: "); +#endif + yychar = YYLEX; } - return (c); -} - -static int -yy_string_unget (c) - int c; -{ - *(--bash_input.location.string) = c; - return (c); -} -void -with_input_from_string (string, name) - char *string, *name; -{ - INPUT_STREAM location; + /* Convert token to internal form (in yychar1) for indexing tables with */ - location.string = string; - init_yy_io (yy_string_get, yy_string_unget, st_string, name, location); -} + if (yychar <= 0) /* This means end of input. */ + { + yychar1 = 0; + yychar = YYEOF; /* Don't call YYLEX any more */ -/* **************************************************************** */ -/* */ -/* Let input come from STREAM. */ -/* */ -/* **************************************************************** */ +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Now at end of input.\n"); +#endif + } + else + { + yychar1 = YYTRANSLATE(yychar); -/* 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. */ +#if YYDEBUG != 0 + if (yydebug) + { + fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]); + /* Give the individual parser a way to print the precise meaning + of a token, for further debugging info. */ +#ifdef YYPRINT + YYPRINT (stderr, yychar, yylval); +#endif + fprintf (stderr, ")\n"); + } +#endif + } -static int -yy_stream_get () -{ - int result; + yyn += yychar1; + if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1) + goto yydefault; - result = EOF; - if (bash_input.location.file) - result = getc_with_restart (bash_input.location.file); + yyn = yytable[yyn]; - return (result); -} + /* yyn is what to do for this token type in this state. + Negative => reduce, -yyn is rule number. + Positive => shift, yyn is new state. + New state is final state => don't bother to shift, + just return success. + 0, or most negative number => error. */ -static int -yy_stream_unget (c) - int c; -{ - return (ungetc_with_restart (c, bash_input.location.file)); -} + if (yyn < 0) + { + if (yyn == YYFLAG) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + else if (yyn == 0) + goto yyerrlab; -void -with_input_from_stream (stream, name) - FILE *stream; - char *name; -{ - INPUT_STREAM location; + if (yyn == YYFINAL) + YYACCEPT; - location.file = stream; - init_yy_io (yy_stream_get, yy_stream_unget, st_stream, name, location); -} + /* Shift the lookahead token. */ -typedef struct stream_saver { - struct stream_saver *next; - BASH_INPUT bash_input; - int line; -#if defined (BUFFERED_INPUT) - BUFFERED_STREAM *bstream; -#endif /* BUFFERED_INPUT */ -} STREAM_SAVER; +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]); +#endif -/* The globally known line number. */ -int line_number = 0; + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; -#if defined (COND_COMMAND) -static int cond_lineno; -static int cond_token; + *++yyvsp = yylval; +#ifdef YYLSP_NEEDED + *++yylsp = yylloc; #endif -STREAM_SAVER *stream_list = (STREAM_SAVER *)NULL; + /* count tokens shifted since error; after three, turn off error status. */ + if (yyerrstatus) yyerrstatus--; -void -push_stream (reset_lineno) - int reset_lineno; -{ - STREAM_SAVER *saver = (STREAM_SAVER *)xmalloc (sizeof (STREAM_SAVER)); + yystate = yyn; + goto yynewstate; - xbcopy ((char *)&bash_input, (char *)&(saver->bash_input), sizeof (BASH_INPUT)); +/* Do the default action for the current state. */ +yydefault: -#if defined (BUFFERED_INPUT) - 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 = set_buffered_stream (bash_input.location.buffered_fd, - (BUFFERED_STREAM *)NULL); -#endif /* BUFFERED_INPUT */ + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; - saver->line = line_number; - bash_input.name = (char *)NULL; - saver->next = stream_list; - stream_list = saver; - EOF_Reached = 0; - if (reset_lineno) - line_number = 0; -} +/* Do a reduction. yyn is the number of a rule to reduce with. */ +yyreduce: + yylen = yyr2[yyn]; + if (yylen > 0) + yyval = yyvsp[1-yylen]; /* implement default value of the action */ -void -pop_stream () -{ - if (!stream_list) - EOF_Reached = 1; - else +#if YYDEBUG != 0 + if (yydebug) { - STREAM_SAVER *saver = stream_list; - - EOF_Reached = 0; - stream_list = stream_list->next; - - init_yy_io (saver->bash_input.getter, - saver->bash_input.ungetter, - saver->bash_input.type, - saver->bash_input.name, - saver->bash_input.location); - -#if defined (BUFFERED_INPUT) - /* If we have a buffered stream, restore buffers[fd]. */ - /* If the input file descriptor was changed while this was on the - save stack, update the buffered fd to the new file descriptor and - re-establish the buffer <-> bash_input fd correspondence. */ - if (bash_input.type == st_bstream && bash_input.location.buffered_fd >= 0) - { - if (bash_input_fd_changed) - { - bash_input_fd_changed = 0; - if (default_buffered_input >= 0) - { - bash_input.location.buffered_fd = default_buffered_input; - saver->bstream->b_fd = default_buffered_input; - SET_CLOSE_ON_EXEC (default_buffered_input); - } - } - /* XXX could free buffered stream returned as result here. */ - set_buffered_stream (bash_input.location.buffered_fd, saver->bstream); - } -#endif /* BUFFERED_INPUT */ + int i; - line_number = saver->line; + fprintf (stderr, "Reducing via rule %d (line %d), ", + yyn, yyrline[yyn]); - FREE (saver->bash_input.name); - free (saver); + /* Print the symbols being reduced, and their result. */ + for (i = yyprhs[yyn]; yyrhs[i] > 0; i++) + fprintf (stderr, "%s ", yytname[yyrhs[i]]); + fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]); } -} +#endif -/* Return 1 if a stream of type TYPE is saved on the stack. */ -int -stream_on_stack (type) - enum stream_type type; -{ - register STREAM_SAVER *s; - for (s = stream_list; s; s = s->next) - if (s->bash_input.type == type) - return 1; - return 0; -} + switch (yyn) { -/* Save the current token state and return it in a malloced array. */ -int * -save_token_state () +case 1: +#line 352 "/usr/src/local/chet/src/bash/src/parse.y" { - 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; + /* Case of regular command. Discard the error + safety net,and return the command just parsed. */ + global_command = yyvsp[-1].command; + eof_encountered = 0; + /* discard_parser_constructs (0); */ + YYACCEPT; + ; + break;} +case 2: +#line 361 "/usr/src/local/chet/src/bash/src/parse.y" { - if (ts == 0) - return; - last_read_token = ts[0]; - token_before_that = ts[1]; - two_tokens_ago = ts[2]; -} + /* Case of regular command, but not a very + interesting one. Return a NULL command. */ + global_command = (COMMAND *)NULL; + YYACCEPT; + ; + break;} +case 3: +#line 368 "/usr/src/local/chet/src/bash/src/parse.y" +{ + /* Error during parsing. Return NULL command. */ + global_command = (COMMAND *)NULL; + eof_encountered = 0; + /* discard_parser_constructs (1); */ + if (interactive) + { + YYACCEPT; + } + else + { + YYABORT; + } + ; + break;} +case 4: +#line 383 "/usr/src/local/chet/src/bash/src/parse.y" +{ + /* Case of EOF seen by itself. Do ignoreeof or + not. */ + global_command = (COMMAND *)NULL; + handle_eof_input_unit (); + YYACCEPT; + ; + break;} +case 5: +#line 393 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.word_list = make_word_list (yyvsp[0].word, (WORD_LIST *)NULL); ; + break;} +case 6: +#line 395 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.word_list = make_word_list (yyvsp[0].word, yyvsp[-1].word_list); ; + break;} +case 7: +#line 399 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (1, r_output_direction, redir); + ; + break;} +case 8: +#line 404 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (0, r_input_direction, redir); + ; + break;} +case 9: +#line 409 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (yyvsp[-2].number, r_output_direction, redir); + ; + break;} +case 10: +#line 414 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (yyvsp[-2].number, r_input_direction, redir); + ; + break;} +case 11: +#line 419 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (1, r_appending_to, redir); + ; + break;} +case 12: +#line 424 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (yyvsp[-2].number, r_appending_to, redir); + ; + break;} +case 13: +#line 429 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (0, r_reading_until, redir); + redir_stack[need_here_doc++] = yyval.redirect; + ; + break;} +case 14: +#line 435 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (yyvsp[-2].number, r_reading_until, redir); + redir_stack[need_here_doc++] = yyval.redirect; + ; + break;} +case 15: +#line 441 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (0, r_reading_string, redir); + ; + break;} +case 16: +#line 446 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (yyvsp[-2].number, r_reading_string, redir); + ; + break;} +case 17: +#line 451 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.dest = yyvsp[0].number; + yyval.redirect = make_redirection (0, r_duplicating_input, redir); + ; + break;} +case 18: +#line 456 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.dest = yyvsp[0].number; + yyval.redirect = make_redirection (yyvsp[-2].number, r_duplicating_input, redir); + ; + break;} +case 19: +#line 461 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.dest = yyvsp[0].number; + yyval.redirect = make_redirection (1, r_duplicating_output, redir); + ; + break;} +case 20: +#line 466 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.dest = yyvsp[0].number; + yyval.redirect = make_redirection (yyvsp[-2].number, r_duplicating_output, redir); + ; + break;} +case 21: +#line 471 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (0, r_duplicating_input_word, redir); + ; + break;} +case 22: +#line 476 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (yyvsp[-2].number, r_duplicating_input_word, redir); + ; + break;} +case 23: +#line 481 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (1, r_duplicating_output_word, redir); + ; + break;} +case 24: +#line 486 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (yyvsp[-2].number, r_duplicating_output_word, redir); + ; + break;} +case 25: +#line 491 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection + (0, r_deblank_reading_until, redir); + redir_stack[need_here_doc++] = yyval.redirect; + ; + break;} +case 26: +#line 498 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection + (yyvsp[-2].number, r_deblank_reading_until, redir); + redir_stack[need_here_doc++] = yyval.redirect; + ; + break;} +case 27: +#line 505 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.dest = 0; + yyval.redirect = make_redirection (1, r_close_this, redir); + ; + break;} +case 28: +#line 510 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.dest = 0; + yyval.redirect = make_redirection (yyvsp[-2].number, r_close_this, redir); + ; + break;} +case 29: +#line 515 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.dest = 0; + yyval.redirect = make_redirection (0, r_close_this, redir); + ; + break;} +case 30: +#line 520 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.dest = 0; + yyval.redirect = make_redirection (yyvsp[-2].number, r_close_this, redir); + ; + break;} +case 31: +#line 525 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (1, r_err_and_out, redir); + ; + break;} +case 32: +#line 530 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (yyvsp[-2].number, r_input_output, redir); + ; + break;} +case 33: +#line 535 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (0, r_input_output, redir); + ; + break;} +case 34: +#line 540 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (1, r_output_force, redir); + ; + break;} +case 35: +#line 545 "/usr/src/local/chet/src/bash/src/parse.y" +{ + redir.filename = yyvsp[0].word; + yyval.redirect = make_redirection (yyvsp[-2].number, r_output_force, redir); + ; + break;} +case 36: +#line 552 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.element.word = yyvsp[0].word; yyval.element.redirect = 0; ; + break;} +case 37: +#line 554 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.element.word = yyvsp[0].word; yyval.element.redirect = 0; ; + break;} +case 38: +#line 556 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.element.redirect = yyvsp[0].redirect; yyval.element.word = 0; ; + break;} +case 39: +#line 560 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.redirect = yyvsp[0].redirect; + ; + break;} +case 40: +#line 564 "/usr/src/local/chet/src/bash/src/parse.y" +{ + register REDIRECT *t; -/* - * This is used to inhibit alias expansion and reserved word recognition - * inside case statement pattern lists. A `case statement pattern list' is: - * - * everything between the `in' in a `case word in' and the next ')' - * or `esac' - * everything between a `;;' and the next `)' or `esac' - */ + for (t = yyvsp[-1].redirect; t->next; t = t->next) + ; + t->next = yyvsp[0].redirect; + yyval.redirect = yyvsp[-1].redirect; + ; + break;} +case 41: +#line 575 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = make_simple_command (yyvsp[0].element, (COMMAND *)NULL); ; + break;} +case 42: +#line 577 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = make_simple_command (yyvsp[0].element, yyvsp[-1].command); ; + break;} +case 43: +#line 581 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = clean_simple_command (yyvsp[0].command); ; + break;} +case 44: +#line 583 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[0].command; ; + break;} +case 45: +#line 585 "/usr/src/local/chet/src/bash/src/parse.y" +{ + COMMAND *tc; -#if defined (ALIAS) || defined (DPAREN_ARITHMETIC) + tc = yyvsp[-1].command; + if (tc->redirects) + { + register REDIRECT *t; + for (t = tc->redirects; t->next; t = t->next) + ; + t->next = yyvsp[0].redirect; + } + else + tc->redirects = yyvsp[0].redirect; + yyval.command = yyvsp[-1].command; + ; + break;} +case 46: +#line 601 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[0].command; ; + break;} +case 47: +#line 605 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[0].command; ; + break;} +case 48: +#line 607 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[0].command; ; + break;} +case 49: +#line 609 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = make_while_command (yyvsp[-3].command, yyvsp[-1].command); ; + break;} +case 50: +#line 611 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = make_until_command (yyvsp[-3].command, yyvsp[-1].command); ; + break;} +case 51: +#line 613 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[0].command; ; + break;} +case 52: +#line 615 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[0].command; ; + break;} +case 53: +#line 617 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[0].command; ; + break;} +case 54: +#line 619 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[0].command; ; + break;} +case 55: +#line 621 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[0].command; ; + break;} +case 56: +#line 623 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[0].command; ; + break;} +case 57: +#line 625 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[0].command; ; + break;} +case 58: +#line 629 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_for_command (yyvsp[-4].word, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), yyvsp[-1].command, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 59: +#line 634 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_for_command (yyvsp[-4].word, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), yyvsp[-1].command, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 60: +#line 639 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_for_command (yyvsp[-5].word, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), yyvsp[-1].command, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 61: +#line 644 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_for_command (yyvsp[-5].word, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), yyvsp[-1].command, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 62: +#line 649 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_for_command (yyvsp[-8].word, REVERSE_LIST (yyvsp[-5].word_list, WORD_LIST *), yyvsp[-1].command, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 63: +#line 654 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_for_command (yyvsp[-8].word, REVERSE_LIST (yyvsp[-5].word_list, WORD_LIST *), yyvsp[-1].command, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 64: +#line 659 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_for_command (yyvsp[-7].word, (WORD_LIST *)NULL, yyvsp[-1].command, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 65: +#line 664 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_for_command (yyvsp[-7].word, (WORD_LIST *)NULL, yyvsp[-1].command, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 66: +#line 671 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_arith_for_command (yyvsp[-5].word_list, yyvsp[-1].command, arith_for_lineno); + if (word_top > 0) word_top--; + ; + break;} +case 67: +#line 676 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_arith_for_command (yyvsp[-5].word_list, yyvsp[-1].command, arith_for_lineno); + if (word_top > 0) word_top--; + ; + break;} +case 68: +#line 681 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_arith_for_command (yyvsp[-3].word_list, yyvsp[-1].command, arith_for_lineno); + if (word_top > 0) word_top--; + ; + break;} +case 69: +#line 686 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_arith_for_command (yyvsp[-3].word_list, yyvsp[-1].command, arith_for_lineno); + if (word_top > 0) word_top--; + ; + break;} +case 70: +#line 693 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_select_command (yyvsp[-4].word, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), yyvsp[-1].command, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 71: +#line 698 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_select_command (yyvsp[-4].word, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), yyvsp[-1].command, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 72: +#line 703 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_select_command (yyvsp[-5].word, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), yyvsp[-1].command, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 73: +#line 708 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_select_command (yyvsp[-5].word, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), yyvsp[-1].command, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 74: +#line 713 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_select_command (yyvsp[-8].word, REVERSE_LIST (yyvsp[-5].word_list, WORD_LIST *), yyvsp[-1].command, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 75: +#line 718 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_select_command (yyvsp[-8].word, REVERSE_LIST (yyvsp[-5].word_list, WORD_LIST *), yyvsp[-1].command, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 76: +#line 725 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_case_command (yyvsp[-4].word, (PATTERN_LIST *)NULL, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 77: +#line 730 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_case_command (yyvsp[-5].word, yyvsp[-2].pattern, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 78: +#line 735 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_case_command (yyvsp[-4].word, yyvsp[-1].pattern, word_lineno[word_top]); + if (word_top > 0) word_top--; + ; + break;} +case 79: +#line 742 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = make_function_def (yyvsp[-4].word, yyvsp[0].command, function_dstart, function_bstart); ; + break;} +case 80: +#line 745 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = make_function_def (yyvsp[-4].word, yyvsp[0].command, function_dstart, function_bstart); ; + break;} +case 81: +#line 748 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = make_function_def (yyvsp[-2].word, yyvsp[0].command, function_dstart, function_bstart); ; + break;} +case 82: +#line 753 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[0].command; ; + break;} +case 83: +#line 755 "/usr/src/local/chet/src/bash/src/parse.y" +{ + COMMAND *tc; -#if !defined (ALIAS) -typedef void *alias_t; + tc = yyvsp[-1].command; + /* According to Posix.2 3.9.5, redirections + specified after the body of a function should + 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->redirects) + { + register REDIRECT *t; + for (t = tc->redirects; t->next; t = t->next) + ; + t->next = yyvsp[0].redirect; + } + else + tc->redirects = yyvsp[0].redirect; + yyval.command = yyvsp[-1].command; + ; + break;} +case 84: +#line 786 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = make_subshell_command (yyvsp[-1].command); + yyval.command->flags |= CMD_WANT_SUBSHELL; + ; + break;} +case 85: +#line 793 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = make_if_command (yyvsp[-3].command, yyvsp[-1].command, (COMMAND *)NULL); ; + break;} +case 86: +#line 795 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = make_if_command (yyvsp[-5].command, yyvsp[-3].command, yyvsp[-1].command); ; + break;} +case 87: +#line 797 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = make_if_command (yyvsp[-4].command, yyvsp[-2].command, yyvsp[-1].command); ; + break;} +case 88: +#line 802 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = make_group_command (yyvsp[-1].command); ; + break;} +case 89: +#line 806 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = make_arith_command (yyvsp[0].word_list); ; + break;} +case 90: +#line 810 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[-1].command; ; + break;} +case 91: +#line 814 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = make_if_command (yyvsp[-2].command, yyvsp[0].command, (COMMAND *)NULL); ; + break;} +case 92: +#line 816 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = make_if_command (yyvsp[-4].command, yyvsp[-2].command, yyvsp[0].command); ; + break;} +case 93: +#line 818 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = make_if_command (yyvsp[-3].command, yyvsp[-1].command, yyvsp[0].command); ; + break;} +case 95: +#line 823 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyvsp[0].pattern->next = yyvsp[-1].pattern; yyval.pattern = yyvsp[0].pattern; ; + break;} +case 96: +#line 827 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.pattern = make_pattern_list (yyvsp[-2].word_list, yyvsp[0].command); ; + break;} +case 97: +#line 829 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.pattern = make_pattern_list (yyvsp[-2].word_list, (COMMAND *)NULL); ; + break;} +case 98: +#line 831 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.pattern = make_pattern_list (yyvsp[-2].word_list, yyvsp[0].command); ; + break;} +case 99: +#line 833 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.pattern = make_pattern_list (yyvsp[-2].word_list, (COMMAND *)NULL); ; + break;} +case 101: +#line 838 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyvsp[-1].pattern->next = yyvsp[-2].pattern; yyval.pattern = yyvsp[-1].pattern; ; + break;} +case 102: +#line 842 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.word_list = make_word_list (yyvsp[0].word, (WORD_LIST *)NULL); ; + break;} +case 103: +#line 844 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.word_list = make_word_list (yyvsp[0].word, yyvsp[-2].word_list); ; + break;} +case 104: +#line 853 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = yyvsp[0].command; + if (need_here_doc) + gather_here_documents (); + ; + break;} +case 106: +#line 862 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = yyvsp[0].command; + ; + break;} +case 108: +#line 869 "/usr/src/local/chet/src/bash/src/parse.y" +{ + if (yyvsp[-2].command->type == cm_connection) + yyval.command = connect_async_list (yyvsp[-2].command, (COMMAND *)NULL, '&'); + else + yyval.command = command_connect (yyvsp[-2].command, (COMMAND *)NULL, '&'); + ; + break;} +case 110: +#line 880 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = command_connect (yyvsp[-3].command, yyvsp[0].command, AND_AND); ; + break;} +case 111: +#line 882 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = command_connect (yyvsp[-3].command, yyvsp[0].command, OR_OR); ; + break;} +case 112: +#line 884 "/usr/src/local/chet/src/bash/src/parse.y" +{ + if (yyvsp[-3].command->type == cm_connection) + yyval.command = connect_async_list (yyvsp[-3].command, yyvsp[0].command, '&'); + else + yyval.command = command_connect (yyvsp[-3].command, yyvsp[0].command, '&'); + ; + break;} +case 113: +#line 891 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = command_connect (yyvsp[-3].command, yyvsp[0].command, ';'); ; + break;} +case 114: +#line 893 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = command_connect (yyvsp[-3].command, yyvsp[0].command, ';'); ; + break;} +case 115: +#line 895 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[0].command; ; + break;} +case 118: +#line 903 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.number = '\n'; ; + break;} +case 119: +#line 905 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.number = ';'; ; + break;} +case 120: +#line 907 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.number = yacc_EOF; ; + break;} +case 123: +#line 921 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = yyvsp[0].command; + if (need_here_doc) + gather_here_documents (); + ; + break;} +case 124: +#line 927 "/usr/src/local/chet/src/bash/src/parse.y" +{ + if (yyvsp[-1].command->type == cm_connection) + yyval.command = connect_async_list (yyvsp[-1].command, (COMMAND *)NULL, '&'); + else + yyval.command = command_connect (yyvsp[-1].command, (COMMAND *)NULL, '&'); + if (need_here_doc) + gather_here_documents (); + ; + break;} +case 125: +#line 936 "/usr/src/local/chet/src/bash/src/parse.y" +{ + yyval.command = yyvsp[-1].command; + if (need_here_doc) + gather_here_documents (); + ; + break;} +case 126: +#line 944 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = command_connect (yyvsp[-3].command, yyvsp[0].command, AND_AND); ; + break;} +case 127: +#line 946 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = command_connect (yyvsp[-3].command, yyvsp[0].command, OR_OR); ; + break;} +case 128: +#line 948 "/usr/src/local/chet/src/bash/src/parse.y" +{ + if (yyvsp[-2].command->type == cm_connection) + yyval.command = connect_async_list (yyvsp[-2].command, yyvsp[0].command, '&'); + else + yyval.command = command_connect (yyvsp[-2].command, yyvsp[0].command, '&'); + ; + break;} +case 129: +#line 955 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = command_connect (yyvsp[-2].command, yyvsp[0].command, ';'); ; + break;} +case 130: +#line 958 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[0].command; ; + break;} +case 131: +#line 962 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[0].command; ; + break;} +case 132: +#line 964 "/usr/src/local/chet/src/bash/src/parse.y" +{ + if (yyvsp[0].command) + yyvsp[0].command->flags |= CMD_INVERT_RETURN; + yyval.command = yyvsp[0].command; + ; + break;} +case 133: +#line 970 "/usr/src/local/chet/src/bash/src/parse.y" +{ + if (yyvsp[0].command) + yyvsp[0].command->flags |= yyvsp[-1].number; + yyval.command = yyvsp[0].command; + ; + break;} +case 134: +#line 976 "/usr/src/local/chet/src/bash/src/parse.y" +{ + if (yyvsp[0].command) + yyvsp[0].command->flags |= yyvsp[-2].number|CMD_INVERT_RETURN; + yyval.command = yyvsp[0].command; + ; + break;} +case 135: +#line 982 "/usr/src/local/chet/src/bash/src/parse.y" +{ + if (yyvsp[0].command) + yyvsp[0].command->flags |= yyvsp[-1].number|CMD_INVERT_RETURN; + yyval.command = yyvsp[0].command; + ; + break;} +case 136: +#line 988 "/usr/src/local/chet/src/bash/src/parse.y" +{ + ELEMENT x; + + /* Boy, this is unclean. `time' by itself can + time a null command. We cheat and push a + newline back if the list_terminator was a newline + to avoid the double-newline problem (one to + terminate this, one to terminate the command) */ + x.word = 0; + x.redirect = 0; + yyval.command = make_simple_command (x, (COMMAND *)NULL); + yyval.command->flags |= yyvsp[-1].number; + /* XXX - let's cheat and push a newline back */ + if (yyvsp[0].number == '\n') + token_to_read = '\n'; + ; + break;} +case 137: +#line 1009 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = command_connect (yyvsp[-3].command, yyvsp[0].command, '|'); ; + break;} +case 138: +#line 1011 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.command = yyvsp[0].command; ; + break;} +case 139: +#line 1015 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.number = CMD_TIME_PIPELINE; ; + break;} +case 140: +#line 1017 "/usr/src/local/chet/src/bash/src/parse.y" +{ yyval.number = CMD_TIME_PIPELINE|CMD_TIME_POSIX; ; + break;} +} + /* the action file gets copied in in place of this dollarsign */ +#line 543 "/usr/share/bison.simple" + + yyvsp -= yylen; + yyssp -= yylen; +#ifdef YYLSP_NEEDED + yylsp -= yylen; #endif -#define END_OF_ALIAS 0 +#if YYDEBUG != 0 + if (yydebug) + { + short *ssp1 = yyss - 1; + fprintf (stderr, "state stack now"); + while (ssp1 != yyssp) + fprintf (stderr, " %d", *++ssp1); + fprintf (stderr, "\n"); + } +#endif -/* - * Pseudo-global variables used in implementing token-wise alias expansion. - */ + *++yyvsp = yyval; -/* - * Pushing and popping strings. This works together with shell_getc to - * implement alias expansion on a per-token basis. - */ +#ifdef YYLSP_NEEDED + yylsp++; + if (yylen == 0) + { + yylsp->first_line = yylloc.first_line; + yylsp->first_column = yylloc.first_column; + yylsp->last_line = (yylsp-1)->last_line; + yylsp->last_column = (yylsp-1)->last_column; + yylsp->text = 0; + } + else + { + yylsp->last_line = (yylsp+yylen-1)->last_line; + yylsp->last_column = (yylsp+yylen-1)->last_column; + } +#endif -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. */ + /* Now "shift" the result of the reduction. + Determine what state that goes to, + based on the state we popped back to + and the rule number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTBASE] + *yyssp; + if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTBASE]; + + goto yynewstate; + +yyerrlab: /* here on detecting error */ + + if (! yyerrstatus) + /* If not already recovering from an error, report this error. */ + { + ++yynerrs; + +#ifdef YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (yyn > YYFLAG && yyn < YYLAST) + { + int size = 0; + char *msg; + int x, count; + + count = 0; + /* Start X at -yyn if nec to avoid negative indexes in yycheck. */ + for (x = (yyn < 0 ? -yyn : 0); + x < (sizeof(yytname) / sizeof(char *)); x++) + if (yycheck[x + yyn] == x) + size += strlen(yytname[x]) + 15, count++; + msg = (char *) malloc(size + 15); + if (msg != 0) + { + strcpy(msg, "parse error"); + + if (count < 5) + { + count = 0; + for (x = (yyn < 0 ? -yyn : 0); + x < (sizeof(yytname) / sizeof(char *)); x++) + if (yycheck[x + yyn] == x) + { + strcat(msg, count == 0 ? ", expecting `" : " or `"); + strcat(msg, yytname[x]); + strcat(msg, "'"); + count++; + } + } + yyerror(msg); + free(msg); + } + else + yyerror ("parse error; also virtual memory exceeded"); + } + else +#endif /* YYERROR_VERBOSE */ + yyerror("parse error"); + } + + goto yyerrlab1; +yyerrlab1: /* here on error raised explicitly by an action */ + + if (yyerrstatus == 3) + { + /* if just tried and failed to reuse lookahead token after an error, discard it. */ + + /* return failure if at end of input */ + if (yychar == YYEOF) + YYABORT; + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]); #endif - int saved_line_size, saved_line_index, saved_line_terminator; -} STRING_SAVER; -STRING_SAVER *pushed_string_list = (STRING_SAVER *)NULL; + yychar = YYEMPTY; + } -/* - * Push the current shell_input_line onto a stack of such lines and make S - * the current input. Used when expanding aliases. EXPAND is used to set - * the value of expand_next_token when the string is popped, so that the - * word after the alias in the original line is handled correctly when the - * alias expands to multiple words. TOKEN is the token that was expanded - * into S; it is saved and used to prevent infinite recursive expansion. - */ -static void -push_string (s, expand, ap) - char *s; - int expand; - alias_t *ap; -{ - STRING_SAVER *temp = (STRING_SAVER *) xmalloc (sizeof (STRING_SAVER)); + /* Else will try to reuse lookahead token + after shifting the error token. */ - temp->expand_alias = expand; - temp->saved_line = shell_input_line; - 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; + yyerrstatus = 3; /* Each real token shifted decrements this */ + + goto yyerrhandle; + +yyerrdefault: /* current state does not do anything special for the error token. */ + +#if 0 + /* This is wrong; only states that explicitly want error tokens + should shift them. */ + yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/ + if (yyn) goto yydefault; #endif - temp->next = pushed_string_list; - pushed_string_list = temp; -#if defined (ALIAS) - if (ap) - ap->flags |= AL_BEINGEXPANDED; +yyerrpop: /* pop the current state because it cannot handle the error token */ + + if (yyssp == yyss) YYABORT; + yyvsp--; + yystate = *--yyssp; +#ifdef YYLSP_NEEDED + yylsp--; #endif - shell_input_line = s; - shell_input_line_size = strlen (s); - shell_input_line_index = 0; - shell_input_line_terminator = '\0'; - parser_state &= ~PST_ALEXPNEXT; +#if YYDEBUG != 0 + if (yydebug) + { + short *ssp1 = yyss - 1; + fprintf (stderr, "Error: state stack now"); + while (ssp1 != yyssp) + fprintf (stderr, " %d", *++ssp1); + fprintf (stderr, "\n"); + } +#endif + +yyerrhandle: + + yyn = yypact[yystate]; + if (yyn == YYFLAG) + goto yyerrdefault; + + yyn += YYTERROR; + if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR) + goto yyerrdefault; + + yyn = yytable[yyn]; + if (yyn < 0) + { + if (yyn == YYFLAG) + goto yyerrpop; + yyn = -yyn; + goto yyreduce; + } + else if (yyn == 0) + goto yyerrpop; + + if (yyn == YYFINAL) + YYACCEPT; + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Shifting error token, "); +#endif + + *++yyvsp = yylval; +#ifdef YYLSP_NEEDED + *++yylsp = yylloc; +#endif + + yystate = yyn; + goto yynewstate; + + yyacceptlab: + /* YYACCEPT comes here. */ + if (yyfree_stacks) + { + free (yyss); + free (yyvs); +#ifdef YYLSP_NEEDED + free (yyls); +#endif + } + return 0; + + yyabortlab: + /* YYABORT comes here. */ + if (yyfree_stacks) + { + free (yyss); + free (yyvs); +#ifdef YYLSP_NEEDED + free (yyls); +#endif + } + return 1; } +#line 1019 "/usr/src/local/chet/src/bash/src/parse.y" -/* - * Make the top of the pushed_string stack be the current shell input. - * Only called when there is something on the stack. Called from shell_getc - * when it thinks it has consumed the string generated by an alias expansion - * and needs to return to the original input line. - */ -static void -pop_string () -{ - STRING_SAVER *t; - FREE (shell_input_line); - shell_input_line = pushed_string_list->saved_line; - shell_input_line_index = pushed_string_list->saved_line_index; - shell_input_line_size = pushed_string_list->saved_line_size; - shell_input_line_terminator = pushed_string_list->saved_line_terminator; +/* Possible states for the parser that require it to do special things. */ +#define PST_CASEPAT 0x0001 /* in a case pattern list */ +#define PST_ALEXPNEXT 0x0002 /* expand next word for aliases */ +#define PST_ALLOWOPNBRC 0x0004 /* allow open brace for function def */ +#define PST_NEEDCLOSBRC 0x0008 /* need close brace */ +#define PST_DBLPAREN 0x0010 /* double-paren parsing */ +#define PST_SUBSHELL 0x0020 /* ( ... ) subshell */ +#define PST_CMDSUBST 0x0040 /* $( ... ) command substitution */ +#define PST_CASESTMT 0x0080 /* parsing a case statement */ +#define PST_CONDCMD 0x0100 /* parsing a [[...]] command */ +#define PST_CONDEXPR 0x0200 /* parsing the guts of [[...]] */ +#define PST_ARITHFOR 0x0400 /* parsing an arithmetic for command */ +#define PST_ALEXPAND 0x0800 /* OK to expand aliases - unused */ +#define PST_CMDTOKEN 0x1000 /* command token OK - unused */ +#define PST_COMPASSIGN 0x2000 /* parsing x=(...) compound assignment */ +#define PST_ASSIGNOK 0x4000 /* assignment statement ok in this context */ - if (pushed_string_list->expand_alias) - parser_state |= PST_ALEXPNEXT; - else - parser_state &= ~PST_ALEXPNEXT; +/* Initial size to allocate for tokens, and the + amount to grow them by. */ +#define TOKEN_DEFAULT_INITIAL_SIZE 496 +#define TOKEN_DEFAULT_GROW_SIZE 512 - t = pushed_string_list; - pushed_string_list = pushed_string_list->next; +/* Should we call prompt_again? */ +#define SHOULD_PROMPT() \ + (interactive && (bash_input.type == st_stdin || bash_input.type == st_stream)) #if defined (ALIAS) - if (t->expander) - t->expander->flags &= ~AL_BEINGEXPANDED; +# define expanding_alias() (pushed_string_list && pushed_string_list->expander) +#else +# define expanding_alias() 0 #endif - free ((char *)t); -} +/* The token currently being read. */ +static int current_token; + +/* The last read token, or NULL. read_token () uses this for context + checking. */ +static int last_read_token; + +/* The token read prior to last_read_token. */ +static int token_before_that; + +/* The token read prior to token_before_that. */ +static int two_tokens_ago; + +/* The current parser state. */ +static int parser_state; + +/* Global var is non-zero when end of file has been reached. */ +int EOF_Reached = 0; +#ifdef DEBUG static void -free_string_list () +debug_parser (i) + int i; { - register STRING_SAVER *t, *t1; - - for (t = pushed_string_list; t; ) - { - t1 = t->next; - FREE (t->saved_line); -#if defined (ALIAS) - if (t->expander) - t->expander->flags &= ~AL_BEINGEXPANDED; +#if YYDEBUG != 0 + yydebug = i; #endif - free ((char *)t); - t = t1; - } - pushed_string_list = (STRING_SAVER *)NULL; } +#endif -#endif /* ALIAS || DPAREN_ARITHMETIC */ +/* yy_getc () returns the next available character from input or EOF. + yy_ungetc (c) makes `c' the next character to read. + init_yy_io (get, unget, type, location) makes the function GET the + installed function for getting the next character, makes UNGET the + installed function for un-getting a character, sets the type of stream + (either string or file) from TYPE, and makes LOCATION point to where + the input is coming from. */ -/* 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 - is non-zero, we remove unquoted \ pairs. This is used by - read_secondary_line to read here documents. */ -static char * -read_a_line (remove_quoted_newline) - int remove_quoted_newline; +/* Unconditionally returns end-of-file. */ +int +return_EOF () { - static char *line_buffer = (char *)NULL; - static int buffer_size = 0; - int indx = 0, c, peekc, pass_next; + return (EOF); +} -#if defined (READLINE) - if (interactive && bash_input.type != st_string && no_line_editing) -#else - if (interactive && bash_input.type != st_string) -#endif - print_prompt (); +/* Variable containing the current get and unget functions. + See ./input.h for a clearer description. */ +BASH_INPUT bash_input; - pass_next = 0; - while (1) - { - c = yy_getc (); +/* Set all of the fields in BASH_INPUT to NULL. Free bash_input.name if it + is non-null, avoiding a memory leak. */ +void +initialize_bash_input () +{ + bash_input.type = st_none; + FREE (bash_input.name); + bash_input.name = (char *)NULL; + bash_input.location.file = (FILE *)NULL; + bash_input.location.string = (char *)NULL; + bash_input.getter = (sh_cget_func_t *)NULL; + bash_input.ungetter = (sh_cunget_func_t *)NULL; +} - /* Allow immediate exit if interrupted during input. */ - QUIT; +/* Set the contents of the current bash input stream from + GET, UNGET, TYPE, NAME, and LOCATION. */ +void +init_yy_io (get, unget, type, name, location) + sh_cget_func_t *get; + sh_cunget_func_t *unget; + enum stream_type type; + const char *name; + INPUT_STREAM location; +{ + bash_input.type = type; + FREE (bash_input.name); + bash_input.name = name ? savestring (name) : (char *)NULL; - /* Ignore null bytes in input. */ - if (c == 0) - { -#if 0 - internal_warning ("read_a_line: ignored null byte in input"); + /* XXX */ +#if defined (CRAY) + memcpy((char *)&bash_input.location.string, (char *)&location.string, sizeof(location)); +#else + bash_input.location = location; #endif - continue; - } - - /* If there is no more input, then we return NULL. */ - if (c == EOF) - { - if (interactive && bash_input.type == st_stream) - clearerr (stdin); - if (indx == 0) - return ((char *)NULL); - c = '\n'; - } + bash_input.getter = get; + bash_input.ungetter = unget; +} - /* `+2' in case the final character in the buffer is a newline. */ - RESIZE_MALLOCED_BUFFER (line_buffer, indx, 2, buffer_size, 128); +char * +yy_input_name () +{ + return (bash_input.name ? bash_input.name : "stdin"); +} - /* IF REMOVE_QUOTED_NEWLINES is non-zero, we are reading a - here document with an unquoted delimiter. In this case, - the line will be expanded as if it were in double quotes. - We allow a backslash to escape the next character, but we - need to treat the backslash specially only if a backslash - quoting a backslash-newline pair appears in the line. */ - if (pass_next) - { - line_buffer[indx++] = c; - pass_next = 0; - } - else if (c == '\\' && remove_quoted_newline) - { - peekc = yy_getc (); - if (peekc == '\n') - continue; /* Make the unquoted \ pair disappear. */ - else - { - yy_ungetc (peekc); - pass_next = 1; - line_buffer[indx++] = c; /* Preserve the backslash. */ - } - } - else - line_buffer[indx++] = c; +/* Call this to get the next character of input. */ +static int +yy_getc () +{ + return (*(bash_input.getter)) (); +} - if (c == '\n') - { - line_buffer[indx] = '\0'; - return (line_buffer); - } - } +/* Call this to unget C. That is, to make C the next character + to be read. */ +static int +yy_ungetc (c) + int c; +{ + return (*(bash_input.ungetter)) (c); } -/* Return a line as in read_a_line (), but insure that the prompt is - the secondary prompt. This is used to read the lines of a here - document. REMOVE_QUOTED_NEWLINE is non-zero if we should remove - newlines quoted with backslashes while reading the line. It is - non-zero unless the delimiter of the here document was quoted. */ -char * -read_secondary_line (remove_quoted_newline) - int remove_quoted_newline; +#if defined (BUFFERED_INPUT) +#ifdef INCLUDE_UNUSED +int +input_file_descriptor () { - prompt_string_pointer = &ps2_prompt; - prompt_again (); - return (read_a_line (remove_quoted_newline)); + switch (bash_input.type) + { + case st_stream: + return (fileno (bash_input.location.file)); + case st_bstream: + return (bash_input.location.buffered_fd); + case st_stdin: + default: + return (fileno (stdin)); + } } +#endif +#endif /* BUFFERED_INPUT */ /* **************************************************************** */ /* */ -/* YYLEX () */ +/* Let input be read from readline (). */ /* */ /* **************************************************************** */ -/* Reserved words. These are only recognized as the first word of a - command. */ -STRING_INT_ALIST word_token_alist[] = { - { "if", IF }, - { "then", THEN }, - { "else", ELSE }, - { "elif", ELIF }, - { "fi", FI }, - { "case", CASE }, - { "esac", ESAC }, - { "for", FOR }, -#if defined (SELECT_COMMAND) - { "select", SELECT }, -#endif - { "while", WHILE }, - { "until", UNTIL }, - { "do", DO }, - { "done", DONE }, - { "in", IN }, - { "function", FUNCTION }, -#if defined (COMMAND_TIMING) - { "time", TIME }, -#endif - { "{", '{' }, - { "}", '}' }, - { "!", BANG }, -#if defined (COND_COMMAND) - { "[[", COND_START }, - { "]]", COND_END }, -#endif - { (char *)NULL, 0} -}; - -/* XXX - we should also have an alist with strings for other tokens, so we - can give more descriptive error messages. Look at y.tab.h for the - other tokens. */ +#if defined (READLINE) +char *current_readline_prompt = (char *)NULL; +char *current_readline_line = (char *)NULL; +int current_readline_line_index = 0; -/* These are used by read_token_word, but appear up here so that shell_getc - can use them to decide when to add otherwise blank lines to the history. */ +static int +yy_readline_get () +{ + SigHandler *old_sigint; + int line_len; + unsigned char c; -/* The primary delimiter stack. */ -struct dstack dstack = { (char *)NULL, 0, 0 }; + if (!current_readline_line) + { + if (!bash_readline_initialized) + initialize_readline (); -/* A temporary delimiter stack to be used when decoding prompt strings. - This is needed because command substitutions in prompt strings (e.g., PS2) - can screw up the parser's quoting state. */ -static struct dstack temp_dstack = { (char *)NULL, 0, 0 }; +#if defined (JOB_CONTROL) + if (job_control) + give_terminal_to (shell_pgrp, 0); +#endif /* JOB_CONTROL */ -/* Macro for accessing the top delimiter on the stack. Returns the - delimiter or zero if none. */ -#define current_delimiter(ds) \ - (ds.delimiter_depth ? ds.delimiters[ds.delimiter_depth - 1] : 0) + old_sigint = (SigHandler *)NULL; + if (signal_is_ignored (SIGINT) == 0) + { + old_sigint = (SigHandler *)set_signal_handler (SIGINT, sigint_sighandler); + interrupt_immediately++; + } -#define push_delimiter(ds, character) \ - do \ - { \ - if (ds.delimiter_depth + 2 > ds.delimiter_space) \ - ds.delimiters = xrealloc \ - (ds.delimiters, (ds.delimiter_space += 10) * sizeof (char)); \ - ds.delimiters[ds.delimiter_depth] = character; \ - ds.delimiter_depth++; \ - } \ - while (0) + current_readline_line = readline (current_readline_prompt ? + current_readline_prompt : ""); -#define pop_delimiter(ds) ds.delimiter_depth-- + if (signal_is_ignored (SIGINT) == 0 && old_sigint) + { + interrupt_immediately--; + set_signal_handler (SIGINT, old_sigint); + } -/* Return the next shell input character. This always reads characters - from shell_input_line; when that line is exhausted, it is time to - read the next line. This is called by read_token when the shell is - processing normal command input. */ +#if 0 + /* Reset the prompt to the decoded value of prompt_string_pointer. */ + reset_readline_prompt (); +#endif -/* This implements one-character lookahead/lookbehind across physical input - lines, to avoid something being lost because it's pushed back with - shell_ungetc when we're at the start of a line. */ -static int eol_ungetc_lookahead = 0; + if (current_readline_line == 0) + return (EOF); -static int -shell_getc (remove_quoted_newline) - int remove_quoted_newline; -{ - register int i; - int c; - static int mustpop = 0; + current_readline_line_index = 0; + line_len = strlen (current_readline_line); - QUIT; + current_readline_line = (char *)xrealloc (current_readline_line, 2 + line_len); + current_readline_line[line_len++] = '\n'; + current_readline_line[line_len] = '\0'; + } - if (eol_ungetc_lookahead) + if (current_readline_line[current_readline_line_index] == 0) { - c = eol_ungetc_lookahead; - eol_ungetc_lookahead = 0; + free (current_readline_line); + current_readline_line = (char *)NULL; + return (yy_readline_get ()); + } + else + { + c = current_readline_line[current_readline_line_index++]; return (c); } +} -#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. */ +static int +yy_readline_unget (c) + int c; +{ + if (current_readline_line_index && current_readline_line) + current_readline_line[--current_readline_line_index] = c; + return (c); +} - if (!shell_input_line || ((!shell_input_line[shell_input_line_index]) && - (pushed_string_list == (STRING_SAVER *)NULL))) -#else /* !ALIAS && !DPAREN_ARITHMETIC */ - if (!shell_input_line || !shell_input_line[shell_input_line_index]) -#endif /* !ALIAS && !DPAREN_ARITHMETIC */ - { - line_number++; +void +with_input_from_stdin () +{ + INPUT_STREAM location; - restart_read: + if (bash_input.type != st_stdin && stream_on_stack (st_stdin) == 0) + { + location.string = current_readline_line; + init_yy_io (yy_readline_get, yy_readline_unget, + st_stdin, "readline stdin", location); + } +} - /* Allow immediate exit if interrupted during input. */ - QUIT; +#else /* !READLINE */ - i = 0; - shell_input_line_terminator = 0; +void +with_input_from_stdin () +{ + with_input_from_stream (stdin, "stdin"); +} +#endif /* !READLINE */ -#if defined (JOB_CONTROL) - /* This can cause a problem when reading a command as the result - of a trap, when the trap is called from flush_child. This call - had better not cause jobs to disappear from the job table in - that case, or we will have big trouble. */ - notify_and_cleanup (); -#else /* !JOB_CONTROL */ - cleanup_dead_jobs (); -#endif /* !JOB_CONTROL */ +/* **************************************************************** */ +/* */ +/* Let input come from STRING. STRING is zero terminated. */ +/* */ +/* **************************************************************** */ -#if defined (READLINE) - if (interactive && bash_input.type != st_string && no_line_editing) -#else - if (interactive && bash_input.type != st_string) -#endif - print_prompt (); +static int +yy_string_get () +{ + register char *string; + register unsigned char c; - if (bash_input.type == st_stream) - clearerr (stdin); + string = bash_input.location.string; - while (1) - { - c = yy_getc (); + /* If the string doesn't exist, or is empty, EOF found. */ + if (string && *string) + { + c = *string++; + bash_input.location.string = string; + return (c); + } + else + return (EOF); +} - /* Allow immediate exit if interrupted during input. */ - QUIT; +static int +yy_string_unget (c) + int c; +{ + *(--bash_input.location.string) = c; + return (c); +} - if (c == '\0') - { -#if 0 - internal_warning ("shell_getc: ignored null byte in input"); -#endif - continue; - } +void +with_input_from_string (string, name) + char *string; + const char *name; +{ + INPUT_STREAM location; - RESIZE_MALLOCED_BUFFER (shell_input_line, i, 2, shell_input_line_size, 256); + location.string = string; + init_yy_io (yy_string_get, yy_string_unget, st_string, name, location); +} - if (c == EOF) - { - if (bash_input.type == st_stream) - clearerr (stdin); +/* **************************************************************** */ +/* */ +/* Let input come from STREAM. */ +/* */ +/* **************************************************************** */ - if (i == 0) - shell_input_line_terminator = EOF; +/* 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. */ - shell_input_line[i] = '\0'; - break; - } +static int +yy_stream_get () +{ + int result; - shell_input_line[i++] = c; + result = EOF; + if (bash_input.location.file) + { + if (interactive) + interrupt_immediately++; + result = getc_with_restart (bash_input.location.file); + if (interactive) + interrupt_immediately--; + } + return (result); +} - if (c == '\n') - { - shell_input_line[--i] = '\0'; - current_command_line_count++; - break; - } - } +static int +yy_stream_unget (c) + int c; +{ + return (ungetc_with_restart (c, bash_input.location.file)); +} - shell_input_line_index = 0; - shell_input_line_len = i; /* == strlen (shell_input_line) */ +void +with_input_from_stream (stream, name) + FILE *stream; + const char *name; +{ + INPUT_STREAM location; -#if defined (HISTORY) - if (remember_on_history && shell_input_line && shell_input_line[0]) - { - char *expansions; -# if defined (BANG_HISTORY) - int old_hist; + location.file = stream; + init_yy_io (yy_stream_get, yy_stream_unget, st_stream, name, location); +} - /* If the current delimiter is a single quote, we should not be - performing history expansion, even if we're on a different - line from the original single quote. */ - old_hist = history_expansion_inhibited; - if (current_delimiter (dstack) == '\'') - history_expansion_inhibited = 1; -# endif - expansions = pre_process_line (shell_input_line, 1, 1); -# if defined (BANG_HISTORY) - history_expansion_inhibited = old_hist; -# endif - if (expansions != shell_input_line) - { - free (shell_input_line); - shell_input_line = expansions; - shell_input_line_len = shell_input_line ? - strlen (shell_input_line) : 0; - if (!shell_input_line_len) - current_command_line_count--; +typedef struct stream_saver { + struct stream_saver *next; + BASH_INPUT bash_input; + int line; +#if defined (BUFFERED_INPUT) + BUFFERED_STREAM *bstream; +#endif /* BUFFERED_INPUT */ +} STREAM_SAVER; - /* We have to force the xrealloc below because we don't know - the true allocated size of shell_input_line anymore. */ - shell_input_line_size = shell_input_line_len; - } - } - /* Try to do something intelligent with blank lines encountered while - entering multi-line commands. XXX - this is grotesque */ - else if (remember_on_history && shell_input_line && - shell_input_line[0] == '\0' && - current_command_line_count > 1) - { - if (current_delimiter (dstack)) - /* We know shell_input_line[0] == 0 and we're reading some sort of - quoted string. This means we've got a line consisting of only - a newline in a quoted string. We want to make sure this line - gets added to the history. */ - maybe_add_history (shell_input_line); - else - { - char *hdcs; - hdcs = history_delimiting_chars (); - if (hdcs && hdcs[0] == ';') - maybe_add_history (shell_input_line); - } - } +/* The globally known line number. */ +int line_number = 0; -#endif /* HISTORY */ +#if defined (COND_COMMAND) +static int cond_lineno; +static int cond_token; +#endif - if (shell_input_line) - { - /* Lines that signify the end of the shell's input should not be - echoed. */ - if (echo_input_at_read && (shell_input_line[0] || - shell_input_line_terminator != EOF)) - fprintf (stderr, "%s\n", shell_input_line); - } - else - { - shell_input_line_size = 0; - prompt_string_pointer = ¤t_prompt_string; - prompt_again (); - goto restart_read; - } +STREAM_SAVER *stream_list = (STREAM_SAVER *)NULL; - /* Add the newline to the end of this string, iff the string does - not already end in an EOF character. */ - if (shell_input_line_terminator != EOF) - { - if (shell_input_line_len + 3 > shell_input_line_size) - shell_input_line = xrealloc (shell_input_line, - 1 + (shell_input_line_size += 2)); +void +push_stream (reset_lineno) + int reset_lineno; +{ + STREAM_SAVER *saver = (STREAM_SAVER *)xmalloc (sizeof (STREAM_SAVER)); - shell_input_line[shell_input_line_len] = '\n'; - shell_input_line[shell_input_line_len + 1] = '\0'; - } - } + xbcopy ((char *)&bash_input, (char *)&(saver->bash_input), sizeof (BASH_INPUT)); - c = shell_input_line[shell_input_line_index]; +#if defined (BUFFERED_INPUT) + 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 = set_buffered_stream (bash_input.location.buffered_fd, + (BUFFERED_STREAM *)NULL); +#endif /* BUFFERED_INPUT */ - if (c) - shell_input_line_index++; + saver->line = line_number; + bash_input.name = (char *)NULL; + saver->next = stream_list; + stream_list = saver; + EOF_Reached = 0; + if (reset_lineno) + line_number = 0; +} - if (c == '\\' && remove_quoted_newline && - shell_input_line[shell_input_line_index] == '\n') +void +pop_stream () +{ + if (!stream_list) + EOF_Reached = 1; + else { - prompt_again (); - line_number++; - goto restart_read; - } + STREAM_SAVER *saver = stream_list; -#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. - Do it transparently; just return the next character of the string popped - to. */ - if (!c && (pushed_string_list != (STRING_SAVER *)NULL)) - { - if (mustpop) - { - pop_string (); - c = shell_input_line[shell_input_line_index]; - if (c) - shell_input_line_index++; - mustpop--; - } - else + EOF_Reached = 0; + stream_list = stream_list->next; + + init_yy_io (saver->bash_input.getter, + saver->bash_input.ungetter, + saver->bash_input.type, + saver->bash_input.name, + saver->bash_input.location); + +#if defined (BUFFERED_INPUT) + /* If we have a buffered stream, restore buffers[fd]. */ + /* If the input file descriptor was changed while this was on the + save stack, update the buffered fd to the new file descriptor and + re-establish the buffer <-> bash_input fd correspondence. */ + if (bash_input.type == st_bstream && bash_input.location.buffered_fd >= 0) { - mustpop++; - c = ' '; + if (bash_input_fd_changed) + { + bash_input_fd_changed = 0; + if (default_buffered_input >= 0) + { + bash_input.location.buffered_fd = default_buffered_input; + saver->bstream->b_fd = default_buffered_input; + SET_CLOSE_ON_EXEC (default_buffered_input); + } + } + /* XXX could free buffered stream returned as result here. */ + set_buffered_stream (bash_input.location.buffered_fd, saver->bstream); } - } -#endif /* ALIAS || DPAREN_ARITHMETIC */ +#endif /* BUFFERED_INPUT */ - if (!c && shell_input_line_terminator == EOF) - return ((shell_input_line_index != 0) ? '\n' : EOF); + line_number = saver->line; - return ((unsigned char)c); + FREE (saver->bash_input.name); + free (saver); + } } -/* Put C back into the input for the shell. */ -static void -shell_ungetc (c) - int c; +/* Return 1 if a stream of type TYPE is saved on the stack. */ +int +stream_on_stack (type) + enum stream_type type; { - if (shell_input_line && shell_input_line_index) - shell_input_line[--shell_input_line_index] = c; - else - eol_ungetc_lookahead = c; -} + register STREAM_SAVER *s; -static void -shell_ungetchar () -{ - if (shell_input_line && shell_input_line_index) - shell_input_line_index--; + for (s = stream_list; s; s = s->next) + if (s->bash_input.type == type) + return 1; + return 0; } -/* Discard input until CHARACTER is seen, then push that character back - onto the input stream. */ -static void -discard_until (character) - int character; +/* Save the current token state and return it in a malloced array. */ +int * +save_token_state () { - int c; - - while ((c = shell_getc (0)) != EOF && c != character) - ; + int *ret; - if (c != EOF) - shell_ungetc (c); + ret = (int *)xmalloc (3 * sizeof (int)); + ret[0] = last_read_token; + ret[1] = token_before_that; + ret[2] = two_tokens_ago; + return ret; } void -execute_prompt_command (command) - char *command; +restore_token_state (ts) + int *ts; { - Function *temp_last, *temp_this; - char *last_lastarg; - int temp_exit_value, temp_eof_encountered; - - temp_last = last_shell_builtin; - temp_this = this_shell_builtin; - temp_exit_value = last_command_exit_value; - temp_eof_encountered = eof_encountered; - last_lastarg = get_string_value ("_"); - if (last_lastarg) - last_lastarg = savestring (last_lastarg); + if (ts == 0) + return; + last_read_token = ts[0]; + token_before_that = ts[1]; + two_tokens_ago = ts[2]; +} - parse_and_execute (savestring (command), "PROMPT_COMMAND", SEVAL_NONINT|SEVAL_NOHIST); +/* + * This is used to inhibit alias expansion and reserved word recognition + * inside case statement pattern lists. A `case statement pattern list' is: + * + * everything between the `in' in a `case word in' and the next ')' + * or `esac' + * everything between a `;;' and the next `)' or `esac' + */ - last_shell_builtin = temp_last; - this_shell_builtin = temp_this; - last_command_exit_value = temp_exit_value; - eof_encountered = temp_eof_encountered; +#if defined (ALIAS) || defined (DPAREN_ARITHMETIC) - bind_variable ("_", last_lastarg); - FREE (last_lastarg); +#define END_OF_ALIAS 0 - if (token_to_read == '\n') /* reset_parser was called */ - token_to_read = 0; -} +/* + * Pseudo-global variables used in implementing token-wise alias expansion. + */ -/* Place to remember the token. We try to keep the buffer - at a reasonable size, but it can grow. */ -static char *token = (char *)NULL; +/* + * Pushing and popping strings. This works together with shell_getc to + * implement alias expansion on a per-token basis. + */ -/* Current size of the token buffer. */ -static int token_buffer_size; +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; -/* Command to read_token () explaining what we want it to do. */ -#define READ 0 -#define RESET 1 -#define prompt_is_ps1 \ - (!prompt_string_pointer || prompt_string_pointer == &ps1_prompt) +STRING_SAVER *pushed_string_list = (STRING_SAVER *)NULL; -/* Function for yyparse to call. yylex keeps track of - the last two tokens read, and calls read_token. */ -static int -yylex () +/* + * Push the current shell_input_line onto a stack of such lines and make S + * the current input. Used when expanding aliases. EXPAND is used to set + * the value of expand_next_token when the string is popped, so that the + * word after the alias in the original line is handled correctly when the + * alias expands to multiple words. TOKEN is the token that was expanded + * into S; it is saved and used to prevent infinite recursive expansion. + */ +static void +push_string (s, expand, ap) + char *s; + int expand; + alias_t *ap; { - if (interactive && (current_token == 0 || current_token == '\n')) - { - /* Before we print a prompt, we might have to check mailboxes. - We do this only if it is time to do so. Notice that only here - is the mail alarm reset; nothing takes place in check_mail () - except the checking of mail. Please don't change this. */ - if (prompt_is_ps1 && time_to_check_mail ()) - { - check_mail (); - reset_mail_timer (); - } + STRING_SAVER *temp = (STRING_SAVER *)xmalloc (sizeof (STRING_SAVER)); - /* Avoid printing a prompt if we're not going to read anything, e.g. - after resetting the parser with read_token (RESET). */ - if (token_to_read == 0 && interactive) - prompt_again (); - } + temp->expand_alias = expand; + temp->saved_line = shell_input_line; + 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; - two_tokens_ago = token_before_that; - token_before_that = last_read_token; - last_read_token = current_token; - current_token = read_token (READ); - return (current_token); -} +#if defined (ALIAS) + if (ap) + ap->flags |= AL_BEINGEXPANDED; +#endif -/* When non-zero, we have read the required tokens - which allow ESAC to be the next one read. */ -static int esacs_needed_count; + shell_input_line = s; + shell_input_line_size = strlen (s); + shell_input_line_index = 0; + shell_input_line_terminator = '\0'; +#if 0 + parser_state &= ~PST_ALEXPNEXT; /* XXX */ +#endif -void -gather_here_documents () -{ - int r = 0; - while (need_here_doc) - { - make_here_document (redir_stack[r++]); - need_here_doc--; - } + set_line_mbstate (); } -/* When non-zero, an open-brace used to create a group is awaiting a close - brace partner. */ -static int open_brace_count; +/* + * Make the top of the pushed_string stack be the current shell input. + * Only called when there is something on the stack. Called from shell_getc + * when it thinks it has consumed the string generated by an alias expansion + * and needs to return to the original input line. + */ +static void +pop_string () +{ + STRING_SAVER *t; -#define command_token_position(token) \ - (((token) == ASSIGNMENT_WORD) || \ - ((token) != SEMI_SEMI && reserved_word_acceptable(token))) + FREE (shell_input_line); + shell_input_line = pushed_string_list->saved_line; + shell_input_line_index = pushed_string_list->saved_line_index; + shell_input_line_size = pushed_string_list->saved_line_size; + shell_input_line_terminator = pushed_string_list->saved_line_terminator; -#define assignment_acceptable(token) command_token_position(token) && \ - ((parser_state & PST_CASEPAT) == 0) + if (pushed_string_list->expand_alias) + parser_state |= PST_ALEXPNEXT; + else + parser_state &= ~PST_ALEXPNEXT; -/* Check to see if TOKEN is a reserved word and return the token - value if it is. */ -#define CHECK_FOR_RESERVED_WORD(tok) \ - do { \ - if (!dollar_present && !quoted && \ - reserved_word_acceptable (last_read_token)) \ - { \ - int i; \ - for (i = 0; word_token_alist[i].word != (char *)NULL; i++) \ - if (STREQ (tok, word_token_alist[i].word)) \ - { \ - 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) \ - open_brace_count--; \ - return (word_token_alist[i].token); \ - } \ - } \ - } while (0) + t = pushed_string_list; + pushed_string_list = pushed_string_list->next; #if defined (ALIAS) + if (t->expander) + t->expander->flags &= ~AL_BEINGEXPANDED; +#endif - /* OK, we have a token. Let's try to alias expand it, if (and only if) - it's eligible. + free ((char *)t); - It is eligible for expansion if the shell is in interactive mode, and - the token is unquoted and the last token read was a command - separator (or expand_next_token is set), and we are currently - processing an alias (pushed_string_list is non-empty) and this - token is not the same as the current or any previously - processed alias. + set_line_mbstate (); +} - Special cases that disqualify: - In a pattern list in a case statement (parser_state & PST_CASEPAT). */ -static int -alias_expand_token (token) - char *token; +static void +free_string_list () { - char *expanded; - alias_t *ap; + register STRING_SAVER *t, *t1; - if (((parser_state & PST_ALEXPNEXT) || command_token_position (last_read_token)) && - (parser_state & PST_CASEPAT) == 0) + for (t = pushed_string_list; t; ) { - ap = find_alias (token); - - /* Currently expanding this token. */ - if (ap && (ap->flags & AL_BEINGEXPANDED)) - return (NO_EXPANSION); - - expanded = ap ? savestring (ap->value) : (char *)NULL; - if (expanded) - { - push_string (expanded, ap->flags & AL_EXPANDNEXT, ap); - return (RE_READ_TOKEN); - } - else - /* This is an eligible token that does not have an expansion. */ - return (NO_EXPANSION); + t1 = t->next; + FREE (t->saved_line); +#if defined (ALIAS) + if (t->expander) + t->expander->flags &= ~AL_BEINGEXPANDED; +#endif + free ((char *)t); + t = t1; } - return (NO_EXPANSION); + pushed_string_list = (STRING_SAVER *)NULL; } -#endif /* ALIAS */ -static int -time_command_acceptable () +#endif /* ALIAS || DPAREN_ARITHMETIC */ + +void +free_pushed_string_input () { -#if defined (COMMAND_TIMING) - switch (last_read_token) - { - case 0: - case ';': - case '\n': - case AND_AND: - case OR_OR: - case '&': - case DO: - case THEN: - case ELSE: - case '{': /* } */ - case '(': /* ) */ - return 1; - default: - return 0; - } -#else - return 0; -#endif /* COMMAND_TIMING */ +#if defined (ALIAS) || defined (DPAREN_ARITHMETIC) + free_string_list (); +#endif } -/* 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. - - DO is recognized if the last token was WORD and the token - before that was FOR or SELECT. +/* 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 + is non-zero, we remove unquoted \ pairs. This is used by + read_secondary_line to read here documents. */ +static char * +read_a_line (remove_quoted_newline) + int remove_quoted_newline; +{ + static char *line_buffer = (char *)NULL; + static int buffer_size = 0; + int indx = 0, c, peekc, pass_next; - ESAC is recognized if the last token caused `esacs_needed_count' - to be set +#if defined (READLINE) + if (no_line_editing && SHOULD_PROMPT ()) +#else + if (SHOULD_PROMPT ()) +#endif + print_prompt (); - `{' is recognized if the last token as WORD and the token - before that was FUNCTION, or if we just parsed an arithmetic - `for' command. + pass_next = 0; + while (1) + { + /* Allow immediate exit if interrupted during input. */ + QUIT; - `}' is recognized if there is an unclosed `{' present. + c = yy_getc (); - `-p' is returned as TIMEOPT if the last read token was TIME. + /* Ignore null bytes in input. */ + if (c == 0) + { +#if 0 + internal_warning ("read_a_line: ignored null byte in input"); +#endif + continue; + } - ']]' is returned as COND_END if the parser is currently parsing - a conditional expression ((parser_state & PST_CONDEXPR) != 0) + /* If there is no more input, then we return NULL. */ + if (c == EOF) + { + if (interactive && bash_input.type == st_stream) + clearerr (stdin); + if (indx == 0) + return ((char *)NULL); + c = '\n'; + } - `time' is returned as TIME if and only if it is immediately - preceded by one of `;', `\n', `||', `&&', or `&'. -*/ + /* `+2' in case the final character in the buffer is a newline. */ + RESIZE_MALLOCED_BUFFER (line_buffer, indx, 2, buffer_size, 128); -static int -special_case_tokens (token) - char *token; -{ - if ((last_read_token == WORD) && -#if defined (SELECT_COMMAND) - ((token_before_that == FOR) || (token_before_that == CASE) || (token_before_that == SELECT)) && -#else - ((token_before_that == FOR) || (token_before_that == CASE)) && -#endif - (token[0] == 'i' && token[1] == 'n' && token[2] == 0)) - { - if (token_before_that == CASE) + /* IF REMOVE_QUOTED_NEWLINES is non-zero, we are reading a + here document with an unquoted delimiter. In this case, + the line will be expanded as if it were in double quotes. + We allow a backslash to escape the next character, but we + need to treat the backslash specially only if a backslash + quoting a backslash-newline pair appears in the line. */ + if (pass_next) { - parser_state |= PST_CASEPAT; - esacs_needed_count++; + line_buffer[indx++] = c; + pass_next = 0; } - return (IN); - } - - if (last_read_token == WORD && -#if defined (SELECT_COMMAND) - (token_before_that == FOR || token_before_that == SELECT) && -#else - (token_before_that == FOR) && -#endif - (token[0] == 'd' && token[1] == 'o' && token[2] == '\0')) - return (DO); - - /* Ditto for ESAC in the CASE case. - Specifically, this handles "case word in esac", which is a legal - construct, certainly because someone will pass an empty arg to the - case construct, and we don't want it to barf. Of course, we should - insist that the case construct has at least one pattern in it, but - the designers disagree. */ - if (esacs_needed_count) - { - esacs_needed_count--; - if (STREQ (token, "esac")) + else if (c == '\\' && remove_quoted_newline) { - parser_state &= ~PST_CASEPAT; - return (ESAC); + peekc = yy_getc (); + if (peekc == '\n') + { + line_number++; + continue; /* Make the unquoted \ pair disappear. */ + } + else + { + yy_ungetc (peekc); + pass_next = 1; + line_buffer[indx++] = c; /* Preserve the backslash. */ + } } - } + else + line_buffer[indx++] = c; - /* The start of a shell function definition. */ - if (parser_state & PST_ALLOWOPNBRC) - { - parser_state &= ~PST_ALLOWOPNBRC; - if (token[0] == '{' && token[1] == '\0') /* } */ + if (c == '\n') { - open_brace_count++; - function_bstart = line_number; - return ('{'); /* } */ + line_buffer[indx] = '\0'; + return (line_buffer); } } +} - /* We allow a `do' after a for ((...)) without an intervening - list_terminator */ - if (last_read_token == ARITH_FOR_EXPRS && token[0] == 'd' && token[1] == 'o' && !token[2]) - return (DO); - if (last_read_token == ARITH_FOR_EXPRS && token[0] == '{' && token[1] == '\0') /* } */ - { - open_brace_count++; - return ('{'); /* } */ - } +/* Return a line as in read_a_line (), but insure that the prompt is + the secondary prompt. This is used to read the lines of a here + document. REMOVE_QUOTED_NEWLINE is non-zero if we should remove + newlines quoted with backslashes while reading the line. It is + non-zero unless the delimiter of the here document was quoted. */ +char * +read_secondary_line (remove_quoted_newline) + int remove_quoted_newline; +{ + prompt_string_pointer = &ps2_prompt; + if (SHOULD_PROMPT()) + prompt_again (); + return (read_a_line (remove_quoted_newline)); +} - if (open_brace_count && reserved_word_acceptable (last_read_token) && token[0] == '}' && !token[1]) - { - open_brace_count--; /* { */ - return ('}'); - } +/* **************************************************************** */ +/* */ +/* YYLEX () */ +/* */ +/* **************************************************************** */ +/* Reserved words. These are only recognized as the first word of a + command. */ +STRING_INT_ALIST word_token_alist[] = { + { "if", IF }, + { "then", THEN }, + { "else", ELSE }, + { "elif", ELIF }, + { "fi", FI }, + { "case", CASE }, + { "esac", ESAC }, + { "for", FOR }, +#if defined (SELECT_COMMAND) + { "select", SELECT }, +#endif + { "while", WHILE }, + { "until", UNTIL }, + { "do", DO }, + { "done", DONE }, + { "in", IN }, + { "function", FUNCTION }, #if defined (COMMAND_TIMING) - /* Handle -p after `time'. */ - if (last_read_token == TIME && token[0] == '-' && token[1] == 'p' && !token[2]) - return (TIMEOPT); + { "time", TIME }, +#endif + { "{", '{' }, + { "}", '}' }, + { "!", BANG }, +#if defined (COND_COMMAND) + { "[[", COND_START }, + { "]]", COND_END }, #endif + { (char *)NULL, 0} +}; -#if defined (COMMAND_TIMING) - if (STREQ (token, "time") && ((parser_state & PST_CASEPAT) == 0) && time_command_acceptable ()) - return (TIME); -#endif /* COMMAND_TIMING */ +/* other tokens that can be returned by read_token() */ +STRING_INT_ALIST other_token_alist[] = { + /* Multiple-character tokens with special values */ + { "-p", TIMEOPT }, + { "&&", AND_AND }, + { "||", OR_OR }, + { ">>", GREATER_GREATER }, + { "<<", LESS_LESS }, + { "<&", LESS_AND }, + { ">&", GREATER_AND }, + { ";;", SEMI_SEMI }, + { "<<-", LESS_LESS_MINUS }, + { "<<<", LESS_LESS_LESS }, + { "&>", AND_GREATER }, + { "<>", LESS_GREATER }, + { ">|", GREATER_BAR }, + { "EOF", yacc_EOF }, + /* Tokens whose value is the character itself */ + { ">", '>' }, + { "<", '<' }, + { "-", '-' }, + { "{", '{' }, + { "}", '}' }, + { ";", ';' }, + { "(", '(' }, + { ")", ')' }, + { "|", '|' }, + { "&", '&' }, + { "newline", '\n' }, + { (char *)NULL, 0} +}; -#if defined (COND_COMMAND) /* [[ */ - if ((parser_state & PST_CONDEXPR) && token[0] == ']' && token[1] == ']' && token[2] == '\0') - return (COND_END); -#endif +/* others not listed here: + WORD look at yylval.word + ASSIGNMENT_WORD look at yylval.word + NUMBER look at yylval.number + ARITH_CMD look at yylval.word_list + ARITH_FOR_EXPRS look at yylval.word_list + COND_CMD look at yylval.command +*/ - return (-1); -} +/* These are used by read_token_word, but appear up here so that shell_getc + can use them to decide when to add otherwise blank lines to the history. */ -/* Called from shell.c when Control-C is typed at top level. Or - by the error rule at top level. */ -void -reset_parser () -{ - dstack.delimiter_depth = 0; /* No delimiters found so far. */ - open_brace_count = 0; +/* The primary delimiter stack. */ +struct dstack dstack = { (char *)NULL, 0, 0 }; - parser_state = 0; +/* A temporary delimiter stack to be used when decoding prompt strings. + This is needed because command substitutions in prompt strings (e.g., PS2) + can screw up the parser's quoting state. */ +static struct dstack temp_dstack = { (char *)NULL, 0, 0 }; -#if defined (ALIAS) || defined (DPAREN_ARITHMETIC) - if (pushed_string_list) - free_string_list (); -#endif /* ALIAS || DPAREN_ARITHMETIC */ +/* Macro for accessing the top delimiter on the stack. Returns the + delimiter or zero if none. */ +#define current_delimiter(ds) \ + (ds.delimiter_depth ? ds.delimiters[ds.delimiter_depth - 1] : 0) - if (shell_input_line) - { - free (shell_input_line); - shell_input_line = (char *)NULL; - shell_input_line_size = shell_input_line_index = 0; - } +#define push_delimiter(ds, character) \ + do \ + { \ + if (ds.delimiter_depth + 2 > ds.delimiter_space) \ + ds.delimiters = (char *)xrealloc \ + (ds.delimiters, (ds.delimiter_space += 10) * sizeof (char)); \ + ds.delimiters[ds.delimiter_depth] = character; \ + ds.delimiter_depth++; \ + } \ + while (0) - FREE (word_desc_to_read); - word_desc_to_read = (WORD_DESC *)NULL; +#define pop_delimiter(ds) ds.delimiter_depth-- - last_read_token = '\n'; - token_to_read = '\n'; -} +/* Return the next shell input character. This always reads characters + from shell_input_line; when that line is exhausted, it is time to + read the next line. This is called by read_token when the shell is + processing normal command input. */ + +/* This implements one-character lookahead/lookbehind across physical input + lines, to avoid something being lost because it's pushed back with + shell_ungetc when we're at the start of a line. */ +static int eol_ungetc_lookahead = 0; -/* Read the next token. Command can be READ (normal operation) or - RESET (to normalize state). */ static int -read_token (command) - int command; +shell_getc (remove_quoted_newline) + int remove_quoted_newline; { - int character; /* Current character. */ - int peek_char; /* Temporary look-ahead character. */ - int result; /* The thing to return. */ + register int i; + int c; + unsigned char uc; + static int mustpop = 0; - if (command == RESET) + QUIT; + + if (sigwinch_received) { - reset_parser (); - return ('\n'); + sigwinch_received = 0; + get_new_window_size (0, (int *)0, (int *)0); } - - if (token_to_read) + + if (eol_ungetc_lookahead) { - result = token_to_read; - if (token_to_read == WORD || token_to_read == ASSIGNMENT_WORD) - { - yylval.word = word_desc_to_read; - word_desc_to_read = (WORD_DESC *)NULL; - } - token_to_read = 0; - return (result); + c = eol_ungetc_lookahead; + eol_ungetc_lookahead = 0; + return (c); } -#if defined (COND_COMMAND) - if ((parser_state & (PST_CONDCMD|PST_CONDEXPR)) == PST_CONDCMD) +#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 && !DPAREN_ARITHMETIC */ + if (!shell_input_line || !shell_input_line[shell_input_line_index]) +#endif /* !ALIAS && !DPAREN_ARITHMETIC */ { - cond_lineno = line_number; - parser_state |= PST_CONDEXPR; - yylval.command = parse_cond_command (); - if (cond_token != COND_END) + line_number++; + + restart_read: + + /* Allow immediate exit if interrupted during input. */ + QUIT; + + i = 0; + shell_input_line_terminator = 0; + + /* If the shell is interatctive, but not currently printing a prompt + (interactive_shell && interactive == 0), we don't want to print + notifies or cleanup the jobs -- we want to defer it until we do + print the next prompt. */ + if (interactive_shell == 0 || SHOULD_PROMPT()) { - 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); +#if defined (JOB_CONTROL) + /* This can cause a problem when reading a command as the result + of a trap, when the trap is called from flush_child. This call + had better not cause jobs to disappear from the job table in + that case, or we will have big trouble. */ + notify_and_cleanup (); +#else /* !JOB_CONTROL */ + cleanup_dead_jobs (); +#endif /* !JOB_CONTROL */ } - token_to_read = COND_END; - parser_state &= ~(PST_CONDEXPR|PST_CONDCMD); - return (COND_CMD); - } + +#if defined (READLINE) + if (no_line_editing && SHOULD_PROMPT()) +#else + if (SHOULD_PROMPT()) #endif + print_prompt (); -#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 () */ - re_read_token: -#endif /* ALIAS */ - - /* Read a single word from input. Start by skipping blanks. */ - while ((character = shell_getc (1)) != EOF && whitespace (character)) - ; + if (bash_input.type == st_stream) + clearerr (stdin); - if (character == EOF) - { - EOF_Reached = 1; - return (yacc_EOF); - } + while (1) + { + c = yy_getc (); - if (character == '#' && (!interactive || interactive_comments)) - { - /* A comment. Discard until EOL or EOF, and then return a newline. */ - discard_until ('\n'); - shell_getc (0); - character = '\n'; /* this will take the next if statement and return. */ - } + /* Allow immediate exit if interrupted during input. */ + QUIT; - if (character == '\n') - { - /* If we're about to return an unquoted newline, we can go and collect - the text of any pending here document. */ - if (need_here_doc) - gather_here_documents (); + if (c == '\0') + { +#if 0 + internal_warning ("shell_getc: ignored null byte in input"); +#endif + continue; + } -#if defined (ALIAS) - parser_state &= ~PST_ALEXPNEXT; -#endif /* ALIAS */ + RESIZE_MALLOCED_BUFFER (shell_input_line, i, 2, shell_input_line_size, 256); - return (character); - } + if (c == EOF) + { + if (bash_input.type == st_stream) + clearerr (stdin); - /* Shell meta-characters. */ - if (shellmeta (character) && ((parser_state & PST_DBLPAREN) == 0)) - { -#if defined (ALIAS) - /* Turn off alias tokenization iff this character sequence would - not leave us ready to read a command. */ - if (character == '<' || character == '>') - parser_state &= ~PST_ALEXPNEXT; -#endif /* ALIAS */ + if (i == 0) + shell_input_line_terminator = EOF; - peek_char = shell_getc (1); - if (character == peek_char) - { - switch (character) - { - case '<': - /* If '<' then we could be at "<<" or at "<<-". We have to - look ahead one more character. */ - peek_char = shell_getc (1); - if (peek_char == '-') - return (LESS_LESS_MINUS); - else - { - shell_ungetc (peek_char); - return (LESS_LESS); - } + shell_input_line[i] = '\0'; + break; + } - case '>': - return (GREATER_GREATER); + shell_input_line[i++] = c; - case ';': - parser_state |= PST_CASEPAT; -#if defined (ALIAS) - parser_state &= ~PST_ALEXPNEXT; -#endif /* ALIAS */ - return (SEMI_SEMI); + if (c == '\n') + { + shell_input_line[--i] = '\0'; + current_command_line_count++; + break; + } + } - case '&': - return (AND_AND); + shell_input_line_index = 0; + shell_input_line_len = i; /* == strlen (shell_input_line) */ - case '|': - return (OR_OR); + set_line_mbstate (); -#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; +#if defined (HISTORY) + if (remember_on_history && shell_input_line && shell_input_line[0]) + { + char *expansions; +# if defined (BANG_HISTORY) + int old_hist; - 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 */ - } + /* If the current delimiter is a single quote, we should not be + performing history expansion, even if we're on a different + line from the original single quote. */ + old_hist = history_expansion_inhibited; + if (current_delimiter (dstack) == '\'') + history_expansion_inhibited = 1; # endif -# if defined (DPAREN_ARITHMETIC) - if (reserved_word_acceptable (last_read_token)) - { - int cmdtyp, sline; - char *wval; - WORD_DESC *wd; - - sline = line_number; - cmdtyp = parse_arith_cmd (&wval); - if (cmdtyp == 1) /* arithmetic command */ - { - 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 */ - { - push_string (wval, 0, (alias_t *)NULL); - if ((parser_state & PST_CASEPAT) == 0) - parser_state |= PST_SUBSHELL; - return (character); - } - else /* ERROR */ - return -1; - } - break; + expansions = pre_process_line (shell_input_line, 1, 1); +# if defined (BANG_HISTORY) + history_expansion_inhibited = old_hist; # endif -#endif + if (expansions != shell_input_line) + { + free (shell_input_line); + shell_input_line = expansions; + shell_input_line_len = shell_input_line ? + strlen (shell_input_line) : 0; + if (!shell_input_line_len) + current_command_line_count--; + + /* We have to force the xrealloc below because we don't know + the true allocated size of shell_input_line anymore. */ + shell_input_line_size = shell_input_line_len; + + set_line_mbstate (); + } + } + /* Try to do something intelligent with blank lines encountered while + entering multi-line commands. XXX - this is grotesque */ + else if (remember_on_history && shell_input_line && + shell_input_line[0] == '\0' && + current_command_line_count > 1) + { + if (current_delimiter (dstack)) + /* We know shell_input_line[0] == 0 and we're reading some sort of + quoted string. This means we've got a line consisting of only + a newline in a quoted string. We want to make sure this line + gets added to the history. */ + maybe_add_history (shell_input_line); + else + { + char *hdcs; + hdcs = history_delimiting_chars (); + if (hdcs && hdcs[0] == ';') + maybe_add_history (shell_input_line); } } - else if (character == '<' && peek_char == '&') - return (LESS_AND); - else if (character == '>' && peek_char == '&') - return (GREATER_AND); - else if (character == '<' && peek_char == '>') - return (LESS_GREATER); - else if (character == '>' && peek_char == '|') - return (GREATER_BAR); - else if (peek_char == '>' && character == '&') - return (AND_GREATER); - shell_ungetc (peek_char); +#endif /* HISTORY */ - /* If we look like we are reading the start of a function - definition, then let the reader know about it so that - we will do the right thing with `{'. */ - if (character == ')' && last_read_token == '(' && token_before_that == WORD) + if (shell_input_line) { - parser_state |= PST_ALLOWOPNBRC; -#if defined (ALIAS) - parser_state &= ~PST_ALEXPNEXT; -#endif /* ALIAS */ - function_dstart = line_number; + /* Lines that signify the end of the shell's input should not be + echoed. */ + if (echo_input_at_read && (shell_input_line[0] || + shell_input_line_terminator != EOF)) + fprintf (stderr, "%s\n", shell_input_line); + } + else + { + shell_input_line_size = 0; + prompt_string_pointer = ¤t_prompt_string; + if (SHOULD_PROMPT ()) + prompt_again (); + goto restart_read; } - /* case pattern lists may be preceded by an optional left paren. If - we're not trying to parse a case pattern list, the left paren - indicates a subshell. */ - if (character == '(' && (parser_state & PST_CASEPAT) == 0) /* ) */ - parser_state |= PST_SUBSHELL; - /*(*/ - else if ((parser_state & PST_CASEPAT) && character == ')') - parser_state &= ~PST_CASEPAT; - /*(*/ - else if ((parser_state & PST_SUBSHELL) && character == ')') - parser_state &= ~PST_SUBSHELL; + /* Add the newline to the end of this string, iff the string does + not already end in an EOF character. */ + if (shell_input_line_terminator != EOF) + { + if (shell_input_line_len + 3 > shell_input_line_size) + shell_input_line = (char *)xrealloc (shell_input_line, + 1 + (shell_input_line_size += 2)); -#if defined (PROCESS_SUBSTITUTION) - /* Check for the constructs which introduce process substitution. - Shells running in `posix mode' don't do process substitution. */ - if (posixly_correct || - ((character != '>' && character != '<') || peek_char != '(')) -#endif /* PROCESS_SUBSTITUTION */ - return (character); + shell_input_line[shell_input_line_len] = '\n'; + shell_input_line[shell_input_line_len + 1] = '\0'; + + set_line_mbstate (); + } } - /* Hack <&- (close stdin) case. */ - if (character == '-' && (last_read_token == LESS_AND || last_read_token == GREATER_AND)) - return (character); + uc = shell_input_line[shell_input_line_index]; - /* Okay, if we got this far, we have to read a word. Read one, - and then check it against the known ones. */ - result = read_token_word (character); -#if defined (ALIAS) - if (result == RE_READ_TOKEN) - goto re_read_token; -#endif - return result; + if (uc) + shell_input_line_index++; + +#if defined (ALIAS) || defined (DPAREN_ARITHMETIC) + /* If UC 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. + Do it transparently; just return the next character of the string popped + to. */ + if (!uc && (pushed_string_list != (STRING_SAVER *)NULL)) + { + pop_string (); + uc = shell_input_line[shell_input_line_index]; + if (uc) + shell_input_line_index++; + } +#endif /* ALIAS || DPAREN_ARITHMETIC */ + + if MBTEST(uc == '\\' && remove_quoted_newline && shell_input_line[shell_input_line_index] == '\n') + { + if (SHOULD_PROMPT ()) + prompt_again (); + line_number++; + goto restart_read; + } + + if (!uc && shell_input_line_terminator == EOF) + return ((shell_input_line_index != 0) ? '\n' : EOF); + + return (uc); +} + +/* Put C back into the input for the shell. This might need changes for + HANDLE_MULTIBYTE around EOLs. Since we (currently) never push back a + character different than we read, shell_input_line_property doesn't need + to change when manipulating shell_input_line. The define for + last_shell_getc_is_singlebyte should take care of it, though. */ +static void +shell_ungetc (c) + int c; +{ + if (shell_input_line && shell_input_line_index) + shell_input_line[--shell_input_line_index] = c; + else + eol_ungetc_lookahead = c; +} + +#ifdef INCLUDE_UNUSED +/* Back the input pointer up by one, effectively `ungetting' a character. */ +static void +shell_ungetchar () +{ + if (shell_input_line && shell_input_line_index) + shell_input_line_index--; } +#endif -/* Match a $(...) or other grouping construct. This has to handle embedded - quoted strings ('', ``, "") and nested constructs. It also must handle - reprompting the user, if necessary, after reading a newline, and returning - correct error values if it reads EOF. */ +/* Discard input until CHARACTER is seen, then push that character back + onto the input stream. */ +static void +discard_until (character) + int character; +{ + int c; -#define P_FIRSTCLOSE 0x01 -#define P_ALLOWESC 0x02 + while ((c = shell_getc (0)) != EOF && c != character) + ; -static char matched_pair_error; -static char * -parse_matched_pair (qc, open, close, lenp, flags) - int qc; /* `"' if this construct is within double quotes */ - int open, close; - int *lenp, flags; + if (c != EOF) + shell_ungetc (c); +} + +void +execute_prompt_command (command) + char *command; { - int count, ch, was_dollar; - int pass_next_character, nestlen, ttranslen, start_lineno; - char *ret, *nestret, *ttrans; - int retind, retsize; + char *last_lastarg; + sh_parser_state_t ps; - count = 1; - pass_next_character = was_dollar = 0; + save_parser_state (&ps); + last_lastarg = get_string_value ("_"); + if (last_lastarg) + last_lastarg = savestring (last_lastarg); - ret = xmalloc (retsize = 64); - retind = 0; + parse_and_execute (savestring (command), "PROMPT_COMMAND", SEVAL_NONINT|SEVAL_NOHIST); - start_lineno = line_number; - while (count) - { - ch = shell_getc ((qc != '\'' || (flags & P_ALLOWESC)) && pass_next_character == 0); - if (ch == EOF) - { - free (ret); - parser_error (start_lineno, "unexpected EOF while looking for matching `%c'", close); - EOF_Reached = 1; /* XXX */ - return (&matched_pair_error); - } + restore_parser_state (&ps); + bind_variable ("_", last_lastarg, 0); + FREE (last_lastarg); - /* Possible reprompting. */ - if (ch == '\n' && interactive && - (bash_input.type == st_stdin || bash_input.type == st_stream)) - prompt_again (); + if (token_to_read == '\n') /* reset_parser was called */ + token_to_read = 0; +} - if (pass_next_character) /* last char was backslash */ - { - pass_next_character = 0; - if (qc != '\'' && ch == '\n') /* double-quoted \ disappears. */ - { - if (retind > 0) retind--; /* swallow previously-added backslash */ - continue; - } +/* Place to remember the token. We try to keep the buffer + at a reasonable size, but it can grow. */ +static char *token = (char *)NULL; - RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64); - if (ch == CTLESC || ch == CTLNUL) - ret[retind++] = CTLESC; - ret[retind++] = ch; - continue; - } - else if (ch == CTLESC || ch == CTLNUL) /* special shell escapes */ - { - RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64); - ret[retind++] = CTLESC; - ret[retind++] = ch; - continue; - } - 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++; +/* Current size of the token buffer. */ +static int token_buffer_size; - /* Add this character. */ - RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64); - ret[retind++] = ch; +/* Command to read_token () explaining what we want it to do. */ +#define READ 0 +#define RESET 1 +#define prompt_is_ps1 \ + (!prompt_string_pointer || prompt_string_pointer == &ps1_prompt) - if (open == '\'') /* '' inside grouping construct */ +/* Function for yyparse to call. yylex keeps track of + the last two tokens read, and calls read_token. */ +static int +yylex () +{ + if (interactive && (current_token == 0 || current_token == '\n')) + { + /* Before we print a prompt, we might have to check mailboxes. + We do this only if it is time to do so. Notice that only here + is the mail alarm reset; nothing takes place in check_mail () + except the checking of mail. Please don't change this. */ + if (prompt_is_ps1 && time_to_check_mail ()) { - if ((flags & P_ALLOWESC) && ch == '\\') - pass_next_character++; - continue; + check_mail (); + reset_mail_timer (); } - if (ch == '\\') /* backslashes */ - pass_next_character++; - - if (open != close) /* a grouping construct */ - { - if (shellquote (ch)) - { - /* '', ``, or "" inside $(...) or other grouping construct. */ - push_delimiter (dstack, ch); - if (was_dollar && ch == '\'') /* $'...' inside group */ - nestret = parse_matched_pair (ch, ch, ch, &nestlen, P_ALLOWESC); - else - nestret = parse_matched_pair (ch, ch, ch, &nestlen, 0); - pop_delimiter (dstack); - if (nestret == &matched_pair_error) - { - free (ret); - return &matched_pair_error; - } - if (was_dollar && ch == '\'') - { - /* Translate $'...' here. */ - ttrans = ansiexpand (nestret, 0, nestlen - 1, &ttranslen); - free (nestret); - nestret = sh_single_quote (ttrans); - free (ttrans); - nestlen = strlen (nestret); - retind -= 2; /* back up before the $' */ - } - else if (was_dollar && ch == '"') - { - /* Locale expand $"..." here. */ - ttrans = localeexpand (nestret, 0, nestlen - 1, start_lineno, &ttranslen); - free (nestret); - nestret = xmalloc (ttranslen + 3); - nestret[0] = '"'; - strcpy (nestret + 1, ttrans); - nestret[ttranslen + 1] = '"'; - nestret[ttranslen += 2] = '\0'; - free (ttrans); - nestlen = ttranslen; - retind -= 2; /* back up before the $" */ - } - if (nestlen) - { - RESIZE_MALLOCED_BUFFER (ret, retind, nestlen, retsize, 64); - strcpy (ret + retind, nestret); - retind += nestlen; - } - FREE (nestret); - } - } - /* Parse an old-style command substitution within double quotes as a - single word. */ - /* XXX - sh and ksh93 don't do this - XXX */ - else if (open == '"' && ch == '`') - { - nestret = parse_matched_pair (0, '`', '`', &nestlen, 0); - if (nestret == &matched_pair_error) - { - free (ret); - return &matched_pair_error; - } - if (nestlen) - { - RESIZE_MALLOCED_BUFFER (ret, retind, nestlen, retsize, 64); - strcpy (ret + retind, nestret); - retind += nestlen; - } - FREE (nestret); - } - else if (was_dollar && (ch == '(' || ch == '{' || ch == '[')) /* ) } ] */ - /* check for $(), $[], or ${} inside quoted string. */ - { - if (open == ch) /* undo previous increment */ - count--; - if (ch == '(') /* ) */ - nestret = parse_matched_pair (0, '(', ')', &nestlen, 0); - else if (ch == '{') /* } */ - nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE); - else if (ch == '[') /* ] */ - nestret = parse_matched_pair (0, '[', ']', &nestlen, 0); - if (nestret == &matched_pair_error) - { - free (ret); - return &matched_pair_error; - } - if (nestlen) - { - RESIZE_MALLOCED_BUFFER (ret, retind, nestlen, retsize, 64); - strcpy (ret + retind, nestret); - retind += nestlen; - } - FREE (nestret); - } - was_dollar = (ch == '$'); + /* Avoid printing a prompt if we're not going to read anything, e.g. + after resetting the parser with read_token (RESET). */ + if (token_to_read == 0 && SHOULD_PROMPT ()) + prompt_again (); } - ret[retind] = '\0'; - if (lenp) - *lenp = retind; - return ret; + two_tokens_ago = token_before_that; + token_before_that = last_read_token; + last_read_token = current_token; + current_token = read_token (READ); + return (current_token); } -#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- - allocated buffer and make *ep point to that buffer. Return -1 on an - error, for example EOF. */ -static int -parse_arith_cmd (ep) - char **ep; +/* When non-zero, we have read the required tokens + which allow ESAC to be the next one read. */ +static int esacs_needed_count; + +void +gather_here_documents () { - int exp_lineno, rval, c; - char *ttok, *token; - int ttoklen; + int r = 0; + while (need_here_doc) + { + make_here_document (redir_stack[r++]); + need_here_doc--; + } +} + +/* When non-zero, an open-brace used to create a group is awaiting a close + brace partner. */ +static int open_brace_count; + +#define command_token_position(token) \ + (((token) == ASSIGNMENT_WORD) || \ + ((token) != SEMI_SEMI && reserved_word_acceptable(token))) + +#define assignment_acceptable(token) \ + (command_token_position(token) && ((parser_state & PST_CASEPAT) == 0)) + +/* Check to see if TOKEN is a reserved word and return the token + value if it is. */ +#define CHECK_FOR_RESERVED_WORD(tok) \ + do { \ + if (!dollar_present && !quoted && \ + reserved_word_acceptable (last_read_token)) \ + { \ + int i; \ + for (i = 0; word_token_alist[i].word != (char *)NULL; i++) \ + if (STREQ (tok, word_token_alist[i].word)) \ + { \ + if ((parser_state & PST_CASEPAT) && (word_token_alist[i].token != ESAC)) \ + break; \ + if (word_token_alist[i].token == TIME && time_command_acceptable () == 0) \ + 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) \ + open_brace_count--; \ + return (word_token_alist[i].token); \ + } \ + } \ + } while (0) - exp_lineno = line_number; - ttok = parse_matched_pair (0, '(', ')', &ttoklen, 0); - rval = 1; - if (ttok == &matched_pair_error) - return -1; - /* Check that the next character is the closing right paren. If - not, this is a syntax error. ( */ - if ((c = shell_getc (0)) != ')') - rval = 0; +#if defined (ALIAS) - token = xmalloc (ttoklen + 4); + /* OK, we have a token. Let's try to alias expand it, if (and only if) + it's eligible. - /* (( ... )) -> "..." */ - token[0] = (rval == 1) ? '"' : '('; - strncpy (token + 1, ttok, ttoklen - 1); /* don't copy the final `)' */ - if (rval == 1) - { - token[ttoklen] = '"'; - token[ttoklen+1] = '\0'; - } - else - { - token[ttoklen] = ')'; - token[ttoklen+1] = c; - token[ttoklen+2] = '\0'; - } - *ep = token; - FREE (ttok); - return rval; -} -#endif /* DPAREN_ARITHMETIC || ARITH_FOR_COMMAND */ + It is eligible for expansion if EXPAND_ALIASES is set, and + the token is unquoted and the last token read was a command + separator (or expand_next_token is set), and we are currently + processing an alias (pushed_string_list is non-empty) and this + token is not the same as the current or any previously + processed alias. -#if defined (COND_COMMAND) -static COND_COM *cond_term (); -static COND_COM *cond_and (); -static COND_COM *cond_or (); -static COND_COM *cond_expr (); + Special cases that disqualify: + In a pattern list in a case statement (parser_state & PST_CASEPAT). */ -static COND_COM * -cond_expr () +static char * +mk_alexpansion (s) + char *s; { - return (cond_or ()); + int l; + char *r; + + l = strlen (s); + r = xmalloc (l + 2); + strcpy (r, s); + if (r[l -1] != ' ') + r[l++] = ' '; + r[l] = '\0'; + return r; } -static COND_COM * -cond_or () +static int +alias_expand_token (tokstr) + char *tokstr; { - COND_COM *l, *r; + char *expanded; + alias_t *ap; - l = cond_and (); - if (cond_token == OR_OR) + if (((parser_state & PST_ALEXPNEXT) || command_token_position (last_read_token)) && + (parser_state & PST_CASEPAT) == 0) { - r = cond_or (); - l = make_cond_node (COND_OR, (WORD_DESC *)NULL, l, r); - } - return l; -} + ap = find_alias (tokstr); -static COND_COM * -cond_and () -{ - COND_COM *l, *r; + /* Currently expanding this token. */ + if (ap && (ap->flags & AL_BEINGEXPANDED)) + return (NO_EXPANSION); - l = cond_term (); - if (cond_token == AND_AND) - { - r = cond_and (); - l = make_cond_node (COND_AND, (WORD_DESC *)NULL, l, r); + /* mk_alexpansion puts an extra space on the end of the alias expansion, + so the lookahead by the parser works right. If this gets changed, + make sure the code in shell_getc that deals with reaching the end of + an expanded alias is changed with it. */ + expanded = ap ? mk_alexpansion (ap->value) : (char *)NULL; + + if (expanded) + { + push_string (expanded, ap->flags & AL_EXPANDNEXT, ap); + return (RE_READ_TOKEN); + } + else + /* This is an eligible token that does not have an expansion. */ + return (NO_EXPANSION); } - return l; + return (NO_EXPANSION); } +#endif /* ALIAS */ static int -cond_skip_newlines () +time_command_acceptable () { - while ((cond_token = read_token (READ)) == '\n') +#if defined (COMMAND_TIMING) + switch (last_read_token) { - if (interactive && (bash_input.type == st_stdin || bash_input.type == st_stream)) - prompt_again (); + case 0: + case ';': + case '\n': + case AND_AND: + case OR_OR: + case '&': + case DO: + case THEN: + case ELSE: + case '{': /* } */ + case '(': /* ) */ + return 1; + default: + return 0; } - return (cond_token); +#else + return 0; +#endif /* COMMAND_TIMING */ } -#define COND_RETURN_ERROR() \ - do { cond_token = COND_ERROR; return ((COND_COM *)NULL); } while (0) +/* 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. -static COND_COM * -cond_term () + DO is recognized if the last token was WORD and the token + before that was FOR or SELECT. + + ESAC is recognized if the last token caused `esacs_needed_count' + to be set + + `{' is recognized if the last token as WORD and the token + before that was FUNCTION, or if we just parsed an arithmetic + `for' command. + + `}' is recognized if there is an unclosed `{' present. + + `-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 +special_case_tokens (tokstr) + char *tokstr; { - WORD_DESC *op; - COND_COM *term, *tleft, *tright; - int tok, lineno; + if ((last_read_token == WORD) && +#if defined (SELECT_COMMAND) + ((token_before_that == FOR) || (token_before_that == CASE) || (token_before_that == SELECT)) && +#else + ((token_before_that == FOR) || (token_before_that == CASE)) && +#endif + (tokstr[0] == 'i' && tokstr[1] == 'n' && tokstr[2] == 0)) + { + if (token_before_that == CASE) + { + parser_state |= PST_CASEPAT; + esacs_needed_count++; + } + return (IN); + } - /* 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) + if (last_read_token == WORD && +#if defined (SELECT_COMMAND) + (token_before_that == FOR || token_before_that == SELECT) && +#else + (token_before_that == FOR) && +#endif + (tokstr[0] == 'd' && tokstr[1] == 'o' && tokstr[2] == '\0')) + return (DO); + + /* Ditto for ESAC in the CASE case. + Specifically, this handles "case word in esac", which is a legal + construct, certainly because someone will pass an empty arg to the + case construct, and we don't want it to barf. Of course, we should + insist that the case construct has at least one pattern in it, but + the designers disagree. */ + if (esacs_needed_count) { - COND_RETURN_ERROR (); + esacs_needed_count--; + if (STREQ (tokstr, "esac")) + { + parser_state &= ~PST_CASEPAT; + return (ESAC); + } } - else if (tok == '(') + + /* The start of a shell function definition. */ + if (parser_state & PST_ALLOWOPNBRC) { - term = cond_expr (); - if (cond_token != ')') + parser_state &= ~PST_ALLOWOPNBRC; + if (tokstr[0] == '{' && tokstr[1] == '\0') /* } */ { - if (term) - dispose_cond_node (term); /* ( */ - parser_error (lineno, "expected `)'"); - COND_RETURN_ERROR (); + open_brace_count++; + function_bstart = line_number; + return ('{'); /* } */ } - 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'))) + + /* We allow a `do' after a for ((...)) without an intervening + list_terminator */ + if (last_read_token == ARITH_FOR_EXPRS && tokstr[0] == 'd' && tokstr[1] == 'o' && !tokstr[2]) + return (DO); + if (last_read_token == ARITH_FOR_EXPRS && tokstr[0] == '{' && tokstr[1] == '\0') /* } */ + { + open_brace_count++; + return ('{'); /* } */ + } + + if (open_brace_count && reserved_word_acceptable (last_read_token) && tokstr[0] == '}' && !tokstr[1]) + { + open_brace_count--; /* { */ + return ('}'); + } + +#if defined (COMMAND_TIMING) + /* Handle -p after `time'. */ + if (last_read_token == TIME && tokstr[0] == '-' && tokstr[1] == 'p' && !tokstr[2]) + return (TIMEOPT); +#endif + +#if 0 +#if defined (COMMAND_TIMING) + if (STREQ (token, "time") && ((parser_state & PST_CASEPAT) == 0) && time_command_acceptable ()) + return (TIME); +#endif /* COMMAND_TIMING */ +#endif + +#if defined (COND_COMMAND) /* [[ */ + if ((parser_state & PST_CONDEXPR) && tokstr[0] == ']' && tokstr[1] == ']' && tokstr[2] == '\0') + return (COND_END); +#endif + + return (-1); +} + +/* Called from shell.c when Control-C is typed at top level. Or + by the error rule at top level. */ +void +reset_parser () +{ + dstack.delimiter_depth = 0; /* No delimiters found so far. */ + open_brace_count = 0; + + parser_state = 0; + +#if defined (ALIAS) || defined (DPAREN_ARITHMETIC) + if (pushed_string_list) + free_string_list (); +#endif /* ALIAS || DPAREN_ARITHMETIC */ + + if (shell_input_line) + { + free (shell_input_line); + shell_input_line = (char *)NULL; + shell_input_line_size = shell_input_line_index = 0; + } + + FREE (word_desc_to_read); + word_desc_to_read = (WORD_DESC *)NULL; + + last_read_token = '\n'; + token_to_read = '\n'; +} + +/* Read the next token. Command can be READ (normal operation) or + RESET (to normalize state). */ +static int +read_token (command) + int command; +{ + int character; /* Current character. */ + int peek_char; /* Temporary look-ahead character. */ + int result; /* The thing to return. */ + + if (command == RESET) { - if (tok == WORD) - dispose_word (yylval.word); /* not needed */ - term = cond_term (); - if (term) - term->flags |= CMD_INVERT_RETURN; + reset_parser (); + return ('\n'); } - else if (tok == WORD && test_unop (yylval.word->word)) + + if (token_to_read) { - op = yylval.word; - tok = read_token (READ); - if (tok == WORD) + result = token_to_read; + if (token_to_read == WORD || token_to_read == ASSIGNMENT_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); + yylval.word = word_desc_to_read; + word_desc_to_read = (WORD_DESC *)NULL; } - else + token_to_read = 0; + 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) { - dispose_word (op); - parser_error (line_number, "unexpected argument to conditional unary operator"); - COND_RETURN_ERROR (); + cond_error (); + return (-1); } + token_to_read = COND_END; + parser_state &= ~(PST_CONDEXPR|PST_CONDCMD); + return (COND_CMD); + } +#endif - (void)cond_skip_newlines (); +#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 () */ + re_read_token: +#endif /* ALIAS */ + + /* Read a single word from input. Start by skipping blanks. */ + while ((character = shell_getc (1)) != EOF && whitespace (character)) + ; + + if (character == EOF) + { + EOF_Reached = 1; + return (yacc_EOF); } - else if (tok == WORD) /* left argument to binary operator */ + + if MBTEST(character == '#' && (!interactive || interactive_comments)) { - /* lhs */ - tleft = make_cond_node (COND_TERM, yylval.word, (COND_COM *)NULL, (COND_COM *)NULL); + /* A comment. Discard until EOL or EOF, and then return a newline. */ + discard_until ('\n'); + shell_getc (0); + character = '\n'; /* this will take the next if statement and return. */ + } - /* 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); /* ( */ - /* There should be a check before blindly accepting the `)' that we have - seen the opening `('. */ - else if (tok == COND_END || tok == AND_AND || tok == OR_OR || tok == ')') - { - /* Special case. [[ x ]] is equivalent to [[ -n x ]], just like - the test command. Similarly for [[ x && expr ]] or - [[ x || expr ]] or [[ (x) ]]. */ - 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 (); - } + if (character == '\n') + { + /* If we're about to return an unquoted newline, we can go and collect + the text of any pending here document. */ + if (need_here_doc) + gather_here_documents (); - /* 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 (); - } +#if defined (ALIAS) + parser_state &= ~PST_ALEXPNEXT; +#endif /* ALIAS */ - (void)cond_skip_newlines (); + parser_state &= ~PST_ASSIGNOK; + + return (character); } - else + + /* Shell meta-characters. */ + if MBTEST(shellmeta (character) && ((parser_state & PST_DBLPAREN) == 0)) { - 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); -} +#if defined (ALIAS) + /* Turn off alias tokenization iff this character sequence would + not leave us ready to read a command. */ + if (character == '<' || character == '>') + parser_state &= ~PST_ALEXPNEXT; +#endif /* ALIAS */ -/* 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; + parser_state &= ~PST_ASSIGNOK; - cexp = cond_expr (); - return (make_cond_command (cexp)); -} + peek_char = shell_getc (1); + if (character == peek_char) + { + switch (character) + { + case '<': + /* If '<' then we could be at "<<" or at "<<-". We have to + look ahead one more character. */ + peek_char = shell_getc (1); + if (peek_char == '-') + return (LESS_LESS_MINUS); + else if (peek_char == '<') + return (LESS_LESS_LESS); + else + { + shell_ungetc (peek_char); + return (LESS_LESS); + } + + case '>': + return (GREATER_GREATER); + + case ';': + parser_state |= PST_CASEPAT; +#if defined (ALIAS) + parser_state &= ~PST_ALEXPNEXT; +#endif /* ALIAS */ + + return (SEMI_SEMI); + + case '&': + return (AND_AND); + + case '|': + return (OR_OR); + +#if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND) + case '(': /* ) */ + result = parse_dparen (character); + if (result == -2) + break; + else + return result; #endif + } + } + else if MBTEST(character == '<' && peek_char == '&') + return (LESS_AND); + else if MBTEST(character == '>' && peek_char == '&') + return (GREATER_AND); + else if MBTEST(character == '<' && peek_char == '>') + return (LESS_GREATER); + else if MBTEST(character == '>' && peek_char == '|') + return (GREATER_BAR); + else if MBTEST(peek_char == '>' && character == '&') + return (AND_GREATER); -static int -read_token_word (character) - int character; -{ - /* The value for YYLVAL when a WORD is read. */ - WORD_DESC *the_word; + shell_ungetc (peek_char); - /* Index into the token that we are building. */ - int token_index; + /* If we look like we are reading the start of a function + definition, then let the reader know about it so that + we will do the right thing with `{'. */ + if MBTEST(character == ')' && last_read_token == '(' && token_before_that == WORD) + { + parser_state |= PST_ALLOWOPNBRC; +#if defined (ALIAS) + parser_state &= ~PST_ALEXPNEXT; +#endif /* ALIAS */ + function_dstart = line_number; + } - /* ALL_DIGITS becomes zero when we see a non-digit. */ - int all_digits; + /* case pattern lists may be preceded by an optional left paren. If + we're not trying to parse a case pattern list, the left paren + indicates a subshell. */ + if MBTEST(character == '(' && (parser_state & PST_CASEPAT) == 0) /* ) */ + parser_state |= PST_SUBSHELL; + /*(*/ + else if MBTEST((parser_state & PST_CASEPAT) && character == ')') + parser_state &= ~PST_CASEPAT; + /*(*/ + else if MBTEST((parser_state & PST_SUBSHELL) && character == ')') + parser_state &= ~PST_SUBSHELL; - /* DOLLAR_PRESENT becomes non-zero if we see a `$'. */ - int dollar_present; +#if defined (PROCESS_SUBSTITUTION) + /* Check for the constructs which introduce process substitution. + Shells running in `posix mode' don't do process substitution. */ + if MBTEST(posixly_correct || ((character != '>' && character != '<') || peek_char != '(')) /*)*/ +#endif /* PROCESS_SUBSTITUTION */ + return (character); + } - /* QUOTED becomes non-zero if we see one of ("), ('), (`), or (\). */ - int quoted; + /* Hack <&- (close stdin) case. Also <&N- (dup and close). */ + if MBTEST(character == '-' && (last_read_token == LESS_AND || last_read_token == GREATER_AND)) + return (character); - /* Non-zero means to ignore the value of the next character, and just - to add it no matter what. */ - int pass_next_character; + /* Okay, if we got this far, we have to read a word. Read one, + and then check it against the known ones. */ + result = read_token_word (character); +#if defined (ALIAS) + if (result == RE_READ_TOKEN) + goto re_read_token; +#endif + return result; +} - /* The current delimiting character. */ - int cd; - int result, peek_char; - char *ttok, *ttrans; - int ttoklen, ttranslen; +/* + * Match a $(...) or other grouping construct. This has to handle embedded + * quoted strings ('', ``, "") and nested constructs. It also must handle + * reprompting the user, if necessary, after reading a newline, and returning + * correct error values if it reads EOF. + */ +#define P_FIRSTCLOSE 0x01 +#define P_ALLOWESC 0x02 +#define P_DQUOTE 0x04 +#define P_COMMAND 0x08 /* parsing a command, so look for comments */ - if (token_buffer_size < TOKEN_DEFAULT_INITIAL_SIZE) - token = xrealloc (token, token_buffer_size = TOKEN_DEFAULT_INITIAL_SIZE); +static char matched_pair_error; +static char * +parse_matched_pair (qc, open, close, lenp, flags) + int qc; /* `"' if this construct is within double quotes */ + int open, close; + int *lenp, flags; +{ + int count, ch, was_dollar, in_comment, check_comment; + int pass_next_character, nestlen, ttranslen, start_lineno; + char *ret, *nestret, *ttrans; + int retind, retsize, rflags; - token_index = 0; - all_digits = isdigit (character); - dollar_present = quoted = pass_next_character = 0; + count = 1; + pass_next_character = was_dollar = in_comment = 0; + check_comment = (flags & P_COMMAND) && qc != '\'' && qc != '"' && (flags & P_DQUOTE) == 0; + + /* RFLAGS is the set of flags we want to pass to recursive calls. */ + rflags = (qc == '"') ? P_DQUOTE : (flags & P_DQUOTE); + + ret = (char *)xmalloc (retsize = 64); + retind = 0; - for (;;) + start_lineno = line_number; + while (count) { - if (character == EOF) - goto got_token; - - if (pass_next_character) +#if 0 + ch = shell_getc ((qc != '\'' || (flags & P_ALLOWESC)) && pass_next_character == 0); +#else + ch = shell_getc (qc != '\'' && pass_next_character == 0); +#endif + if (ch == EOF) { - pass_next_character = 0; - goto got_character; + free (ret); + parser_error (start_lineno, _("unexpected EOF while looking for matching `%c'"), close); + EOF_Reached = 1; /* XXX */ + return (&matched_pair_error); } - cd = current_delimiter (dstack); + /* Possible reprompting. */ + if (ch == '\n' && SHOULD_PROMPT ()) + prompt_again (); - /* Handle backslashes. Quote lots of things when not inside of - double-quotes, quote some things inside of double-quotes. */ - if (character == '\\') + if (in_comment) { - peek_char = shell_getc (0); + /* Add this character. */ + RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64); + ret[retind++] = ch; - /* Backslash-newline is ignored in all cases except - when quoted with single quotes. */ - if (peek_char == '\n') - { - character = '\n'; - goto next_character; - } - else - { - shell_ungetc (peek_char); + if (ch == '\n') + in_comment = 0; - /* If the next character is to be quoted, note it now. */ - if (cd == 0 || cd == '`' || - (cd == '"' && (sh_syntaxtab[peek_char] & CBSDQUOTE))) - pass_next_character++; + continue; + } + /* Not exactly right yet */ + else if (check_comment && in_comment == 0 && ch == '#' && (retind == 0 || ret[retind-1] == '\n' || whitespace (ret[retind -1]))) + in_comment = 1; - quoted = 1; - goto got_character; + if (pass_next_character) /* last char was backslash */ + { + pass_next_character = 0; + if (qc != '\'' && ch == '\n') /* double-quoted \ disappears. */ + { + if (retind > 0) retind--; /* swallow previously-added backslash */ + continue; } - } - /* Parse a matched pair of quote characters. */ - if (shellquote (character)) + RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64); + if MBTEST(ch == CTLESC || ch == CTLNUL) + ret[retind++] = CTLESC; + ret[retind++] = ch; + continue; + } + else if MBTEST(ch == CTLESC || ch == CTLNUL) /* special shell escapes */ { - push_delimiter (dstack, character); - ttok = parse_matched_pair (character, character, character, &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; - strcpy (token + token_index, ttok); - token_index += ttoklen; - all_digits = 0; - quoted = 1; - dollar_present |= (character == '"' && strchr (ttok, '$') != 0); - FREE (ttok); - goto next_character; + RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64); + ret[retind++] = CTLESC; + ret[retind++] = ch; + continue; } + else if MBTEST(ch == close) /* ending delimiter */ + count--; +#if 1 + /* handle nested ${...} specially. */ + else if MBTEST(open != close && was_dollar && open == '{' && ch == open) /* } */ + count++; +#endif + else if MBTEST(((flags & P_FIRSTCLOSE) == 0) && ch == open) /* nested begin */ + count++; -#ifdef EXTENDED_GLOB - /* Parse a ksh-style extended pattern matching specification. */ - if (extended_glob && PATTERN_CHAR (character)) + /* Add this character. */ + RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64); + ret[retind++] = ch; + + if (open == '\'') /* '' inside grouping construct */ { - 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); + if MBTEST((flags & P_ALLOWESC) && ch == '\\') + pass_next_character++; + continue; } -#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 (shellexp (character)) + if MBTEST(ch == '\\') /* backslashes */ + pass_next_character++; + + if (open != close) /* a grouping construct */ { - peek_char = shell_getc (1); - /* $(...), <(...), >(...), $((...)), ${...}, and $[...] constructs */ - if (peek_char == '(' || - ((peek_char == '{' || peek_char == '[') && character == '$')) /* ) ] } */ + if MBTEST(shellquote (ch)) { - if (peek_char == '{') /* } */ - ttok = parse_matched_pair (cd, '{', '}', &ttoklen, P_FIRSTCLOSE); - else if (peek_char == '(') /* ) */ - { - /* XXX - push and pop the `(' as a delimiter for use by - 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. ) */ - push_delimiter (dstack, peek_char); - ttok = parse_matched_pair (cd, '(', ')', &ttoklen, 0); - pop_delimiter (dstack); - } + /* '', ``, or "" inside $(...) or other grouping construct. */ + push_delimiter (dstack, ch); + if MBTEST(was_dollar && ch == '\'') /* $'...' inside group */ + nestret = parse_matched_pair (ch, ch, ch, &nestlen, P_ALLOWESC|rflags); else - ttok = parse_matched_pair (cd, '[', ']', &ttoklen, 0); - 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 = 1; - all_digits = 0; - goto next_character; - } - /* This handles $'...' and $"..." new-style quoted strings. */ - else if (character == '$' && (peek_char == '\'' || peek_char == '"')) - { - int first_line; - - first_line = line_number; - push_delimiter (dstack, peek_char); - ttok = parse_matched_pair (peek_char, peek_char, peek_char, - &ttoklen, - (peek_char == '\'') ? P_ALLOWESC : 0); + nestret = parse_matched_pair (ch, ch, ch, &nestlen, rflags); pop_delimiter (dstack); - if (ttok == &matched_pair_error) - return -1; - if (peek_char == '\'') + if (nestret == &matched_pair_error) { - 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 = sh_single_quote (ttrans); - free (ttrans); - ttrans = ttok; - ttranslen = strlen (ttrans); + free (ret); + return &matched_pair_error; } - else + if MBTEST(was_dollar && ch == '\'' && (extended_quote || (rflags & P_DQUOTE) == 0)) { - /* Try to locale-expand the converted string. */ - ttrans = localeexpand (ttok, 0, ttoklen - 1, first_line, &ttranslen); - free (ttok); + /* Translate $'...' here. */ + ttrans = ansiexpand (nestret, 0, nestlen - 1, &ttranslen); + xfree (nestret); - /* Add the double quotes back */ - ttok = xmalloc (ttranslen + 3); - ttok[0] = '"'; - strcpy (ttok + 1, ttrans); - ttok[ttranslen + 1] = '"'; - ttok[ttranslen += 2] = '\0'; + if ((rflags & P_DQUOTE) == 0) + { + nestret = sh_single_quote (ttrans); + free (ttrans); + nestlen = strlen (nestret); + } + else + { + nestret = ttrans; + nestlen = ttranslen; + } + retind -= 2; /* back up before the $' */ + } + else if MBTEST(was_dollar && ch == '"' && (extended_quote || (rflags & P_DQUOTE) == 0)) + { + /* Locale expand $"..." here. */ + ttrans = localeexpand (nestret, 0, nestlen - 1, start_lineno, &ttranslen); + xfree (nestret); + + nestret = sh_mkdoublequoted (ttrans, ttranslen, 0); free (ttrans); - ttrans = ttok; + nestlen = ttranslen + 2; + retind -= 2; /* back up before the $" */ } - RESIZE_MALLOCED_BUFFER (token, token_index, ttranslen + 2, - token_buffer_size, - TOKEN_DEFAULT_GROW_SIZE); - strcpy (token + token_index, ttrans); - token_index += ttranslen; - FREE (ttrans); - quoted = 1; - all_digits = 0; - goto next_character; - } - /* This could eventually be extended to recognize all of the - shell's single-character parameter expansions, and set flags.*/ - else if (character == '$' && peek_char == '$') - { - ttok = xmalloc (3); - ttok[0] = ttok[1] = '$'; - ttok[2] = '\0'; - RESIZE_MALLOCED_BUFFER (token, token_index, 3, - token_buffer_size, - TOKEN_DEFAULT_GROW_SIZE); - strcpy (token + token_index, ttok); - token_index += 2; - dollar_present = 1; - all_digits = 0; - FREE (ttok); - goto next_character; + if (nestlen) + { + RESIZE_MALLOCED_BUFFER (ret, retind, nestlen, retsize, 64); + strcpy (ret + retind, nestret); + retind += nestlen; + } + FREE (nestret); } - else - shell_ungetc (peek_char); } - -#if defined (ARRAY_VARS) - /* Identify possible compound array variable assignment. */ - else if (character == '=' && token_index > 0) + /* Parse an old-style command substitution within double quotes as a + single word. */ + /* XXX - sh and ksh93 don't do this - XXX */ + else if MBTEST(open == '"' && ch == '`') { - peek_char = shell_getc (1); - if (peek_char == '(') /* ) */ + nestret = parse_matched_pair (0, '`', '`', &nestlen, rflags); +add_nestret: + if (nestret == &matched_pair_error) { - ttok = parse_matched_pair (cd, '(', ')', &ttoklen, 0); - if (ttok == &matched_pair_error) - return -1; /* Bail immediately. */ - if (ttok[0] == '(') /* ) */ - { - FREE (ttok); - return -1; - } - 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); - all_digits = 0; - goto next_character; + free (ret); + return &matched_pair_error; } - else - shell_ungetc (peek_char); - } -#endif - - /* When not parsing a multi-character word construct, shell meta- - characters break words. */ - if (shellbreak (character)) + if (nestlen) + { + RESIZE_MALLOCED_BUFFER (ret, retind, nestlen, retsize, 64); + strcpy (ret + retind, nestret); + retind += nestlen; + } + FREE (nestret); + } + else if MBTEST(qc == '`' && (ch == '"' || ch == '\'') && in_comment == 0) { - shell_ungetc (character); - goto got_token; + nestret = parse_matched_pair (0, ch, ch, &nestlen, rflags); + goto add_nestret; } + else if MBTEST(was_dollar && (ch == '(' || ch == '{' || ch == '[')) /* ) } ] */ + /* check for $(), $[], or ${} inside quoted string. */ + { + if (open == ch) /* undo previous increment */ + count--; + if (ch == '(') /* ) */ + nestret = parse_matched_pair (0, '(', ')', &nestlen, rflags); + else if (ch == '{') /* } */ + nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE|rflags); + else if (ch == '[') /* ] */ + nestret = parse_matched_pair (0, '[', ']', &nestlen, rflags); - got_character: - - all_digits &= isdigit (character); - dollar_present |= character == '$'; - - if (character == CTLESC || character == CTLNUL) - token[token_index++] = CTLESC; + goto add_nestret; + } + was_dollar = MBTEST(ch == '$'); + } - token[token_index++] = character; + ret[retind] = '\0'; + if (lenp) + *lenp = retind; + return ret; +} - RESIZE_MALLOCED_BUFFER (token, token_index, 1, token_buffer_size, - TOKEN_DEFAULT_GROW_SIZE); +#if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND) +/* Parse a double-paren construct. It can be either an arithmetic + command, an arithmetic `for' command, or a nested subshell. Returns + the parsed token, -1 on error, or -2 if we didn't do anything and + should just go on. */ +static int +parse_dparen (c) + int c; +{ + int cmdtyp, len, sline; + char *wval, *wv2; + WORD_DESC *wd; - next_character: - if (character == '\n' && interactive && - (bash_input.type == st_stdin || bash_input.type == st_stream)) - prompt_again (); +#if defined (ARITH_FOR_COMMAND) + if (last_read_token == FOR) + { + arith_for_lineno = line_number; + cmdtyp = parse_arith_cmd (&wval, 0); + if (cmdtyp == 1) + { + wd = alloc_word_desc (); + wd->word = wval; + wd = make_word (wval); + yylval.word_list = make_word_list (wd, (WORD_LIST *)NULL); + return (ARITH_FOR_EXPRS); + } + else + return -1; /* ERROR */ + } +#endif - /* We want to remove quoted newlines (that is, a \ pair) - unless we are within single quotes or pass_next_character is - set (the shell equivalent of literal-next). */ - cd = current_delimiter (dstack); - character = shell_getc (cd != '\'' && pass_next_character == 0); - } /* end for (;;) */ +#if defined (DPAREN_ARITHMETIC) + if (reserved_word_acceptable (last_read_token)) + { + sline = line_number; -got_token: + cmdtyp = parse_arith_cmd (&wval, 0); + if (cmdtyp == 1) /* arithmetic command */ + { + wd = alloc_word_desc (); + wd->word = wval; + wd->flags = W_QUOTED|W_NOSPLIT|W_NOGLOB|W_DQUOTE; + yylval.word_list = make_word_list (wd, (WORD_LIST *)NULL); + return (ARITH_CMD); + } + else if (cmdtyp == 0) /* nested subshell */ + { + push_string (wval, 0, (alias_t *)NULL); + if ((parser_state & PST_CASEPAT) == 0) + parser_state |= PST_SUBSHELL; + return (c); + } + else /* ERROR */ + return -1; + } +#endif - token[token_index] = '\0'; + return -2; /* XXX */ +} - /* Check to see what thing we should return. If the last_read_token - is a `<', or a `&', or the character which ended this token is - a '>' or '<', then, and ONLY then, is this input token a NUMBER. - Otherwise, it is just a word, and should be returned as such. */ - if (all_digits && (character == '<' || character == '>' || - last_read_token == LESS_AND || - last_read_token == GREATER_AND)) - { - yylval.number = atoi (token); - return (NUMBER); - } +/* 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- + allocated buffer and make *ep point to that buffer. Return -1 on an + error, for example EOF. */ +static int +parse_arith_cmd (ep, adddq) + char **ep; + int adddq; +{ + int exp_lineno, rval, c; + char *ttok, *tokstr; + int ttoklen; - /* Check for special case tokens. */ - result = special_case_tokens (token); - if (result >= 0) - return result; + exp_lineno = line_number; + ttok = parse_matched_pair (0, '(', ')', &ttoklen, 0); + rval = 1; + if (ttok == &matched_pair_error) + return -1; + /* Check that the next character is the closing right paren. If + not, this is a syntax error. ( */ + c = shell_getc (0); + if MBTEST(c != ')') + rval = 0; -#if defined (ALIAS) - /* Posix.2 does not allow reserved words to be aliased, so check for all - of them, including special cases, before expanding the current token - as an alias. */ - if (posixly_correct) - CHECK_FOR_RESERVED_WORD (token); + tokstr = (char *)xmalloc (ttoklen + 4); - /* Aliases are expanded iff EXPAND_ALIASES is non-zero, and quoting - inhibits alias expansion. */ - if (expand_aliases && quoted == 0) + /* if ADDDQ != 0 then (( ... )) -> "..." */ + if (rval == 1 && adddq) /* arith cmd, add double quotes */ { - result = alias_expand_token (token); - if (result == RE_READ_TOKEN) - return (RE_READ_TOKEN); - else if (result == NO_EXPANSION) - parser_state &= ~PST_ALEXPNEXT; + tokstr[0] = '"'; + strncpy (tokstr + 1, ttok, ttoklen - 1); + tokstr[ttoklen] = '"'; + tokstr[ttoklen+1] = '\0'; } - - /* If not in Posix.2 mode, check for reserved words after alias - expansion. */ - if (posixly_correct == 0) -#endif - CHECK_FOR_RESERVED_WORD (token); - - the_word = (WORD_DESC *)xmalloc (sizeof (WORD_DESC)); - the_word->word = xmalloc (1 + token_index); - the_word->flags = 0; - strcpy (the_word->word, token); - if (dollar_present) - the_word->flags |= W_HASDOLLAR; - if (quoted) - the_word->flags |= W_QUOTED; - /* A word is an assignment if it appears at the beginning of a - simple command, or after another assignment word. This is - context-dependent, so it cannot be handled in the grammar. */ - if (assignment (token)) + else if (rval == 1) /* arith cmd, don't add double quotes */ { - the_word->flags |= W_ASSIGNMENT; - /* Don't perform word splitting on assignment statements. */ - if (assignment_acceptable (last_read_token)) - the_word->flags |= W_NOSPLIT; + strncpy (tokstr, ttok, ttoklen - 1); + tokstr[ttoklen-1] = '\0'; + } + else /* nested subshell */ + { + tokstr[0] = '('; + strncpy (tokstr + 1, ttok, ttoklen - 1); + tokstr[ttoklen] = ')'; + tokstr[ttoklen+1] = c; + tokstr[ttoklen+2] = '\0'; } - yylval.word = the_word; + *ep = tokstr; + FREE (ttok); + return rval; +} +#endif /* DPAREN_ARITHMETIC || ARITH_FOR_COMMAND */ - result = ((the_word->flags & (W_ASSIGNMENT|W_NOSPLIT)) == (W_ASSIGNMENT|W_NOSPLIT)) - ? ASSIGNMENT_WORD : WORD; +#if defined (COND_COMMAND) +static void +cond_error () +{ + char *etext; - if (last_read_token == FUNCTION) + if (EOF_Reached && cond_token != COND_ERROR) /* [[ */ + parser_error (cond_lineno, _("unexpected EOF while looking for `]]'")); + else if (cond_token != COND_ERROR) { - parser_state |= PST_ALLOWOPNBRC; - function_dstart = line_number; + if (etext = error_token_from_token (cond_token)) + { + parser_error (cond_lineno, _("syntax error in conditional expression: unexpected token `%s'"), etext); + free (etext); + } + else + parser_error (cond_lineno, _("syntax error in conditional expression")); } +} - return (result); +static COND_COM * +cond_expr () +{ + return (cond_or ()); } -/* $'...' ANSI-C expand the portion of STRING between START and END and - return the result. The result cannot be longer than the input string. */ -static char * -ansiexpand (string, start, end, lenp) - char *string; - int start, end, *lenp; +static COND_COM * +cond_or () { - char *temp, *t; - int len, tlen; + COND_COM *l, *r; - temp = xmalloc (end - start + 1); - for (tlen = 0, len = start; len < end; ) - temp[tlen++] = string[len++]; - temp[tlen] = '\0'; + l = cond_and (); + if (cond_token == OR_OR) + { + r = cond_or (); + l = make_cond_node (COND_OR, (WORD_DESC *)NULL, l, r); + } + return l; +} - if (*temp) +static COND_COM * +cond_and () +{ + COND_COM *l, *r; + + l = cond_term (); + if (cond_token == AND_AND) { - t = ansicstr (temp, tlen, 0, (int *)NULL, lenp); - free (temp); - return (t); + r = cond_and (); + l = make_cond_node (COND_AND, (WORD_DESC *)NULL, l, r); } - else + return l; +} + +static int +cond_skip_newlines () +{ + while ((cond_token = read_token (READ)) == '\n') { - if (lenp) - *lenp = 0; - return (temp); + if (SHOULD_PROMPT ()) + prompt_again (); } + return (cond_token); } -/* 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; +#define COND_RETURN_ERROR() \ + do { cond_token = COND_ERROR; return ((COND_COM *)NULL); } while (0) + +static COND_COM * +cond_term () { - register int c, len; - char *result, *r, *s; + WORD_DESC *op; + COND_COM *term, *tleft, *tright; + int tok, lineno; + char *etext; - for (len = 0, s = string; s && *s; s++) + /* 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) { - len++; - if (*s == '"' || *s == '\\') - len++; - else if (*s == '\n') - len += 5; + COND_RETURN_ERROR (); } - - r = result = xmalloc (len + 3); - *r++ = '"'; + else if (tok == '(') + { + term = cond_expr (); + if (cond_token != ')') + { + if (term) + dispose_cond_node (term); /* ( */ + if (etext = error_token_from_token (cond_token)) + { + parser_error (lineno, _("unexpected token `%s', expected `)'"), etext); + free (etext); + } + else + 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); + if (etext = error_token_from_token (tok)) + { + parser_error (line_number, _("unexpected argument `%s' to conditional unary operator"), etext); + free (etext); + } + else + parser_error (line_number, _("unexpected argument to conditional unary operator")); + COND_RETURN_ERROR (); + } - for (s = string; s && (c = *s); s++) + (void)cond_skip_newlines (); + } + else if (tok == WORD) /* left argument to binary operator */ { - if (c == '\n') /* -> \n"" */ + /* 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; +#if defined (COND_REGEXP) + else if (tok == WORD && STREQ (yylval.word->word,"=~")) + op = yylval.word; +#endif + else if (tok == '<' || tok == '>') + op = make_word_from_token (tok); /* ( */ + /* There should be a check before blindly accepting the `)' that we have + seen the opening `('. */ + else if (tok == COND_END || tok == AND_AND || tok == OR_OR || tok == ')') + { + /* Special case. [[ x ]] is equivalent to [[ -n x ]], just like + the test command. Similarly for [[ x && expr ]] or + [[ x || expr ]] or [[ (x) ]]. */ + op = make_word ("-n"); + term = make_cond_node (COND_UNARY, op, tleft, (COND_COM *)NULL); + cond_token = tok; + return (term); + } + else { - *r++ = '\\'; - *r++ = 'n'; - *r++ = '"'; - *r++ = '\n'; - *r++ = '"'; - if (foundnlp) - *foundnlp = 1; - continue; + if (etext = error_token_from_token (tok)) + { + parser_error (line_number, _("unexpected token `%s', conditional binary operator expected"), etext); + free (etext); + } + else + parser_error (line_number, _("conditional binary operator expected")); + dispose_cond_node (tleft); + COND_RETURN_ERROR (); } - 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. - The string will be left without the leading `$' by the caller. - If translation is performed, the translated string will be double-quoted - by the caller. The length of the translated string is returned in LENP, - if non-null. */ -static char * -localeexpand (string, start, end, lineno, lenp) - char *string; - int start, end, lineno, *lenp; -{ - 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 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 - into `\n""'. 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) + /* rhs */ + tok = read_token (READ); + if (tok == WORD) { - 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); + tright = make_cond_node (COND_TERM, yylval.word, (COND_COM *)NULL, (COND_COM *)NULL); + term = make_cond_node (COND_BINARY, op, tleft, tright); } else - printf ("\"%s\"\n", temp); + { + if (etext = error_token_from_token (tok)) + { + parser_error (line_number, _("unexpected argument `%s' to conditional binary operator"), etext); + free (etext); + } + else + parser_error (line_number, _("unexpected argument to conditional binary operator")); + dispose_cond_node (tleft); + dispose_word (op); + COND_RETURN_ERROR (); + } - if (lenp) - *lenp = tlen; - return (temp); - } - else if (*temp) - { - t = localetrans (temp, tlen, &len); - free (temp); - if (lenp) - *lenp = len; - return (t); + (void)cond_skip_newlines (); } else { - if (lenp) - *lenp = 0; - return (temp); + if (tok < 256) + parser_error (line_number, _("unexpected token `%c' in conditional command"), tok); + else if (etext = error_token_from_token (tok)) + { + parser_error (line_number, _("unexpected token `%s' in conditional command"), etext); + free (etext); + } + else + parser_error (line_number, _("unexpected token %d in conditional command"), tok); + COND_RETURN_ERROR (); } + 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 -/* Return 1 if TOKEN is a token that after being read would allow - a reserved word to be seen, else 0. */ +#if defined (ARRAY_VARS) +/* When this is called, it's guaranteed that we don't care about anything + in t beyond i. We do save and restore the chars, though. */ static int -reserved_word_acceptable (token) - int token; +token_is_assignment (t, i) + char *t; + int i; { - if (token == '\n' || token == ';' || token == '(' || token == ')' || - token == '|' || token == '&' || token == '{' || - token == '}' || /* XXX */ - token == AND_AND || - token == BANG || - token == TIME || token == TIMEOPT || - token == DO || - token == ELIF || - token == ELSE || - token == FI || - token == IF || - token == OR_OR || - token == SEMI_SEMI || - token == THEN || - token == UNTIL || - token == WHILE || - token == DONE || /* XXX these two are experimental */ - token == ESAC || - token == 0) - return (1); - else - return (0); + unsigned char c, c1; + int r; + + c = t[i]; c1 = t[i+1]; + t[i] = '='; t[i+1] = '\0'; + r = assignment (t, (parser_state & PST_COMPASSIGN) != 0); + t[i] = c; t[i+1] = c1; + return r; } -/* Return the index of TOKEN in the alist of reserved words, or -1 if - TOKEN is not a shell reserved word. */ -int -find_reserved_word (token) - char *token; +/* XXX - possible changes here for `+=' */ +static int +token_is_ident (t, i) + char *t; + int i; { - int i; - for (i = 0; word_token_alist[i].word; i++) - if (STREQ (token, word_token_alist[i].word)) - return i; - return -1; + unsigned char c; + int r; + + c = t[i]; + t[i] = '\0'; + r = legal_identifier (t); + t[i] = c; + return r; } +#endif -#if 0 -#if defined (READLINE) -/* Called after each time readline is called. This insures that whatever - the new prompt string is gets propagated to readline's local prompt - variable. */ -static void -reset_readline_prompt () +static int +read_token_word (character) + int character; { - char *temp_prompt; + /* The value for YYLVAL when a WORD is read. */ + WORD_DESC *the_word; - if (prompt_string_pointer) + /* Index into the token that we are building. */ + int token_index; + + /* ALL_DIGITS becomes zero when we see a non-digit. */ + int all_digit_token; + + /* DOLLAR_PRESENT becomes non-zero if we see a `$'. */ + int dollar_present; + + /* COMPOUND_ASSIGNMENT becomes non-zero if we are parsing a compound + assignment. */ + int compound_assignment; + + /* QUOTED becomes non-zero if we see one of ("), ('), (`), or (\). */ + int quoted; + + /* Non-zero means to ignore the value of the next character, and just + to add it no matter what. */ + int pass_next_character; + + /* The current delimiting character. */ + int cd; + int result, peek_char; + char *ttok, *ttrans; + int ttoklen, ttranslen; + intmax_t lvalue; + + if (token_buffer_size < TOKEN_DEFAULT_INITIAL_SIZE) + token = (char *)xrealloc (token, token_buffer_size = TOKEN_DEFAULT_INITIAL_SIZE); + + token_index = 0; + all_digit_token = DIGIT (character); + dollar_present = quoted = pass_next_character = compound_assignment = 0; + + for (;;) { - temp_prompt = (*prompt_string_pointer) - ? decode_prompt_string (*prompt_string_pointer) - : (char *)NULL; + if (character == EOF) + goto got_token; - if (temp_prompt == 0) + if (pass_next_character) { - temp_prompt = xmalloc (1); - temp_prompt[0] = '\0'; + pass_next_character = 0; + goto got_character; } - FREE (current_readline_prompt); - current_readline_prompt = temp_prompt; - } -} -#endif /* READLINE */ -#endif /* 0 */ + cd = current_delimiter (dstack); -#if defined (HISTORY) -/* A list of tokens which can be followed by newlines, but not by - semi-colons. When concatenating multiple lines of history, the - newline separator for such tokens is replaced with a space. */ -static int no_semi_successors[] = { - '\n', '{', '(', ')', ';', '&', '|', - CASE, DO, ELSE, IF, SEMI_SEMI, THEN, UNTIL, WHILE, AND_AND, OR_OR, IN, - 0 -}; + /* Handle backslashes. Quote lots of things when not inside of + double-quotes, quote some things inside of double-quotes. */ + if MBTEST(character == '\\') + { + peek_char = shell_getc (0); -/* If we are not within a delimited expression, try to be smart - about which separators can be semi-colons and which must be - newlines. Returns the string that should be added into the - history entry. */ -char * -history_delimiting_chars () -{ - register int i; + /* Backslash-newline is ignored in all cases except + when quoted with single quotes. */ + if (peek_char == '\n') + { + character = '\n'; + goto next_character; + } + else + { + shell_ungetc (peek_char); - if (dstack.delimiter_depth != 0) - return ("\n"); - - /* First, handle some special cases. */ - /*(*/ - /* If we just read `()', assume it's a function definition, and don't - add a semicolon. If the token before the `)' was not `(', and we're - not in the midst of parsing a case statement, assume it's a - parenthesized command and add the semicolon. */ - /*)(*/ - if (token_before_that == ')') - { - if (two_tokens_ago == '(') /*)*/ /* function def */ - return " "; - /* This does not work for subshells inside case statement - command lists. It's a suboptimal solution. */ - else if (parser_state & PST_CASESTMT) /* case statement pattern */ - return " "; - else - return "; "; /* (...) subshell */ - } - else if (token_before_that == WORD && two_tokens_ago == FUNCTION) - return " "; /* function def using `function name' without `()' */ + /* If the next character is to be quoted, note it now. */ + if (cd == 0 || cd == '`' || + (cd == '"' && peek_char >= 0 && (sh_syntaxtab[peek_char] & CBSDQUOTE))) + pass_next_character++; - 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 ";"; - } + quoted = 1; + goto got_character; + } + } - for (i = 0; no_semi_successors[i]; i++) - { - if (token_before_that == no_semi_successors[i]) - return (" "); - } + /* Parse a matched pair of quote characters. */ + if MBTEST(shellquote (character)) + { + push_delimiter (dstack, character); + ttok = parse_matched_pair (character, character, character, &ttoklen, (character == '`') ? P_COMMAND : 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; + strcpy (token + token_index, ttok); + token_index += ttoklen; + all_digit_token = 0; + quoted = 1; + dollar_present |= (character == '"' && strchr (ttok, '$') != 0); + FREE (ttok); + goto next_character; + } - return ("; "); -} -#endif /* HISTORY */ +#ifdef EXTENDED_GLOB + /* Parse a ksh-style extended pattern matching specification. */ + if (extended_glob && PATTERN_CHAR (character)) + { + peek_char = shell_getc (1); + if MBTEST(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_digit_token = 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 (shellexp (character)) + { + peek_char = shell_getc (1); + /* $(...), <(...), >(...), $((...)), ${...}, and $[...] constructs */ + if MBTEST(peek_char == '(' || \ + ((peek_char == '{' || peek_char == '[') && character == '$')) /* ) ] } */ + { + if (peek_char == '{') /* } */ + ttok = parse_matched_pair (cd, '{', '}', &ttoklen, P_FIRSTCLOSE); + else if (peek_char == '(') /* ) */ + { + /* XXX - push and pop the `(' as a delimiter for use by + 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. ) */ + push_delimiter (dstack, peek_char); + ttok = parse_matched_pair (cd, '(', ')', &ttoklen, P_COMMAND); + pop_delimiter (dstack); + } + else + ttok = parse_matched_pair (cd, '[', ']', &ttoklen, 0); + 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 = 1; + all_digit_token = 0; + goto next_character; + } + /* This handles $'...' and $"..." new-style quoted strings. */ + else if MBTEST(character == '$' && (peek_char == '\'' || peek_char == '"')) + { + int first_line; -/* Issue a prompt, or prepare to issue a prompt when the next character - is read. */ -static void -prompt_again () -{ - char *temp_prompt; + first_line = line_number; + 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); + free (ttok); - if (!interactive) /* XXX */ - return; + /* Insert the single quotes and correctly quote any + embedded single quotes (allowed because P_ALLOWESC was + passed to parse_matched_pair). */ + ttok = sh_single_quote (ttrans); + free (ttrans); + ttranslen = strlen (ttok); + ttrans = ttok; + } + else + { + /* Try to locale)-expand the converted string. */ + ttrans = localeexpand (ttok, 0, ttoklen - 1, first_line, &ttranslen); + free (ttok); - ps1_prompt = get_string_value ("PS1"); - ps2_prompt = get_string_value ("PS2"); + /* Add the double quotes back */ + ttok = sh_mkdoublequoted (ttrans, ttranslen, 0); + free (ttrans); + ttranslen += 2; + ttrans = ttok; + } - if (!prompt_string_pointer) - prompt_string_pointer = &ps1_prompt; + RESIZE_MALLOCED_BUFFER (token, token_index, ttranslen + 2, + token_buffer_size, + TOKEN_DEFAULT_GROW_SIZE); + strcpy (token + token_index, ttrans); + token_index += ttranslen; + FREE (ttrans); + quoted = 1; + all_digit_token = 0; + goto next_character; + } + /* This could eventually be extended to recognize all of the + shell's single-character parameter expansions, and set flags.*/ + else if MBTEST(character == '$' && peek_char == '$') + { + ttok = (char *)xmalloc (3); + ttok[0] = ttok[1] = '$'; + ttok[2] = '\0'; + RESIZE_MALLOCED_BUFFER (token, token_index, 3, + token_buffer_size, + TOKEN_DEFAULT_GROW_SIZE); + strcpy (token + token_index, ttok); + token_index += 2; + dollar_present = 1; + all_digit_token = 0; + FREE (ttok); + goto next_character; + } + else + shell_ungetc (peek_char); + } - temp_prompt = *prompt_string_pointer - ? decode_prompt_string (*prompt_string_pointer) - : (char *)NULL; +#if defined (ARRAY_VARS) + /* Identify possible array subscript assignment; match [...] */ + else if MBTEST(character == '[' && token_index > 0 && assignment_acceptable (last_read_token) && token_is_ident (token, token_index)) /* ] */ + { + ttok = parse_matched_pair (cd, '[', ']', &ttoklen, 0); + 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; + strcpy (token + token_index, ttok); + token_index += ttoklen; + FREE (ttok); + all_digit_token = 0; + goto next_character; + } + /* Identify possible compound array variable assignment. */ + else if MBTEST(character == '=' && token_index > 0 && (assignment_acceptable (last_read_token) || (parser_state & PST_ASSIGNOK)) && token_is_assignment (token, token_index)) + { + peek_char = shell_getc (1); + if MBTEST(peek_char == '(') /* ) */ + { + ttok = parse_compound_assignment (&ttoklen); - if (temp_prompt == 0) - { - temp_prompt = xmalloc (1); - temp_prompt[0] = '\0'; - } + RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 4, + token_buffer_size, + TOKEN_DEFAULT_GROW_SIZE); - current_prompt_string = *prompt_string_pointer; - prompt_string_pointer = &ps2_prompt; + token[token_index++] = '='; + token[token_index++] = '('; + if (ttok) + { + strcpy (token + token_index, ttok); + token_index += ttoklen; + } + token[token_index++] = ')'; + FREE (ttok); + all_digit_token = 0; + compound_assignment = 1; +#if 0 + goto next_character; +#else + goto got_token; /* ksh93 seems to do this */ +#endif + } + else + shell_ungetc (peek_char); + } +#endif -#if defined (READLINE) - if (!no_line_editing) - { - FREE (current_readline_prompt); - current_readline_prompt = temp_prompt; - } - else -#endif /* READLINE */ - { - FREE (current_decoded_prompt); - current_decoded_prompt = temp_prompt; - } -} + /* When not parsing a multi-character word construct, shell meta- + characters break words. */ + if MBTEST(shellbreak (character)) + { + shell_ungetc (character); + goto got_token; + } -int -get_current_prompt_level () -{ - return ((current_prompt_string && current_prompt_string == ps2_prompt) ? 2 : 1); -} + got_character: -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 () -{ - fprintf (stderr, "%s", current_decoded_prompt); - fflush (stderr); -} + all_digit_token &= DIGIT (character); + dollar_present |= character == '$'; -/* Return a string which will be printed as a prompt. The string - may contain special characters which are decoded as follows: + if (character == CTLESC || character == CTLNUL) + token[token_index++] = CTLESC; - \a bell (ascii 07) - \e escape (ascii 033) - \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 - \T the time in 12-hour hh:mm:ss format - \@ the time in 12-hour am/pm format - \v the version of bash (e.g., 2.00) - \V the release of bash, version + patchlevel (e.g., 2.00.0) - \w the current working directory - \W the last element of $PWD - \u your username - \# the command number of this command - \! the history number of this command - \$ a $ or a # if you are root - \nnn character code nnn in octal - \\ a backslash - \[ begin a sequence of non-printing chars - \] end a sequence of non-printing chars -*/ -#define PROMPT_GROWTH 48 -char * -decode_prompt_string (string) - char *string; -{ - WORD_LIST *list; - char *result, *t; - struct dstack save_dstack; -#if defined (PROMPT_STRING_DECODE) - int result_size, result_index; - int c, n; - char *temp, octal_string[4]; - time_t the_time; + token[token_index++] = character; - result = xmalloc (result_size = PROMPT_GROWTH); - result[result_index = 0] = 0; - temp = (char *)NULL; + RESIZE_MALLOCED_BUFFER (token, token_index, 1, token_buffer_size, + TOKEN_DEFAULT_GROW_SIZE); - while (c = *string++) - { - if (posixly_correct && c == '!') - { - if (*string == '!') - { - temp = savestring ("!"); - goto add_string; - } - else - { -#if !defined (HISTORY) - temp = savestring ("1"); -#else /* HISTORY */ - temp = itos (history_number ()); -#endif /* HISTORY */ - string--; /* add_string increments string again. */ - goto add_string; - } - } - if (c == '\\') - { - c = *string; + next_character: + if (character == '\n' && SHOULD_PROMPT ()) + prompt_again (); - switch (c) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - strncpy (octal_string, string, 3); - octal_string[3] = '\0'; + /* We want to remove quoted newlines (that is, a \ pair) + unless we are within single quotes or pass_next_character is + set (the shell equivalent of literal-next). */ + cd = current_delimiter (dstack); + character = shell_getc (cd != '\'' && pass_next_character == 0); + } /* end for (;;) */ - n = read_octal (octal_string); - temp = xmalloc (3); +got_token: - if (n == CTLESC || n == CTLNUL) - { - temp[0] = CTLESC; - temp[1] = n; - temp[2] = '\0'; - } - else if (n == -1) - { - temp[0] = '\\'; - temp[1] = '\0'; - } - else - { - temp[0] = n; - temp[1] = '\0'; - } + token[token_index] = '\0'; - for (c = 0; n != -1 && c < 3 && ISOCTAL (*string); c++) - string++; + /* Check to see what thing we should return. If the last_read_token + is a `<', or a `&', or the character which ended this token is + a '>' or '<', then, and ONLY then, is this input token a NUMBER. + Otherwise, it is just a word, and should be returned as such. */ + if MBTEST(all_digit_token && (character == '<' || character == '>' || \ + last_read_token == LESS_AND || \ + last_read_token == GREATER_AND)) + { + if (legal_number (token, &lvalue) && (int)lvalue == lvalue) + yylval.number = lvalue; + else + yylval.number = -1; + return (NUMBER); + } - c = 0; - goto add_string; + /* Check for special case tokens. */ + result = (last_shell_getc_is_singlebyte) ? special_case_tokens (token) : -1; + if (result >= 0) + return result; - case 't': - case 'd': - case 'T': - case '@': - /* Make the current time/date into a string. */ - the_time = time (0); - temp = ctime (&the_time); +#if defined (ALIAS) + /* Posix.2 does not allow reserved words to be aliased, so check for all + of them, including special cases, before expanding the current token + as an alias. */ + if MBTEST(posixly_correct) + CHECK_FOR_RESERVED_WORD (token); - temp = (c != 'd') ? savestring (temp + 11) : savestring (temp); - temp[(c != 'd') ? 8 : 10] = '\0'; + /* Aliases are expanded iff EXPAND_ALIASES is non-zero, and quoting + inhibits alias expansion. */ + if (expand_aliases && quoted == 0) + { + result = alias_expand_token (token); + if (result == RE_READ_TOKEN) + return (RE_READ_TOKEN); + else if (result == NO_EXPANSION) + parser_state &= ~PST_ALEXPNEXT; + } - /* quick and dirty conversion to 12-hour time */ - if (c == 'T' || c == '@') - { - if (c == '@') - { - temp[5] = 'a'; /* am/pm format */ - temp[6] = 'm'; - temp[7] = '\0'; - } - c = temp[2]; - temp[2] = '\0'; - n = atoi (temp); - temp[2] = c; - n -= 12; - if (n > 0) - { - temp[0] = (n / 10) + '0'; - temp[1] = (n % 10) + '0'; - } - if (n >= 0 && temp[5] == 'a') - temp[5] = 'p'; - } - goto add_string; + /* If not in Posix.2 mode, check for reserved words after alias + expansion. */ + if MBTEST(posixly_correct == 0) +#endif + CHECK_FOR_RESERVED_WORD (token); - case 'r': - temp = xmalloc (2); - temp[0] = '\r'; - temp[1] = '\0'; - goto add_string; + the_word = (WORD_DESC *)xmalloc (sizeof (WORD_DESC)); + the_word->word = (char *)xmalloc (1 + token_index); + the_word->flags = 0; + strcpy (the_word->word, token); + if (dollar_present) + the_word->flags |= W_HASDOLLAR; + if (quoted) + the_word->flags |= W_QUOTED; + if (compound_assignment) + the_word->flags |= W_COMPASSIGN; + /* A word is an assignment if it appears at the beginning of a + simple command, or after another assignment word. This is + context-dependent, so it cannot be handled in the grammar. */ + if (assignment (token, (parser_state & PST_COMPASSIGN) != 0)) + { + the_word->flags |= W_ASSIGNMENT; + /* Don't perform word splitting on assignment statements. */ + if (assignment_acceptable (last_read_token) || (parser_state & PST_COMPASSIGN) != 0) + the_word->flags |= W_NOSPLIT; + } - case 'n': - temp = xmalloc (3); - temp[0] = no_line_editing ? '\n' : '\r'; - temp[1] = no_line_editing ? '\0' : '\n'; - temp[2] = '\0'; - goto add_string; + if (command_token_position (last_read_token)) + { + struct builtin *b; + b = builtin_address_internal (token, 0); + if (b && (b->flags & ASSIGNMENT_BUILTIN)) + parser_state |= PST_ASSIGNOK; + } - case 's': - temp = base_pathname (shell_name); - temp = savestring (temp); - goto add_string; + yylval.word = the_word; - case 'v': - case 'V': - temp = xmalloc (8); - if (c == 'v') - strcpy (temp, dist_version); - else - sprintf (temp, "%s.%d", dist_version, patch_level); - goto add_string; + result = ((the_word->flags & (W_ASSIGNMENT|W_NOSPLIT)) == (W_ASSIGNMENT|W_NOSPLIT)) + ? ASSIGNMENT_WORD : WORD; - case 'w': - case 'W': - { - /* Use the value of PWD because it is much more efficient. */ - char t_string[PATH_MAX]; - int tlen; + switch (last_read_token) + { + case FUNCTION: + parser_state |= PST_ALLOWOPNBRC; + function_dstart = line_number; + break; + case CASE: + case SELECT: + case FOR: + if (word_top < MAX_CASE_NEST) + word_top++; + word_lineno[word_top] = line_number; + break; + } - temp = get_string_value ("PWD"); + return (result); +} - if (temp == 0) - { - if (getcwd (t_string, sizeof(t_string)) == 0) - { - t_string[0] = '.'; - tlen = 1; - } - else - tlen = strlen (t_string); - } - else - { - tlen = sizeof (t_string) - 1; - strncpy (t_string, temp, tlen); - } - t_string[tlen] = '\0'; +/* Return 1 if TOKSYM is a token that after being read would allow + a reserved word to be seen, else 0. */ +static int +reserved_word_acceptable (toksym) + int toksym; +{ + switch (toksym) + { + case '\n': + case ';': + case '(': + case ')': + case '|': + case '&': + case '{': + case '}': /* XXX */ + case AND_AND: + case BANG: + case DO: + case DONE: + case ELIF: + case ELSE: + case ESAC: + case FI: + case IF: + case OR_OR: + case SEMI_SEMI: + case THEN: + case TIME: + case TIMEOPT: + case UNTIL: + case WHILE: + case 0: + return 1; + default: + return 0; + } +} + +/* Return the index of TOKEN in the alist of reserved words, or -1 if + TOKEN is not a shell reserved word. */ +int +find_reserved_word (tokstr) + char *tokstr; +{ + int i; + for (i = 0; word_token_alist[i].word; i++) + if (STREQ (tokstr, word_token_alist[i].word)) + return i; + return -1; +} -#define ROOT_PATH(x) ((x)[0] == '/' && (x)[1] == 0) -#define DOUBLE_SLASH_ROOT(x) ((x)[0] == '/' && (x)[1] == '/' && (x)[2] == 0) - if (c == 'W') - { - if (ROOT_PATH (t_string) == 0 && DOUBLE_SLASH_ROOT (t_string) == 0) - { - t = strrchr (t_string, '/'); - if (t) - strcpy (t_string, t + 1); - } - } -#undef ROOT_PATH -#undef DOUBLE_SLASH_ROOT - else - /* polite_directory_format is guaranteed to return a string - no longer than PATH_MAX - 1 characters. */ - strcpy (t_string, polite_directory_format (t_string)); +#if 0 +#if defined (READLINE) +/* Called after each time readline is called. This insures that whatever + the new prompt string is gets propagated to readline's local prompt + variable. */ +static void +reset_readline_prompt () +{ + char *temp_prompt; - /* If we're going to be expanding the prompt string later, - quote the directory name. */ - if (promptvars || posixly_correct) - /* Make sure that expand_prompt_string is called with a - second argument of Q_DOUBLE_QUOTE if we use this - function here. */ - temp = sh_backslash_quote_for_double_quotes (t_string); - else - temp = savestring (t_string); + if (prompt_string_pointer) + { + temp_prompt = (*prompt_string_pointer) + ? decode_prompt_string (*prompt_string_pointer) + : (char *)NULL; - goto add_string; - } + if (temp_prompt == 0) + { + temp_prompt = (char *)xmalloc (1); + temp_prompt[0] = '\0'; + } - case 'u': - if (current_user.user_name == 0) - get_current_user_info (); - temp = savestring (current_user.user_name); - goto add_string; + FREE (current_readline_prompt); + current_readline_prompt = temp_prompt; + } +} +#endif /* READLINE */ +#endif /* 0 */ + +#if defined (HISTORY) +/* A list of tokens which can be followed by newlines, but not by + semi-colons. When concatenating multiple lines of history, the + newline separator for such tokens is replaced with a space. */ +static int no_semi_successors[] = { + '\n', '{', '(', ')', ';', '&', '|', + CASE, DO, ELSE, IF, SEMI_SEMI, THEN, UNTIL, WHILE, AND_AND, OR_OR, IN, + 0 +}; + +/* If we are not within a delimited expression, try to be smart + about which separators can be semi-colons and which must be + newlines. Returns the string that should be added into the + history entry. */ +char * +history_delimiting_chars () +{ + register int i; + + if (dstack.delimiter_depth != 0) + return ("\n"); + + /* First, handle some special cases. */ + /*(*/ + /* If we just read `()', assume it's a function definition, and don't + add a semicolon. If the token before the `)' was not `(', and we're + not in the midst of parsing a case statement, assume it's a + parenthesized command and add the semicolon. */ + /*)(*/ + if (token_before_that == ')') + { + if (two_tokens_ago == '(') /*)*/ /* function def */ + return " "; + /* This does not work for subshells inside case statement + command lists. It's a suboptimal solution. */ + else if (parser_state & PST_CASESTMT) /* case statement pattern */ + return " "; + else + return "; "; /* (...) subshell */ + } + else if (token_before_that == WORD && two_tokens_ago == FUNCTION) + return " "; /* function def using `function name' without `()' */ - case 'h': - case 'H': - temp = savestring (current_host_name); - if (c == 'h' && (t = (char *)strchr (temp, '.'))) - *t = '\0'; - goto add_string; + 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 ";"; + } + else if (two_tokens_ago == CASE && token_before_that == WORD && (parser_state & PST_CASESTMT)) + return " "; - case '#': - temp = itos (current_command_number); - goto add_string; + for (i = 0; no_semi_successors[i]; i++) + { + if (token_before_that == no_semi_successors[i]) + return (" "); + } - case '!': -#if !defined (HISTORY) - temp = savestring ("1"); -#else /* HISTORY */ - temp = itos (history_number ()); + return ("; "); +} #endif /* HISTORY */ - goto add_string; - - case '$': - t = temp = xmalloc (3); - if ((promptvars || posixly_correct) && (current_user.euid != 0)) - *t++ = '\\'; - *t++ = current_user.euid == 0 ? '#' : '$'; - *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; +/* Issue a prompt, or prepare to issue a prompt when the next character + is read. */ +static void +prompt_again () +{ + char *temp_prompt; -#if defined (READLINE) - case '[': - case ']': - temp = xmalloc (3); - temp[0] = '\001'; - temp[1] = (c == '[') ? RL_PROMPT_START_IGNORE : RL_PROMPT_END_IGNORE; - temp[2] = '\0'; - goto add_string; -#endif /* READLINE */ + if (interactive == 0 || expanding_alias()) /* XXX */ + return; - case '\\': - temp = xmalloc (2); - temp[0] = c; - temp[1] = '\0'; - goto add_string; + ps1_prompt = get_string_value ("PS1"); + ps2_prompt = get_string_value ("PS2"); - case 'a': - case 'e': - temp = xmalloc (2); - temp[0] = (c == 'a') ? '\07' : '\033'; - temp[1] = '\0'; - goto add_string; + if (!prompt_string_pointer) + prompt_string_pointer = &ps1_prompt; - default: - temp = xmalloc (3); - temp[0] = '\\'; - temp[1] = c; - temp[2] = '\0'; + temp_prompt = *prompt_string_pointer + ? decode_prompt_string (*prompt_string_pointer) + : (char *)NULL; - add_string: - if (c) - string++; - result = - sub_append_string (temp, result, &result_index, &result_size); - temp = (char *)NULL; /* Freed in sub_append_string (). */ - result[result_index] = '\0'; - break; - } - } - else - { - RESIZE_MALLOCED_BUFFER (result, result_index, 3, result_size, PROMPT_GROWTH); - result[result_index++] = c; - result[result_index] = '\0'; - } + if (temp_prompt == 0) + { + temp_prompt = (char *)xmalloc (1); + temp_prompt[0] = '\0'; } -#else /* !PROMPT_STRING_DECODE */ - result = savestring (string); -#endif /* !PROMPT_STRING_DECODE */ - /* Save the delimiter stack and point `dstack' to temp space so any - command substitutions in the prompt string won't result in screwing - up the parser's quoting state. */ - save_dstack = dstack; - dstack = temp_dstack; - dstack.delimiter_depth = 0; + current_prompt_string = *prompt_string_pointer; + prompt_string_pointer = &ps2_prompt; - /* Perform variable and parameter expansion and command substitution on - the prompt string. */ - if (promptvars || posixly_correct) +#if defined (READLINE) + if (!no_line_editing) { - list = expand_prompt_string (result, Q_DOUBLE_QUOTES); - free (result); - result = string_list (list); - dispose_words (list); + FREE (current_readline_prompt); + current_readline_prompt = temp_prompt; } else +#endif /* READLINE */ { - t = dequote_string (result); - free (result); - result = t; + FREE (current_decoded_prompt); + current_decoded_prompt = temp_prompt; } - - dstack = save_dstack; - - return (result); } -/* Report a syntax error, and restart the parser. Call here for fatal - errors. */ int -yyerror () +get_current_prompt_level () { - report_syntax_error ((char *)NULL); - reset_parser (); - return (0); + return ((current_prompt_string && current_prompt_string == ps2_prompt) ? 2 : 1); } -/* Report a syntax error with line numbers, etc. - Call here for recoverable errors. If you have a message to print, - then place it in MESSAGE, otherwise pass NULL and this will figure - out an appropriate message for you. */ +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 -report_syntax_error (message) - char *message; +print_prompt () { - char *msg, *t; - int token_end, i; - char msg2[2]; + fprintf (stderr, "%s", current_decoded_prompt); + fflush (stderr); +} - if (message) - { - parser_error (line_number, "%s", message); - if (interactive && EOF_Reached) - EOF_Reached = 0; - last_command_exit_value = EX_USAGE; - return; - } +/* Return a string which will be printed as a prompt. The string + may contain special characters which are decoded as follows: - /* If the line of input we're reading is not null, try to find the - objectionable token. */ - if (shell_input_line && *shell_input_line) - { - t = shell_input_line; - i = shell_input_line_index; - token_end = 0; + \a bell (ascii 07) + \d the date in Day Mon Date format + \e escape (ascii 033) + \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 + \r CR + \s the name of the shell + \t the time in 24-hour hh:mm:ss format + \T the time in 12-hour hh:mm:ss format + \@ the time in 12-hour hh:mm am/pm format + \A the time in 24-hour hh:mm format + \D{fmt} the result of passing FMT to strftime(3) + \u your username + \v the version of bash (e.g., 2.00) + \V the release of bash, version + patchlevel (e.g., 2.00.0) + \w the current working directory + \W the last element of $PWD + \! the history number of this command + \# the command number of this command + \$ a $ or a # if you are root + \nnn character code nnn in octal + \\ a backslash + \[ begin a sequence of non-printing chars + \] end a sequence of non-printing chars +*/ +#define PROMPT_GROWTH 48 +char * +decode_prompt_string (string) + char *string; +{ + WORD_LIST *list; + char *result, *t; + struct dstack save_dstack; + int last_exit_value; +#if defined (PROMPT_STRING_DECODE) + int result_size, result_index; + int c, n; + char *temp, octal_string[4]; + struct tm *tm; + time_t the_time; + char timebuf[128]; + char *timefmt; - if (i && t[i] == '\0') - i--; + result = (char *)xmalloc (result_size = PROMPT_GROWTH); + result[result_index = 0] = 0; + temp = (char *)NULL; - while (i && (whitespace (t[i]) || t[i] == '\n')) - i--; + while (c = *string++) + { + if (posixly_correct && c == '!') + { + if (*string == '!') + { + temp = savestring ("!"); + goto add_string; + } + else + { +#if !defined (HISTORY) + temp = savestring ("1"); +#else /* HISTORY */ + temp = itos (history_number ()); +#endif /* HISTORY */ + string--; /* add_string increments string again. */ + goto add_string; + } + } + if (c == '\\') + { + c = *string; - if (i) - token_end = i + 1; + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + strncpy (octal_string, string, 3); + octal_string[3] = '\0'; - while (i && (member (t[i], " \n\t;|&") == 0)) - i--; + n = read_octal (octal_string); + temp = (char *)xmalloc (3); - while (i != token_end && (whitespace (t[i]) || t[i] == '\n')) - i++; + if (n == CTLESC || n == CTLNUL) + { + temp[0] = CTLESC; + temp[1] = n; + temp[2] = '\0'; + } + else if (n == -1) + { + temp[0] = '\\'; + temp[1] = '\0'; + } + else + { + temp[0] = n; + temp[1] = '\0'; + } - /* Print the offending token. */ - if (token_end || (i == 0 && token_end == 0)) - { - if (token_end) - msg = substring (t, i, token_end); - else /* one-character token */ - { - msg2[0] = t[i]; - msg2[1] = '\0'; - msg = msg2; - } + for (c = 0; n != -1 && c < 3 && ISOCTAL (*string); c++) + string++; - parser_error (line_number, "syntax error near unexpected token `%s'", msg); + c = 0; /* tested at add_string: */ + goto add_string; - if (msg != msg2) - free (msg); - } + case 'd': + case 't': + case 'T': + case '@': + case 'A': + /* Make the current time/date into a string. */ + (void) time (&the_time); + tm = localtime (&the_time); + + if (c == 'd') + n = strftime (timebuf, sizeof (timebuf), "%a %b %d", tm); + else if (c == 't') + n = strftime (timebuf, sizeof (timebuf), "%H:%M:%S", tm); + else if (c == 'T') + n = strftime (timebuf, sizeof (timebuf), "%I:%M:%S", tm); + else if (c == '@') + n = strftime (timebuf, sizeof (timebuf), "%I:%M %p", tm); + else if (c == 'A') + n = strftime (timebuf, sizeof (timebuf), "%H:%M", tm); + + if (n == 0) + timebuf[0] = '\0'; + else + timebuf[sizeof(timebuf) - 1] = '\0'; - /* If not interactive, print the line containing the error. */ - if (interactive == 0) - { - msg = savestring (shell_input_line); - token_end = strlen (msg); - while (token_end && msg[token_end - 1] == '\n') - msg[--token_end] = '\0'; + temp = savestring (timebuf); + goto add_string; - parser_error (line_number, "`%s'", msg); - free (msg); - } - } - else - { - msg = EOF_Reached ? "syntax error: unexpected end of file" : "syntax error"; - parser_error (line_number, "%s", msg); - /* When the shell is interactive, this file uses EOF_Reached - only for error reporting. Other mechanisms are used to - decide whether or not to exit. */ - if (interactive && EOF_Reached) - EOF_Reached = 0; - } - last_command_exit_value = EX_USAGE; -} + case 'D': /* strftime format */ + if (string[1] != '{') /* } */ + goto not_escape; -/* ??? Needed function. ??? We have to be able to discard the constructs - created during parsing. In the case of error, we want to return - allocated objects to the memory pool. In the case of no error, we want - to throw away the information about where the allocated objects live. - (dispose_command () will actually free the command. */ -static void -discard_parser_constructs (error_p) - int error_p; -{ -} + (void) time (&the_time); + tm = localtime (&the_time); + string += 2; /* skip { */ + timefmt = xmalloc (strlen (string) + 3); + for (t = timefmt; *string && *string != '}'; ) + *t++ = *string++; + *t = '\0'; + c = *string; /* tested at add_string */ + if (timefmt[0] == '\0') + { + timefmt[0] = '%'; + timefmt[1] = 'X'; /* locale-specific current time */ + timefmt[2] = '\0'; + } + n = strftime (timebuf, sizeof (timebuf), timefmt, tm); + free (timefmt); -/* Do that silly `type "bye" to exit' stuff. You know, "ignoreeof". */ + if (n == 0) + timebuf[0] = '\0'; + else + timebuf[sizeof(timebuf) - 1] = '\0'; -/* A flag denoting whether or not ignoreeof is set. */ -int ignoreeof = 0; + if (promptvars || posixly_correct) + /* Make sure that expand_prompt_string is called with a + second argument of Q_DOUBLE_QUOTES if we use this + function here. */ + temp = sh_backslash_quote_for_double_quotes (timebuf); + else + temp = savestring (timebuf); + goto add_string; + + case 'n': + temp = (char *)xmalloc (3); + temp[0] = no_line_editing ? '\n' : '\r'; + temp[1] = no_line_editing ? '\0' : '\n'; + temp[2] = '\0'; + goto add_string; -/* The number of times that we have encountered an EOF character without - another character intervening. When this gets above the limit, the - shell terminates. */ -int eof_encountered = 0; + case 's': + temp = base_pathname (shell_name); + temp = savestring (temp); + goto add_string; -/* The limit for eof_encountered. */ -int eof_encountered_limit = 10; + case 'v': + case 'V': + temp = (char *)xmalloc (16); + if (c == 'v') + strcpy (temp, dist_version); + else + sprintf (temp, "%s.%d", dist_version, patch_level); + goto add_string; -/* If we have EOF as the only input unit, this user wants to leave - the shell. If the shell is not interactive, then just leave. - Otherwise, if ignoreeof is set, and we haven't done this the - required number of times in a row, print a message. */ -static void -handle_eof_input_unit () -{ - if (interactive) - { - /* shell.c may use this to decide whether or not to write out the - history, among other things. We use it only for error reporting - in this file. */ - if (EOF_Reached) - EOF_Reached = 0; + case 'w': + case 'W': + { + /* Use the value of PWD because it is much more efficient. */ + char t_string[PATH_MAX], *t; + int tlen; - /* If the user wants to "ignore" eof, then let her do so, kind of. */ - if (ignoreeof) - { - if (eof_encountered < eof_encountered_limit) - { - fprintf (stderr, "Use \"%s\" to leave the shell.\n", - login_shell ? "logout" : "exit"); - eof_encountered++; - /* Reset the prompt string to be $PS1. */ - prompt_string_pointer = (char **)NULL; - prompt_again (); - last_read_token = current_token = '\n'; - return; - } - } + temp = get_string_value ("PWD"); - /* In this case EOF should exit the shell. Do it now. */ - reset_parser (); - exit_builtin ((WORD_LIST *)NULL); - } - else - { - /* We don't write history files, etc., for non-interactive shells. */ - EOF_Reached = 1; - } -} + if (temp == 0) + { + if (getcwd (t_string, sizeof(t_string)) == 0) + { + t_string[0] = '.'; + tlen = 1; + } + else + tlen = strlen (t_string); + } + else + { + tlen = sizeof (t_string) - 1; + strncpy (t_string, temp, tlen); + } + t_string[tlen] = '\0'; -static WORD_LIST parse_string_error; +#define ROOT_PATH(x) ((x)[0] == '/' && (x)[1] == 0) +#define DOUBLE_SLASH_ROOT(x) ((x)[0] == '/' && (x)[1] == '/' && (x)[2] == 0) + /* Abbreviate \W as ~ if $PWD == $HOME */ + if (c == 'W' && (((t = get_string_value ("HOME")) == 0) || STREQ (t, t_string) == 0)) + { + if (ROOT_PATH (t_string) == 0 && DOUBLE_SLASH_ROOT (t_string) == 0) + { + t = strrchr (t_string, '/'); + if (t) + strcpy (t_string, t + 1); + } + } +#undef ROOT_PATH +#undef DOUBLE_SLASH_ROOT + else + /* polite_directory_format is guaranteed to return a string + no longer than PATH_MAX - 1 characters. */ + strcpy (t_string, polite_directory_format (t_string)); -/* 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; - int orig_line_count; -#if defined (HISTORY) - int old_remember_on_history, old_history_expansion_inhibited; -#endif + /* If we're going to be expanding the prompt string later, + quote the directory name. */ + if (promptvars || posixly_correct) + /* Make sure that expand_prompt_string is called with a + second argument of Q_DOUBLE_QUOTES if we use this + function here. */ + temp = sh_backslash_quote_for_double_quotes (t_string); + else + temp = savestring (t_string); -#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 + goto add_string; + } - orig_line_number = line_number; - orig_line_count = current_command_line_count; - orig_input_terminator = shell_input_line_terminator; + case 'u': + if (current_user.user_name == 0) + get_current_user_info (); + temp = savestring (current_user.user_name); + goto add_string; - push_stream (1); - last_read_token = '\n'; - current_command_line_count = 0; + case 'h': + case 'H': + temp = savestring (current_host_name); + if (c == 'h' && (t = (char *)strchr (temp, '.'))) + *t = '\0'; + goto add_string; - 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 == '\n') /* Allow newlines in compound assignments */ - continue; - 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 (); + case '#': + temp = itos (current_command_number); + goto add_string; -#if defined (HISTORY) - remember_on_history = old_remember_on_history; -# if defined (BANG_HISTORY) - history_expansion_inhibited = old_history_expansion_inhibited; -# endif /* BANG_HISTORY */ + case '!': +#if !defined (HISTORY) + temp = savestring ("1"); +#else /* HISTORY */ + temp = itos (history_number ()); #endif /* HISTORY */ + goto add_string; - current_command_line_count = orig_line_count; - shell_input_line_terminator = orig_input_terminator; + case '$': + t = temp = (char *)xmalloc (3); + if ((promptvars || posixly_correct) && (current_user.euid != 0)) + *t++ = '\\'; + *t++ = current_user.euid == 0 ? '#' : '$'; + *t = '\0'; + goto add_string; - 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); - } + case 'j': + temp = itos (count_all_jobs ()); + goto add_string; - return (REVERSE_LIST (wl, WORD_LIST *)); -} -#line 4199 "y.tab.c" -/* allocate initial stack or double stack size, up to YYMAXDEPTH */ -static int yygrowstack() -{ - int newsize, i; - short *newss; - YYSTYPE *newvs; - - if ((newsize = yystacksize) == 0) - newsize = YYINITSTACKSIZE; - else if (newsize >= YYMAXDEPTH) - return -1; - else if ((newsize *= 2) > YYMAXDEPTH) - newsize = YYMAXDEPTH; - i = yyssp - yyss; - newss = yyss ? (short *)realloc(yyss, newsize * sizeof *newss) : - (short *)malloc(newsize * sizeof *newss); - if (newss == NULL) - return -1; - yyss = newss; - yyssp = newss + i; - newvs = yyvs ? (YYSTYPE *)realloc(yyvs, newsize * sizeof *newvs) : - (YYSTYPE *)malloc(newsize * sizeof *newvs); - if (newvs == NULL) - return -1; - yyvs = newvs; - yyvsp = newvs + i; - yystacksize = newsize; - yysslim = yyss + newsize - 1; - return 0; -} + 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; -#define YYABORT goto yyabort -#define YYREJECT goto yyabort -#define YYACCEPT goto yyaccept -#define YYERROR goto yyerrlab +#if defined (READLINE) + case '[': + case ']': + if (no_line_editing) + { + string++; + break; + } + temp = (char *)xmalloc (3); + temp[0] = '\001'; + temp[1] = (c == '[') ? RL_PROMPT_START_IGNORE : RL_PROMPT_END_IGNORE; + temp[2] = '\0'; + goto add_string; +#endif /* READLINE */ -#ifndef YYPARSE_PARAM -#if defined(__cplusplus) || __STDC__ -#define YYPARSE_PARAM_ARG void -#define YYPARSE_PARAM_DECL -#else /* ! ANSI-C/C++ */ -#define YYPARSE_PARAM_ARG -#define YYPARSE_PARAM_DECL -#endif /* ANSI-C/C++ */ -#else /* YYPARSE_PARAM */ -#ifndef YYPARSE_PARAM_TYPE -#define YYPARSE_PARAM_TYPE void * -#endif -#if defined(__cplusplus) || __STDC__ -#define YYPARSE_PARAM_ARG YYPARSE_PARAM_TYPE YYPARSE_PARAM -#define YYPARSE_PARAM_DECL -#else /* ! ANSI-C/C++ */ -#define YYPARSE_PARAM_ARG YYPARSE_PARAM -#define YYPARSE_PARAM_DECL YYPARSE_PARAM_TYPE YYPARSE_PARAM; -#endif /* ANSI-C/C++ */ -#endif /* ! YYPARSE_PARAM */ + case '\\': + case 'a': + case 'e': + case 'r': + temp = (char *)xmalloc (2); + if (c == 'a') + temp[0] = '\07'; + else if (c == 'e') + temp[0] = '\033'; + else if (c == 'r') + temp[0] = '\r'; + else /* (c == '\\') */ + temp[0] = c; + temp[1] = '\0'; + goto add_string; -int -yyparse (YYPARSE_PARAM_ARG) - YYPARSE_PARAM_DECL -{ - register int yym, yyn, yystate; -#if YYDEBUG - register const char *yys; + default: +not_escape: + temp = (char *)xmalloc (3); + temp[0] = '\\'; + temp[1] = c; + temp[2] = '\0'; - if ((yys = getenv("YYDEBUG"))) - { - yyn = *yys; - if (yyn >= '0' && yyn <= '9') - yydebug = yyn - '0'; + add_string: + if (c) + string++; + result = + sub_append_string (temp, result, &result_index, &result_size); + temp = (char *)NULL; /* Freed in sub_append_string (). */ + result[result_index] = '\0'; + break; + } + } + else + { + RESIZE_MALLOCED_BUFFER (result, result_index, 3, result_size, PROMPT_GROWTH); + result[result_index++] = c; + result[result_index] = '\0'; + } } -#endif - - yynerrs = 0; - yyerrflag = 0; - yychar = (-1); +#else /* !PROMPT_STRING_DECODE */ + result = savestring (string); +#endif /* !PROMPT_STRING_DECODE */ - if (yyss == NULL && yygrowstack()) goto yyoverflow; - yyssp = yyss; - yyvsp = yyvs; - *yyssp = yystate = 0; + /* Save the delimiter stack and point `dstack' to temp space so any + command substitutions in the prompt string won't result in screwing + up the parser's quoting state. */ + save_dstack = dstack; + dstack = temp_dstack; + dstack.delimiter_depth = 0; -yyloop: - if ((yyn = yydefred[yystate])) goto yyreduce; - if (yychar < 0) - { - if ((yychar = yylex()) < 0) yychar = 0; -#if YYDEBUG - if (yydebug) - { - yys = 0; - if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; - if (!yys) yys = "illegal-symbol"; - printf("%sdebug: state %d, reading %d (%s)\n", - YYPREFIX, yystate, yychar, yys); - } -#endif - } - if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 && - yyn <= YYTABLESIZE && yycheck[yyn] == yychar) - { -#if YYDEBUG - if (yydebug) - printf("%sdebug: state %d, shifting to state %d\n", - YYPREFIX, yystate, yytable[yyn]); -#endif - if (yyssp >= yysslim && yygrowstack()) - { - goto yyoverflow; - } - *++yyssp = yystate = yytable[yyn]; - *++yyvsp = yylval; - yychar = (-1); - if (yyerrflag > 0) --yyerrflag; - goto yyloop; - } - if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 && - yyn <= YYTABLESIZE && yycheck[yyn] == yychar) - { - yyn = yytable[yyn]; - goto yyreduce; - } - if (yyerrflag) goto yyinrecovery; -#if defined(lint) || defined(__GNUC__) - goto yynewerror; -#endif -yynewerror: - yyerror("syntax error"); -#if defined(lint) || defined(__GNUC__) - goto yyerrlab; -#endif -yyerrlab: - ++yynerrs; -yyinrecovery: - if (yyerrflag < 3) - { - yyerrflag = 3; - for (;;) - { - if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 && - yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) - { -#if YYDEBUG - if (yydebug) - printf("%sdebug: state %d, error recovery shifting\ - to state %d\n", YYPREFIX, *yyssp, yytable[yyn]); -#endif - if (yyssp >= yysslim && yygrowstack()) - { - goto yyoverflow; - } - *++yyssp = yystate = yytable[yyn]; - *++yyvsp = yylval; - goto yyloop; - } - else - { -#if YYDEBUG - if (yydebug) - printf("%sdebug: error recovery discarding state %d\n", - YYPREFIX, *yyssp); -#endif - if (yyssp <= yyss) goto yyabort; - --yyssp; - --yyvsp; - } - } - } - else - { - if (yychar == 0) goto yyabort; -#if YYDEBUG - if (yydebug) - { - yys = 0; - if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; - if (!yys) yys = "illegal-symbol"; - printf("%sdebug: state %d, error recovery discards token %d (%s)\n", - YYPREFIX, yystate, yychar, yys); - } -#endif - yychar = (-1); - goto yyloop; - } -yyreduce: -#if YYDEBUG - if (yydebug) - printf("%sdebug: state %d, reducing by rule %d (%s)\n", - YYPREFIX, yystate, yyn, yyrule[yyn]); -#endif - yym = yylen[yyn]; - yyval = yyvsp[1-yym]; - switch (yyn) + /* Perform variable and parameter expansion and command substitution on + the prompt string. */ + if (promptvars || posixly_correct) { -case 1: -#line 241 "/usr/homes/chet/src/bash/src/parse.y" -{ - /* Case of regular command. Discard the error - safety net,and return the command just parsed. */ - global_command = yyvsp[-1].command; - eof_encountered = 0; - discard_parser_constructs (0); - YYACCEPT; - } -break; -case 2: -#line 250 "/usr/homes/chet/src/bash/src/parse.y" -{ - /* Case of regular command, but not a very - interesting one. Return a NULL command. */ - global_command = (COMMAND *)NULL; - YYACCEPT; - } -break; -case 3: -#line 257 "/usr/homes/chet/src/bash/src/parse.y" -{ - /* Error during parsing. Return NULL command. */ - global_command = (COMMAND *)NULL; - eof_encountered = 0; - discard_parser_constructs (1); - if (interactive) - { - YYACCEPT; - } - else - { - YYABORT; - } - } -break; -case 4: -#line 272 "/usr/homes/chet/src/bash/src/parse.y" -{ - /* Case of EOF seen by itself. Do ignoreeof or - not. */ - global_command = (COMMAND *)NULL; - handle_eof_input_unit (); - YYACCEPT; - } -break; -case 5: -#line 282 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.word_list = make_word_list (yyvsp[0].word, (WORD_LIST *)NULL); } -break; -case 6: -#line 284 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.word_list = make_word_list (yyvsp[0].word, yyvsp[-1].word_list); } -break; -case 7: -#line 288 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (1, r_output_direction, redir); - } -break; -case 8: -#line 293 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (0, r_input_direction, redir); - } -break; -case 9: -#line 298 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (yyvsp[-2].number, r_output_direction, redir); - } -break; -case 10: -#line 303 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (yyvsp[-2].number, r_input_direction, redir); - } -break; -case 11: -#line 308 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (1, r_appending_to, redir); - } -break; -case 12: -#line 313 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (yyvsp[-2].number, r_appending_to, redir); - } -break; -case 13: -#line 318 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (0, r_reading_until, redir); - redir_stack[need_here_doc++] = yyval.redirect; - } -break; -case 14: -#line 324 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (yyvsp[-2].number, r_reading_until, redir); - redir_stack[need_here_doc++] = yyval.redirect; - } -break; -case 15: -#line 330 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.dest = yyvsp[0].number; - yyval.redirect = make_redirection (0, r_duplicating_input, redir); - } -break; -case 16: -#line 335 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.dest = yyvsp[0].number; - yyval.redirect = make_redirection (yyvsp[-2].number, r_duplicating_input, redir); - } -break; -case 17: -#line 340 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.dest = yyvsp[0].number; - yyval.redirect = make_redirection (1, r_duplicating_output, redir); - } -break; -case 18: -#line 345 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.dest = yyvsp[0].number; - yyval.redirect = make_redirection (yyvsp[-2].number, r_duplicating_output, redir); - } -break; -case 19: -#line 350 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (0, r_duplicating_input_word, redir); - } -break; -case 20: -#line 355 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (yyvsp[-2].number, r_duplicating_input_word, redir); - } -break; -case 21: -#line 360 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (1, r_duplicating_output_word, redir); - } -break; -case 22: -#line 365 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (yyvsp[-2].number, r_duplicating_output_word, redir); - } -break; -case 23: -#line 370 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection - (0, r_deblank_reading_until, redir); - redir_stack[need_here_doc++] = yyval.redirect; - } -break; -case 24: -#line 377 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection - (yyvsp[-2].number, r_deblank_reading_until, redir); - redir_stack[need_here_doc++] = yyval.redirect; - } -break; -case 25: -#line 384 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.dest = 0L; - yyval.redirect = make_redirection (1, r_close_this, redir); - } -break; -case 26: -#line 389 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.dest = 0L; - yyval.redirect = make_redirection (yyvsp[-2].number, r_close_this, redir); - } -break; -case 27: -#line 394 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.dest = 0L; - yyval.redirect = make_redirection (0, r_close_this, redir); - } -break; -case 28: -#line 399 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.dest = 0L; - yyval.redirect = make_redirection (yyvsp[-2].number, r_close_this, redir); - } -break; -case 29: -#line 404 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (1, r_err_and_out, redir); - } -break; -case 30: -#line 409 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (yyvsp[-2].number, r_input_output, redir); - } -break; -case 31: -#line 414 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (0, r_input_output, redir); - } -break; -case 32: -#line 419 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (1, r_output_force, redir); - } -break; -case 33: -#line 424 "/usr/homes/chet/src/bash/src/parse.y" -{ - redir.filename = yyvsp[0].word; - yyval.redirect = make_redirection (yyvsp[-2].number, r_output_force, redir); - } -break; -case 34: -#line 431 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.element.word = yyvsp[0].word; yyval.element.redirect = 0; } -break; -case 35: -#line 433 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.element.word = yyvsp[0].word; yyval.element.redirect = 0; } -break; -case 36: -#line 435 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.element.redirect = yyvsp[0].redirect; yyval.element.word = 0; } -break; -case 37: -#line 439 "/usr/homes/chet/src/bash/src/parse.y" -{ - yyval.redirect = yyvsp[0].redirect; - } -break; -case 38: -#line 443 "/usr/homes/chet/src/bash/src/parse.y" -{ - register REDIRECT *t; + last_exit_value = last_command_exit_value; + list = expand_prompt_string (result, Q_DOUBLE_QUOTES); + free (result); + result = string_list (list); + dispose_words (list); + last_command_exit_value = last_exit_value; + } + else + { + t = dequote_string (result); + free (result); + result = t; + } - for (t = yyvsp[-1].redirect; t->next; t = t->next) - ; - t->next = yyvsp[0].redirect; - yyval.redirect = yyvsp[-1].redirect; - } -break; -case 39: -#line 454 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_simple_command (yyvsp[0].element, (COMMAND *)NULL); } -break; -case 40: -#line 456 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_simple_command (yyvsp[0].element, yyvsp[-1].command); } -break; -case 41: -#line 460 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = clean_simple_command (yyvsp[0].command); } -break; -case 42: -#line 462 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[0].command; } -break; -case 43: -#line 464 "/usr/homes/chet/src/bash/src/parse.y" -{ - COMMAND *tc; + dstack = save_dstack; - tc = yyvsp[-1].command; - if (tc->redirects) - { - register REDIRECT *t; - for (t = tc->redirects; t->next; t = t->next) - ; - t->next = yyvsp[0].redirect; - } - else - tc->redirects = yyvsp[0].redirect; - yyval.command = yyvsp[-1].command; - } -break; -case 44: -#line 480 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[0].command; } -break; -case 45: -#line 484 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[0].command; } -break; -case 46: -#line 486 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[0].command; } -break; -case 47: -#line 488 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_while_command (yyvsp[-3].command, yyvsp[-1].command); } -break; -case 48: -#line 490 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_until_command (yyvsp[-3].command, yyvsp[-1].command); } -break; -case 49: -#line 492 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[0].command; } -break; -case 50: -#line 494 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[0].command; } -break; -case 51: -#line 496 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[0].command; } -break; -case 52: -#line 498 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[0].command; } -break; -case 53: -#line 500 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[0].command; } -break; -case 54: -#line 502 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[0].command; } -break; -case 55: -#line 504 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[0].command; } -break; -case 56: -#line 508 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_for_command (yyvsp[-4].word, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), yyvsp[-1].command); } -break; -case 57: -#line 510 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_for_command (yyvsp[-4].word, add_string_to_list ("$@", (WORD_LIST *)NULL), yyvsp[-1].command); } -break; -case 58: -#line 512 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_for_command (yyvsp[-5].word, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), yyvsp[-1].command); } -break; -case 59: -#line 514 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_for_command (yyvsp[-5].word, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), yyvsp[-1].command); } -break; -case 60: -#line 516 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_for_command (yyvsp[-8].word, REVERSE_LIST (yyvsp[-5].word_list, WORD_LIST *), yyvsp[-1].command); } -break; -case 61: -#line 518 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_for_command (yyvsp[-8].word, REVERSE_LIST (yyvsp[-5].word_list, WORD_LIST *), yyvsp[-1].command); } -break; -case 62: -#line 522 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_arith_for_command (yyvsp[-5].word_list, yyvsp[-1].command, arith_for_lineno); } -break; -case 63: -#line 524 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_arith_for_command (yyvsp[-5].word_list, yyvsp[-1].command, arith_for_lineno); } -break; -case 64: -#line 526 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_arith_for_command (yyvsp[-3].word_list, yyvsp[-1].command, arith_for_lineno); } -break; -case 65: -#line 528 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_arith_for_command (yyvsp[-3].word_list, yyvsp[-1].command, arith_for_lineno); } -break; -case 66: -#line 532 "/usr/homes/chet/src/bash/src/parse.y" -{ - yyval.command = make_select_command (yyvsp[-4].word, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), yyvsp[-1].command); - } -break; -case 67: -#line 536 "/usr/homes/chet/src/bash/src/parse.y" -{ - yyval.command = make_select_command (yyvsp[-4].word, add_string_to_list ("$@", (WORD_LIST *)NULL), yyvsp[-1].command); - } -break; -case 68: -#line 540 "/usr/homes/chet/src/bash/src/parse.y" -{ - yyval.command = make_select_command (yyvsp[-5].word, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), yyvsp[-1].command); - } -break; -case 69: -#line 544 "/usr/homes/chet/src/bash/src/parse.y" -{ - yyval.command = make_select_command (yyvsp[-5].word, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), yyvsp[-1].command); - } -break; -case 70: -#line 548 "/usr/homes/chet/src/bash/src/parse.y" -{ - yyval.command = make_select_command (yyvsp[-8].word, (WORD_LIST *)reverse_list (yyvsp[-5].word_list), yyvsp[-1].command); - } -break; -case 71: -#line 552 "/usr/homes/chet/src/bash/src/parse.y" -{ - yyval.command = make_select_command (yyvsp[-8].word, (WORD_LIST *)reverse_list (yyvsp[-5].word_list), yyvsp[-1].command); - } -break; -case 72: -#line 558 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_case_command (yyvsp[-4].word, (PATTERN_LIST *)NULL); } -break; -case 73: -#line 560 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_case_command (yyvsp[-5].word, yyvsp[-2].pattern); } -break; -case 74: -#line 562 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_case_command (yyvsp[-4].word, yyvsp[-1].pattern); } -break; -case 75: -#line 566 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_function_def (yyvsp[-4].word, yyvsp[0].command, function_dstart, function_bstart); } -break; -case 76: -#line 569 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_function_def (yyvsp[-4].word, yyvsp[0].command, function_dstart, function_bstart); } -break; -case 77: -#line 572 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_function_def (yyvsp[-2].word, yyvsp[0].command, function_dstart, function_bstart); } -break; -case 78: -#line 577 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[0].command; } -break; -case 79: -#line 579 "/usr/homes/chet/src/bash/src/parse.y" -{ - COMMAND *tc; + return (result); +} - tc = yyvsp[-1].command; - /* According to Posix.2 3.9.5, redirections - specified after the body of a function should - 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->redirects) - { - register REDIRECT *t; - for (t = tc->redirects; t->next; t = t->next) - ; - t->next = yyvsp[0].redirect; - } - else - tc->redirects = yyvsp[0].redirect; - yyval.command = yyvsp[-1].command; - } -break; -case 80: -#line 610 "/usr/homes/chet/src/bash/src/parse.y" -{ - yyval.command = make_subshell_command (yyvsp[-1].command); - yyval.command->flags |= CMD_WANT_SUBSHELL; - } -break; -case 81: -#line 617 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_if_command (yyvsp[-3].command, yyvsp[-1].command, (COMMAND *)NULL); } -break; -case 82: -#line 619 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_if_command (yyvsp[-5].command, yyvsp[-3].command, yyvsp[-1].command); } -break; -case 83: -#line 621 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_if_command (yyvsp[-4].command, yyvsp[-2].command, yyvsp[-1].command); } -break; -case 84: -#line 626 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_group_command (yyvsp[-1].command); } -break; -case 85: -#line 630 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_arith_command (yyvsp[0].word_list); } -break; -case 86: -#line 634 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[-1].command; } -break; -case 87: -#line 638 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_if_command (yyvsp[-2].command, yyvsp[0].command, (COMMAND *)NULL); } -break; -case 88: -#line 640 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_if_command (yyvsp[-4].command, yyvsp[-2].command, yyvsp[0].command); } -break; -case 89: -#line 642 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = make_if_command (yyvsp[-3].command, yyvsp[-1].command, yyvsp[0].command); } -break; -case 91: -#line 647 "/usr/homes/chet/src/bash/src/parse.y" -{ yyvsp[0].pattern->next = yyvsp[-1].pattern; yyval.pattern = yyvsp[0].pattern; } -break; -case 92: -#line 651 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.pattern = make_pattern_list (yyvsp[-2].word_list, yyvsp[0].command); } -break; -case 93: -#line 653 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.pattern = make_pattern_list (yyvsp[-2].word_list, (COMMAND *)NULL); } -break; -case 94: -#line 655 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.pattern = make_pattern_list (yyvsp[-2].word_list, yyvsp[0].command); } -break; -case 95: -#line 657 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.pattern = make_pattern_list (yyvsp[-2].word_list, (COMMAND *)NULL); } -break; -case 97: -#line 662 "/usr/homes/chet/src/bash/src/parse.y" -{ yyvsp[-1].pattern->next = yyvsp[-2].pattern; yyval.pattern = yyvsp[-1].pattern; } -break; -case 98: -#line 666 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.word_list = make_word_list (yyvsp[0].word, (WORD_LIST *)NULL); } -break; -case 99: -#line 668 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.word_list = make_word_list (yyvsp[0].word, yyvsp[-2].word_list); } -break; -case 100: -#line 677 "/usr/homes/chet/src/bash/src/parse.y" -{ - yyval.command = yyvsp[0].command; - if (need_here_doc) - gather_here_documents (); - } -break; -case 102: -#line 686 "/usr/homes/chet/src/bash/src/parse.y" -{ - yyval.command = yyvsp[0].command; - } -break; -case 104: -#line 693 "/usr/homes/chet/src/bash/src/parse.y" -{ - if (yyvsp[-2].command->type == cm_connection) - yyval.command = connect_async_list (yyvsp[-2].command, (COMMAND *)NULL, '&'); - else - yyval.command = command_connect (yyvsp[-2].command, (COMMAND *)NULL, '&'); - } -break; -case 106: -#line 704 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = command_connect (yyvsp[-3].command, yyvsp[0].command, AND_AND); } -break; -case 107: -#line 706 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = command_connect (yyvsp[-3].command, yyvsp[0].command, OR_OR); } -break; -case 108: -#line 708 "/usr/homes/chet/src/bash/src/parse.y" +/************************************************ + * * + * ERROR HANDLING * + * * + ************************************************/ + +/* Report a syntax error, and restart the parser. Call here for fatal + errors. */ +int +yyerror (msg) + const char *msg; { - if (yyvsp[-3].command->type == cm_connection) - yyval.command = connect_async_list (yyvsp[-3].command, yyvsp[0].command, '&'); - else - yyval.command = command_connect (yyvsp[-3].command, yyvsp[0].command, '&'); - } -break; -case 109: -#line 715 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = command_connect (yyvsp[-3].command, yyvsp[0].command, ';'); } -break; -case 110: -#line 717 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = command_connect (yyvsp[-3].command, yyvsp[0].command, ';'); } -break; -case 111: -#line 719 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[0].command; } -break; -case 117: -#line 738 "/usr/homes/chet/src/bash/src/parse.y" + report_syntax_error ((char *)NULL); + reset_parser (); + return (0); +} + +static char * +error_token_from_token (token) + int token; { - yyval.command = yyvsp[0].command; - if (need_here_doc) - gather_here_documents (); - } -break; -case 118: -#line 744 "/usr/homes/chet/src/bash/src/parse.y" + char *t; + + if (t = find_token_in_alist (token, word_token_alist, 0)) + return t; + + if (t = find_token_in_alist (token, other_token_alist, 0)) + return t; + + t = (char *)NULL; + /* This stuff is dicy and needs closer inspection */ + switch (current_token) + { + case WORD: + case ASSIGNMENT_WORD: + if (yylval.word) + t = savestring (yylval.word->word); + break; + case NUMBER: + t = itos (yylval.number); + break; + case ARITH_CMD: + if (yylval.word_list) + t = string_list (yylval.word_list); + break; + case ARITH_FOR_EXPRS: + if (yylval.word_list) + t = string_list_internal (yylval.word_list, " ; "); + break; + case COND_CMD: + t = (char *)NULL; /* punt */ + break; + } + + return t; +} + +static char * +error_token_from_text () { - if (yyvsp[-1].command->type == cm_connection) - yyval.command = connect_async_list (yyvsp[-1].command, (COMMAND *)NULL, '&'); - else - yyval.command = command_connect (yyvsp[-1].command, (COMMAND *)NULL, '&'); - if (need_here_doc) - gather_here_documents (); - } -break; -case 119: -#line 753 "/usr/homes/chet/src/bash/src/parse.y" + char *msg, *t; + int token_end, i; + + t = shell_input_line; + i = shell_input_line_index; + token_end = 0; + msg = (char *)NULL; + + if (i && t[i] == '\0') + i--; + + while (i && (whitespace (t[i]) || t[i] == '\n')) + i--; + + if (i) + token_end = i + 1; + + while (i && (member (t[i], " \n\t;|&") == 0)) + i--; + + while (i != token_end && (whitespace (t[i]) || t[i] == '\n')) + i++; + + /* Return our idea of the offending token. */ + if (token_end || (i == 0 && token_end == 0)) + { + if (token_end) + msg = substring (t, i, token_end); + else /* one-character token */ + { + msg = (char *)xmalloc (2); + msg[0] = t[i]; + msg[1] = '\0'; + } + } + + return (msg); +} + +static void +print_offending_line () { - yyval.command = yyvsp[-1].command; - if (need_here_doc) - gather_here_documents (); - } -break; -case 120: -#line 761 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = command_connect (yyvsp[-3].command, yyvsp[0].command, AND_AND); } -break; -case 121: -#line 763 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = command_connect (yyvsp[-3].command, yyvsp[0].command, OR_OR); } -break; -case 122: -#line 765 "/usr/homes/chet/src/bash/src/parse.y" + char *msg; + int token_end; + + msg = savestring (shell_input_line); + token_end = strlen (msg); + while (token_end && msg[token_end - 1] == '\n') + msg[--token_end] = '\0'; + + parser_error (line_number, "`%s'", msg); + free (msg); +} + +/* Report a syntax error with line numbers, etc. + Call here for recoverable errors. If you have a message to print, + then place it in MESSAGE, otherwise pass NULL and this will figure + out an appropriate message for you. */ +static void +report_syntax_error (message) + char *message; { - if (yyvsp[-2].command->type == cm_connection) - yyval.command = connect_async_list (yyvsp[-2].command, yyvsp[0].command, '&'); - else - yyval.command = command_connect (yyvsp[-2].command, yyvsp[0].command, '&'); - } -break; -case 123: -#line 772 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = command_connect (yyvsp[-2].command, yyvsp[0].command, ';'); } -break; -case 124: -#line 775 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[0].command; } -break; -case 125: -#line 779 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[0].command; } -break; -case 126: -#line 781 "/usr/homes/chet/src/bash/src/parse.y" + char *msg; + + if (message) + { + parser_error (line_number, "%s", message); + if (interactive && EOF_Reached) + EOF_Reached = 0; + last_command_exit_value = EX_USAGE; + return; + } + + /* If the line of input we're reading is not null, try to find the + objectionable token. First, try to figure out what token the + parser's complaining about by looking at current_token. */ + if (current_token != 0 && EOF_Reached == 0 && (msg = error_token_from_token (current_token))) + { + parser_error (line_number, _("syntax error near unexpected token `%s'"), msg); + free (msg); + + if (interactive == 0) + print_offending_line (); + + last_command_exit_value = EX_USAGE; + return; + } + + /* If looking at the current token doesn't prove fruitful, try to find the + offending token by analyzing the text of the input line near the current + input line index and report what we find. */ + if (shell_input_line && *shell_input_line) + { + msg = error_token_from_text (); + if (msg) + { + parser_error (line_number, _("syntax error near `%s'"), msg); + free (msg); + } + + /* If not interactive, print the line containing the error. */ + if (interactive == 0) + print_offending_line (); + } + else + { + msg = EOF_Reached ? _("syntax error: unexpected end of file") : _("syntax error"); + parser_error (line_number, "%s", msg); + /* When the shell is interactive, this file uses EOF_Reached + only for error reporting. Other mechanisms are used to + decide whether or not to exit. */ + if (interactive && EOF_Reached) + EOF_Reached = 0; + } + + last_command_exit_value = EX_USAGE; +} + +/* ??? Needed function. ??? We have to be able to discard the constructs + created during parsing. In the case of error, we want to return + allocated objects to the memory pool. In the case of no error, we want + to throw away the information about where the allocated objects live. + (dispose_command () will actually free the command.) */ +static void +discard_parser_constructs (error_p) + int error_p; { - yyvsp[0].command->flags |= CMD_INVERT_RETURN; - yyval.command = yyvsp[0].command; - } -break; -case 127: -#line 786 "/usr/homes/chet/src/bash/src/parse.y" +} + +/************************************************ + * * + * EOF HANDLING * + * * + ************************************************/ + +/* Do that silly `type "bye" to exit' stuff. You know, "ignoreeof". */ + +/* A flag denoting whether or not ignoreeof is set. */ +int ignoreeof = 0; + +/* The number of times that we have encountered an EOF character without + another character intervening. When this gets above the limit, the + shell terminates. */ +int eof_encountered = 0; + +/* The limit for eof_encountered. */ +int eof_encountered_limit = 10; + +/* If we have EOF as the only input unit, this user wants to leave + the shell. If the shell is not interactive, then just leave. + Otherwise, if ignoreeof is set, and we haven't done this the + required number of times in a row, print a message. */ +static void +handle_eof_input_unit () { - yyvsp[0].command->flags |= yyvsp[-1].number; - yyval.command = yyvsp[0].command; - } -break; -case 128: -#line 791 "/usr/homes/chet/src/bash/src/parse.y" + if (interactive) + { + /* shell.c may use this to decide whether or not to write out the + history, among other things. We use it only for error reporting + in this file. */ + if (EOF_Reached) + EOF_Reached = 0; + + /* If the user wants to "ignore" eof, then let her do so, kind of. */ + if (ignoreeof) + { + if (eof_encountered < eof_encountered_limit) + { + fprintf (stderr, _("Use \"%s\" to leave the shell.\n"), + login_shell ? "logout" : "exit"); + eof_encountered++; + /* Reset the parsing state. */ + last_read_token = current_token = '\n'; + /* Reset the prompt string to be $PS1. */ + prompt_string_pointer = (char **)NULL; + prompt_again (); + return; + } + } + + /* In this case EOF should exit the shell. Do it now. */ + reset_parser (); + exit_builtin ((WORD_LIST *)NULL); + } + else + { + /* We don't write history files, etc., for non-interactive shells. */ + EOF_Reached = 1; + } +} + +/************************************************ + * * + * STRING PARSING FUNCTIONS * + * * + ************************************************/ + +/* It's very important that these two functions treat the characters + between ( and ) identically. */ + +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, flags, whom) + char *s; + int flags; + const char *whom; { - yyvsp[0].command->flags |= yyvsp[-2].number|CMD_INVERT_RETURN; - yyval.command = yyvsp[0].command; - } -break; -case 129: -#line 796 "/usr/homes/chet/src/bash/src/parse.y" + WORD_LIST *wl; + int tok, orig_current_token, orig_line_number, orig_input_terminator; + int orig_line_count; + int old_echo_input, old_expand_aliases; +#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_line_count = current_command_line_count; + orig_input_terminator = shell_input_line_terminator; + old_echo_input = echo_input_at_read; + old_expand_aliases = expand_aliases; + + push_stream (1); + last_read_token = WORD; /* WORD to allow reserved words here */ + current_command_line_count = 0; + echo_input_at_read = expand_aliases = 0; + + with_input_from_string (s, whom); + wl = (WORD_LIST *)NULL; + + if (flags & 1) + parser_state |= PST_COMPASSIGN; + + while ((tok = read_token (READ)) != yacc_EOF) + { + if (tok == '\n' && *bash_input.location.string == '\0') + break; + if (tok == '\n') /* Allow newlines in compound assignments */ + continue; + if (tok != WORD && tok != ASSIGNMENT_WORD) + { + line_number = orig_line_number + line_number - 1; + orig_current_token = current_token; + current_token = tok; + yyerror (NULL); /* does the right thing */ + current_token = orig_current_token; + 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 */ + + echo_input_at_read = old_echo_input; + expand_aliases = old_expand_aliases; + + current_command_line_count = orig_line_count; + shell_input_line_terminator = orig_input_terminator; + + if (flags & 1) + parser_state &= ~PST_COMPASSIGN; + + 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 *)); +} + +static char * +parse_compound_assignment (retlenp) + int *retlenp; { - yyvsp[0].command->flags |= yyvsp[-1].number|CMD_INVERT_RETURN; - yyval.command = yyvsp[0].command; - } -break; -case 130: -#line 804 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = command_connect (yyvsp[-3].command, yyvsp[0].command, '|'); } -break; -case 131: -#line 806 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.command = yyvsp[0].command; } -break; -case 132: -#line 810 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.number = CMD_TIME_PIPELINE; } -break; -case 133: -#line 812 "/usr/homes/chet/src/bash/src/parse.y" -{ yyval.number = CMD_TIME_PIPELINE|CMD_TIME_POSIX; } -break; -#line 5117 "y.tab.c" + WORD_LIST *wl, *rl; + int tok, orig_line_number, orig_token_size; + char *saved_token, *ret; + + saved_token = token; + orig_token_size = token_buffer_size; + orig_line_number = line_number; + + last_read_token = WORD; /* WORD to allow reserved words here */ + + token = (char *)NULL; + token_buffer_size = 0; + + wl = (WORD_LIST *)NULL; /* ( */ + parser_state |= PST_COMPASSIGN; + + while ((tok = read_token (READ)) != ')') + { + if (tok == '\n') /* Allow newlines in compound assignments */ + { + if (SHOULD_PROMPT ()) + prompt_again (); + continue; + } + if (tok != WORD && tok != ASSIGNMENT_WORD) + { + current_token = tok; /* for error reporting */ + if (tok == yacc_EOF) /* ( */ + parser_error (orig_line_number, _("unexpected EOF while looking for matching `)'")); + else + yyerror(NULL); /* does the right thing */ + if (wl) + dispose_words (wl); + wl = &parse_string_error; + break; + } + wl = make_word_list (yylval.word, wl); + } + + FREE (token); + token = saved_token; + token_buffer_size = orig_token_size; + + parser_state &= ~PST_COMPASSIGN; + + if (wl == &parse_string_error) + { + last_command_exit_value = EXECUTION_FAILURE; + last_read_token = '\n'; /* XXX */ + if (interactive_shell == 0 && posixly_correct) + jump_to_top_level (FORCE_EOF); + else + jump_to_top_level (DISCARD); } - yyssp -= yym; - yystate = *yyssp; - yyvsp -= yym; - yym = yylhs[yyn]; - if (yystate == 0 && yym == 0) + + last_read_token = WORD; + if (wl) { -#if YYDEBUG - if (yydebug) - printf("%sdebug: after reduction, shifting from state 0 to\ - state %d\n", YYPREFIX, YYFINAL); + rl = REVERSE_LIST (wl, WORD_LIST *); + ret = string_list (rl); + dispose_words (rl); + } + else + ret = (char *)NULL; + + if (retlenp) + *retlenp = (ret && *ret) ? strlen (ret) : 0; + return ret; +} + +/************************************************ + * * + * SAVING AND RESTORING PARTIAL PARSE STATE * + * * + ************************************************/ + +sh_parser_state_t * +save_parser_state (ps) + sh_parser_state_t *ps; +{ +#if defined (ARRAY_VARS) + SHELL_VAR *v; #endif - yystate = YYFINAL; - *++yyssp = YYFINAL; - *++yyvsp = yyval; - if (yychar < 0) - { - if ((yychar = yylex()) < 0) yychar = 0; -#if YYDEBUG - if (yydebug) - { - yys = 0; - if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; - if (!yys) yys = "illegal-symbol"; - printf("%sdebug: state %d, reading %d (%s)\n", - YYPREFIX, YYFINAL, yychar, yys); - } + + if (ps == 0) + ps = (sh_parser_state_t *)xmalloc (sizeof (sh_parser_state_t)); + if (ps == 0) + return ((sh_parser_state_t *)NULL); + + ps->parser_state = parser_state; + ps->token_state = save_token_state (); + + ps->input_line_terminator = shell_input_line_terminator; + ps->eof_encountered = eof_encountered; + + ps->current_command_line_count = current_command_line_count; + +#if defined (HISTORY) + ps->remember_on_history = remember_on_history; +# if defined (BANG_HISTORY) + ps->history_expansion_inhibited = history_expansion_inhibited; +# endif #endif - } - if (yychar == 0) goto yyaccept; - goto yyloop; + + ps->last_command_exit_value = last_command_exit_value; +#if defined (ARRAY_VARS) + v = find_variable ("PIPESTATUS"); + if (v && array_p (v) && array_cell (v)) + ps->pipestatus = array_copy (array_cell (v)); + else + ps->pipestatus = (ARRAY *)NULL; +#endif + + ps->last_shell_builtin = last_shell_builtin; + ps->this_shell_builtin = this_shell_builtin; + + ps->expand_aliases = expand_aliases; + ps->echo_input_at_read = echo_input_at_read; + + return (ps); +} + +void +restore_parser_state (ps) + sh_parser_state_t *ps; +{ +#if defined (ARRAY_VARS) + SHELL_VAR *v; +#endif + + if (ps == 0) + return; + + parser_state = ps->parser_state; + if (ps->token_state) + { + restore_token_state (ps->token_state); + free (ps->token_state); + } + + shell_input_line_terminator = ps->input_line_terminator; + eof_encountered = ps->eof_encountered; + + current_command_line_count = ps->current_command_line_count; + +#if defined (HISTORY) + remember_on_history = ps->remember_on_history; +# if defined (BANG_HISTORY) + history_expansion_inhibited = ps->history_expansion_inhibited; +# endif +#endif + + last_command_exit_value = ps->last_command_exit_value; +#if defined (ARRAY_VARS) + v = find_variable ("PIPESTATUS"); + if (v && array_p (v) && array_cell (v)) + { + array_dispose (array_cell (v)); + var_setarray (v, ps->pipestatus); } - if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && - yyn <= YYTABLESIZE && yycheck[yyn] == yystate) - yystate = yytable[yyn]; - else - yystate = yydgoto[yym]; -#if YYDEBUG - if (yydebug) - printf("%sdebug: after reduction, shifting from state %d \ -to state %d\n", YYPREFIX, *yyssp, yystate); #endif - if (yyssp >= yysslim && yygrowstack()) + + last_shell_builtin = ps->last_shell_builtin; + this_shell_builtin = ps->this_shell_builtin; + + expand_aliases = ps->expand_aliases; + echo_input_at_read = ps->echo_input_at_read; +} + +/************************************************ + * * + * MULTIBYTE CHARACTER HANDLING * + * * + ************************************************/ + +#if defined (HANDLE_MULTIBYTE) +static void +set_line_mbstate () +{ + int i, previ, len, c; + mbstate_t mbs, prevs; + size_t mbclen; + + if (shell_input_line == NULL) + return; + len = strlen (shell_input_line); /* XXX - shell_input_line_len ? */ + FREE (shell_input_line_property); + shell_input_line_property = (char *)xmalloc (len + 1); + + memset (&prevs, '\0', sizeof (mbstate_t)); + for (i = previ = 0; i < len; i++) { - goto yyoverflow; + mbs = prevs; + + c = shell_input_line[i]; + if (c == EOF) + { + int j; + for (j = i; j < len; j++) + shell_input_line_property[j] = 1; + break; + } + + mbclen = mbrlen (shell_input_line + previ, i - previ + 1, &mbs); + if (mbclen == 1 || mbclen == (size_t)-1) + { + mbclen = 1; + previ = i + 1; + } + else if (mbclen == (size_t)-2) + mbclen = 0; + else if (mbclen > 1) + { + mbclen = 0; + previ = i + 1; + prevs = mbs; + } + else + { + /* XXX - what to do if mbrlen returns 0? (null wide character) */ + int j; + for (j = i; j < len; j++) + shell_input_line_property[j] = 1; + break; + } + + shell_input_line_property[i] = mbclen; } - *++yyssp = yystate; - *++yyvsp = yyval; - goto yyloop; -yyoverflow: - yyerror("yacc stack overflow"); -yyabort: - return (1); -yyaccept: - return (0); } +#endif /* HANDLE_MULTIBYTE */