Imported from ../bash-3.2.48.tar.gz.
[platform/upstream/bash.git] / bashline.c
index 6a4963a..fa4055e 100644 (file)
@@ -1,6 +1,6 @@
 /* bashline.c -- Bash's interface to the readline library. */
 
-/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2006 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -100,6 +100,7 @@ static int history_and_alias_expand_line __P((int, int));
 #endif
 
 /* Helper functions for Readline. */
+static void bash_directory_expansion __P((char **));
 static int bash_directory_completion_hook __P((char **));
 static int filename_completion_ignore __P((char **));
 static int bash_push_line __P((void));
@@ -225,6 +226,8 @@ static char *bash_nohostname_word_break_characters = " \t\n\"'><=;|&(:";
 
 static rl_hook_func_t *old_rl_startup_hook = (rl_hook_func_t *)NULL;
 
+static int dot_in_path = 0;
+
 /* What kind of quoting is performed by bash_quote_filename:
        COMPLETE_DQUOTE = double-quoting the filename
        COMPLETE_SQUOTE = single_quoting the filename
@@ -235,6 +238,9 @@ static rl_hook_func_t *old_rl_startup_hook = (rl_hook_func_t *)NULL;
 #define COMPLETE_BSQUOTE 3
 static int completion_quoting_style = COMPLETE_BSQUOTE;
 
+/* Flag values for the final argument to bash_default_completion */
+#define DEFCOMP_CMDPOS         1
+
 /* Change the readline VI-mode keymaps into or out of Posix.2 compliance.
    Called when the shell is put into or out of `posix' mode. */
 void
@@ -292,7 +298,7 @@ enable_hostname_completion (on_or_off)
       /* See if we have anything to do. */
       at = strchr (rl_completer_word_break_characters, '@');
       if ((at == 0 && on_or_off == 0) || (at != 0 && on_or_off != 0))
-        return;
+        return old_value;
 
       /* We have something to do.  Do it. */
       nval = (char *)xmalloc (strlen (rl_completer_word_break_characters) + 1 + on_or_off);
@@ -446,7 +452,11 @@ initialize_readline ()
 
 #endif /* SPECIFIC_COMPLETION_FUNCTIONS */
 
-  rl_bind_key_if_unbound_in_map (TAB, dynamic_complete_history, emacs_meta_keymap);
+  kseq[0] = TAB;
+  kseq[1] = '\0';
+  func = rl_function_of_keyseq (kseq, emacs_meta_keymap, (int *)NULL);
+  if (func == 0 || func == rl_tab_insert)
+    rl_bind_key_in_map (TAB, dynamic_complete_history, emacs_meta_keymap);
 
   /* Tell the completer that we want a crack first. */
   rl_attempted_completion_function = attempt_shell_completion;
@@ -795,6 +805,7 @@ operate_and_get_next (count, c)
 
 #define VI_EDIT_COMMAND                "fc -e \"${VISUAL:-${EDITOR:-vi}}\""
 #define EMACS_EDIT_COMMAND     "fc -e \"${VISUAL:-${EDITOR:-emacs}}\""
+#define POSIX_VI_EDIT_COMMAND  "fc -e vi"
 
 static int
 edit_and_execute_command (count, c, editing_mode, edit_command)
@@ -854,7 +865,10 @@ static int
 vi_edit_and_execute_command (count, c)
      int count, c;
 {
-  return (edit_and_execute_command (count, c, VI_EDITING_MODE, VI_EDIT_COMMAND));
+  if (posixly_correct)
+    return (edit_and_execute_command (count, c, VI_EDITING_MODE, POSIX_VI_EDIT_COMMAND));
+  else
+    return (edit_and_execute_command (count, c, VI_EDITING_MODE, VI_EDIT_COMMAND));
 }
 #endif /* VI_MODE */
 
@@ -988,7 +1002,7 @@ attempt_shell_completion (text, start, end)
      const char *text;
      int start, end;
 {
-  int in_command_position, ti, saveti, qc;
+  int in_command_position, ti, saveti, qc, dflags;
   char **matches, *command_separator_chars;
 
   command_separator_chars = COMMAND_SEPARATORS;
@@ -1070,7 +1084,7 @@ attempt_shell_completion (text, start, end)
       s = find_cmd_start (start);
       e = find_cmd_end (end);
       n = find_cmd_name (s);
-      if (e > s)
+      if (e > s && assignment (n, 0) == 0)
        prog_complete_matches = programmable_completions (n, text, s, e, &foundcs);
       else
        foundcs = 0;
@@ -1101,15 +1115,20 @@ attempt_shell_completion (text, start, end)
 #endif
 
   if (matches == 0)
-    matches = bash_default_completion (text, start, end, qc, in_command_position);
+    {
+      dflags = 0;
+      if (in_command_position)
+       dflags |= DEFCOMP_CMDPOS;
+      matches = bash_default_completion (text, start, end, qc, dflags);
+    }
 
   return matches;
 }
 
 char **
-bash_default_completion (text, start, end, qc, in_command_position)
+bash_default_completion (text, start, end, qc, compflags)
      const char *text;
-     int start, end, qc, in_command_position;
+     int start, end, qc, compflags;
 {
   char **matches;
 
@@ -1137,9 +1156,11 @@ bash_default_completion (text, start, end, qc, in_command_position)
   /* And last, (but not least) if this word is in a command position, then
      complete over possible command names, including aliases, functions,
      and command names. */
-  if (!matches && in_command_position)
+  if (matches == 0 && (compflags & DEFCOMP_CMDPOS))
     {
-      if (start == 0 && end == 0 && text[0] == '\0' && no_empty_command_completion)
+      /* If END == START and text[0] == 0, we are trying to complete an empty
+        command word. */
+      if (no_empty_command_completion && end == start && text[0] == '\0')
        {
          matches = (char **)NULL;
          rl_ignore_some_completions_function = bash_ignore_everything;
@@ -1148,6 +1169,7 @@ bash_default_completion (text, start, end, qc, in_command_position)
        {
 #define CMD_IS_DIR(x)  (absolute_pathname(x) == 0 && absolute_program(x) == 0 && *(x) != '~' && test_for_directory (x))
 
+         dot_in_path = 0;
          matches = rl_completion_matches (text, command_word_completion_function);
 
          /* If we are attempting command completion and nothing matches, we
@@ -1157,27 +1179,28 @@ bash_default_completion (text, start, end, qc, in_command_position)
             filenames and leave directories in the match list. */
          if (matches == (char **)NULL)
            rl_ignore_some_completions_function = bash_ignore_filenames;
-#if 0
-         else if (matches[1] == 0 && CMD_IS_DIR(matches[0]))
-           /* Turn off rl_filename_completion_desired so readline doesn't
-              append a slash if there is a directory with the same name
-              in the current directory, or other filename-specific things.
-              If the name begins with a slash, we're either completing a
-              full pathname or a directory pathname, and readline won't be
-              looking in the current directory anyway, so there's no
-              conflict. */
-           rl_filename_completion_desired = 0;
+         else if (matches[1] == 0 && CMD_IS_DIR(matches[0]) && dot_in_path == 0)
+           /* If we found a single match, without looking in the current
+              directory (because it's not in $PATH), but the found name is
+              also a command in the current directory, suppress appending any
+              terminating character, since it's ambiguous. */
+           {
+             rl_completion_suppress_append = 1;
+             rl_filename_completion_desired = 0;
+           }
          else if (matches[0] && matches[1] && STREQ (matches[0], matches[1]) && CMD_IS_DIR (matches[0]))
            /* There are multiple instances of the same match (duplicate
               completions haven't yet been removed).  In this case, all of
               the matches will be the same, and the duplicate removal code
-              will distill them all down to one.  We turn off
-              rl_filename_completion_desired for the same reason as above.
+              will distill them all down to one.  We turn on
+              rl_completion_suppress_append for the same reason as above.
               Remember: we only care if there's eventually a single unique
               completion.  If there are multiple completions this won't
               make a difference and the problem won't occur. */
-           rl_filename_completion_desired = 0;
-#endif
+           {
+             rl_completion_suppress_append = 1;
+             rl_filename_completion_desired = 0;
+           }
        }
     }
 
@@ -1211,23 +1234,31 @@ command_word_completion_function (hint_text, state)
   static char *path = (char *)NULL;
   static char *val = (char *)NULL;
   static char *filename_hint = (char *)NULL;
-  static int path_index, hint_len, istate;
-  static int mapping_over, local_index;
+  static char *dequoted_hint = (char *)NULL;
+  static int path_index, hint_len, dequoted_len, istate, igncase;
+  static int mapping_over, local_index, searching_path, hint_is_dir;
   static SHELL_VAR **varlist = (SHELL_VAR **)NULL;
 #if defined (ALIAS)
   static alias_t **alias_list = (alias_t **)NULL;
 #endif /* ALIAS */
+  char *temp;
 
   /* We have to map over the possibilities for command words.  If we have
      no state, then make one just for that purpose. */
   if (!state)
     {
+      if (dequoted_hint && dequoted_hint != hint)
+       free (dequoted_hint);
       if (hint)
        free (hint);
 
-      mapping_over = 0;
+      mapping_over = searching_path = 0;
+      hint_is_dir = CMD_IS_DIR (hint_text);
       val = (char *)NULL;
 
+      temp = rl_variable_value ("completion-ignore-case");
+      igncase = strcmp (temp, "on") == 0;
+
       /* If this is an absolute program name, do not check it against
         aliases, reserved words, functions or builtins.  We must check
         whether or not it is unique, and, if so, whether that filename
@@ -1240,10 +1271,24 @@ command_word_completion_function (hint_text, state)
            hint = bash_tilde_expand (hint_text, 0);
          else
            hint = savestring (hint_text);
-         hint_len = strlen (hint);
+
+         dequoted_hint = hint;
+         /* If readline's completer found a quote character somewhere, but
+            didn't set the quote character, there must have been a quote
+            character embedded in the filename.  It can't be at the start of
+            the filename, so we need to dequote the filename before we look
+            in the file system for it. */
+         if (rl_completion_found_quote && rl_completion_quote_character == 0)
+           {
+             dequoted_hint = bash_dequote_filename (hint, 0);
+             free (hint);
+             hint = dequoted_hint;
+           }
+         dequoted_len = hint_len = strlen (hint);
 
          if (filename_hint)
            free (filename_hint);
+
          filename_hint = savestring (hint);
 
          mapping_over = 4;
@@ -1251,11 +1296,17 @@ command_word_completion_function (hint_text, state)
          goto inner;
        }
 
-      hint = savestring (hint_text);
-      hint_len = strlen (hint);
+      dequoted_hint = hint = savestring (hint_text);
+      dequoted_len = hint_len = strlen (hint);
 
+      if (rl_completion_found_quote && rl_completion_quote_character == 0)
+       {
+         dequoted_hint = bash_dequote_filename (hint, 0);
+         dequoted_len = strlen (dequoted_hint);
+       }
+      
       path = get_string_value ("PATH");
-      path_index = 0;
+      path_index = dot_in_path = 0;
 
       /* Initialize the variables for each type of command word. */
       local_index = 0;
@@ -1341,6 +1392,16 @@ command_word_completion_function (hint_text, state)
       mapping_over++;
     }
 
+  /* If the text passed is a directory in the current directory, return it
+     as a possible match.  Executables in directories in the current
+     directory can be specified using relative pathnames and successfully
+     executed even when `.' is not in $PATH. */
+  if (hint_is_dir)
+    {
+      hint_is_dir = 0; /* only return the hint text once */
+      return (savestring (hint_text));
+    }
+    
   /* Repeatedly call filename_completion_function while we have
      members of PATH left.  Question:  should we stat each file?
      Answer: we call executable_file () on each file. */
@@ -1358,6 +1419,7 @@ command_word_completion_function (hint_text, state)
          (current_path = extract_colon_unit (path, &path_index)) == 0)
        return ((char *)NULL);
 
+      searching_path = 1;
       if (*current_path == 0)
        {
          free (current_path);
@@ -1373,11 +1435,13 @@ command_word_completion_function (hint_text, state)
          current_path = t;
        }
 
+      if (current_path[0] == '.' && current_path[1] == '\0')
+       dot_in_path = 1;
+
       if (filename_hint)
        free (filename_hint);
 
       filename_hint = sh_makepath (current_path, hint, 0);
-
       free (current_path);
     }
 
@@ -1397,19 +1461,34 @@ command_word_completion_function (hint_text, state)
   else
     {
       int match, freetemp;
-      char *temp;
+#if 0
+      char *temp;              /* shadows previous declaration */
+#endif
 
       if (absolute_program (hint))
        {
-         match = strncmp (val, hint, hint_len) == 0;
+         if (igncase == 0)
+           match = strncmp (val, hint, hint_len) == 0;
+         else
+           match = strncasecmp (val, hint, hint_len) == 0;
+
          /* If we performed tilde expansion, restore the original
             filename. */
          if (*hint_text == '~')
            {
-             int l, tl, vl;
+             int l, tl, vl, dl;
+             char *rd;
              vl = strlen (val);
              tl = strlen (hint_text);
+#if 0
              l = vl - hint_len;        /* # of chars added */
+#else
+             rd = savestring (filename_hint);
+             bash_directory_expansion (&rd);
+             dl = strlen (rd);
+             l = vl - dl;              /* # of chars added */
+             free (rd);
+#endif
              temp = (char *)xmalloc (l + 2 + tl);
              strcpy (temp, hint_text);
              strcpy (temp + tl, val + vl - l);
@@ -1425,7 +1504,10 @@ command_word_completion_function (hint_text, state)
          if (temp)
            {
              temp++;
-             freetemp = match = strncmp (temp, hint, hint_len) == 0;
+             if (igncase == 0)
+               freetemp = match = strncmp (temp, hint, hint_len) == 0;
+             else
+               freetemp = match = strncasecmp (temp, hint, hint_len) == 0;
              if (match)
                temp = savestring (temp);
            }
@@ -1433,9 +1515,18 @@ command_word_completion_function (hint_text, state)
            freetemp = match = 0;
        }
 
+#if 0
       /* If we have found a match, and it is an executable file or a
         directory name, return it. */
       if (match && executable_or_directory (val))
+#else
+      /* If we have found a match, and it is an executable file, return it.
+        We don't return directory names when searching $PATH, since the
+        bash execution code won't find executables in directories which
+        appear in directories in $PATH when they're specified using
+        relative pathnames. */
+      if (match && (searching_path ? executable_file (val) : executable_or_directory (val)))
+#endif
        {
          free (val);
          val = "";             /* So it won't be NULL. */
@@ -1504,6 +1595,13 @@ command_subst_completion_function (text, state)
       /* If there is more than one match, rl_completion_matches has already
         put the lcd in matches[0].  Skip over it. */
       cmd_index = matches && matches[0] && matches[1];
+
+      /* If there's a single match and it's a directory, set the append char
+        to the expected `/'.  Otherwise, don't append anything. */
+      if (matches && matches[0] && matches[1] == 0 && test_for_directory (matches[0]))
+       rl_completion_append_character = '/';
+      else
+       rl_completion_suppress_append = 1;
     }
 
   if (!matches || !matches[cmd_index])
@@ -1664,8 +1762,9 @@ bash_servicename_completion_function (text, state)
       if (snamelen == 0 || (STREQN (sname, srvent->s_name, snamelen)))
        break;
       /* Not primary, check aliases */
-      for (alist = srvent->s_aliases; aentry = *alist; alist++)
+      for (alist = srvent->s_aliases; *alist; alist++)
        {
+         aentry = *alist;
          if (STREQN (sname, aentry, snamelen))
            {
              afound = 1;
@@ -1888,7 +1987,7 @@ tcsh_magic_space (count, ignore)
   else
     return (1);
 }
-#endif
+#endif /* BANG_HISTORY */
 
 /* History and alias expand the line. */
 static int
@@ -1897,7 +1996,10 @@ history_and_alias_expand_line (count, ignore)
 {
   char *new_line;
 
+  new_line = 0;
+#if defined (BANG_HISTORY)
   new_line = history_expand_line_internal (rl_line_buffer);
+#endif
 
 #if defined (ALIAS)
   if (new_line)
@@ -1933,7 +2035,10 @@ shell_expand_line (count, ignore)
   char *new_line;
   WORD_LIST *expanded_string;
 
+  new_line = 0;
+#if defined (BANG_HISTORY)
   new_line = history_expand_line_internal (rl_line_buffer);
+#endif
 
 #if defined (ALIAS)
   if (new_line)
@@ -2187,6 +2292,34 @@ bash_ignore_everything (names)
   return 0;
 }
 
+/* Simulate the expansions that will be performed by
+   rl_filename_completion_function.  This must be called with the address of
+   a pointer to malloc'd memory. */
+static void
+bash_directory_expansion (dirname)
+     char **dirname;
+{
+  char *d, *nd;
+
+  d = savestring (*dirname);
+
+  if (rl_directory_rewrite_hook)
+    (*rl_directory_rewrite_hook) (&d);
+
+  if (rl_directory_completion_hook && (*rl_directory_completion_hook) (&d))
+    {
+      free (*dirname);
+      *dirname = d;
+    }
+  else if (rl_completion_found_quote)
+    {
+      nd = bash_dequote_filename (d, rl_completion_quote_character);
+      free (*dirname);
+      free (d);
+      *dirname = nd;
+    }
+}
+  
 /* Handle symbolic link references and other directory name
    expansions while hacking completion. */
 static int
@@ -2224,7 +2357,7 @@ bash_directory_completion_hook (dirname)
   if (should_expand_dirname)  
     {
       new_dirname = savestring (local_dirname);
-      wl = expand_prompt_string (new_dirname, 0);      /* does the right thing */
+      wl = expand_prompt_string (new_dirname, 0, W_NOCOMSUB);  /* does the right thing */
       if (wl)
        {
          *dirname = string_list (wl);
@@ -2245,6 +2378,13 @@ bash_directory_completion_hook (dirname)
          return 1;
        }
     }
+  else 
+    {
+      /* Dequote the filename even if we don't expand it. */
+      new_dirname = bash_dequote_filename (local_dirname, rl_completion_quote_character);
+      free (local_dirname);
+      local_dirname = *dirname = new_dirname;
+    }
 
   if (!no_symbolic_links && (local_dirname[0] != '.' || local_dirname[1]))
     {
@@ -2265,9 +2405,12 @@ bash_directory_completion_hook (dirname)
       if (temp1[len1 - 1] == '/')
        {
          len2 = strlen (temp2);
-         temp2 = (char *)xrealloc (temp2, len2 + 2);
-         temp2[len2] = '/';
-         temp2[len2 + 1] = '\0';
+         if (len2 > 2)         /* don't append `/' to `/' or `//' */
+           {
+             temp2 = (char *)xrealloc (temp2, len2 + 2);
+             temp2[len2] = '/';
+             temp2[len2 + 1] = '\0';
+           }
        }
       free (local_dirname);
       *dirname = temp2;
@@ -2513,7 +2656,7 @@ glob_complete_word (text, state)
   static char **matches = (char **)NULL;
   static int ind;
   int glen;
-  char *ret;
+  char *ret, *ttext;
 
   if (state == 0)
     {
@@ -2523,17 +2666,22 @@ glob_complete_word (text, state)
        FREE (globorig);
       FREE (globtext);
 
+      ttext = bash_tilde_expand (text, 0);
+
       if (rl_explicit_arg)
        {
-         globorig = savestring (text);
-         glen = strlen (text);
+         globorig = savestring (ttext);
+         glen = strlen (ttext);
          globtext = (char *)xmalloc (glen + 2);
-         strcpy (globtext, text);
+         strcpy (globtext, ttext);
          globtext[glen] = '*';
          globtext[glen+1] = '\0';
        }
       else
-        globtext = globorig = savestring (text);
+        globtext = globorig = savestring (ttext);
+
+      if (ttext != text)
+       free (ttext);
 
       matches = shell_glob_filename (globtext);
       if (GLOB_FAILED (matches))
@@ -2779,10 +2927,6 @@ bash_quote_filename (s, rtype, qcp)
      to perform tilde expansion, because single and double
      quotes inhibit tilde expansion by the shell. */
 
-  mtext = s;
-  if (mtext[0] == '~' && rtype == SINGLE_MATCH)
-    mtext = bash_tilde_expand (s, 0);
-
   cs = completion_quoting_style;
   /* Might need to modify the default completion style based on *qcp,
      since it's set to any user-provided opening quote.  We also change
@@ -2790,7 +2934,7 @@ bash_quote_filename (s, rtype, qcp)
      the word being completed contains newlines, since those are not
      quoted correctly using backslashes (a backslash-newline pair is
      special to the shell parser). */
-  if (*qcp == '\0' && cs == COMPLETE_BSQUOTE && xstrchr (mtext, '\n'))
+  if (*qcp == '\0' && cs == COMPLETE_BSQUOTE && xstrchr (s, '\n'))
     cs = COMPLETE_SQUOTE;
   else if (*qcp == '"')
     cs = COMPLETE_DQUOTE;
@@ -2798,17 +2942,23 @@ bash_quote_filename (s, rtype, qcp)
     cs = COMPLETE_SQUOTE;
 #if defined (BANG_HISTORY)
   else if (*qcp == '\0' && history_expansion && cs == COMPLETE_DQUOTE &&
-          history_expansion_inhibited == 0 && xstrchr (mtext, '!'))
+          history_expansion_inhibited == 0 && xstrchr (s, '!'))
     cs = COMPLETE_BSQUOTE;
 
   if (*qcp == '"' && history_expansion && cs == COMPLETE_DQUOTE &&
-       history_expansion_inhibited == 0 && xstrchr (mtext, '!'))
+       history_expansion_inhibited == 0 && xstrchr (s, '!'))
     {
       cs = COMPLETE_BSQUOTE;
       *qcp = '\0';
     }
 #endif
 
+  /* Don't tilde-expand backslash-quoted filenames, since only single and
+     double quotes inhibit tilde expansion. */
+  mtext = s;
+  if (mtext[0] == '~' && rtype == SINGLE_MATCH && cs != COMPLETE_BSQUOTE)
+    mtext = bash_tilde_expand (s, 0);
+
   switch (cs)
     {
     case COMPLETE_DQUOTE:
@@ -3021,11 +3171,7 @@ bash_directory_completion_matches (text)
   char *dfn;
   int qc;
 
-#if 0
-  qc = (text[0] == '"' || text[0] == '\'') ? text[0] : 0;
-#else
   qc = rl_dispatching ? rl_completion_quote_character : 0;  
-#endif
   dfn = bash_dequote_filename ((char *)text, qc);
   m1 = rl_completion_matches (dfn, rl_filename_completion_function);
   free (dfn);