Bash-4.3 distribution sources and documentation
[platform/upstream/bash.git] / lib / readline / histexpand.c
index 4de8c0a..4899b9d 100644 (file)
@@ -1,6 +1,6 @@
 /* histexpand.c -- history expansion. */
 
-/* Copyright (C) 1989-2009 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2012 Free Software Foundation, Inc.
 
    This file contains the GNU History Library (History), a set of
    routines for managing the text of previously typed lines.
@@ -245,7 +245,7 @@ get_history_event (string, caller_index, delimiting_quote)
 
 #define FAIL_SEARCH() \
   do { \
-    history_offset = history_length; free (temp) ; return (char *)NULL; \
+    history_offset = history_length; xfree (temp) ; return (char *)NULL; \
   } while (0)
 
   /* If there is no search string, try to use the previous search string,
@@ -254,7 +254,7 @@ get_history_event (string, caller_index, delimiting_quote)
     {
       if (search_string)
         {
-          free (temp);
+          xfree (temp);
           temp = savestring (search_string);
         }
       else
@@ -272,6 +272,8 @@ get_history_event (string, caller_index, delimiting_quote)
       if (local_index == 0 || substring_okay)
        {
          entry = current_history ();
+         if (entry == 0)
+           FAIL_SEARCH ();
          history_offset = history_length;
        
          /* If this was a substring search, then remember the
@@ -285,7 +287,7 @@ get_history_event (string, caller_index, delimiting_quote)
              search_match = history_find_word (entry->line, local_index);
            }
          else
-           free (temp);
+           xfree (temp);
 
          return (entry->line);
        }
@@ -305,16 +307,20 @@ get_history_event (string, caller_index, delimiting_quote)
 /* Extract the contents of STRING as if it is enclosed in single quotes.
    SINDEX, when passed in, is the offset of the character immediately
    following the opening single quote; on exit, SINDEX is left pointing
-   to the closing single quote. */
+   to the closing single quote.  FLAGS currently used to allow backslash
+   to escape a single quote (e.g., for bash $'...'). */
 static void
-hist_string_extract_single_quoted (string, sindex)
+hist_string_extract_single_quoted (string, sindex, flags)
      char *string;
-     int *sindex;
+     int *sindex, flags;
 {
   register int i;
 
   for (i = *sindex; string[i] && string[i] != '\''; i++)
-    ;
+    {
+      if ((flags & 1) && string[i] == '\\' && string[i+1])
+        i++;
+    }
 
   *sindex = i;
 }
@@ -504,7 +510,7 @@ postproc_subst_rhs ()
        }
     }
   new[j] = '\0';
-  free (subst_rhs);
+  xfree (subst_rhs);
   subst_rhs = new;
   subst_rhs_len = j;
 }
@@ -515,9 +521,9 @@ postproc_subst_rhs ()
    the returned string.  Returns the new index into string in
    *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */
 static int
-history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
+history_expand_internal (string, start, qc, end_index_ptr, ret_string, current_line)
      char *string;
-     int start, *end_index_ptr;
+     int start, qc, *end_index_ptr;
      char **ret_string;
      char *current_line;       /* for !# */
 {
@@ -553,35 +559,12 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
       event = current_line;
     }
   else
-    {
-      int quoted_search_delimiter = 0;
-
-      /* If the character before this `!' is a double or single
-        quote, then this expansion takes place inside of the
-        quoted string.  If we have to search for some text ("!foo"),
-        allow the delimiter to end the search string. */
-#if defined (HANDLE_MULTIBYTE)
-      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
-       {
-         int ch, l;
-         l = _rl_find_prev_mbchar (string, i, MB_FIND_ANY);
-         ch = string[l];
-         /* XXX - original patch had i - 1 ???  If i == 0 it would fail. */
-         if (i && (ch == '\'' || ch == '"'))
-           quoted_search_delimiter = ch;
-       }
-      else
-#endif /* HANDLE_MULTIBYTE */    
-       if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
-         quoted_search_delimiter = string[i - 1];
-
-      event = get_history_event (string, &i, quoted_search_delimiter);
-    }
+    event = get_history_event (string, &i, qc);
          
   if (event == 0)
     {
       *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND);
-      free (result);
+      xfree (result);
       return (-1);
     }
 
@@ -595,7 +578,7 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
   if (word_spec == (char *)&error_pointer)
     {
       *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC);
-      free (result);
+      xfree (result);
       return (-1);
     }
 
@@ -628,8 +611,8 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
        {
        default:
          *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER);
-         free (result);
-         free (temp);
+         xfree (result);
+         xfree (temp);
          return -1;
 
        case 'q':
@@ -654,7 +637,7 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
            {
              tstr++;
              t = savestring (tstr);
-             free (temp);
+             xfree (temp);
              temp = t;
            }
          break;
@@ -679,7 +662,7 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
          if (tstr)
            {
              t = savestring (tstr);
-             free (temp);
+             xfree (temp);
              temp = t;
            }
          break;
@@ -755,8 +738,8 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
            if (subst_lhs_len == 0)
              {
                *ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST);
-               free (result);
-               free (temp);
+               xfree (result);
+               xfree (temp);
                return -1;
              }
 
@@ -765,8 +748,8 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
            if (subst_lhs_len > l_temp)
              {
                *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
-               free (result);
-               free (temp);
+               xfree (result);
+               xfree (temp);
                return (-1);
              }
 
@@ -807,7 +790,7 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
                             temp + si + subst_lhs_len,
                             l_temp - (si + subst_lhs_len));
                    new_event[len] = '\0';
-                   free (temp);
+                   xfree (temp);
                    temp = new_event;
 
                    failed = 0;
@@ -843,14 +826,14 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
              continue;         /* don't want to increment i */
 
            *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
-           free (result);
-           free (temp);
+           xfree (result);
+           xfree (temp);
            return (-1);
          }
        }
       i += 2;
     }
-  /* Done with modfiers. */
+  /* Done with modifiers. */
   /* Believe it or not, we have to back the pointer up by one. */
   --i;
 
@@ -865,7 +848,7 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
       else
        x = savestring (temp);
 
-      free (temp);
+      xfree (temp);
       temp = x;
     }
 
@@ -873,7 +856,7 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
   if (n >= result_len)
     result = (char *)xrealloc (result, n + 2);
   strcpy (result, temp);
-  free (temp);
+  xfree (temp);
 
   *end_index_ptr = i;
   *ret_string = result;
@@ -924,7 +907,7 @@ history_expand (hstring, output)
      char **output;
 {
   register int j;
-  int i, r, l, passc, cc, modified, eindex, only_printing, dquote;
+  int i, r, l, passc, cc, modified, eindex, only_printing, dquote, squote, flag;
   char *string;
 
   /* The output string, and its length. */
@@ -987,7 +970,7 @@ history_expand (hstring, output)
 
       /* `!' followed by one of the characters in history_no_expand_chars
         is NOT an expansion. */
-      for (i = dquote = 0; string[i]; i++)
+      for (i = dquote = squote = 0; string[i]; i++)
        {
 #if defined (HANDLE_MULTIBYTE)
          if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
@@ -1016,7 +999,14 @@ history_expand (hstring, output)
            }
          else if (string[i] == history_expansion_char)
            {
-             if (!cc || member (cc, history_no_expand_chars))
+             if (cc == 0 || member (cc, history_no_expand_chars))
+               continue;
+             /* DQUOTE won't be set unless history_quotes_inhibit_expansion
+                is set.  The idea here is to treat double-quoted strings the
+                same as the word outside double quotes; in effect making the
+                double quote part of history_no_expand_chars when DQUOTE is
+                set. */
+             else if (dquote && cc == '"')
                continue;
              /* If the calling application has set
                 history_inhibit_expansion_function to a function that checks
@@ -1044,8 +1034,9 @@ history_expand (hstring, output)
          else if (dquote == 0 && history_quotes_inhibit_expansion && string[i] == '\'')
            {
              /* If this is bash, single quotes inhibit history expansion. */
+             flag = (i > 0 && string[i - 1] == '$');
              i++;
-             hist_string_extract_single_quoted (string, &i);
+             hist_string_extract_single_quoted (string, &i, flag);
            }
          else if (history_quotes_inhibit_expansion && string[i] == '\\')
            {
@@ -1059,16 +1050,16 @@ history_expand (hstring, output)
          
       if (string[i] != history_expansion_char)
        {
-         free (result);
+         xfree (result);
          *output = savestring (string);
          return (0);
        }
     }
 
   /* Extract and perform the substitution. */
-  for (passc = dquote = i = j = 0; i < l; i++)
+  for (passc = dquote = squote = i = j = 0; i < l; i++)
     {
-      int tchar = string[i];
+      int qc, tchar = string[i];
 
       if (passc)
        {
@@ -1096,7 +1087,7 @@ history_expand (hstring, output)
          if (strlen (mb) > 1)
            {
              ADD_STRING (mb);
-             break;
+             continue;
            }
        }
 #endif /* HANDLE_MULTIBYTE */
@@ -1125,20 +1116,32 @@ history_expand (hstring, output)
        case '\'':
          {
            /* If history_quotes_inhibit_expansion is set, single quotes
-              inhibit history expansion. */
-           if (dquote == 0 && history_quotes_inhibit_expansion)
+              inhibit history expansion, otherwise they are treated like
+              double quotes. */
+           if (squote)
+             {
+               squote = 0;
+               ADD_CHAR (tchar);
+             }
+           else if (dquote == 0 && history_quotes_inhibit_expansion)
              {
                int quote, slen;
 
+               flag = (i > 0 && string[i - 1] == '$');
                quote = i++;
-               hist_string_extract_single_quoted (string, &i);
+               hist_string_extract_single_quoted (string, &i, flag);
 
                slen = i - quote + 2;
                temp = (char *)xmalloc (slen);
                strncpy (temp, string + quote, slen);
                temp[slen - 1] = '\0';
                ADD_STRING (temp);
-               free (temp);
+               xfree (temp);
+             }
+           else if (dquote == 0 && squote == 0 && history_quotes_inhibit_expansion == 0)
+             {
+               squote = 1;
+               ADD_CHAR (string[i]);
              }
            else
              ADD_CHAR (string[i]);
@@ -1151,7 +1154,7 @@ history_expand (hstring, output)
              temp = (char *)xmalloc (l - i + 1);
              strcpy (temp, string + i);
              ADD_STRING (temp);
-             free (temp);
+             xfree (temp);
              i = l;
            }
          else
@@ -1164,7 +1167,9 @@ history_expand (hstring, output)
          /* If the history_expansion_char is followed by one of the
             characters in history_no_expand_chars, then it is not a
             candidate for expansion of any kind. */
-         if (member (cc, history_no_expand_chars))
+         if (cc == 0 || member (cc, history_no_expand_chars) ||
+                        (dquote && cc == '"') ||
+                        (history_inhibit_expansion_function && (*history_inhibit_expansion_function) (string, i)))
            {
              ADD_CHAR (string[i]);
              break;
@@ -1183,20 +1188,20 @@ history_expand (hstring, output)
                  temp = (char *)xmalloc (1 + strlen (result));
                  strcpy (temp, result);
                  ADD_STRING (temp);
-                 free (temp);
+                 xfree (temp);
                }
              i++;
              break;
            }
 #endif
-
-         r = history_expand_internal (string, i, &eindex, &temp, result);
+         qc = squote ? '\'' : (dquote ? '"' : 0);
+         r = history_expand_internal (string, i, qc, &eindex, &temp, result);
          if (r < 0)
            {
              *output = temp;
-             free (result);
+             xfree (result);
              if (string != hstring)
-               free (string);
+               xfree (string);
              return -1;
            }
          else
@@ -1206,7 +1211,7 @@ history_expand (hstring, output)
                  modified++;
                  if (*temp)
                    ADD_STRING (temp);
-                 free (temp);
+                 xfree (temp);
                }
              only_printing = r == 1;
              i = eindex;
@@ -1217,7 +1222,7 @@ history_expand (hstring, output)
 
   *output = result;
   if (string != hstring)
-    free (string);
+    xfree (string);
 
   if (only_printing)
     {
@@ -1398,8 +1403,8 @@ history_arg_extract (first, last, string)
     }
 
   for (i = 0; i < len; i++)
-    free (list[i]);
-  free (list);
+    xfree (list[i]);
+  xfree (list);
 
   return (result);
 }
@@ -1410,10 +1415,10 @@ history_tokenize_word (string, ind)
      int ind;
 {
   register int i;
-  int delimiter;
+  int delimiter, nestdelim, delimopen;
 
   i = ind;
-  delimiter = 0;
+  delimiter = nestdelim = 0;
 
   if (member (string[i], "()\n"))
     {
@@ -1434,17 +1439,29 @@ history_tokenize_word (string, ind)
          i += 2;
          return i;
        }
-      else
+      else if ((peek == '&' && (string[i] == '>' || string[i] == '<')) ||
+               (peek == '>' && string[i] == '&'))
        {
-         if ((peek == '&' && (string[i] == '>' || string[i] == '<')) ||
-             (peek == '>' && string[i] == '&') ||
-             (peek == '(' && (string[i] == '>' || string[i] == '<')) || /* ) */
-             (peek == '(' && string[i] == '$')) /* ) */
-           {
-             i += 2;
-             return i;
-           }
+         i += 2;
+         return i;
+       }
+      /* XXX - separated out for later -- bash-4.2 */
+      else if ((peek == '(' && (string[i] == '>' || string[i] == '<')) || /* ) */
+              (peek == '(' && string[i] == '$')) /*)*/
+       {
+         i += 2;
+         delimopen = '(';
+         delimiter = ')';
+         nestdelim = 1;
+         goto get_word;
        }
+#if 0
+      else if (peek == '\'' && string[i] == '$')
+        {
+         i += 2;       /* XXX */
+         return i;
+        }
+#endif
 
       if (string[i] != '$')
        {
@@ -1453,9 +1470,25 @@ history_tokenize_word (string, ind)
        }
     }
 
+  /* same code also used for $(...)/<(...)/>(...) above */
+  if (member (string[i], "!@?+*"))
+    {
+      int peek = string[i + 1];
+
+      if (peek == '(')         /*)*/
+       {
+         /* Shell extended globbing patterns */
+         i += 2;
+         delimopen = '(';
+         delimiter = ')';      /* XXX - not perfect */
+         nestdelim = 1;
+       }
+    }
+
+get_word:
   /* Get word from string + i; */
 
-  if (member (string[i], HISTORY_QUOTE_CHARACTERS))
+  if (delimiter == 0 && member (string[i], HISTORY_QUOTE_CHARACTERS))
     delimiter = string[i++];
 
   for (; string[i]; i++)
@@ -1473,16 +1506,31 @@ history_tokenize_word (string, ind)
          continue;
        }
 
+      /* delimiter must be set and set to something other than a quote if
+        nestdelim is set, so these tests are safe. */
+      if (nestdelim && string[i] == delimopen)
+       {
+         nestdelim++;
+         continue;
+       }
+      if (nestdelim && string[i] == delimiter)
+       {
+         nestdelim--;
+         if (nestdelim == 0)
+           delimiter = 0;
+         continue;
+       }
+      
       if (delimiter && string[i] == delimiter)
        {
          delimiter = 0;
          continue;
        }
 
-      if (!delimiter && (member (string[i], history_word_delimiters)))
+      if (delimiter == 0 && (member (string[i], history_word_delimiters)))
        break;
 
-      if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS))
+      if (delimiter == 0 && member (string[i], HISTORY_QUOTE_CHARACTERS))
        delimiter = string[i];
     }
 
@@ -1579,7 +1627,7 @@ freewords (words, start)
   register int i;
 
   for (i = start; words[i]; i++)
-    free (words[i]);
+    xfree (words[i]);
 }
 
 /* Find and return the word which contains the character at index IND
@@ -1603,8 +1651,8 @@ history_find_word (line, ind)
     }
   s = words[wind];
   for (i = 0; i < wind; i++)
-    free (words[i]);
+    xfree (words[i]);
   freewords (words, wind + 1);
-  free (words);
+  xfree (words);
   return s;
 }