Imported from ../bash-3.2.48.tar.gz.
[platform/upstream/bash.git] / parse.y
diff --git a/parse.y b/parse.y
index 363430e..8a2b081 100644 (file)
--- a/parse.y
+++ b/parse.y
@@ -1,6 +1,6 @@
 /* Yacc grammar for bash. */
 
-/* Copyright (C) 1989-2004 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2006 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -50,6 +50,7 @@
 #include "parser.h"
 #include "mailcheck.h"
 #include "test.h"
+#include "builtins.h"
 #include "builtins/common.h"
 #include "builtins/builtext.h"
 
@@ -117,7 +118,6 @@ extern int current_command_number;
 extern int sourcelevel;
 extern int posixly_correct;
 extern int last_command_exit_value;
-extern int interrupt_immediately;
 extern char *shell_name, *current_host_name;
 extern char *dist_version;
 extern int patch_level;
@@ -205,10 +205,6 @@ static void reset_readline_prompt __P((void));
 #endif
 static void print_prompt __P((void));
 
-#if defined (HISTORY)
-char *history_delimiting_chars __P((void));
-#endif
-
 #if defined (HANDLE_MULTIBYTE)
 static void set_line_mbstate __P((void));
 static char *shell_input_line_property = NULL;
@@ -961,22 +957,26 @@ pipeline_command: pipeline
                        { $$ = $1; }
        |       BANG pipeline
                        {
-                         $2->flags |= CMD_INVERT_RETURN;
+                         if ($2)
+                           $2->flags |= CMD_INVERT_RETURN;
                          $$ = $2;
                        }
        |       timespec pipeline
                        {
-                         $2->flags |= $1;
+                         if ($2)
+                           $2->flags |= $1;
                          $$ = $2;
                        }
        |       timespec BANG pipeline
                        {
-                         $3->flags |= $1|CMD_INVERT_RETURN;
+                         if ($3)
+                           $3->flags |= $1|CMD_INVERT_RETURN;
                          $$ = $3;
                        }
        |       BANG timespec pipeline
                        {
-                         $3->flags |= $2|CMD_INVERT_RETURN;
+                         if ($3)
+                           $3->flags |= $2|CMD_INVERT_RETURN;
                          $$ = $3;
                        }
        |       timespec list_terminator
@@ -1028,6 +1028,8 @@ timespec: TIME
 #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 */
+#define PST_REGEXP     0x8000          /* parsing an ERE/BRE as a single word */
 
 /* Initial size to allocate for tokens, and the
    amount to grow them by. */
@@ -1206,10 +1208,12 @@ yy_readline_get ()
          old_sigint = (SigHandler *)set_signal_handler (SIGINT, sigint_sighandler);
          interrupt_immediately++;
        }
+      terminate_immediately = 1;
 
       current_readline_line = readline (current_readline_prompt ?
                                          current_readline_prompt : "");
 
+      terminate_immediately = 0;
       if (signal_is_ignored (SIGINT) == 0 && old_sigint)
        {
          interrupt_immediately--;
@@ -1339,8 +1343,19 @@ yy_stream_get ()
 
   result = EOF;
   if (bash_input.location.file)
-    result = getc_with_restart (bash_input.location.file);
-
+    {
+      if (interactive)
+       {
+         interrupt_immediately++;
+         terminate_immediately++;
+       }
+      result = getc_with_restart (bash_input.location.file);
+      if (interactive)
+       {
+         interrupt_immediately--;
+         terminate_immediately--;
+       }
+    }
   return (result);
 }
 
@@ -1653,11 +1668,11 @@ read_a_line (remove_quoted_newline)
   pass_next = 0;
   while (1)
     {
-      c = yy_getc ();
-
       /* Allow immediate exit if interrupted during input. */
       QUIT;
 
+      c = yy_getc ();
+
       /* Ignore null bytes in input. */
       if (c == 0)
        {
@@ -1858,10 +1873,15 @@ shell_getc (remove_quoted_newline)
   register int i;
   int c;
   unsigned char uc;
-  static int mustpop = 0;
 
   QUIT;
 
+  if (sigwinch_received)
+    {
+      sigwinch_received = 0;
+      get_new_window_size (0, (int *)0, (int *)0);
+    }
+      
   if (eol_ungetc_lookahead)
     {
       c = eol_ungetc_lookahead;
@@ -2055,14 +2075,6 @@ shell_getc (remove_quoted_newline)
   if (uc)
     shell_input_line_index++;
 
-  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 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
@@ -2078,6 +2090,14 @@ shell_getc (remove_quoted_newline)
     }
 #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);
 
@@ -2125,8 +2145,8 @@ discard_until (character)
 }
 
 void
-execute_prompt_command (command)
-     char *command;
+execute_variable_command (command, vname)
+     char *command, *vname;
 {
   char *last_lastarg;
   sh_parser_state_t ps;
@@ -2136,10 +2156,10 @@ execute_prompt_command (command)
   if (last_lastarg)
     last_lastarg = savestring (last_lastarg);
 
-  parse_and_execute (savestring (command), "PROMPT_COMMAND", SEVAL_NONINT|SEVAL_NOHIST);
+  parse_and_execute (savestring (command), vname, SEVAL_NONINT|SEVAL_NOHIST);
 
   restore_parser_state (&ps);
-  bind_variable ("_", last_lastarg);
+  bind_variable ("_", last_lastarg, 0);
   FREE (last_lastarg);
 
   if (token_to_read == '\n')   /* reset_parser was called */
@@ -2539,7 +2559,7 @@ read_token (command)
 #endif /* ALIAS */
 
   /* Read a single word from input.  Start by skipping blanks. */
-  while ((character = shell_getc (1)) != EOF && whitespace (character))
+  while ((character = shell_getc (1)) != EOF && shellblank (character))
     ;
 
   if (character == EOF)
@@ -2567,9 +2587,14 @@ read_token (command)
       parser_state &= ~PST_ALEXPNEXT;
 #endif /* ALIAS */
 
+      parser_state &= ~PST_ASSIGNOK;
+
       return (character);
     }
 
+  if (parser_state & PST_REGEXP)
+    goto tokword;
+
   /* Shell meta-characters. */
   if MBTEST(shellmeta (character) && ((parser_state & PST_DBLPAREN) == 0))
     {
@@ -2580,6 +2605,8 @@ read_token (command)
        parser_state &= ~PST_ALEXPNEXT;
 #endif /* ALIAS */
 
+      parser_state &= ~PST_ASSIGNOK;
+
       peek_char = shell_getc (1);
       if (character == peek_char)
        {
@@ -2607,6 +2634,7 @@ read_token (command)
 #if defined (ALIAS)
              parser_state &= ~PST_ALEXPNEXT;
 #endif /* ALIAS */
+
              return (SEMI_SEMI);
 
            case '&':
@@ -2674,6 +2702,7 @@ read_token (command)
   if MBTEST(character == '-' && (last_read_token == LESS_AND || last_read_token == GREATER_AND))
     return (character);
 
+tokword:
   /* 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);
@@ -2687,13 +2716,14 @@ read_token (command)
 /*
  * 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 (unless the
- * P_NONL flag is passed), and returning correct error values if it reads
- * EOF.
+ * 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 */
+#define P_BACKQUOTE    0x10    /* parsing a backquoted command substitution */
 
 static char matched_pair_error;
 static char *
@@ -2702,13 +2732,15 @@ parse_matched_pair (qc, open, close, lenp, flags)
      int open, close;
      int *lenp, flags;
 {
-  int count, ch, was_dollar;
-  int pass_next_character, nestlen, ttranslen, start_lineno;
+  int count, ch, was_dollar, in_comment, check_comment;
+  int pass_next_character, backq_backslash, nestlen, ttranslen, start_lineno;
   char *ret, *nestret, *ttrans;
   int retind, retsize, rflags;
 
+/* itrace("parse_matched_pair: open = %c close = %c", open, close); */
   count = 1;
-  pass_next_character = was_dollar = 0;
+  pass_next_character = backq_backslash = was_dollar = in_comment = 0;
+  check_comment = (flags & P_COMMAND) && qc != '`' && 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);
@@ -2719,7 +2751,8 @@ parse_matched_pair (qc, open, close, lenp, flags)
   start_lineno = line_number;
   while (count)
     {
-      ch = shell_getc ((qc != '\'' || (flags & P_ALLOWESC)) && pass_next_character == 0);
+      ch = shell_getc (qc != '\'' && pass_next_character == 0 && backq_backslash == 0);
+
       if (ch == EOF)
        {
          free (ret);
@@ -2732,6 +2765,30 @@ parse_matched_pair (qc, open, close, lenp, flags)
       if (ch == '\n' && SHOULD_PROMPT ())
        prompt_again ();
 
+      if (in_comment)
+       {
+         /* Add this character. */
+         RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
+         ret[retind++] = ch;
+
+         if (ch == '\n')
+           in_comment = 0;
+
+         continue;
+       }
+      /* Not exactly right yet, should handle shell metacharacters, too.  If
+        any changes are made to this test, make analogous changes to subst.c:
+        extract_delimited_string(). */
+      else if MBTEST(check_comment && in_comment == 0 && ch == '#' && (retind == 0 || ret[retind-1] == '\n' || whitespace (ret[retind - 1])))
+       in_comment = 1;
+
+      /* last char was backslash inside backquoted command substitution */
+      if (backq_backslash)
+       {
+         backq_backslash = 0;
+         /* Placeholder for adding special characters */
+       }
+
       if (pass_next_character)         /* last char was backslash */
        {
          pass_next_character = 0;
@@ -2756,11 +2813,9 @@ parse_matched_pair (qc, open, close, lenp, flags)
        }
       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++;
 
@@ -2772,6 +2827,10 @@ parse_matched_pair (qc, open, close, lenp, flags)
        {
          if MBTEST((flags & P_ALLOWESC) && ch == '\\')
            pass_next_character++;
+#if 0
+         else if MBTEST((flags & P_BACKQUOTE) && ch == '\\')
+           backq_backslash++;
+#endif
          continue;
        }
 
@@ -2799,9 +2858,18 @@ parse_matched_pair (qc, open, close, lenp, flags)
                  /* Translate $'...' here. */
                  ttrans = ansiexpand (nestret, 0, nestlen - 1, &ttranslen);
                  xfree (nestret);
-                 nestret = sh_single_quote (ttrans);
-                 free (ttrans);
-                 nestlen = strlen (nestret);
+
+                 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))
@@ -2809,13 +2877,10 @@ parse_matched_pair (qc, open, close, lenp, flags)
                  /* Locale expand $"..." here. */
                  ttrans = localeexpand (nestret, 0, nestlen - 1, start_lineno, &ttranslen);
                  xfree (nestret);
-                 nestret = (char *)xmalloc (ttranslen + 3);
-                 nestret[0] = '"';
-                 strcpy (nestret + 1, ttrans);
-                 nestret[ttranslen + 1] = '"';
-                 nestret[ttranslen += 2] = '\0';
+
+                 nestret = sh_mkdoublequoted (ttrans, ttranslen, 0);
                  free (ttrans);
-                 nestlen = ttranslen;
+                 nestlen = ttranslen + 2;
                  retind -= 2;          /* back up before the $" */
                }
 
@@ -2834,6 +2899,7 @@ parse_matched_pair (qc, open, close, lenp, flags)
       else if MBTEST(open == '"' && ch == '`')
        {
          nestret = parse_matched_pair (0, '`', '`', &nestlen, rflags);
+add_nestret:
          if (nestret == &matched_pair_error)
            {
              free (ret);
@@ -2847,29 +2913,30 @@ parse_matched_pair (qc, open, close, lenp, flags)
            }
          FREE (nestret);
        }
-      else if MBTEST(was_dollar && (ch == '(' || ch == '{' || ch == '['))      /* ) } ] */
+#if 0
+      else if MBTEST(qc == '`' && (ch == '"' || ch == '\'') && in_comment == 0)
+       {
+         /* Add P_BACKQUOTE so backslash quotes the next character and
+            shell_getc does the right thing with \<newline>.  We do this for
+            a measure  of backwards compatibility -- it's not strictly the
+            right POSIX thing. */
+         nestret = parse_matched_pair (0, ch, ch, &nestlen, rflags|P_BACKQUOTE);
+         goto add_nestret;
+       }
+#endif
+      else if MBTEST(open != '`' && 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);
+           nestret = parse_matched_pair (0, '(', ')', &nestlen, rflags & ~P_DQUOTE);
          else if (ch == '{')           /* } */
            nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE|rflags);
          else if (ch == '[')           /* ] */
            nestret = parse_matched_pair (0, '[', ']', &nestlen, rflags);
-         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);
+
+         goto add_nestret;
        }
       was_dollar = MBTEST(ch == '$');
     }
@@ -2889,8 +2956,8 @@ static int
 parse_dparen (c)
      int c;
 {
-  int cmdtyp, len, sline;
-  char *wval, *wv2;
+  int cmdtyp, sline;
+  char *wval;
   WORD_DESC *wd;
 
 #if defined (ARITH_FOR_COMMAND)
@@ -2900,9 +2967,9 @@ parse_dparen (c)
       cmdtyp = parse_arith_cmd (&wval, 0);
       if (cmdtyp == 1)
        {
-         wd = make_word (wval);
+         wd = alloc_word_desc ();
+         wd->word = wval;
          yylval.word_list = make_word_list (wd, (WORD_LIST *)NULL);
-         free (wval);
          return (ARITH_FOR_EXPRS);
        }
       else
@@ -2914,21 +2981,14 @@ parse_dparen (c)
   if (reserved_word_acceptable (last_read_token))
     {
       sline = line_number;
-#if 0
-      cmdtyp = parse_arith_cmd (&wval, 1);
-#else
+
       cmdtyp = parse_arith_cmd (&wval, 0);
-#endif
       if (cmdtyp == 1) /* arithmetic command */
        {
-         wd = make_word (wval);
-#if 0
-         wd->flags = W_QUOTED;
-#else
-         wd->flags = W_QUOTED|W_NOSPLIT|W_NOGLOB;
-#endif
+         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);
-         free (wval);  /* make_word copies it */
          return (ARITH_CMD);
        }
       else if (cmdtyp == 0)    /* nested subshell */
@@ -3147,8 +3207,11 @@ cond_term ()
       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;
+      else if (tok == WORD && STREQ (yylval.word->word, "=~"))
+       {
+         op = yylval.word;
+         parser_state |= PST_REGEXP;
+       }
 #endif
       else if (tok == '<' || tok == '>')
        op = make_word_from_token (tok);  /* ( */
@@ -3179,6 +3242,7 @@ cond_term ()
 
       /* rhs */
       tok = read_token (READ);
+      parser_state &= ~PST_REGEXP;
       if (tok == WORD)
        {
          tright = make_cond_node (COND_TERM, yylval.word, (COND_COM *)NULL, (COND_COM *)NULL);
@@ -3246,6 +3310,7 @@ token_is_assignment (t, i)
   return r;
 }
 
+/* XXX - possible changes here for `+=' */
 static int
 token_is_ident (t, i)
      char *t;
@@ -3278,6 +3343,10 @@ read_token_word (character)
   /* 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;
 
@@ -3297,7 +3366,7 @@ read_token_word (character)
 
   token_index = 0;
   all_digit_token = DIGIT (character);
-  dollar_present = quoted = pass_next_character = 0;
+  dollar_present = quoted = pass_next_character = compound_assignment = 0;
 
   for (;;)
     {
@@ -3307,7 +3376,7 @@ read_token_word (character)
       if (pass_next_character)
        {
          pass_next_character = 0;
-         goto got_character;
+         goto got_escaped_character;
        }
 
       cd = current_delimiter (dstack);
@@ -3343,7 +3412,7 @@ read_token_word (character)
       if MBTEST(shellquote (character))
        {
          push_delimiter (dstack, character);
-         ttok = parse_matched_pair (character, character, character, &ttoklen, 0);
+         ttok = parse_matched_pair (character, character, character, &ttoklen, (character == '`') ? P_COMMAND : 0);
          pop_delimiter (dstack);
          if (ttok == &matched_pair_error)
            return -1;          /* Bail immediately. */
@@ -3359,9 +3428,34 @@ read_token_word (character)
          goto next_character;
        }
 
+#ifdef COND_REGEXP
+      /* When parsing a regexp as a single word inside a conditional command,
+        we need to special-case characters special to both the shell and
+        regular expressions.  Right now, that is only '(' and '|'. */ /*)*/
+      if MBTEST((parser_state & PST_REGEXP) && (character == '(' || character == '|'))         /*)*/
+        {
+          if (character == '|')
+            goto got_character;
+
+         push_delimiter (dstack, character);
+         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;
+         strcpy (token + token_index, ttok);
+         token_index += ttoklen;
+         FREE (ttok);
+         dollar_present = all_digit_token = 0;
+         goto next_character;
+        }
+#endif /* COND_REGEXP */
+
 #ifdef EXTENDED_GLOB
       /* Parse a ksh-style extended pattern matching specification. */
-      if (extended_glob && PATTERN_CHAR (character))
+      if MBTEST(extended_glob && PATTERN_CHAR (character))
        {
          peek_char = shell_getc (1);
          if MBTEST(peek_char == '(')           /* ) */
@@ -3406,7 +3500,7 @@ read_token_word (character)
                     history literally rather than causing a possibly-
                     incorrect `;' to be added. ) */
                  push_delimiter (dstack, peek_char);
-                 ttok = parse_matched_pair (cd, '(', ')', &ttoklen, 0);
+                 ttok = parse_matched_pair (cd, '(', ')', &ttoklen, P_COMMAND);
                  pop_delimiter (dstack);
                }
              else
@@ -3442,13 +3536,14 @@ read_token_word (character)
                {
                  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);
+                 ttranslen = strlen (ttok);
                  ttrans = ttok;
-                 ttranslen = strlen (ttrans);
                }
              else
                {
@@ -3457,12 +3552,9 @@ read_token_word (character)
                  free (ttok);
 
                  /* Add the double quotes back */
-                 ttok = (char *)xmalloc (ttranslen + 3);
-                 ttok[0] = '"';
-                 strcpy (ttok + 1, ttrans);
-                 ttok[ttranslen + 1] = '"';
-                 ttok[ttranslen += 2] = '\0';
+                 ttok = sh_mkdoublequoted (ttrans, ttranslen, 0);
                  free (ttrans);
+                 ttranslen += 2;
                  ttrans = ttok;
                }
 
@@ -3515,7 +3607,7 @@ read_token_word (character)
          goto next_character;
         }
       /* Identify possible compound array variable assignment. */
-      else if MBTEST(character == '=' && token_index > 0 && token_is_assignment (token, token_index))
+      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 == '(')           /* ) */
@@ -3536,7 +3628,12 @@ read_token_word (character)
              token[token_index++] = ')';
              FREE (ttok);
              all_digit_token = 0;
+             compound_assignment = 1;
+#if 1
              goto next_character;
+#else
+             goto got_token;           /* ksh93 seems to do this */
+#endif
            }
          else
            shell_ungetc (peek_char);
@@ -3553,12 +3650,14 @@ read_token_word (character)
 
     got_character:
 
-      all_digit_token &= DIGIT (character);
-      dollar_present |= character == '$';
-
       if (character == CTLESC || character == CTLNUL)
        token[token_index++] = CTLESC;
 
+    got_escaped_character:
+
+      all_digit_token &= DIGIT (character);
+      dollar_present |= character == '$';
+
       token[token_index++] = character;
 
       RESIZE_MALLOCED_BUFFER (token, token_index, 1, token_buffer_size,
@@ -3630,7 +3729,9 @@ got_token:
   if (dollar_present)
     the_word->flags |= W_HASDOLLAR;
   if (quoted)
-    the_word->flags |= W_QUOTED;
+    the_word->flags |= W_QUOTED;               /*(*/
+  if (compound_assignment && token[token_index-1] == ')')
+    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. */
@@ -3642,6 +3743,16 @@ got_token:
        the_word->flags |= W_NOSPLIT;
     }
 
+  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;
+      else if (STREQ (token, "eval") || STREQ (token, "let"))
+       parser_state |= PST_ASSIGNOK;
+    }
+
   yylval.word = the_word;
 
   result = ((the_word->flags & (W_ASSIGNMENT|W_NOSPLIT)) == (W_ASSIGNMENT|W_NOSPLIT))
@@ -3799,6 +3910,8 @@ history_delimiting_chars ()
        return " ";
       return ";";
     }
+  else if (two_tokens_ago == CASE && token_before_that == WORD && (parser_state & PST_CASESTMT))
+    return " ";
 
   for (i = 0; no_semi_successors[i]; i++)
     {
@@ -3916,7 +4029,7 @@ decode_prompt_string (string)
   int last_exit_value;
 #if defined (PROMPT_STRING_DECODE)
   int result_size, result_index;
-  int c, n;
+  int c, n, i;
   char *temp, octal_string[4];
   struct tm *tm;  
   time_t the_time;
@@ -4078,7 +4191,7 @@ decode_prompt_string (string)
            case 'W':
              {
                /* Use the value of PWD because it is much more efficient. */
-               char t_string[PATH_MAX], *t;
+               char t_string[PATH_MAX];
                int tlen;
 
                temp = get_string_value ("PWD");
@@ -4103,7 +4216,7 @@ decode_prompt_string (string)
 #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, temp) == 0))
+               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)
                      {
@@ -4188,9 +4301,12 @@ decode_prompt_string (string)
                  break;
                }
              temp = (char *)xmalloc (3);
-             temp[0] = '\001';
-             temp[1] = (c == '[') ? RL_PROMPT_START_IGNORE : RL_PROMPT_END_IGNORE;
-             temp[2] = '\0';
+             n = (c == '[') ? RL_PROMPT_START_IGNORE : RL_PROMPT_END_IGNORE;
+             i = 0;
+             if (n == CTLESC || n == CTLNUL)
+               temp[i++] = CTLESC;
+             temp[i++] = n;
+             temp[i] = '\0';
              goto add_string;
 #endif /* READLINE */
 
@@ -4250,7 +4366,7 @@ not_escape:
   if (promptvars || posixly_correct)
     {
       last_exit_value = last_command_exit_value;
-      list = expand_prompt_string (result, Q_DOUBLE_QUOTES);
+      list = expand_prompt_string (result, Q_DOUBLE_QUOTES, 0);
       free (result);
       result = string_list (list);
       dispose_words (list);
@@ -4286,15 +4402,15 @@ yyerror (msg)
 }
 
 static char *
-error_token_from_token (token)
-     int token;
+error_token_from_token (tok)
+     int tok;
 {
   char *t;
 
-  if (t = find_token_in_alist (token, word_token_alist, 0))
+  if (t = find_token_in_alist (tok, word_token_alist, 0))
     return t;
 
-  if (t = find_token_in_alist (token, other_token_alist, 0))
+  if (t = find_token_in_alist (tok, other_token_alist, 0))
     return t;
 
   t = (char *)NULL;
@@ -4582,7 +4698,7 @@ parse_string_to_word_list (s, flags, whom)
          line_number = orig_line_number + line_number - 1;
          orig_current_token = current_token;
          current_token = tok;
-         yyerror ((char *)NULL);       /* does the right thing */
+         yyerror (NULL);       /* does the right thing */
          current_token = orig_current_token;
          if (wl)
            dispose_words (wl);
@@ -4628,18 +4744,21 @@ parse_compound_assignment (retlenp)
      int *retlenp;
 {
   WORD_LIST *wl, *rl;
-  int tok, orig_line_number, orig_token_size;
+  int tok, orig_line_number, orig_token_size, orig_last_token, assignok;
   char *saved_token, *ret;
 
   saved_token = token;
   orig_token_size = token_buffer_size;
   orig_line_number = line_number;
+  orig_last_token = last_read_token;
 
   last_read_token = WORD;      /* WORD to allow reserved words here */
 
   token = (char *)NULL;
   token_buffer_size = 0;
 
+  assignok = parser_state&PST_ASSIGNOK;                /* XXX */
+
   wl = (WORD_LIST *)NULL;      /* ( */
   parser_state |= PST_COMPASSIGN;
 
@@ -4657,7 +4776,7 @@ parse_compound_assignment (retlenp)
          if (tok == yacc_EOF)  /* ( */
            parser_error (orig_line_number, _("unexpected EOF while looking for matching `)'"));
          else
-           yyerror ((char *)NULL);     /* does the right thing */
+           yyerror(NULL);      /* does the right thing */
          if (wl)
            dispose_words (wl);
          wl = &parse_string_error;
@@ -4682,7 +4801,8 @@ parse_compound_assignment (retlenp)
        jump_to_top_level (DISCARD);
     }
 
-  last_read_token = WORD;
+  last_read_token = orig_last_token;           /* XXX - was WORD? */
+
   if (wl)
     {
       rl = REVERSE_LIST (wl, WORD_LIST *);
@@ -4694,6 +4814,10 @@ parse_compound_assignment (retlenp)
 
   if (retlenp)
     *retlenp = (ret && *ret) ? strlen (ret) : 0;
+
+  if (assignok)
+    parser_state |= PST_ASSIGNOK;
+
   return ret;
 }
 
@@ -4712,7 +4836,7 @@ save_parser_state (ps)
 #endif
 
   if (ps == 0)
-    ps = xmalloc (sizeof (sh_parser_state_t));
+    ps = (sh_parser_state_t *)xmalloc (sizeof (sh_parser_state_t));
   if (ps == 0)
     return ((sh_parser_state_t *)NULL);