-#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 <stdlib.h>
-#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"
#endif
#include <stdio.h>
+#include "chartypes.h"
#include <signal.h>
#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 <readline/readline.h>
#if defined (ALIAS)
# include "alias.h"
+#else
+typedef void *alias_t;
#endif /* ALIAS */
#if defined (PROMPT_STRING_DECODE)
# include <sys/param.h>
# endif
# include <time.h>
+# if defined (TM_IN_SYS_TIME)
+# include <sys/types.h>
+# include <sys/time.h>
+# 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;
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;
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
/* */
/* **************************************************************** */
-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;
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;
/* 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. */
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 <stdio.h>
+
+#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 <alloca.h>
+#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 <malloc.h>
#endif
-#if YYDEBUG
-#include <stdio.h>
+#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 <malloc.h> */
+ #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
+\f
+/* 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
+\f
+#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"
+\f
+ 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 \<newline> 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 \<newline> 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 \<newline> 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 \<newline> 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 \<newline> 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 \<newline> 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 \<newline> 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') /* <NL> -> \n"<NL>" */
+ /* 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 <NL>
- into `\n"<NL>"'. If we find a newline in TEMP, we first output a
- `msgid ""' line and then the translated string; otherwise we output the
- `msgid' and translated string all on one line. */
- if (dump_translatable_strings)
- {
- if (dump_po_strings)
+ /* 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 \<newline> 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 */