]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - lib/readline/complete.c
Bash-4.2 distribution sources and documentation
[thirdparty/bash.git] / lib / readline / complete.c
index bda22048a22e30b29b9e50b32a3d9f27fcd98bed..e67cfeb3b7fbc52d9d41e268a5058c6de2c91149 100644 (file)
@@ -1,6 +1,6 @@
 /* complete.c -- filename completion for readline. */
 
-/* Copyright (C) 1987-2009 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2011 Free Software Foundation, Inc.
 
    This file is part of the GNU Readline Library (Readline), a library
    for reading lines of text with interactive input and history editing.
@@ -119,9 +119,11 @@ static char **remove_duplicate_matches PARAMS((char **));
 static void insert_match PARAMS((char *, int, int, char *));
 static int append_to_match PARAMS((char *, int, int, int));
 static void insert_all_matches PARAMS((char **, int, char *));
+static int complete_fncmp PARAMS((const char *, int, const char *, int));
 static void display_matches PARAMS((char **));
 static int compute_lcd_of_matches PARAMS((char **, int, const char *));
 static int postprocess_matches PARAMS((char ***, int));
+static int complete_get_screenwidth PARAMS((void));
 
 static char *make_quoted_replacement PARAMS((char *, int, char *));
 
@@ -157,10 +159,14 @@ int _rl_print_completions_horizontally;
 #if defined (__MSDOS__) && !defined (__DJGPP__)
 int _rl_completion_case_fold = 1;
 #else
-int _rl_completion_case_fold;
+int _rl_completion_case_fold = 0;
 #endif
 
-/* If non-zero, don't match hidden files (filenames beginning with a `.' on
+/* Non-zero means that `-' and `_' are equivalent when comparing filenames
+  for completion. */
+int _rl_completion_case_map = 0;
+
+/* If zero, don't match hidden files (filenames beginning with a `.' on
    Unix) when doing filename completion. */
 int _rl_match_hidden_files = 1;
 
@@ -170,6 +176,10 @@ int _rl_match_hidden_files = 1;
    display prefix replaced with an ellipsis. */
 int _rl_completion_prefix_display_length = 0;
 
+/* The readline-private number of screen columns to use when displaying
+   matches.  If < 0 or > _rl_screenwidth, it is ignored. */
+int _rl_completion_columns = -1;
+
 /* Global variables available to applications using readline. */
 
 #if defined (VISIBLE_STATS)
@@ -185,6 +195,10 @@ int rl_visible_stats = 0;
    after the `e' in `Makefile' won't result in `Makefilefile'. */
 int _rl_skip_completed_text = 0;
 
+/* If non-zero, menu completion displays the common prefix first in the
+   cycle of possible completions instead of the last. */
+int _rl_menu_complete_prefix_first = 0;
+
 /* If non-zero, then this is the address of a function to call when
    completing on a directory name.  The function is called with
    the address of a string (the current directory name) as an arg. */
@@ -467,6 +481,14 @@ get_y_or_n (for_pager)
 {
   int c;
 
+  /* For now, disable pager in callback mode, until we later convert to state
+     driven functions.  Have to wait until next major version to add new
+     state definition, since it will change value of RL_STATE_DONE. */
+#if defined (READLINE_CALLBACKS)
+  if (RL_ISSTATE (RL_STATE_CALLBACK))
+    return 1;
+#endif
+
   for (;;)
     {
       RL_SETSTATE(RL_STATE_MOREINPUT);
@@ -829,7 +851,7 @@ print_filename (to_print, full_pathname, prefix_bytes)
          if (path_isdir (new_full_pathname))
            extension_char = '/';
 
-         free (new_full_pathname);
+         xfree (new_full_pathname);
          to_print[-1] = c;
        }
       else
@@ -844,7 +866,7 @@ print_filename (to_print, full_pathname, prefix_bytes)
              extension_char = '/';
        }
 
-      free (s);
+      xfree (s);
       if (extension_char)
        {
          putc (extension_char, rl_outstream);
@@ -1081,7 +1103,7 @@ remove_duplicate_matches (matches)
     {
       if (strcmp (matches[i], matches[i + 1]) == 0)
        {
-         free (matches[i]);
+         xfree (matches[i]);
          matches[i] = (char *)&dead_slot;
        }
       else
@@ -1099,7 +1121,7 @@ remove_duplicate_matches (matches)
   temp_array[j] = (char *)NULL;
 
   if (matches[0] != (char *)&dead_slot)
-    free (matches[0]);
+    xfree (matches[0]);
 
   /* Place the lowest common denominator back in [0]. */
   temp_array[0] = lowest_common;
@@ -1109,7 +1131,7 @@ remove_duplicate_matches (matches)
      insert. */
   if (j == 2 && strcmp (temp_array[0], temp_array[1]) == 0)
     {
-      free (temp_array[1]);
+      xfree (temp_array[1]);
       temp_array[1] = (char *)NULL;
     }
   return (temp_array);
@@ -1288,7 +1310,7 @@ postprocess_matches (matchesp, matching_filenames)
   if (rl_ignore_completion_duplicates)
     {
       temp_matches = remove_duplicate_matches (matches);
-      free (matches);
+      xfree (matches);
       matches = temp_matches;
     }
 
@@ -1325,6 +1347,23 @@ postprocess_matches (matchesp, matching_filenames)
   return (1);
 }
 
+static int
+complete_get_screenwidth ()
+{
+  int cols;
+  char *envcols;
+
+  cols = _rl_completion_columns;
+  if (cols >= 0 && cols <= _rl_screenwidth)
+    return cols;
+  envcols = getenv ("COLUMNS");
+  if (envcols && *envcols)
+    cols = atoi (envcols);
+  if (cols >= 0 && cols <= _rl_screenwidth)
+    return cols;
+  return _rl_screenwidth;
+}
+
 /* A convenience function for displaying a list of strings in
    columnar format on readline's output stream.  MATCHES is the list
    of strings, in argv format, LEN is the number of strings in MATCHES,
@@ -1334,7 +1373,7 @@ rl_display_match_list (matches, len, max)
      char **matches;
      int len, max;
 {
-  int count, limit, printed_len, lines;
+  int count, limit, printed_len, lines, cols;
   int i, j, k, l, common_length, sind;
   char *temp, *t;
 
@@ -1355,12 +1394,17 @@ rl_display_match_list (matches, len, max)
     }
 
   /* How many items of MAX length can we fit in the screen window? */
+  cols = complete_get_screenwidth ();
   max += 2;
-  limit = _rl_screenwidth / max;
-  if (limit != 1 && (limit * max == _rl_screenwidth))
+  limit = cols / max;
+  if (limit != 1 && (limit * max == cols))
     limit--;
 
-  /* Avoid a possible floating exception.  If max > _rl_screenwidth,
+  /* If cols == 0, limit will end up -1 */
+  if (cols < _rl_screenwidth && limit < 0)
+    limit = 1;
+
+  /* Avoid a possible floating exception.  If max > cols,
      limit will be 0 and a divide-by-zero fault will result. */
   if (limit == 0)
     limit = 1;
@@ -1608,7 +1652,7 @@ insert_match (match, start, mtype, qc)
       else
        _rl_replace_text (replacement, start, end);
       if (replacement != match)
-        free (replacement);
+        xfree (replacement);
     }
 }
 
@@ -1675,7 +1719,7 @@ append_to_match (text, delimiter, quote_char, nontrivial_match)
          if (rl_point == rl_end && temp_string_index)
            rl_insert_text (temp_string);
        }
-      free (filename);
+      xfree (filename);
     }
   else
     {
@@ -1711,7 +1755,7 @@ insert_all_matches (matches, point, qc)
          rl_insert_text (rp);
          rl_insert_text (" ");
          if (rp != matches[i])
-           free (rp);
+           xfree (rp);
        }
     }
   else
@@ -1720,7 +1764,7 @@ insert_all_matches (matches, point, qc)
       rl_insert_text (rp);
       rl_insert_text (" ");
       if (rp != matches[0])
-       free (rp);
+       xfree (rp);
     }
   rl_end_undo_group ();
 }
@@ -1735,8 +1779,8 @@ _rl_free_match_list (matches)
     return;
 
   for (i = 0; matches[i]; i++)
-    free (matches[i]);
-  free (matches);
+    xfree (matches[i]);
+  xfree (matches);
 }
 
 /* Complete the word at or before point.
@@ -1757,6 +1801,9 @@ rl_complete_internal (what_to_do)
   int start, end, delimiter, found_quote, i, nontrivial_lcd;
   char *text, *saved_line_buffer;
   char quote_char;
+#if 1
+  int tlen, mlen;
+#endif
 
   RL_SETSTATE(RL_STATE_COMPLETING);
 
@@ -1784,7 +1831,11 @@ rl_complete_internal (what_to_do)
   /* nontrivial_lcd is set if the common prefix adds something to the word
      being completed. */
   nontrivial_lcd = matches && strcmp (text, matches[0]) != 0;
-  free (text);
+#if 1
+  if (what_to_do == '!' || what_to_do == '@')
+    tlen = strlen (text);
+#endif
+  xfree (text);
 
   if (matches == 0)
     {
@@ -1817,8 +1868,25 @@ rl_complete_internal (what_to_do)
     case '!':
     case '@':
       /* Insert the first match with proper quoting. */
+#if 0
       if (*matches[0])
        insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, &quote_char);
+#else
+      if (what_to_do == TAB)
+        {
+          if (*matches[0])
+           insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, &quote_char);
+        }
+      else if (*matches[0] && matches[1] == 0)
+       /* should we perform the check only if there are multiple matches? */
+       insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, &quote_char);
+      else if (*matches[0])    /* what_to_do != TAB && multiple matches */
+       {
+         mlen = *matches[0] ? strlen (matches[0]) : 0;
+         if (mlen >= tlen)
+           insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, &quote_char);
+       }
+#endif
 
       /* If there are more matches, ring the bell to indicate.
         If we are in vi mode, Posix.2 says to not ring the bell.
@@ -1872,7 +1940,7 @@ rl_complete_internal (what_to_do)
   if (saved_line_buffer)
     {
       completion_changed_buffer = strcmp (rl_line_buffer, saved_line_buffer) != 0;
-      free (saved_line_buffer);
+      xfree (saved_line_buffer);
     }
 
   RL_UNSETSTATE(RL_STATE_COMPLETING);
@@ -1939,7 +2007,7 @@ rl_completion_matches (text, entry_function)
     compute_lcd_of_matches (match_list, matches, text);
   else                         /* There were no matches. */
     {
-      free (match_list);
+      xfree (match_list);
       match_list = (char **)NULL;
     }
   return (match_list);
@@ -2005,6 +2073,62 @@ rl_username_completion_function (text, state)
 #endif /* !__WIN32__ && !__OPENNT */
 }
 
+/* Return non-zero if CONVFN matches FILENAME up to the length of FILENAME
+   (FILENAME_LEN).  If _rl_completion_case_fold is set, compare without
+   regard to the alphabetic case of characters.  CONVFN is the possibly-
+   converted directory entry; FILENAME is what the user typed. */
+static int
+complete_fncmp (convfn, convlen, filename, filename_len)
+     const char *convfn;
+     int convlen;
+     const char *filename;
+     int filename_len;
+{
+  register char *s1, *s2;
+  int d, len;
+
+  /* Otherwise, if these match up to the length of filename, then
+     it is a match. */
+  if (_rl_completion_case_fold && _rl_completion_case_map)
+    {
+      /* Case-insensitive comparison treating _ and - as equivalent */
+      if (filename_len == 0)
+       return 1;
+      if (convlen < filename_len)
+       return 0;
+      s1 = (char *)convfn;
+      s2 = (char *)filename;
+      len = filename_len;
+      do
+       {
+         d = _rl_to_lower (*s1) - _rl_to_lower (*s2);
+         /* *s1 == [-_] && *s2 == [-_] */
+         if ((*s1 == '-' || *s1 == '_') && (*s2 == '-' || *s2 == '_'))
+           d = 0;
+         if (d != 0)
+           return 0;
+         s1++; s2++;   /* already checked convlen >= filename_len */
+       }
+      while (--len != 0);
+      return 1;
+    }
+  else if (_rl_completion_case_fold)
+    {
+      if ((_rl_to_lower (convfn[0]) == _rl_to_lower (filename[0])) &&
+         (convlen >= filename_len) &&
+         (_rl_strnicmp (filename, convfn, filename_len) == 0))
+       return 1;
+    }
+  else
+    {
+      if ((convfn[0] == filename[0]) &&
+         (convlen >= filename_len) &&
+         (strncmp (filename, convfn, filename_len) == 0))
+       return 1;
+    }
+  return 0;
+}
+
 /* Okay, now we write the entry_function for filename completion.  In the
    general case.  Note that completion in the shell is a little different
    because of all the pathnames that must be followed when looking up the
@@ -2071,32 +2195,41 @@ rl_filename_completion_function (text, state)
 
       /* We aren't done yet.  We also support the "~user" syntax. */
 
-      /* Save the version of the directory that the user typed. */
-      users_dirname = savestring (dirname);
+      /* Save the version of the directory that the user typed, dequoting
+        it if necessary. */
+      if (rl_completion_found_quote && rl_filename_dequoting_function)
+       users_dirname = (*rl_filename_dequoting_function) (dirname, rl_completion_quote_character);
+      else
+       users_dirname = savestring (dirname);
 
       if (*dirname == '~')
        {
          temp = tilde_expand (dirname);
-         free (dirname);
+         xfree (dirname);
          dirname = temp;
        }
 
+      /* We have saved the possibly-dequoted version of the directory name
+        the user typed.  Now transform the directory name we're going to
+        pass to opendir(2).  The directory rewrite hook modifies only the
+        directory name; the directory completion hook modifies both the
+        directory name passed to opendir(2) and the version the user
+        typed.  Both the directory completion and rewrite hooks should perform
+        any necessary dequoting.  The hook functions return 1 if they modify
+        the directory name argument.  If either hook returns 0, it should
+        not modify the directory name pointer passed as an argument. */
       if (rl_directory_rewrite_hook)
        (*rl_directory_rewrite_hook) (&dirname);
-
-      /* The directory completion hook should perform any necessary
-        dequoting. */
-      if (rl_directory_completion_hook && (*rl_directory_completion_hook) (&dirname))
+      else if (rl_directory_completion_hook && (*rl_directory_completion_hook) (&dirname))
        {
-         free (users_dirname);
+         xfree (users_dirname);
          users_dirname = savestring (dirname);
        }
       else if (rl_completion_found_quote && rl_filename_dequoting_function)
        {
          /* delete single and double quotes */
-         temp = (*rl_filename_dequoting_function) (users_dirname, rl_completion_quote_character);
-         free (users_dirname);
-         users_dirname = temp;
+         xfree (dirname);
+         dirname = savestring (users_dirname);
        }
       directory = opendir (dirname);
 
@@ -2105,7 +2238,7 @@ rl_filename_completion_function (text, state)
        {
          /* delete single and double quotes */
          temp = (*rl_filename_dequoting_function) (filename, rl_completion_quote_character);
-         free (filename);
+         xfree (filename);
          filename = temp;
        }
       filename_len = strlen (filename);
@@ -2147,22 +2280,8 @@ rl_filename_completion_function (text, state)
        }
       else
        {
-         /* Otherwise, if these match up to the length of filename, then
-            it is a match. */
-         if (_rl_completion_case_fold)
-           {
-             if ((_rl_to_lower (convfn[0]) == _rl_to_lower (filename[0])) &&
-                 (convlen >= filename_len) &&
-                 (_rl_strnicmp (filename, convfn, filename_len) == 0))
-               break;
-           }
-         else
-           {
-             if ((convfn[0] == filename[0]) &&
-                 (convlen >= filename_len) &&
-                 (strncmp (filename, convfn, filename_len) == 0))
-               break;
-           }
+         if (complete_fncmp (convfn, convlen, filename, filename_len))
+           break;
        }
     }
 
@@ -2175,17 +2294,17 @@ rl_filename_completion_function (text, state)
        }
       if (dirname)
        {
-         free (dirname);
+         xfree (dirname);
          dirname = (char *)NULL;
        }
       if (filename)
        {
-         free (filename);
+         xfree (filename);
          filename = (char *)NULL;
        }
       if (users_dirname)
        {
-         free (users_dirname);
+         xfree (users_dirname);
          users_dirname = (char *)NULL;
        }
 
@@ -2225,7 +2344,7 @@ rl_filename_completion_function (text, state)
        temp = savestring (convfn);
 
       if (convfn != dentry)
-       free (convfn);
+       xfree (convfn);
 
       return (temp);
     }
@@ -2304,14 +2423,14 @@ rl_old_menu_complete (count, invoking_key)
 
       if (matches == 0 || postprocess_matches (&matches, matching_filenames) == 0)
        {
-         rl_ding ();
+         rl_ding ();
          FREE (matches);
          matches = (char **)0;
          FREE (orig_text);
          orig_text = (char *)0;
-         completion_changed_buffer = 0;
-         RL_UNSETSTATE(RL_STATE_COMPLETING);
-          return (0);
+         completion_changed_buffer = 0;
+         RL_UNSETSTATE(RL_STATE_COMPLETING);
+         return (0);
        }
 
       RL_UNSETSTATE(RL_STATE_COMPLETING);
@@ -2340,7 +2459,10 @@ rl_old_menu_complete (count, invoking_key)
 
   match_list_index += count;
   if (match_list_index < 0)
-    match_list_index += match_list_size;
+    {
+      while (match_list_index < 0)
+       match_list_index += match_list_size;
+    }
   else
     match_list_index %= match_list_size;
 
@@ -2375,7 +2497,7 @@ rl_menu_complete (count, ignore)
   static int full_completion = 0;      /* set to 1 if menu completion should reinitialize on next call */
   static int orig_start, orig_end;
   static char quote_char;
-  static int delimiter;
+  static int delimiter, cstate;
 
   /* The first time through, we generate the list of matches and set things
      up to insert them. */
@@ -2428,14 +2550,14 @@ rl_menu_complete (count, ignore)
 
       if (matches == 0 || postprocess_matches (&matches, matching_filenames) == 0)
        {
-         rl_ding ();
+         rl_ding ();
          FREE (matches);
          matches = (char **)0;
          FREE (orig_text);
          orig_text = (char *)0;
-         completion_changed_buffer = 0;
-         RL_UNSETSTATE(RL_STATE_COMPLETING);
-          return (0);
+         completion_changed_buffer = 0;
+         RL_UNSETSTATE(RL_STATE_COMPLETING);
+         return (0);
        }
 
       RL_UNSETSTATE(RL_STATE_COMPLETING);
@@ -2483,6 +2605,11 @@ rl_menu_complete (count, ignore)
          full_completion = 1;
          return (0);
        }
+      else if (_rl_menu_complete_prefix_first && match_list_size > 1)
+       {
+         rl_ding ();
+         return (0);
+       }
     }
 
   /* Now we have the list of matches.  Replace the text between
@@ -2500,7 +2627,10 @@ rl_menu_complete (count, ignore)
 
   match_list_index += count;
   if (match_list_index < 0)
-    match_list_index += match_list_size;
+    {
+      while (match_list_index < 0)
+       match_list_index += match_list_size;
+    }
   else
     match_list_index %= match_list_size;