Imported from ../bash-2.05.tar.gz.
[platform/upstream/bash.git] / parse.y
diff --git a/parse.y b/parse.y
index 3c37c73..f9d33ee 100644 (file)
--- a/parse.y
+++ b/parse.y
@@ -24,6 +24,8 @@
 #include "bashtypes.h"
 #include "bashansi.h"
 
+#include "filecntl.h"
+
 #if defined (HAVE_UNISTD_H)
 #  include <unistd.h>
 #endif
@@ -77,9 +79,6 @@
 #define YYDEBUG 0
 
 #if defined (EXTENDED_GLOB)
-#define PATTERN_CHAR(c) \
-       ((c) == '@' || (c) == '*' || (c) == '+' || (c) == '?' || (c) == '!')
-
 extern int extended_glob;
 #endif
 
@@ -127,6 +126,10 @@ static void reset_readline_prompt ();
 #endif
 static void print_prompt ();
 
+#if defined (HISTORY)
+char *history_delimiting_chars ();
+#endif
+
 extern int yyerror ();
 
 /* Default prompt strings */
@@ -220,7 +223,7 @@ static REDIRECTEE redir;
 %type <command> arith_command
 %type <command> cond_command
 %type <command> arith_for_command
-%type <command> function_def if_command elif_clause subshell
+%type <command> function_def function_body if_command elif_clause subshell
 %type <redirect> redirection redirection_list
 %type <element> simple_command_element
 %type <word_list> word_list pattern
@@ -462,25 +465,6 @@ command:   simple_command
                          COMMAND *tc;
 
                          tc = $1;
-                         /* 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->type == cm_function_def)
-                           {
-                             tc = tc->value.Function_def->command;
-                             if (tc->type == cm_group)
-                               tc = tc->value.Group->command;
-                           }
                          if (tc->redirects)
                            {
                              register REDIRECT *t;
@@ -492,6 +476,8 @@ command:    simple_command
                            tc->redirects = $2;
                          $$ = $1;
                        }
+       |       function_def
+                       { $$ = $1; }
        ;
 
 shell_command: for_command
@@ -516,8 +502,6 @@ shell_command:      for_command
                        { $$ = $1; }
        |       arith_for_command
                        { $$ = $1; }
-       |       function_def
-                       { $$ = $1; }
        ;
 
 for_command:   FOR WORD newline_list DO compound_list DONE
@@ -538,7 +522,12 @@ arith_for_command: FOR ARITH_FOR_EXPRS list_terminator newline_list DO compound_
                                { $$ = make_arith_for_command ($2, $6, arith_for_lineno); }
        |               FOR ARITH_FOR_EXPRS list_terminator newline_list '{' compound_list '}'
                                { $$ = make_arith_for_command ($2, $6, arith_for_lineno); }
+       |               FOR ARITH_FOR_EXPRS DO compound_list DONE
+                               { $$ = make_arith_for_command ($2, $4, arith_for_lineno); }
+       |               FOR ARITH_FOR_EXPRS '{' compound_list '}'
+                               { $$ = make_arith_for_command ($2, $4, arith_for_lineno); }
        ;
+
 select_command:        SELECT WORD newline_list DO list DONE
                        {
                          $$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5);
@@ -573,17 +562,50 @@ case_command:     CASE WORD newline_list IN newline_list ESAC
                        { $$ = make_case_command ($2, $5); }
        ;
 
-function_def:  WORD '(' ')' newline_list group_command
+function_def:  WORD '(' ')' newline_list function_body
                        { $$ = make_function_def ($1, $5, function_dstart, function_bstart); }
 
-
-       |       FUNCTION WORD '(' ')' newline_list group_command
+       |       FUNCTION WORD '(' ')' newline_list function_body
                        { $$ = make_function_def ($2, $6, function_dstart, function_bstart); }
 
-       |       FUNCTION WORD newline_list group_command
+       |       FUNCTION WORD newline_list function_body
                        { $$ = make_function_def ($2, $4, function_dstart, function_bstart); }
        ;
 
+
+function_body: shell_command
+                       { $$ = $1; }
+       |       shell_command redirection_list
+                       {
+                         COMMAND *tc;
+
+                         tc = $1;
+                         /* 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 = $2;
+                           }
+                         else
+                           tc->redirects = $2;
+                         $$ = $1;
+                       }
+       ;
+
 subshell:      '(' compound_list ')'
                        {
                          $$ = make_subshell_command ($2);
@@ -600,7 +622,7 @@ if_command: IF compound_list THEN compound_list FI
        ;
 
 
-group_command: '{' list '}'
+group_command: '{' compound_list '}'
                        { $$ = make_group_command ($2); }
        ;
 
@@ -809,15 +831,6 @@ timespec:  TIME
 #define TOKEN_DEFAULT_INITIAL_SIZE 496
 #define TOKEN_DEFAULT_GROW_SIZE 512
 
-/* Shell meta-characters that, when unquoted, separate words. */
-#define shellmeta(c)   (strchr (shell_meta_chars, (c)) != 0)
-#define shellbreak(c)  (strchr (shell_break_chars, (c)) != 0)
-#define shellquote(c)  ((c) == '"' || (c) == '`' || (c) == '\'')
-#define shellexp(c)    ((c) == '$' || (c) == '<' || (c) == '>')
-
-char *shell_meta_chars = "()<>;&|";
-char *shell_break_chars = "()<>;&| \t\n";
-
 /* The token currently being read. */
 static int current_token;
 
@@ -966,7 +979,7 @@ yy_readline_get ()
 
 #if defined (JOB_CONTROL)
       if (job_control)
-       give_terminal_to (shell_pgrp);
+       give_terminal_to (shell_pgrp, 0);
 #endif /* JOB_CONTROL */
 
       if (signal_is_ignored (SIGINT) == 0)
@@ -1196,18 +1209,20 @@ pop_stream ()
         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)
+       {
+         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 */
 
       line_number = saver->line;
@@ -1412,8 +1427,14 @@ read_a_line (remove_quoted_newline)
       /* Allow immediate exit if interrupted during input. */
       QUIT;
 
+      /* Ignore null bytes in input. */
       if (c == 0)
-       continue;
+       {
+#if 0
+         internal_warning ("read_a_line: ignored null byte in input");
+#endif
+         continue;
+       }
 
       /* If there is no more input, then we return NULL. */
       if (c == EOF)
@@ -1435,10 +1456,10 @@ read_a_line (remove_quoted_newline)
         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 ();
@@ -1516,8 +1537,8 @@ STRING_INT_ALIST word_token_alist[] = {
 };
 
 /* 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. */
+        can give more descriptive error messages.  Look at y.tab.h for the
+        other tokens. */
 
 /* 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. */
@@ -1553,6 +1574,11 @@ static struct dstack temp_dstack = { (char *)NULL, 0, 0 };
    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;
+
 static int
 shell_getc (remove_quoted_newline)
      int remove_quoted_newline;
@@ -1563,6 +1589,13 @@ shell_getc (remove_quoted_newline)
 
   QUIT;
 
+  if (eol_ungetc_lookahead)
+    {
+      c = eol_ungetc_lookahead;
+      eol_ungetc_lookahead = 0;
+      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
@@ -1604,11 +1637,21 @@ shell_getc (remove_quoted_newline)
       if (bash_input.type == st_stream)
        clearerr (stdin);
 
-      while (c = yy_getc ())
+      while (1)
        {
+         c = yy_getc ();
+
          /* Allow immediate exit if interrupted during input. */
          QUIT;
 
+         if (c == '\0')
+           {
+#if 0
+             internal_warning ("shell_getc: ignored null byte in input");
+#endif
+             continue;
+           }
+
          RESIZE_MALLOCED_BUFFER (shell_input_line, i, 2, shell_input_line_size, 256);
 
          if (c == EOF)
@@ -1632,6 +1675,7 @@ shell_getc (remove_quoted_newline)
              break;
            }
        }
+
       shell_input_line_index = 0;
       shell_input_line_len = i;                /* == strlen (shell_input_line) */
 
@@ -1663,20 +1707,29 @@ shell_getc (remove_quoted_newline)
                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. */
+                the true allocated size of shell_input_line anymore. */
              shell_input_line_size = shell_input_line_len;
            }
        }
-      /* XXX - this is grotesque */
+      /* 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 && current_delimiter (dstack))
+              current_command_line_count > 1)
        {
-         /* 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);
+         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);
+           }
        }
 
 #endif /* HISTORY */
@@ -1732,18 +1785,18 @@ shell_getc (remove_quoted_newline)
   if (!c && (pushed_string_list != (STRING_SAVER *)NULL))
     {
       if (mustpop)
-        {
-          pop_string ();
-          c = shell_input_line[shell_input_line_index];
+       {
+         pop_string ();
+         c = shell_input_line[shell_input_line_index];
          if (c)
            shell_input_line_index++;
          mustpop--;
-        }
+       }
       else
-        {
-          mustpop++;
-          c = ' ';
-        }
+       {
+         mustpop++;
+         c = ' ';
+       }
     }
 #endif /* ALIAS || DPAREN_ARITHMETIC */
 
@@ -1760,6 +1813,8 @@ shell_ungetc (c)
 {
   if (shell_input_line && shell_input_line_index)
     shell_input_line[--shell_input_line_index] = c;
+  else
+    eol_ungetc_lookahead = c;
 }
 
 static void
@@ -1974,8 +2029,8 @@ time_command_acceptable ()
     case DO:
     case THEN:
     case ELSE:
-    case '{':
-    case '(':
+    case '{':          /* } */
+    case '(':          /* ) */
       return 1;
     default:
       return 0;
@@ -1996,9 +2051,10 @@ time_command_acceptable ()
        to be set
 
        `{' is recognized if the last token as WORD and the token
-       before that was FUNCTION.
+       before that was FUNCTION, or if we just parsed an arithmetic
+       `for' command.
 
-       `}' is recognized if there is an unclosed `{' prsent.
+       `}' is recognized if there is an unclosed `{' present.
 
        `-p' is returned as TIMEOPT if the last read token was TIME.
 
@@ -2066,6 +2122,16 @@ special_case_tokens (token)
        }
     }
 
+  /* 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 ('{');                    /* } */
+    }
+
   if (open_brace_count && reserved_word_acceptable (last_read_token) && token[0] == '}' && !token[1])
     {
       open_brace_count--;              /* { */
@@ -2263,7 +2329,7 @@ read_token (command)
                  if (cmdtyp == 1)
                    {
                      /* parse_arith_cmd adds quotes at the beginning and end
-                        of the string it returns; we need to take those out. */
+                        of the string it returns; we need to take those out. */
                      len = strlen (wval);
                      wv2 = xmalloc (len);
                      strncpy (wv2, wval + 1, len - 2);
@@ -2342,7 +2408,7 @@ read_token (command)
        parser_state |= PST_SUBSHELL;
       /*(*/
       else if ((parser_state & PST_CASEPAT) && character == ')')
-        parser_state &= ~PST_CASEPAT;
+       parser_state &= ~PST_CASEPAT;
       /*(*/
       else if ((parser_state & PST_SUBSHELL) && character == ')')
        parser_state &= ~PST_SUBSHELL;
@@ -2386,8 +2452,8 @@ parse_matched_pair (qc, open, close, lenp, flags)
      int *lenp, flags;
 {
   int count, ch, was_dollar;
-  int pass_next_character, nestlen, start_lineno;
-  char *ret, *nestret;
+  int pass_next_character, nestlen, ttranslen, start_lineno;
+  char *ret, *nestret, *ttrans;
   int retind, retsize;
 
   count = 1;
@@ -2440,7 +2506,7 @@ parse_matched_pair (qc, open, close, lenp, flags)
 #if 1
       /* handle nested ${...} specially. */
       else if (open != close && was_dollar && open == '{' && ch == open) /* } */
-        count++;
+       count++;
 #endif
       else if (((flags & P_FIRSTCLOSE) == 0) && ch == open)            /* nested begin */
        count++;
@@ -2465,13 +2531,40 @@ parse_matched_pair (qc, open, close, lenp, flags)
            {
              /* '', ``, or "" inside $(...) or other grouping construct. */
              push_delimiter (dstack, ch);
-             nestret = parse_matched_pair (ch, ch, ch, &nestlen, 0);
+             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);
@@ -2696,14 +2789,16 @@ cond_term ()
       /* binop */
       tok = read_token (READ);
       if (tok == WORD && test_binop (yylval.word->word))
-        op = yylval.word;
+       op = yylval.word;
       else if (tok == '<' || tok == '>')
-        op = make_word_from_token (tok);
-      else if (tok == COND_END || tok == AND_AND || tok == OR_OR)
+       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 ]] */
+            [[ x || expr ]] or [[ (x) ]]. */
          op = make_word ("-n");
          term = make_cond_node (COND_UNARY, op, tleft, (COND_COM *)NULL);
          cond_token = tok;
@@ -2789,7 +2884,7 @@ read_token_word (character)
     token = xrealloc (token, token_buffer_size = TOKEN_DEFAULT_INITIAL_SIZE);
 
   token_index = 0;
-  all_digits = digit (character);
+  all_digits = isdigit (character);
   dollar_present = quoted = pass_next_character = 0;
 
   for (;;)
@@ -2824,7 +2919,7 @@ read_token_word (character)
 
              /* If the next character is to be quoted, note it now. */
              if (cd == 0 || cd == '`' ||
-                 (cd == '"' && member (peek_char, slashify_in_quotes)))
+                 (cd == '"' && (sh_syntaxtab[peek_char] & CBSDQUOTE)))
                pass_next_character++;
 
              quoted = 1;
@@ -2854,7 +2949,7 @@ read_token_word (character)
 
 #ifdef EXTENDED_GLOB
       /* Parse a ksh-style extended pattern matching specification. */
-      if (extended_glob && PATTERN_CHAR(character))
+      if (extended_glob && PATTERN_CHAR (character))
        {
          peek_char = shell_getc (1);
          if (peek_char == '(')         /* ) */
@@ -2882,11 +2977,7 @@ read_token_word (character)
 
       /* If the delimiter character is not single quote, parse some of
         the shell expansions that must be read as a single word. */
-#if defined (PROCESS_SUBSTITUTION)
-      if (character == '$' || character == '<' || character == '>')
-#else
-      if (character == '$')
-#endif /* !PROCESS_SUBSTITUTION */
+      if (shellexp (character))
        {
          peek_char = shell_getc (1);
          /* $(...), <(...), >(...), $((...)), ${...}, and $[...] constructs */
@@ -2894,7 +2985,7 @@ read_token_word (character)
                ((peek_char == '{' || peek_char == '[') && character == '$'))   /* ) ] } */
            {
              if (peek_char == '{')             /* } */
-               ttok = parse_matched_pair (cd, '{', '}', &ttoklen, P_FIRSTCLOSE);
+               ttok = parse_matched_pair (cd, '{', '}', &ttoklen, P_FIRSTCLOSE);
              else if (peek_char == '(')                /* ) */
                {
                  /* XXX - push and pop the `(' as a delimiter for use by
@@ -2942,7 +3033,7 @@ read_token_word (character)
                  /* Insert the single quotes and correctly quote any
                     embedded single quotes (allowed because P_ALLOWESC was
                     passed to parse_matched_pair). */
-                 ttok = single_quote (ttrans);
+                 ttok = sh_single_quote (ttrans);
                  free (ttrans);
                  ttrans = ttok;
                  ttranslen = strlen (ttrans);
@@ -3035,7 +3126,7 @@ read_token_word (character)
 
     got_character:
 
-      all_digits &= digit (character);
+      all_digits &= isdigit (character);
       dollar_present |= character == '$';
 
       if (character == CTLESC || character == CTLNUL)
@@ -3181,7 +3272,7 @@ mk_msgstr (string, foundnlp)
       if (*s == '"' || *s == '\\')
        len++;
       else if (*s == '\n')
-        len += 5;
+       len += 5;
     }
   
   r = result = xmalloc (len + 3);
@@ -3380,9 +3471,9 @@ history_delimiting_chars ()
       /* 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 " ";
+       return " ";
       else     
-        return "; ";                           /* (...) subshell */
+       return "; ";                            /* (...) subshell */
     }
   else if (token_before_that == WORD && two_tokens_ago == FUNCTION)
     return " ";                /* function def using `function name' without `()' */
@@ -3392,7 +3483,7 @@ history_delimiting_chars ()
       /* 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 ";";
@@ -3560,7 +3651,6 @@ decode_prompt_string (string)
 
              if (n == CTLESC || n == CTLNUL)
                {
-                 string += 3;
                  temp[0] = CTLESC;
                  temp[1] = n;
                  temp[2] = '\0';
@@ -3572,11 +3662,13 @@ decode_prompt_string (string)
                }
              else
                {
-                 string += 3;
                  temp[0] = n;
                  temp[1] = '\0';
                }
 
+             for (c = 0; n != -1 && c < 3 && ISOCTAL (*string); c++)
+               string++;
+
              c = 0;
              goto add_string;
 
@@ -3655,7 +3747,7 @@ decode_prompt_string (string)
                  {
                    if (getcwd (t_string, sizeof(t_string)) == 0)
                      {
-                       t_string[0] = '.';
+                       t_string[0] = '.';
                        tlen = 1;
                      }
                    else
@@ -3668,12 +3760,19 @@ decode_prompt_string (string)
                  }
                t_string[tlen] = '\0';
 
+#define ROOT_PATH(x)   ((x)[0] == '/' && (x)[1] == 0)
+#define DOUBLE_SLASH_ROOT(x)   ((x)[0] == '/' && (x)[1] == '/' && (x)[2] == 0)
                if (c == 'W')
                  {
-                   t = strrchr (t_string, '/');
-                   if (t && t != t_string)
-                     strcpy (t_string, t + 1);
+                   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. */
@@ -3682,14 +3781,10 @@ decode_prompt_string (string)
                /* If we're going to be expanding the prompt string later,
                   quote the directory name. */
                if (promptvars || posixly_correct)
-#if 0
-                 temp = backslash_quote (t_string);
-#else
                  /* Make sure that expand_prompt_string is called with a
                     second argument of Q_DOUBLE_QUOTE if we use this
                     function here. */
-                 temp = backslash_quote_for_double_quotes (t_string);
-#endif
+                 temp = sh_backslash_quote_for_double_quotes (t_string);
                else
                  temp = savestring (t_string);
 
@@ -3804,11 +3899,7 @@ decode_prompt_string (string)
      the prompt string. */
   if (promptvars || posixly_correct)
     {
-#if 0
-      list = expand_string_unsplit (result, Q_DOUBLE_QUOTES);
-#else
       list = expand_prompt_string (result, Q_DOUBLE_QUOTES);
-#endif
       free (result);
       result = string_list (list);
       dispose_words (list);
@@ -3998,6 +4089,7 @@ parse_string_to_word_list (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
@@ -4011,10 +4103,12 @@ parse_string_to_word_list (s, whom)
 #endif
 
   orig_line_number = line_number;
+  orig_line_count = current_command_line_count;
   orig_input_terminator = shell_input_line_terminator;
 
   push_stream (1);
   last_read_token = '\n';
+  current_command_line_count = 0;
 
   with_input_from_string (s, whom);
   wl = (WORD_LIST *)NULL;
@@ -4022,6 +4116,8 @@ parse_string_to_word_list (s, whom)
     {
       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;
@@ -4044,6 +4140,7 @@ parse_string_to_word_list (s, whom)
 #  endif /* BANG_HISTORY */
 #endif /* HISTORY */
 
+  current_command_line_count = orig_line_count;
   shell_input_line_terminator = orig_input_terminator;
 
   if (wl == &parse_string_error)