/* parse.y - Yacc grammar for bash. */
-/* Copyright (C) 1989-2010 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2012 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#define NEED_STRFTIME_DECL /* used in externs.h */
#include "shell.h"
+#include "typemax.h" /* SIZE_MAX if needed */
#include "trap.h"
#include "flags.h"
#include "parser.h"
static int reserved_word_acceptable __P((int));
static int yylex __P((void));
+
+static void push_heredoc __P((REDIRECT *));
+static char *mk_alexpansion __P((char *));
static int alias_expand_token __P((char *));
static int time_command_acceptable __P((void));
static int special_case_tokens __P((char *));
/* Variables to manage the task of reading here documents, because we need to
defer the reading until after a complete command has been collected. */
-static REDIRECT *redir_stack[10];
+#define HEREDOC_MAX 16
+
+static REDIRECT *redir_stack[HEREDOC_MAX];
int need_here_doc;
/* Where shell input comes from. History expansion is performed on each
line when the shell is interactive. */
static char *shell_input_line = (char *)NULL;
-static int shell_input_line_index;
-static int shell_input_line_size; /* Amount allocated for shell_input_line. */
-static int shell_input_line_len; /* strlen (shell_input_line) */
+static size_t shell_input_line_index;
+static size_t shell_input_line_size; /* Amount allocated for shell_input_line. */
+static size_t shell_input_line_len; /* strlen (shell_input_line) */
/* Either zero or EOF. */
static int shell_input_line_terminator;
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_lineno[MAX_CASE_NEST+1];
static int word_top = -1;
/* If non-zero, it is the token that we want read_token to return
source.dest = 0;
redir.filename = $2;
$$ = make_redirection (source, r_reading_until, redir, 0);
- redir_stack[need_here_doc++] = $$;
+ push_heredoc ($$);
}
| NUMBER LESS_LESS WORD
{
source.dest = $1;
redir.filename = $3;
$$ = make_redirection (source, r_reading_until, redir, 0);
- redir_stack[need_here_doc++] = $$;
+ push_heredoc ($$);
}
| REDIR_WORD LESS_LESS WORD
{
source.filename = $1;
redir.filename = $3;
$$ = make_redirection (source, r_reading_until, redir, REDIR_VARASSIGN);
- redir_stack[need_here_doc++] = $$;
+ push_heredoc ($$);
}
| LESS_LESS_MINUS WORD
{
source.dest = 0;
redir.filename = $2;
$$ = make_redirection (source, r_deblank_reading_until, redir, 0);
- redir_stack[need_here_doc++] = $$;
+ push_heredoc ($$);
}
| NUMBER LESS_LESS_MINUS WORD
{
source.dest = $1;
redir.filename = $3;
$$ = make_redirection (source, r_deblank_reading_until, redir, 0);
- redir_stack[need_here_doc++] = $$;
+ push_heredoc ($$);
}
| REDIR_WORD LESS_LESS_MINUS WORD
{
source.filename = $1;
redir.filename = $3;
$$ = make_redirection (source, r_deblank_reading_until, redir, REDIR_VARASSIGN);
- redir_stack[need_here_doc++] = $$;
+ push_heredoc ($$);
}
| LESS_LESS_LESS WORD
{
old_sigint = (SigHandler *)IMPOSSIBLE_TRAP_HANDLER;
if (signal_is_ignored (SIGINT) == 0)
{
- interrupt_immediately++;
+ /* interrupt_immediately++; */
old_sigint = (SigHandler *)set_signal_handler (SIGINT, sigint_sighandler);
}
- terminate_immediately = 1;
current_readline_line = readline (current_readline_prompt ?
current_readline_prompt : "");
- terminate_immediately = 0;
+ CHECK_TERMSIG;
if (signal_is_ignored (SIGINT) == 0)
{
- interrupt_immediately--;
+ /* interrupt_immediately--; */
if (old_sigint != IMPOSSIBLE_TRAP_HANDLER)
set_signal_handler (SIGINT, old_sigint);
}
result = EOF;
if (bash_input.location.file)
{
+#if 0
if (interactive)
- {
- interrupt_immediately++;
- terminate_immediately++;
- }
+ interrupt_immediately++;
+#endif
+
+ /* XXX - don't need terminate_immediately; getc_with_restart checks
+ for terminating signals itself if read returns < 0 */
result = getc_with_restart (bash_input.location.file);
+
+#if 0
if (interactive)
- {
- interrupt_immediately--;
- terminate_immediately--;
- }
+ interrupt_immediately--;
+#endif
}
return (result);
}
* implement alias expansion on a per-token basis.
*/
+#define PSH_ALIAS 0x01
+#define PSH_DPAREN 0x02
+#define PSH_SOURCE 0x04
+
typedef struct string_saver {
struct string_saver *next;
int expand_alias; /* Value to set expand_alias to when string is popped. */
#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;
+ size_t saved_line_size, saved_line_index;
+ int saved_line_terminator;
+ int flags;
} STRING_SAVER;
STRING_SAVER *pushed_string_list = (STRING_SAVER *)NULL;
temp->saved_line_size = shell_input_line_size;
temp->saved_line_index = shell_input_line_index;
temp->saved_line_terminator = shell_input_line_terminator;
+ temp->flags = 0;
#if defined (ALIAS)
temp->expander = ap;
+ if (ap)
+ temp->flags = PSH_ALIAS;
#endif
temp->next = pushed_string_list;
pushed_string_list = temp;
#endif
shell_input_line = s;
- shell_input_line_size = strlen (s);
+ shell_input_line_size = STRLEN (s);
shell_input_line_index = 0;
shell_input_line_terminator = '\0';
#if 0
#endif
}
+int
+parser_expanding_alias ()
+{
+ return (expanding_alias ());
+}
+
+void
+parser_save_alias ()
+{
+#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
+ push_string ((char *)NULL, 0, (alias_t *)NULL);
+ pushed_string_list->flags = PSH_SOURCE; /* XXX - for now */
+#else
+ ;
+#endif
+}
+
+void
+parser_restore_alias ()
+{
+#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
+ if (pushed_string_list)
+ pop_string ();
+#else
+ ;
+#endif
+}
+
/* 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
int remove_quoted_newline;
{
register int i;
- int c;
+ int c, truncating;
unsigned char uc;
QUIT;
{
line_number++;
+ /* Let's not let one really really long line blow up memory allocation */
+ if (shell_input_line && shell_input_line_size >= 32768)
+ {
+ free (shell_input_line);
+ shell_input_line = 0;
+ shell_input_line_size = 0;
+ }
+
restart_read:
/* Allow immediate exit if interrupted during input. */
QUIT;
- i = 0;
+ i = truncating = 0;
shell_input_line_terminator = 0;
/* If the shell is interatctive, but not currently printing a prompt
continue;
}
- RESIZE_MALLOCED_BUFFER (shell_input_line, i, 2, shell_input_line_size, 256);
+ /* Theoretical overflow */
+ /* If we can't put 256 bytes more into the buffer, allocate
+ everything we can and fill it as full as we can. */
+ /* XXX - we ignore rest of line using `truncating' flag */
+ if (shell_input_line_size > (SIZE_MAX - 256))
+ {
+ size_t n;
+
+ n = SIZE_MAX - i; /* how much more can we put into the buffer? */
+ if (n <= 2) /* we have to save 1 for the newline added below */
+ {
+ if (truncating == 0)
+ internal_warning("shell_getc: shell_input_line_size (%zu) exceeds SIZE_MAX (%llu): line truncated", shell_input_line_size, SIZE_MAX);
+ shell_input_line[i] = '\0';
+ truncating = 1;
+ }
+ if (shell_input_line_size < SIZE_MAX)
+ {
+ shell_input_line_size = SIZE_MAX;
+ shell_input_line = xrealloc (shell_input_line, shell_input_line_size);
+ }
+ }
+ else
+ RESIZE_MALLOCED_BUFFER (shell_input_line, i, 2, shell_input_line_size, 256);
if (c == EOF)
{
break;
}
- shell_input_line[i++] = c;
+ if (truncating == 0 || c == '\n')
+ shell_input_line[i++] = c;
if (c == '\n')
{
if (shell_input_line)
{
/* Lines that signify the end of the shell's input should not be
- echoed. */
+ echoed. We should not echo lines while parsing command
+ substitutions with recursive calls into the parsing engine; those
+ should only be echoed once when we read the word. That is the
+ reason for the test against shell_eof_token, which is set to a
+ right paren when parsing the contents of command substitutions. */
if (echo_input_at_read && (shell_input_line[0] ||
- shell_input_line_terminator != EOF))
+ shell_input_line_terminator != EOF) &&
+ shell_eof_token == 0)
fprintf (stderr, "%s\n", shell_input_line);
}
else
not already end in an EOF character. */
if (shell_input_line_terminator != EOF)
{
- if (shell_input_line_len + 3 > shell_input_line_size)
+ if (shell_input_line_size < SIZE_MAX-3 && (shell_input_line_len+3 > shell_input_line_size))
shell_input_line = (char *)xrealloc (shell_input_line,
1 + (shell_input_line_size += 2));
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 pushed_string_list != 0 but pushed_string_list->expander == 0 (not
+ currently tested) and the flags value is not PSH_SOURCE, we are not
+ parsing an alias, we have just saved one (push_string, when called by
+ the parse_dparen code) In this case, just go on as well. The PSH_SOURCE
+ case is handled below. */
pop_alias:
- if (uc == 0 && (pushed_string_list != (STRING_SAVER *)NULL))
+ if (uc == 0 && pushed_string_list && pushed_string_list->flags != PSH_SOURCE)
{
pop_string ();
uc = shell_input_line[shell_input_line_index];
if (uc == 0 && shell_input_line_terminator == EOF)
return ((shell_input_line_index != 0) ? '\n' : EOF);
+#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
+ /* We already know that we are not parsing an alias expansion because of the
+ check for expanding_alias() above. This knows how parse_and_execute
+ handles switching to st_string input while an alias is being expanded,
+ hence the check for pushed_string_list without pushed_string_list->expander
+ and the check for PSH_SOURCE as pushed_string_list->flags.
+ parse_and_execute and parse_string both change the input type to st_string
+ and place the string to be parsed and executed into location.string, so
+ we should not stop reading that until the pointer is '\0'.
+ The check for shell_input_line_terminator may be superfluous.
+
+ This solves the problem of `.' inside a multi-line alias with embedded
+ newlines executing things out of order. */
+ if (uc == 0 && bash_input.type == st_string && *bash_input.location.string &&
+ pushed_string_list && pushed_string_list->flags == PSH_SOURCE &&
+ shell_input_line_terminator == 0)
+ {
+ shell_input_line_index = 0;
+ goto restart_read;
+ }
+#endif
+
return (uc);
}
eol_ungetc_lookahead = c;
}
+char *
+parser_remaining_input ()
+{
+ if (shell_input_line == 0)
+ return 0;
+ if (shell_input_line_index < 0 || shell_input_line_index >= shell_input_line_len)
+ return '\0'; /* XXX */
+ return (shell_input_line + shell_input_line_index);
+}
+
#ifdef INCLUDE_UNUSED
/* Back the input pointer up by one, effectively `ungetting' a character. */
static void
which allow ESAC to be the next one read. */
static int esacs_needed_count;
+static void
+push_heredoc (r)
+ REDIRECT *r;
+{
+ if (need_here_doc >= HEREDOC_MAX)
+ {
+ last_command_exit_value = EX_BADUSAGE;
+ need_here_doc = 0;
+ report_syntax_error (_("maximum here-document count exceeded"));
+ reset_parser ();
+ exit_shell (last_command_exit_value);
+ }
+ redir_stack[need_here_doc++] = r;
+}
+
void
gather_here_documents ()
{
int r;
r = 0;
- while (need_here_doc)
+ while (need_here_doc > 0)
{
parser_state |= PST_HEREDOC;
make_here_document (redir_stack[r++], line_number);
case AND_AND:
case OR_OR:
case '&':
+ case WHILE:
case DO:
+ case UNTIL:
+ case IF:
case THEN:
+ case ELIF:
case ELSE:
case '{': /* } */
- case '(': /* ) */
+ case '(': /* )( */
+ case ')': /* only valid in case statement */
case BANG: /* ! time pipeline */
case TIME: /* time time pipeline */
case TIMEOPT: /* time -p time pipeline */
FREE (word_desc_to_read);
word_desc_to_read = (WORD_DESC *)NULL;
+ eol_ungetc_lookahead = 0;
+
current_token = '\n'; /* XXX */
last_read_token = '\n';
token_to_read = '\n';
}
RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64);
- if MBTEST(ch == CTLESC || ch == CTLNUL)
+ if MBTEST(ch == CTLESC)
ret[retind++] = CTLESC;
ret[retind++] = ch;
continue;
treat single quotes as special when inside a double-quoted
${...}. This logic must agree with subst.c:extract_dollar_brace_string
since they share the same defines. */
+ /* FLAG POSIX INTERP 221 */
if (flags & P_DOLBRACE)
{
/* ${param%[%]word} */
dolbrace_state = DOLBRACE_QUOTE;
/* ${param/[/]pat/rep} */
else if MBTEST(dolbrace_state == DOLBRACE_PARAM && ch == '/' && retind > 1)
- dolbrace_state = DOLBRACE_QUOTE;
+ dolbrace_state = DOLBRACE_QUOTE2; /* XXX */
/* ${param^[^]pat} */
else if MBTEST(dolbrace_state == DOLBRACE_PARAM && ch == '^' && retind > 1)
dolbrace_state = DOLBRACE_QUOTE;
within a double-quoted ${...} construct "an even number of
unescaped double-quotes or single-quotes, if any, shall occur." */
/* This was changed in Austin Group Interp 221 */
- if MBTEST(posixly_correct && shell_compatibility_level > 41 && dolbrace_state != DOLBRACE_QUOTE && (flags & P_DQUOTE) && (flags & P_DOLBRACE) && ch == '\'')
+ if MBTEST(posixly_correct && shell_compatibility_level > 41 && dolbrace_state != DOLBRACE_QUOTE && dolbrace_state != DOLBRACE_QUOTE2 && (flags & P_DQUOTE) && (flags & P_DOLBRACE) && ch == '\'')
continue;
/* Could also check open == '`' if we want to parse grouping constructs
ttrans = ansiexpand (nestret, 0, nestlen - 1, &ttranslen);
xfree (nestret);
- if ((rflags & P_DQUOTE) == 0)
+ /* If we're parsing a double-quoted brace expansion and we are
+ not in a place where single quotes are treated specially,
+ make sure we single-quote the results of the ansi
+ expansion because quote removal should remove them later */
+ /* FLAG POSIX INTERP 221 */
+ if ((shell_compatibility_level > 42) && (rflags & P_DQUOTE) && (dolbrace_state == DOLBRACE_QUOTE2) && (flags & P_DOLBRACE))
+ {
+ nestret = sh_single_quote (ttrans);
+ free (ttrans);
+ nestlen = strlen (nestret);
+ }
+ else if ((rflags & P_DQUOTE) == 0)
{
nestret = sh_single_quote (ttrans);
free (ttrans);
ret[retind++] = ch;
if ((tflags & LEX_INCOMMENT) && ch == '\n')
-{
+ {
/*itrace("parse_comsub:%d: lex_incomment -> 0 ch = `%c'", line_number, ch);*/
- tflags &= ~LEX_INCOMMENT;
-}
+ tflags &= ~LEX_INCOMMENT;
+ }
continue;
}
}
RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64);
- if MBTEST(ch == CTLESC || ch == CTLNUL)
+ if MBTEST(ch == CTLESC)
ret[retind++] = CTLESC;
ret[retind++] = ch;
continue;
else if MBTEST(lex_rwlen == 4 && shellbreak (ch))
{
if (STREQN (ret + retind - 4, "case", 4))
-{
- tflags |= LEX_INCASE;
+ {
+ tflags |= LEX_INCASE;
/*itrace("parse_comsub:%d: found `case', lex_incase -> 1 lex_reswdok -> 0", line_number);*/
-}
+ }
else if (STREQN (ret + retind - 4, "esac", 4))
-{
- tflags &= ~LEX_INCASE;
+ {
+ tflags &= ~LEX_INCASE;
/*itrace("parse_comsub:%d: found `esac', lex_incase -> 0 lex_reswdok -> 0", line_number);*/
-}
+ }
tflags &= ~LEX_RESWDOK;
}
else if MBTEST((tflags & LEX_CKCOMMENT) && ch == '#' && (lex_rwlen == 0 || ((tflags & LEX_INWORD) && lex_wlen == 0)))
; /* don't modify LEX_RESWDOK if we're starting a comment */
+ /* Allow `do' followed by space, tab, or newline to preserve the
+ RESWDOK flag, but reset the reserved word length counter so we
+ can read another one. */
+ else if MBTEST(((tflags & LEX_INCASE) == 0) &&
+ (isblank(ch) || ch == '\n') &&
+ lex_rwlen == 2 &&
+ STREQN (ret + retind - 2, "do", 2))
+ {
+/*itrace("parse_comsub:%d: lex_incase == 1 found `%c', found \"do\"", line_number, ch);*/
+ lex_rwlen = 0;
+ }
else if MBTEST((tflags & LEX_INCASE) && ch != '\n')
/* If we can read a reserved word and we're in case, we're at the
point where we can read a new pattern list or an esac. We
handle the esac case above. If we read a newline, we want to
leave LEX_RESWDOK alone. If we read anything else, we want to
turn off LEX_RESWDOK, since we're going to read a pattern list. */
-{
- tflags &= ~LEX_RESWDOK;
+ {
+ tflags &= ~LEX_RESWDOK;
/*itrace("parse_comsub:%d: lex_incase == 1 found `%c', lex_reswordok -> 0", line_number, ch);*/
-}
+ }
else if MBTEST(shellbreak (ch) == 0)
-{
- tflags &= ~LEX_RESWDOK;
+ {
+ tflags &= ~LEX_RESWDOK;
/*itrace("parse_comsub:%d: found `%c', lex_reswordok -> 0", line_number, ch);*/
-}
+ }
+#if 0
+ /* If we find a space or tab but have read something and it's not
+ `do', turn off the reserved-word-ok flag */
+ else if MBTEST(isblank (ch) && lex_rwlen > 0)
+ {
+ tflags &= ~LEX_RESWDOK;
+/*itrace("parse_comsub:%d: found `%c', lex_reswordok -> 0", line_number, ch);*/
+ }
+#endif
}
/* Might be the start of a here-doc delimiter */
ch = peekc; /* fall through and continue XXX */
}
else if MBTEST((tflags & LEX_CKCOMMENT) && (tflags & LEX_INCOMMENT) == 0 && ch == '#' && (((tflags & LEX_RESWDOK) && lex_rwlen == 0) || ((tflags & LEX_INWORD) && lex_wlen == 0)))
-{
+ {
/*itrace("parse_comsub:%d: lex_incomment -> 1 (%d)", line_number, __LINE__);*/
- tflags |= LEX_INCOMMENT;
-}
+ tflags |= LEX_INCOMMENT;
+ }
if MBTEST(ch == CTLESC || ch == CTLNUL) /* special shell escapes */
{
tflags &= ~LEX_INCASE; /* XXX */
#endif
else if MBTEST(ch == close && (tflags & LEX_INCASE) == 0) /* ending delimiter */
-{
- count--;
+ {
+ count--;
/*itrace("parse_comsub:%d: found close: count = %d", line_number, count);*/
-}
+ }
else if MBTEST(((flags & P_FIRSTCLOSE) == 0) && (tflags & LEX_INCASE) == 0 && ch == open) /* nested begin */
-{
- count++;
+ {
+ count++;
/*itrace("parse_comsub:%d: found open: count = %d", line_number, count);*/
-}
+ }
/* Add this character. */
RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
{
sh_parser_state_t ps;
sh_input_line_state_t ls;
- int orig_ind, nc, sflags;
+ int orig_ind, nc, sflags, orig_eof_token;
char *ret, *s, *ep, *ostring;
/*yydebug = 1;*/
sflags |= SEVAL_NOLONGJMP;
save_parser_state (&ps);
save_input_line_state (&ls);
+ orig_eof_token = shell_eof_token;
/*(*/
parser_state |= PST_CMDSUBST|PST_EOFTOKEN; /* allow instant ')' */ /*(*/
shell_eof_token = ')';
parse_string (string, "command substitution", sflags, &ep);
+ shell_eof_token = orig_eof_token;
restore_parser_state (&ps);
reset_parser ();
/* reset_parser clears shell_input_line and associated variables */
restore_input_line_state (&ls);
- if (interactive)
- token_to_read = 0;
+
+ token_to_read = 0;
/* Need to find how many characters parse_and_execute consumed, update
*indp, if flags != 0, copy the portion of the string parsed into RET
else if (cmdtyp == 0) /* nested subshell */
{
push_string (wval, 0, (alias_t *)NULL);
+ pushed_string_list->flags = PSH_DPAREN;
if ((parser_state & PST_CASEPAT) == 0)
parser_state |= PST_SUBSHELL;
return (c);
pop_delimiter (dstack);
if (ttok == &matched_pair_error)
return -1; /* Bail immediately. */
- RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 2,
+ RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 3,
token_buffer_size,
TOKEN_DEFAULT_GROW_SIZE);
token[token_index++] = character;
{
peek_char = shell_getc (1);
/* $(...), <(...), >(...), $((...)), ${...}, and $[...] constructs */
- if MBTEST(peek_char == '(' || \
+ if MBTEST(peek_char == '(' ||
((peek_char == '{' || peek_char == '[') && character == '$')) /* ) ] } */
{
if (peek_char == '{') /* } */
ttok = parse_matched_pair (cd, '[', ']', &ttoklen, 0);
if (ttok == &matched_pair_error)
return -1; /* Bail immediately. */
- RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 2,
+ RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 3,
token_buffer_size,
TOKEN_DEFAULT_GROW_SIZE);
token[token_index++] = character;
ttrans = ttok;
}
- RESIZE_MALLOCED_BUFFER (token, token_index, ttranslen + 2,
+ RESIZE_MALLOCED_BUFFER (token, token_index, ttranslen + 1,
token_buffer_size,
TOKEN_DEFAULT_GROW_SIZE);
strcpy (token + token_index, ttrans);
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;
+ token[token_index++] = '$';
+ token[token_index++] = peek_char;
dollar_present = 1;
all_digit_token = 0;
- FREE (ttok);
goto next_character;
}
else
goto got_token;
}
- got_character:
+got_character:
if (character == CTLESC || character == CTLNUL)
- token[token_index++] = CTLESC;
+ {
+ RESIZE_MALLOCED_BUFFER (token, token_index, 2, token_buffer_size,
+ TOKEN_DEFAULT_GROW_SIZE);
+ token[token_index++] = CTLESC;
+ }
+ else
+got_escaped_character:
+ RESIZE_MALLOCED_BUFFER (token, token_index, 1, token_buffer_size,
+ TOKEN_DEFAULT_GROW_SIZE);
- got_escaped_character:
+ token[token_index++] = character;
all_digit_token &= DIGIT (character);
dollar_present |= character == '$';
- token[token_index++] = character;
-
- RESIZE_MALLOCED_BUFFER (token, token_index, 1, token_buffer_size,
- TOKEN_DEFAULT_GROW_SIZE);
-
next_character:
if (character == '\n' && SHOULD_PROMPT ())
prompt_again ();
got_token:
+ /* Calls to RESIZE_MALLOCED_BUFFER ensure there is sufficient room. */
token[token_index] = '\0';
/* 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 || \
+ 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);
+ {
+ yylval.number = lvalue;
+ return (NUMBER);
+ }
}
/* Check for special case tokens. */
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;
+ {
+ the_word->flags |= W_NOSPLIT;
+ if (parser_state & PST_COMPASSIGN)
+ the_word->flags |= W_NOGLOB; /* XXX - W_NOBRACE? */
+ }
}
if (command_token_position (last_read_token))
{
/* can use token; already copied to the_word */
token[token_index-1] = '\0';
+#if defined (ARRAY_VARS)
+ if (legal_identifier (token+1) || valid_array_reference (token+1))
+#else
if (legal_identifier (token+1))
+#endif
{
strcpy (the_word->word, token+1);
/*itrace("read_token_word: returning REDIR_WORD for %s", the_word->word);*/
return -1;
}
+/* An interface to let the rest of the shell (primarily the completion
+ system) know what the parser is expecting. */
+int
+parser_in_command_position ()
+{
+ return (command_token_position (last_read_token));
+}
+
#if 0
#if defined (READLINE)
/* Called after each time readline is called. This insures that whatever
#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));
+ {
+ /* polite_directory_format is guaranteed to return a string
+ no longer than PATH_MAX - 1 characters. */
+ temp = polite_directory_format (t_string);
+ if (temp != t_string)
+ strcpy (t_string, temp);
+ }
temp = trim_pathname (t_string, PATH_MAX - 1);
/* If we're going to be expanding the prompt string later,
ps->expand_aliases = expand_aliases;
ps->echo_input_at_read = echo_input_at_read;
+ ps->need_here_doc = need_here_doc;
ps->token = token;
ps->token_buffer_size = token_buffer_size;
expand_aliases = ps->expand_aliases;
echo_input_at_read = ps->echo_input_at_read;
+ need_here_doc = ps->need_here_doc;
FREE (token);
token = ps->token;
/* force reallocation */
shell_input_line = 0;
shell_input_line_size = shell_input_line_len = shell_input_line_index = 0;
+
+ return ls;
}
void
static void
set_line_mbstate ()
{
- int i, previ, len, c;
+ int c;
+ size_t i, previ, len;
mbstate_t mbs, prevs;
size_t mbclen;
c = shell_input_line[i];
if (c == EOF)
{
- int j;
+ size_t j;
for (j = i; j < len; j++)
shell_input_line_property[j] = 1;
break;
else
{
/* XXX - what to do if mbrlen returns 0? (null wide character) */
- int j;
+ size_t j;
for (j = i; j < len; j++)
shell_input_line_property[j] = 1;
break;