]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - subst.c
bash-4.4 beta release
[thirdparty/bash.git] / subst.c
diff --git a/subst.c b/subst.c
index 6b5db0f03b8d041bd5e024d41c0a7c4726d8e128..0f6c9ef4e8f085a750165efb1edbc73df4194cad 100644 (file)
--- a/subst.c
+++ b/subst.c
@@ -247,7 +247,7 @@ static char *string_extract __P((char *, int *, char *, int));
 static char *string_extract_double_quoted __P((char *, int *, int));
 static inline char *string_extract_single_quoted __P((char *, int *));
 static inline int skip_single_quoted __P((const char *, size_t, int));
-static int skip_double_quoted __P((char *, size_t, int));
+static int skip_double_quoted __P((char *, size_t, int, int));
 static char *extract_delimited_string __P((char *, int *, char *, char *, char *, int));
 static char *extract_dollar_brace_string __P((char *, int *, int, int));
 static int skip_matched_pair __P((const char *, int, int, int, int));
@@ -631,7 +631,7 @@ unquoted_member (character, string)
          break;
 
        case '"':
-         sindex = skip_double_quoted (string, slen, ++sindex);
+         sindex = skip_double_quoted (string, slen, ++sindex, 0);
          break;
        }
     }
@@ -670,7 +670,7 @@ unquoted_substring (substr, string)
          break;
 
        case '"':
-         sindex = skip_double_quoted (string, slen, ++sindex);
+         sindex = skip_double_quoted (string, slen, ++sindex, 0);
          break;
 
        default:
@@ -969,7 +969,7 @@ add_one_character:
 
 /* This should really be another option to string_extract_double_quoted. */
 static int
-skip_double_quoted (string, slen, sind)
+skip_double_quoted (string, slen, sind, flags)
      char *string;
      size_t slen;
      int sind;
@@ -1012,7 +1012,7 @@ skip_double_quoted (string, slen, sind)
        {
          si = i + 2;
          if (string[i + 1] == LPAREN)
-           ret = extract_command_subst (string, &si, SX_NOALLOC);
+           ret = extract_command_subst (string, &si, SX_NOALLOC|(flags&SX_COMPLETE));
          else
            ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, SX_NOALLOC);
 
@@ -1100,7 +1100,7 @@ string_extract_verbatim (string, slen, sindex, charlist, flags)
   char *temp;
   DECLARE_MBSTATE;
 
-  if (charlist[0] == '\'' && charlist[1] == '\0')
+  if ((flags & SX_NOCTLESC) && charlist[0] == '\'' && charlist[1] == '\0')
     {
       temp = string_extract_single_quoted (string, sindex);
       --*sindex;       /* leave *sindex at separator character */
@@ -1191,12 +1191,15 @@ extract_command_subst (string, sindex, xflags)
      int *sindex;
      int xflags;
 {
-  if (string[*sindex] == LPAREN)
+  char *ret;
+
+  if (string[*sindex] == LPAREN || (xflags & SX_COMPLETE))
     return (extract_delimited_string (string, sindex, "$(", "(", ")", xflags|SX_COMMAND)); /*)*/
   else
     {
       xflags |= (no_longjmp_on_fatal_error ? SX_NOLONGJMP : 0);
-      return (xparse_dolparen (string, string+*sindex, sindex, xflags));
+      ret = xparse_dolparen (string, string+*sindex, sindex, xflags);
+      return ret;
     }
 }
 
@@ -1290,6 +1293,15 @@ extract_delimited_string (string, sindex, opener, alt_opener, closer, flags)
     {
       c = string[i];
 
+      /* If a recursive call or a call to ADVANCE_CHAR leaves the index beyond
+        the end of the string, catch it and cut the loop. */
+      if (i > slen)
+       {
+         i = slen;
+         c = string[i = slen];
+         break;
+       }
+
       if (c == 0)
        break;
 
@@ -1376,7 +1388,7 @@ extract_delimited_string (string, sindex, opener, alt_opener, closer, flags)
        {
          si = i + 1;
          i = (c == '\'') ? skip_single_quoted (string, slen, si)
-                         : skip_double_quoted (string, slen, si);
+                         : skip_double_quoted (string, slen, si, 0);
          continue;
        }
 
@@ -1502,7 +1514,7 @@ extract_dollar_brace_string (string, sindex, quoted, flags)
       if (c == '"')
        {
          si = i + 1;
-         i = skip_double_quoted (string, slen, si);
+         i = skip_double_quoted (string, slen, si, 0);
          /* skip_XXX_quoted leaves index one past close quote */
          continue;
        }
@@ -1689,7 +1701,7 @@ skip_matched_pair (string, start, open, close, flags)
       else if ((flags & 1) == 0 && (c == '\'' || c == '"'))
        {
          i = (c == '\'') ? skip_single_quoted (ss, slen, ++i)
-                         : skip_double_quoted (ss, slen, ++i);
+                         : skip_double_quoted (ss, slen, ++i, 0);
          /* no increment, the skip functions increment past the closing quote. */
        }
       else if ((flags&1) == 0 && c == '$' && (string[i+1] == LPAREN || string[i+1] == LBRACE))
@@ -1738,7 +1750,8 @@ skip_to_delim (string, start, delims, flags)
      char *delims;
      int flags;
 {
-  int i, pass_next, backq, si, c, invert, skipquote, skipcmd, noprocsub;
+  int i, pass_next, backq, dquote, si, c;
+  int invert, skipquote, skipcmd, noprocsub, completeflag, histexp;
   size_t slen;
   char *temp, open[3];
   DECLARE_MBSTATE;
@@ -1749,9 +1762,11 @@ skip_to_delim (string, start, delims, flags)
   invert = (flags & SD_INVERT);
   skipcmd = (flags & SD_NOSKIPCMD) == 0;
   noprocsub = (flags & SD_NOPROCSUB);
+  histexp = (flags & SD_HISTEXP);
+  completeflag = (flags & SD_COMPLETE) ? SX_COMPLETE : 0;
 
   i = start;
-  pass_next = backq = 0;
+  pass_next = backq = dquote = 0;
   while (c = string[i])
     {
       /* If this is non-zero, we should not let quote characters be delimiters
@@ -1788,12 +1803,24 @@ skip_to_delim (string, start, delims, flags)
        }
       else if (skipquote == 0 && invert == 0 && member (c, delims))
        break;
-      else if (c == '\'' || c == '"')
+      /* the usual case is to use skip_xxx_quoted, but we don't skip over double
+        quoted strings when looking for the history expansion character as a
+        delimiter. */
+      else if (histexp && dquote && c == '\'')
+        {
+          i++;
+          continue;
+        }
+      else if (c == '\'')
+       i = skip_single_quoted (string, slen, ++i);
+      else if (histexp && c == '"')
        {
-         i = (c == '\'') ? skip_single_quoted (string, slen, ++i)
-                         : skip_double_quoted (string, slen, ++i);
-         /* no increment, the skip functions increment past the closing quote. */
-       }
+         dquote = 1 - dquote;
+         i++;
+         continue;
+       }     
+      else if (c == '"')
+       i = skip_double_quoted (string, slen, ++i, completeflag);
       else if (c == '$' && ((skipcmd && string[i+1] == LPAREN) || string[i+1] == LBRACE))
        {
          si = i + 2;
@@ -1913,7 +1940,7 @@ char_is_quoted (string, eindex)
       else if (c == '\'' || c == '"')
        {
          i = (c == '\'') ? skip_single_quoted (string, slen, ++i)
-                         : skip_double_quoted (string, slen, ++i);
+                         : skip_double_quoted (string, slen, ++i, SX_COMPLETE);
          if (i > eindex)
            CQ_RETURN(1);
          /* no increment, the skip_xxx functions go one past end */
@@ -1962,7 +1989,7 @@ unclosed_pair (string, eindex, openstr)
       else if (string[i] == '\'' || string[i] == '"')
        {
          i = (string[i] == '\'') ? skip_single_quoted (string, slen, i)
-                                 : skip_double_quoted (string, slen, i);
+                                 : skip_double_quoted (string, slen, i, SX_COMPLETE);
          if (i > eindex)
            return 0;
        }
@@ -2725,7 +2752,7 @@ list_string_with_quotes (string)
       else if (c == '\'')
        i = skip_single_quoted (s, s_len, ++i);
       else if (c == '"')
-       i = skip_double_quoted (s, s_len, ++i);
+       i = skip_double_quoted (s, s_len, ++i, 0);
       else if (c == 0 || spctabnl (c))
        {
          /* We have found the end of a token.  Make a word out of it and
@@ -2777,6 +2804,8 @@ do_compound_assignment (name, value, flags)
         v = make_local_array_variable (name, 0);
       if (v)
        assign_compound_array_list (v, list, flags);
+      if (list)
+       dispose_words (list);
     }
   /* In a function but forcing assignment in global context */
   else if (mkglobal && variable_context)
@@ -2793,6 +2822,8 @@ do_compound_assignment (name, value, flags)
        v = convert_var_to_array (v);
       if (v)
        assign_compound_array_list (v, list, flags);
+      if (list)
+       dispose_words (list);
     }
   else
     v = assign_array_from_string (name, value, flags);
@@ -5453,6 +5484,7 @@ process_substitute (string, open_for_read_in_child)
       free_pushed_string_input ();
       /* Cancel traps, in trap.c. */
       restore_original_signals ();     /* XXX - what about special builtins? bash-4.2 */
+      QUIT;    /* catch any interrupts we got post-fork */
       setup_async_signals ();
       subshell_environment |= SUBSHELL_COMSUB|SUBSHELL_PROCSUB;
     }
@@ -5766,6 +5798,7 @@ command_substitute (string, quoted)
         trap strings.  Set a flag noting that we have to free the
         trap strings if we run trap to change a signal disposition. */
       reset_signal_handlers ();
+      QUIT;    /* catch any interrupts we got post-fork */
       subshell_environment |= SUBSHELL_RESETTRAP;
     }
 
@@ -5843,8 +5876,8 @@ command_substitute (string, quoted)
         {
           builtin_ignoring_errexit = 0;
          change_flag ('e', FLAG_OFF);
-         set_shellopts ();
         }
+      set_shellopts ();
 
       /* If we are expanding a redirection, we can dispose of any temporary
         environment we received, since redirections are not supposed to have
@@ -6156,7 +6189,6 @@ parameter_brace_expand_word (name, var_is_special, quoted, pflags, indp)
   else if (valid_array_reference (name, 0))
     {
 expand_arrayref:
-      /* XXX - does this leak if name[@] or name[*]? */
       if (pflags & PF_ASSIGNRHS)
        {
          var = array_variable_part (name, &tt, (int *)0);
@@ -6309,6 +6341,16 @@ parameter_brace_expand_indir (name, var_is_special, quoted, quoted_dollar_atp, c
   if (t == 0)
     return (WORD_DESC *)NULL;
 
+  if (valid_brace_expansion_word (t, SPECIAL_VAR (t, 0)) == 0)
+    {
+      report_error (_("%s: bad substitution"), t);
+      free (t);
+      w = alloc_word_desc ();
+      w->word = &expand_param_error;
+      w->flags = 0;
+      return (w);
+    }
+       
   w = parameter_brace_expand_word (t, SPECIAL_VAR(t, 0), quoted, 0, 0);
   free (t);
 
@@ -6802,7 +6844,15 @@ get_var_and_type (varname, value, ind, quoted, flags, varp, valp)
     /* XXX - what if vname == 0 || *vname == 0 ? */
   else
     vname = varname;
-    
+
+  if (vname == 0)
+    {
+      vtype = VT_VARIABLE;
+      *varp = (SHELL_VAR *)NULL;
+      *valp = (char *)NULL;
+      return (vtype);
+    }
+
   /* This sets vtype to VT_VARIABLE or VT_POSPARMS */
   vtype = (vname[0] == '@' || vname[0] == '*') && vname[1] == '\0';
   if (vtype == VT_POSPARMS && vname[0] == '*')
@@ -7083,8 +7133,12 @@ pat_subst (string, pat, rep, mflags)
     }
   else if (*string == 0 && (match_pattern (string, pat, mtype, &s, &e) != 0))
     {
-      ret = (char *)xmalloc (STRLEN (rep) + 1);
-      strcpy (ret, rep);
+      replen = STRLEN (rep);
+      ret = (char *)xmalloc (replen + 1);
+      if (replen == 0)
+       ret[0] = '\0';
+      else
+       strcpy (ret, rep);
       return (ret);
     }
 
@@ -7097,22 +7151,22 @@ pat_subst (string, pat, rep, mflags)
        break;
       l = s - str;
 
-      if (rxpand)
+      if (rep && rxpand)
         {
-          int x;
-          mlen = e - s;
-          mstr = xmalloc (mlen + 1);
+         int x;
+         mlen = e - s;
+         mstr = xmalloc (mlen + 1);
          for (x = 0; x < mlen; x++)
            mstr[x] = s[x];
-          mstr[mlen] = '\0';
-          rstr = strcreplace (rep, '&', mstr, 0);
-          rslen = strlen (rstr);
+         mstr[mlen] = '\0';
+         rstr = strcreplace (rep, '&', mstr, 0);
+         rslen = strlen (rstr);
         }
       else
-        {
-          rstr = rep;
-          rslen = replen;
-        }
+       {
+         rstr = rep;
+         rslen = replen;
+       }
         
       RESIZE_MALLOCED_BUFFER (ret, rptr, (l + rslen), rsize, 64);
 
@@ -7533,7 +7587,7 @@ chk_arithsub (s, len)
          break;
 
        case '"':
-         i = skip_double_quoted ((char *)s, len, ++i);
+         i = skip_double_quoted ((char *)s, len, ++i, 0);
          break;
        }
     }
@@ -7592,6 +7646,13 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
       name[1] = '\0';
       t_index++;
     }
+  else if (*name == '!' && t_index > sindex && string[t_index] == '@' && string[t_index+1] == '}')
+    {
+      name = (char *)xrealloc (name, t_index - sindex + 2);
+      name[t_index - sindex] = '@';
+      name[t_index - sindex + 1] = '\0';
+      t_index++;
+    }
 
   ret = 0;
   tflag = 0;
@@ -7668,7 +7729,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
        member (c, "%:=+/") && string[sindex] == RBRACE)
     {
       temp = (char *)NULL;
-      goto bad_substitution;
+      goto bad_substitution;   /* XXX - substitution error */
     }
 
   /* Indirect expansion begins with a `!'.  A valid indirect expansion is
@@ -7695,7 +7756,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
       if (string[sindex - 1] != RBRACE || (valid_length_expression (name) == 0))
        {
          temp = (char *)NULL;
-         goto bad_substitution;
+         goto bad_substitution;        /* substitution error */
        }
 
       number = parameter_brace_expand_length (name);
@@ -7810,12 +7871,17 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
                                        var_is_special) == 0)
     {
       temp = (char *)NULL;
-      goto bad_substitution;
+      goto bad_substitution;           /* substitution error */
     }
 
   if (want_indir)
     {
       tdesc = parameter_brace_expand_indir (name + 1, var_is_special, quoted, quoted_dollar_atp, contains_dollar_at);
+      if (tdesc == &expand_wdesc_error || tdesc == &expand_wdesc_fatal)
+       {
+         temp = (char *)NULL;
+         goto bad_substitution;
+       }
       /* Turn off the W_ARRAYIND flag because there is no way for this function
         to return the index we're supposed to be using. */
       if (tdesc && tdesc->flags)
@@ -7869,7 +7935,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
       if (string[sindex] == RBRACE)
        sindex++;
       else
-       goto bad_substitution;
+       goto bad_substitution;          /* substitution error */
     }
   else
     value = (char *)NULL;
@@ -7964,25 +8030,31 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
     {
     default:
     case '\0':
-    bad_substitution:
+bad_substitution:
       last_command_exit_value = EXECUTION_FAILURE;
       report_error (_("%s: bad substitution"), string ? string : "??");
       FREE (value);
       FREE (temp);
       free (name);
-      return &expand_wdesc_error;
+      if (shell_compatibility_level <= 43)
+       return &expand_wdesc_error;
+      else
+       return ((posixly_correct && interactive_shell == 0) ? &expand_wdesc_fatal : &expand_wdesc_error);
 
     case RBRACE:
       break;
 
     case '@':
       temp1 = parameter_brace_transform (name, temp, ind, value, c, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0);
-      if (temp1 == &expand_param_error || temp1 == &expand_param_fatal)
-       goto bad_substitution;
-
       free (temp);
       free (value);
       free (name);
+      if (temp1 == &expand_param_error || temp1 == &expand_param_fatal)
+       {
+         last_command_exit_value = EXECUTION_FAILURE;
+         report_error (_("%s: bad substitution"), string ? string : "??");
+         return (temp1 == &expand_param_error ? &expand_wdesc_error : &expand_wdesc_fatal);
+       }
 
       ret = alloc_word_desc ();
       ret->word = temp1;
@@ -9997,7 +10069,7 @@ make_internal_declare (word, option)
      char *word;
      char *option;
 {
-  int t;
+  int t, r;
   WORD_LIST *wl;
   WORD_DESC *w;
 
@@ -10009,7 +10081,10 @@ make_internal_declare (word, option)
   wl = make_word_list (w, (WORD_LIST *)NULL);
   wl = make_word_list (make_word (option), wl);
 
-  return (declare_builtin (wl));  
+  r = declare_builtin (wl);
+
+  dispose_words (wl);
+  return r;
 }  
 #endif
 
@@ -10103,7 +10178,14 @@ shell_expand_word_list (tlist, eflags)
 
          opts[opti] = '\0';
          if (opti > 0)
-           make_internal_declare (tlist->word->word, opts);
+           {
+             t = make_internal_declare (tlist->word->word, opts);
+             if (t != EXECUTION_SUCCESS)
+               {
+                 last_command_exit_value = t;
+                 exp_jump_to_top_level (DISCARD);
+               }
+           }
 
          t = do_word_assignment (tlist->word, 0);
          if (t == 0)