]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - parse.y
commit bash-20120907 snapshot
[thirdparty/bash.git] / parse.y
diff --git a/parse.y b/parse.y
index 857873231ffe482dabecfebede2441fe8e7a446d..c0da6301fe078b2863c4da52c0f76f674c151286 100644 (file)
--- a/parse.y
+++ b/parse.y
@@ -1,6 +1,6 @@
 /* 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.
 
@@ -336,7 +336,7 @@ static REDIRECTEE redir;
    third group are recognized only under special circumstances. */
 %token IF THEN ELSE ELIF FI CASE ESAC FOR SELECT WHILE UNTIL DO DONE FUNCTION COPROC
 %token COND_START COND_END COND_ERROR
-%token IN BANG TIME TIMEOPT
+%token IN BANG TIME TIMEOPT TIMEIGN
 
 /* More general tokens. yylex () knows how to make these. */
 %token <word> WORD ASSIGNMENT_WORD REDIR_WORD
@@ -1189,31 +1189,19 @@ simple_list1:   simple_list1 AND_AND newline_list simple_list1
        ;
 
 pipeline_command: pipeline
-                       { $$ = $1; }
-       |       BANG pipeline
+                       { $$ = $1; }                    
+       |       BANG pipeline_command
                        {
                          if ($2)
-                           $2->flags |= CMD_INVERT_RETURN;
+                           $2->flags ^= CMD_INVERT_RETURN;     /* toggle */
                          $$ = $2;
                        }
-       |       timespec pipeline
+       |       timespec pipeline_command
                        {
                          if ($2)
                            $2->flags |= $1;
                          $$ = $2;
                        }
-       |       timespec BANG pipeline
-                       {
-                         if ($3)
-                           $3->flags |= $1|CMD_INVERT_RETURN;
-                         $$ = $3;
-                       }
-       |       BANG timespec pipeline
-                       {
-                         if ($3)
-                           $3->flags |= $2|CMD_INVERT_RETURN;
-                         $$ = $3;
-                       }
        |       timespec list_terminator
                        {
                          ELEMENT x;
@@ -1231,7 +1219,24 @@ pipeline_command: pipeline
                          if ($2 == '\n')
                            token_to_read = '\n';
                        }
-                       
+       |       BANG list_terminator
+                       {
+                         ELEMENT x;
+
+                         /* This is just as unclean.  Posix says that `!'
+                            by itself should be equivalent to `false'.
+                            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;
+                         $$ = make_simple_command (x, (COMMAND *)NULL);
+                         $$->flags |= CMD_INVERT_RETURN;
+                         /* XXX - let's cheat and push a newline back */
+                         if ($2 == '\n')
+                           token_to_read = '\n';
+                       }
        ;
 
 pipeline:      pipeline '|' newline_list pipeline
@@ -1267,6 +1272,8 @@ timespec: TIME
                        { $$ = CMD_TIME_PIPELINE; }
        |       TIME TIMEOPT
                        { $$ = CMD_TIME_PIPELINE|CMD_TIME_POSIX; }
+       |       TIME TIMEOPT TIMEIGN
+                       { $$ = CMD_TIME_PIPELINE|CMD_TIME_POSIX; }
        ;
 %%
 
@@ -1431,12 +1438,11 @@ yy_readline_get ()
          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--;
@@ -1596,16 +1602,15 @@ yy_stream_get ()
   if (bash_input.location.file)
     {
       if (interactive)
-       {
-         interrupt_immediately++;
-         terminate_immediately++;
-       }
+       interrupt_immediately++;
+
+      /* 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 (interactive)
-       {
-         interrupt_immediately--;
-         terminate_immediately--;
-       }
+       interrupt_immediately--;
+
     }
   return (result);
 }
@@ -1640,6 +1645,9 @@ typedef struct stream_saver {
 /* The globally known line number. */
 int line_number = 0;
 
+/* The line number offset set by assigning to LINENO.  Not currently used. */
+int line_number_base = 0;
+
 #if defined (COND_COMMAND)
 static int cond_lineno;
 static int cond_token;
@@ -2063,6 +2071,7 @@ STRING_INT_ALIST word_token_alist[] = {
 /* other tokens that can be returned by read_token() */
 STRING_INT_ALIST other_token_alist[] = {
   /* Multiple-character tokens with special values */
+  { "--", TIMEIGN },
   { "-p", TIMEOPT },
   { "&&", AND_AND },
   { "||", OR_OR },
@@ -2382,6 +2391,7 @@ pop_alias:
           is the last character).  If it's not the last character, we need
           to consume the quoted newline and move to the next character in
           the expansion. */
+#if defined (ALIAS)
        if (expanding_alias () && shell_input_line[shell_input_line_index+1] == '\0')
          {
            uc = 0;
@@ -2392,7 +2402,8 @@ pop_alias:
            shell_input_line_index++;   /* skip newline */
            goto next_alias_char;       /* and get next character */
          }
-       else        
+       else
+#endif 
          goto restart_read;
     }
 
@@ -2488,7 +2499,7 @@ yylex ()
         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 (prompt_is_ps1 && parse_and_execute_level == 0 && time_to_check_mail ())
        {
          check_mail ();
          reset_mail_timer ();
@@ -2650,6 +2661,20 @@ static int
 time_command_acceptable ()
 {
 #if defined (COMMAND_TIMING)
+  int i;
+
+  if (posixly_correct && shell_compatibility_level > 41)
+    {
+      /* Quick check of the rest of the line to find the next token.  If it
+        begins with a `-', Posix says to not return `time' as the token.
+        This was interp 267. */
+      i = shell_input_line_index;
+      while (i < shell_input_line_len && (shell_input_line[i] == ' ' || shell_input_line[i] == '\t'))
+        i++;
+      if (shell_input_line[i] == '-')
+       return 0;
+    }
+
   switch (last_read_token)
     {
     case 0:
@@ -2663,6 +2688,10 @@ time_command_acceptable ()
     case ELSE:
     case '{':          /* } */
     case '(':          /* ) */
+    case BANG:         /* ! time pipeline */
+    case TIME:         /* time time pipeline */
+    case TIMEOPT:      /* time -p time pipeline */
+    case TIMEIGN:      /* time -p -- ... */
       return 1;
     default:
       return 0;
@@ -2689,6 +2718,7 @@ time_command_acceptable ()
        `}' is recognized if there is an unclosed `{' present.
 
        `-p' is returned as TIMEOPT if the last read token was TIME.
+       `--' is returned as TIMEIGN if the last read token was TIMEOPT.
 
        ']]' is returned as COND_END if the parser is currently parsing
        a conditional expression ((parser_state & PST_CONDEXPR) != 0)
@@ -2774,13 +2804,9 @@ special_case_tokens (tokstr)
   /* 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 */
+  /* Handle -- after `time -p'. */
+  if (last_read_token == TIMEOPT && tokstr[0] == '-' && tokstr[1] == '-' && !tokstr[2])
+    return (TIMEIGN);
 #endif
 
 #if defined (COND_COMMAND) /* [[ */
@@ -3139,7 +3165,7 @@ parse_matched_pair (qc, open, close, lenp, flags)
   start_lineno = line_number;
   while (count)
     {
-      ch = shell_getc (qc != '\'' && (tflags & LEX_PASSNEXT) == 0);
+      ch = shell_getc (qc != '\'' && (tflags & (LEX_PASSNEXT)) == 0);
 
       if (ch == EOF)
        {
@@ -3184,7 +3210,11 @@ parse_matched_pair (qc, open, close, lenp, flags)
            }
 
          RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64);
+#if 0
          if MBTEST(ch == CTLESC || ch == CTLNUL)
+#else
+         if MBTEST(ch == CTLESC)
+#endif
            ret[retind++] = CTLESC;
          ret[retind++] = ch;
          continue;
@@ -3232,7 +3262,6 @@ parse_matched_pair (qc, open, close, lenp, flags)
       if MBTEST(ch == '\\')                    /* backslashes */
        tflags |= LEX_PASSNEXT;
 
-#if 0  /* XXX - bash-4.2 */
       /* Based on which dolstate is currently in (param, op, or word),
         decide what the op is.  We're really only concerned if it's % or
         #, so we can turn on a flag that says whether or not we should
@@ -3241,26 +3270,34 @@ parse_matched_pair (qc, open, close, lenp, flags)
         since they share the same defines. */
       if (flags & P_DOLBRACE)
         {
-         if MBTEST(dolbrace_state == DOLBRACE_PARAM && ch == '%' && retind > 0)
+          /* ${param%[%]word} */
+         if MBTEST(dolbrace_state == DOLBRACE_PARAM && ch == '%' && retind > 1)
            dolbrace_state = DOLBRACE_QUOTE;
+          /* ${param#[#]word} */
          else if MBTEST(dolbrace_state == DOLBRACE_PARAM && ch == '#' && retind > 1)
            dolbrace_state = DOLBRACE_QUOTE;
+          /* ${param/[/]pat/rep} */
+         else if MBTEST(dolbrace_state == DOLBRACE_PARAM && ch == '/' && retind > 1)
+           dolbrace_state = DOLBRACE_QUOTE;
+          /* ${param^[^]pat} */
+         else if MBTEST(dolbrace_state == DOLBRACE_PARAM && ch == '^' && retind > 1)
+           dolbrace_state = DOLBRACE_QUOTE;
+          /* ${param,[,]pat} */
+         else if MBTEST(dolbrace_state == DOLBRACE_PARAM && ch == ',' && retind > 1)
+           dolbrace_state = DOLBRACE_QUOTE;
          else if MBTEST(dolbrace_state == DOLBRACE_PARAM && strchr ("#%^,~:-=?+/", ch) != 0)
            dolbrace_state = DOLBRACE_OP;
          else if MBTEST(dolbrace_state == DOLBRACE_OP && strchr ("#%^,~:-=?+/", ch) == 0)
            dolbrace_state = DOLBRACE_WORD;
         }
-#endif
 
-#if 0 /* XXX - bash-4.2 */
       /* The big hammer.  Single quotes aren't special in double quotes.  The
          problem is that Posix used to say the single quotes are semi-special:
          within a double-quoted ${...} construct "an even number of
          unescaped double-quotes or single-quotes, if any, shall occur." */
-      /* This was changed in Interp 221 */
-      if MBTEST(/*posixly_correct && shell_compatibility_level > 41 &&*/ dolbrace_state == DOLBRACE_QUOTE && (flags & P_DQUOTE) && (flags & P_DOLBRACE) && ch == '\'')
+      /* 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 == '\'')
        continue;
-#endif
 
       /* Could also check open == '`' if we want to parse grouping constructs
         inside old-style command substitution. */
@@ -3370,13 +3407,12 @@ parse_comsub (qc, open, close, lenp, flags)
   char *ret, *nestret, *ttrans, *heredelim;
   int retind, retsize, rflags, hdlen;
 
-#if 0  /* XXX - bash-4.2 -- jwm@horde.net */
-  /* Assume $(( introduces arithmetic command and parse accordingly. */
+  /* Posix interp 217 says arithmetic expressions have precedence, so
+     assume $(( introduces arithmetic expansion and parse accordingly. */
   peekc = shell_getc (0);
   shell_ungetc (peekc);
   if (peekc == '(')
     return (parse_matched_pair (qc, open, close, lenp, 0));
-#endif
 
 /*itrace("parse_comsub: qc = `%c' open = %c close = %c", qc, open, close);*/
   count = 1;
@@ -3402,7 +3438,7 @@ parse_comsub (qc, open, close, lenp, flags)
   while (count)
     {
 comsub_readchar:
-      ch = shell_getc (qc != '\'' && (tflags & LEX_PASSNEXT) == 0);
+      ch = shell_getc (qc != '\'' && (tflags & (LEX_INCOMMENT|LEX_PASSNEXT)) == 0);
 
       if (ch == EOF)
        {
@@ -3497,7 +3533,11 @@ eof_error:
            }
 
          RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64);
+#if 0
          if MBTEST(ch == CTLESC || ch == CTLNUL)
+#else
+         if MBTEST(ch == CTLESC)
+#endif
            ret[retind++] = CTLESC;
          ret[retind++] = ch;
          continue;
@@ -3526,7 +3566,7 @@ eof_error:
        }
 
       /* Skip whitespace */
-      if MBTEST(shellblank (ch) && lex_rwlen == 0)
+      if MBTEST(shellblank (ch) && (tflags & LEX_HEREDELIM) == 0 && lex_rwlen == 0)
         {
          /* Add this character. */
          RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
@@ -3801,8 +3841,7 @@ eof_error:
   return ret;
 }
 
-/* XXX - this needs to handle functionality like subst.c:no_longjmp_on_fatal_error;
-   maybe extract_command_subst should handle it. */
+/* Recursively call the parser to parse a $(...) command substitution. */
 char *
 xparse_dolparen (base, string, indp, flags)
      char *base;
@@ -3811,6 +3850,7 @@ xparse_dolparen (base, string, indp, flags)
      int flags;
 {
   sh_parser_state_t ps;
+  sh_input_line_state_t ls;
   int orig_ind, nc, sflags;
   char *ret, *s, *ep, *ostring;
 
@@ -3818,10 +3858,12 @@ xparse_dolparen (base, string, indp, flags)
   orig_ind = *indp;
   ostring = string;
 
+/*itrace("xparse_dolparen: size = %d shell_input_line = `%s'", shell_input_line_size, shell_input_line);*/
   sflags = SEVAL_NONINT|SEVAL_NOHIST|SEVAL_NOFREE;
   if (flags & SX_NOLONGJMP)
     sflags |= SEVAL_NOLONGJMP;
   save_parser_state (&ps);
+  save_input_line_state (&ls);
 
   /*(*/
   parser_state |= PST_CMDSUBST|PST_EOFTOKEN;   /* allow instant ')' */ /*(*/
@@ -3830,6 +3872,8 @@ xparse_dolparen (base, string, indp, flags)
 
   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;
 
@@ -4400,7 +4444,7 @@ read_token_word (character)
              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;
@@ -4422,7 +4466,7 @@ read_token_word (character)
        {
          peek_char = shell_getc (1);
          /* $(...), <(...), >(...), $((...)), ${...}, and $[...] constructs */
-         if MBTEST(peek_char == '(' || \
+         if MBTEST(peek_char == '(' ||
                ((peek_char == '{' || peek_char == '[') && character == '$'))   /* ) ] } */
            {
              if (peek_char == '{')             /* } */
@@ -4442,7 +4486,7 @@ read_token_word (character)
                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;
@@ -4493,7 +4537,7 @@ read_token_word (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);
@@ -4507,17 +4551,13 @@ read_token_word (character)
             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
@@ -4587,21 +4627,24 @@ read_token_word (character)
          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 ();
@@ -4615,21 +4658,22 @@ read_token_word (character)
 
 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. */
@@ -4679,7 +4723,11 @@ got_token:
       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))
@@ -4699,7 +4747,11 @@ got_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);*/
@@ -4761,6 +4813,7 @@ reserved_word_acceptable (toksym)
     case THEN:
     case TIME:
     case TIMEOPT:
+    case TIMEIGN:
     case COPROC:
     case UNTIL:
     case WHILE:
@@ -4771,6 +4824,8 @@ reserved_word_acceptable (toksym)
       if (last_read_token == WORD && token_before_that == COPROC)
        return 1;
 #endif
+      if (last_read_token == WORD && token_before_that == FUNCTION)
+       return 1;
       return 0;
     }
 }
@@ -4788,6 +4843,14 @@ find_reserved_word (tokstr)
   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
@@ -4861,6 +4924,9 @@ history_delimiting_chars (line)
       return (current_command_line_count == 2 ? "\n" : "");
     }
 
+  if (parser_state & PST_COMPASSIGN)
+    return (" ");
+
   /* First, handle some special cases. */
   /*(*/
   /* If we just read `()', assume it's a function definition, and don't
@@ -5101,6 +5167,9 @@ decode_prompt_string (string)
            case 'A':
              /* Make the current time/date into a string. */
              (void) time (&the_time);
+#if defined (HAVE_TZSET)
+             sv_tz ("TZ");             /* XXX -- just make sure */
+#endif
              tm = localtime (&the_time);
 
              if (c == 'd')
@@ -5220,7 +5289,7 @@ decode_prompt_string (string)
                      {
                        t = strrchr (t_string, '/');
                        if (t)
-                         strcpy (t_string, t + 1);
+                         memmove (t_string, t + 1, strlen (t));        /* strlen(t) to copy NULL */
                      }
                  }
 #undef ROOT_PATH
@@ -5507,7 +5576,7 @@ static void
 report_syntax_error (message)
      char *message;
 {
-  char *msg;
+  char *msg, *p;
 
   if (message)
     {
@@ -5523,6 +5592,12 @@ report_syntax_error (message)
      parser's complaining about by looking at current_token. */
   if (current_token != 0 && EOF_Reached == 0 && (msg = error_token_from_token (current_token)))
     {
+      if (ansic_shouldquote (msg))
+       {
+         p = ansic_quote (msg, 0, NULL);
+         free (msg);
+         msg = p;
+       }
       parser_error (line_number, _("syntax error near unexpected token `%s'"), msg);
       free (msg);
 
@@ -5843,6 +5918,8 @@ save_parser_state (ps)
   ps->input_line_terminator = shell_input_line_terminator;
   ps->eof_encountered = eof_encountered;
 
+  ps->prompt_string_pointer = prompt_string_pointer;
+
   ps->current_command_line_count = current_command_line_count;
 
 #if defined (HISTORY)
@@ -5863,6 +5940,12 @@ save_parser_state (ps)
   ps->expand_aliases = expand_aliases;
   ps->echo_input_at_read = echo_input_at_read;
 
+  ps->token = token;
+  ps->token_buffer_size = token_buffer_size;
+  /* Force reallocation on next call to read_token_word */
+  token = 0;
+  token_buffer_size = 0;
+
   return (ps);
 }
 
@@ -5883,6 +5966,8 @@ restore_parser_state (ps)
   shell_input_line_terminator = ps->input_line_terminator;
   eof_encountered = ps->eof_encountered;
 
+  prompt_string_pointer = ps->prompt_string_pointer;
+
   current_command_line_count = ps->current_command_line_count;
 
 #if defined (HISTORY)
@@ -5902,6 +5987,44 @@ restore_parser_state (ps)
 
   expand_aliases = ps->expand_aliases;
   echo_input_at_read = ps->echo_input_at_read;
+
+  FREE (token);
+  token = ps->token;
+  token_buffer_size = ps->token_buffer_size;
+}
+
+sh_input_line_state_t *
+save_input_line_state (ls)
+     sh_input_line_state_t *ls;
+{
+  if (ls == 0)
+    ls = (sh_input_line_state_t *)xmalloc (sizeof (sh_input_line_state_t));
+  if (ls == 0)
+    return ((sh_input_line_state_t *)NULL);
+
+  ls->input_line = shell_input_line;
+  ls->input_line_size = shell_input_line_size;
+  ls->input_line_len = shell_input_line_len;
+  ls->input_line_index = shell_input_line_index;
+
+  /* force reallocation */
+  shell_input_line = 0;
+  shell_input_line_size = shell_input_line_len = shell_input_line_index = 0;
+
+  return ls;
+}
+
+void
+restore_input_line_state (ls)
+     sh_input_line_state_t *ls;
+{
+  FREE (shell_input_line);
+  shell_input_line = ls->input_line;
+  shell_input_line_size = ls->input_line_size;
+  shell_input_line_len = ls->input_line_len;
+  shell_input_line_index = ls->input_line_index;
+
+  set_line_mbstate ();
 }
 
 /************************************************