]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - bashline.c
renamed several functions beginning with legal_; changed all callers
[thirdparty/bash.git] / bashline.c
index 2745c4dd135d38e4ad70733f3b9675d0faa484b9..c767e1f53b4827cac296666788031c6686ca2463 100644 (file)
@@ -593,13 +593,7 @@ initialize_readline (void)
   /* Tell the completer that we want a crack first. */
   rl_attempted_completion_function = attempt_shell_completion;
 
-  /* Tell the completer that we might want to follow symbolic links or
-     do other expansion on directory names. */
-  set_directory_hook ();
-
-  rl_filename_rewrite_hook = bash_filename_rewrite_hook;
-
-  rl_filename_stat_hook = bash_filename_stat_hook;
+  bashline_set_filename_hooks ();
 
   /* Tell the filename completer we want a chance to ignore some names. */
   rl_ignore_some_completions_function = filename_completion_ignore;
@@ -686,8 +680,9 @@ bashline_reset (void)
   rl_filename_quote_characters = default_filename_quote_characters;
   set_filename_bstab (rl_filename_quote_characters);
 
-  set_directory_hook ();
-  rl_filename_stat_hook = bash_filename_stat_hook;
+  rl_filename_quoting_function = bash_quote_filename;
+
+  bashline_set_filename_hooks ();
 
   bashline_reset_event_hook ();
 
@@ -917,7 +912,7 @@ hostnames_matching (const char *text)
        continue;
 
       /* OK, it matches.  Add it to the list. */
-      if (nmatch >= (rsize - 1))
+      if (nmatch + 1 >= rsize)
        {
          rsize = (rsize + 16) - (rsize % 16);
          result = strvec_resize (result, rsize);
@@ -1562,8 +1557,7 @@ attempt_shell_completion (const char *text, int start, int end)
   complete_fullquote = 1;              /* full filename quoting by default */
   rl_filename_quote_characters = default_filename_quote_characters;
   set_filename_bstab (rl_filename_quote_characters);
-  set_directory_hook ();
-  rl_filename_stat_hook = bash_filename_stat_hook;
+  bashline_set_filename_hooks ();
 
   rl_sort_completion_matches = 1;      /* sort by default */
 
@@ -1670,6 +1664,10 @@ attempt_shell_completion (const char *text, int start, int end)
              s1 = s = e1;
              break;
            }
+         else if (s > e)
+           {
+             s = s1 = start; e = e1 = end;     /* reset */
+           }
          /* Skip over assignment statements preceding a command name.  If we
             don't find a command name at all, we can perform command name
             completion.  If we find a partial command name, we should perform
@@ -1689,10 +1687,12 @@ attempt_shell_completion (const char *text, int start, int end)
         end == index of where word to be completed ends
         if (s == start) we are doing command word completion for sure
         if (e1 == end) we are at the end of the command name and completing it */
-      if (start == 0 && end == 0 && e != 0 && text[0] == '\0') /* beginning of non-empty line */
+      if (start == 0 && end == 0 && e != 0 && e1 < rl_end && text[0] == '\0')  /* beginning of non-empty line */
         foundcs = 0;
       else if (start == end && start == s1 && e != 0 && e1 > end)      /* beginning of command name, leading whitespace */
        foundcs = 0;
+      else if (start == 0 && start == end && start < s1 && e != 0 && e1 > end && text[0] == '\0' && have_progcomps)    /* no command name, leading whitespace only */
+        prog_complete_matches = programmable_completions (EMPTYCMD, text, s, e, &foundcs);
       else if (e == 0 && e == s && text[0] == '\0' && have_progcomps)  /* beginning of empty line */
         prog_complete_matches = programmable_completions (EMPTYCMD, text, s, e, &foundcs);
       else if (start == end && text[0] == '\0' && s1 > start && whitespace (rl_line_buffer[start]))
@@ -1736,7 +1736,14 @@ attempt_shell_completion (const char *text, int start, int end)
       /* If we have defined a compspec for the initial (command) word, call
         it and process the results like any other programmable completion. */
       if (in_command_position && have_progcomps && foundcs == 0 && iw_compspec)
-       prog_complete_matches = programmable_completions (INITIALWORD, text, s, e, &foundcs);
+       {
+         /* Do some sanity checking on S and E. Room for more here. */
+         if (s > rl_point)
+           s = rl_point;
+         if (e > rl_end)
+           e = rl_end;
+         prog_complete_matches = programmable_completions (INITIALWORD, text, s, e, &foundcs);
+       }
 
       FREE (n);
       /* XXX - if we found a COMPSPEC for the command, just return whatever
@@ -1848,8 +1855,7 @@ bash_default_completion (const char *text, int start, int end, int qc, int compf
              rl_completion_suppress_append = 1;
              rl_filename_completion_desired = 0;
            }
-#if 0
-         /* TAG:bash-5.3 jidanni@jidanni.org 11/11/2022 */
+#if 1
          else if (matches[0] && matches[1] && STREQ (matches[0], matches[1]) &&
                   matches[2] && STREQ (matches[1], matches[2]) && CMD_IS_DIR (matches[0]))
 #else
@@ -1908,6 +1914,7 @@ bash_command_name_stat_hook (char **name)
   result = search_for_command (cname, 0);
   if (result)
     {
+      FREE (*name);
       *name = result;
       return 1;
     }
@@ -1922,12 +1929,8 @@ executable_completion (const char *filename, int searching_path)
 
   /* This gets an unquoted filename, so we need to quote special characters
      in the filename before the completion hook gets it. */
-#if 0
-  f = savestring (filename);
-#else
   c = 0;
   f = bash_quote_filename ((char *)filename, SINGLE_MATCH, &c);
-#endif
   bash_directory_completion_hook (&f);
   
   r = searching_path ? executable_file (f) : executable_or_directory (f);
@@ -1970,6 +1973,7 @@ command_word_completion_function (const char *hint_text, int state)
        free (dequoted_hint);
       if (hint)
        free (hint);
+      dequoted_hint = hint = (char *)NULL;
 
       mapping_over = searching_path = 0;
       hint_is_dir = CMD_IS_DIR (hint_text);
@@ -2064,7 +2068,7 @@ command_word_completion_function (const char *hint_text, int state)
       if (rl_completion_found_quote && rl_completion_quote_character == 0)
        dequoted_hint = bash_dequote_filename (hint, 0);
       
-      path = get_string_value ("PATH");
+      path = path_value ("PATH", 0);
       path_index = dot_in_path = 0;
 
       /* Initialize the variables for each type of command word. */
@@ -2180,7 +2184,11 @@ globword:
          local_index = 0;
                
          if (glob_matches[1] && rl_completion_type == TAB)     /* multiple matches are bad */
-           return ((char *)NULL);
+           {
+             strvec_dispose (glob_matches);
+             glob_matches = (char **)NULL;
+             return ((char *)NULL);
+           }
        }
 
       while (val = glob_matches[local_index++])
@@ -2252,6 +2260,7 @@ globword:
        free (fnhint);
       if (filename_hint)
        free (filename_hint);
+      fnhint = filename_hint = (char *)NULL;
 
       filename_hint = sh_makepath (current_path, hint, 0);
       /* Need a quoted version (though it doesn't matter much in most
@@ -2347,6 +2356,7 @@ globword:
          t1 = make_absolute (val, t);
          free (t);
          cval = sh_canonpath (t1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
+         free (t1);
        }
       else
 #endif
@@ -2397,7 +2407,10 @@ command_subst_completion_function (const char *text, int state)
       start_len = text - orig_start;
       filename_text = savestring (text);
       if (matches)
-       free (matches);
+       {
+         free (matches);
+         matches = (char **)NULL;
+       }
 
       /*
        * At this point we can entertain the idea of re-parsing
@@ -3066,7 +3079,7 @@ name_is_acceptable (const char *name)
 
   for (nlen = strlen (name), p = fignore.ignores; p->val; p++)
     {
-      if (nlen > p->len && p->len > 0 && STREQ (p->val, &name[nlen - p->len]))
+      if (nlen >= p->len && p->len > 0 && STREQ (p->val, &name[nlen - p->len]))
        return (0);
     }
 
@@ -3315,6 +3328,20 @@ restore_directory_hook (rl_icppfunc_t *hookf)
     rl_directory_rewrite_hook = hookf;
 }
 
+/* Set the readline hooks that affect how directories and filenames are
+   converted. Extern so other parts of the shell can use. */
+void
+bashline_set_filename_hooks (void)
+{
+  /* Tell the completer that we might want to follow symbolic links or
+     do other expansion on directory names. */
+  set_directory_hook ();
+
+  rl_filename_rewrite_hook = bash_filename_rewrite_hook;
+  rl_completion_rewrite_hook = bash_filename_rewrite_hook;
+  rl_filename_stat_hook = bash_filename_stat_hook;
+}
+
 /* Check whether not DIRNAME, with any trailing slash removed, exists.  If
    SHOULD_DEQUOTE is non-zero, we dequote the directory name first. */
 static int
@@ -3873,9 +3900,11 @@ glob_complete_word (const char *text, int state)
     {
       rl_filename_completion_desired = 1;
       FREE (matches);
+      matches = (char **)NULL;
       if (globorig != globtext)
        FREE (globorig);
       FREE (globtext);
+      globorig = globtext = (char *)NULL;
 
       ttext = bash_tilde_expand (text, 0);
 
@@ -4409,6 +4438,34 @@ readline_set_char_offset (int ind, int *varp)
     }
 }
 
+void
+uw_restore_parser_state (void *ps)
+{
+  restore_parser_state (ps);
+}
+
+void
+uw_rl_set_signals (void *ignore)
+{
+  rl_set_signals ();
+}
+
+static void
+unbind_readline_variables (void)
+{
+  check_unbind_variable ("READLINE_LINE");
+  check_unbind_variable ("READLINE_POINT");
+  check_unbind_variable ("READLINE_MARK");
+  check_unbind_variable ("READLINE_ARGUMENT");
+  array_needs_making = 1;
+}
+
+static void
+uw_unbind_readline_variables (void *ignore)
+{
+  unbind_readline_variables ();
+}
+   
 int
 bash_execute_unix_command (int count, int key)
 {
@@ -4488,8 +4545,12 @@ bash_execute_unix_command (int count, int key)
     }
   array_needs_making = 1;
 
+  begin_unwind_frame ("execute-unix-command");
   save_parser_state (&ps);
   rl_clear_signals ();
+  add_unwind_protect (uw_unbind_readline_variables, 0);
+  add_unwind_protect (uw_restore_parser_state, &ps);
+  add_unwind_protect (uw_rl_set_signals, 0);
   r = parse_and_execute (savestring (cmd), "bash_execute_unix_command", SEVAL_NOHIST);
   rl_set_signals ();
   restore_parser_state (&ps);
@@ -4498,18 +4559,15 @@ bash_execute_unix_command (int count, int key)
   maybe_make_readline_line (v ? value_cell (v) : 0);
 
   v = find_variable ("READLINE_POINT");
-  if (v && legal_number (value_cell (v), &mi))
+  if (v && valid_number (value_cell (v), &mi))
     readline_set_char_offset (mi, &rl_point);
 
   v = find_variable ("READLINE_MARK");
-  if (v && legal_number (value_cell (v), &mi))
+  if (v && valid_number (value_cell (v), &mi))
     readline_set_char_offset (mi, &rl_mark);
 
-  check_unbind_variable ("READLINE_LINE");
-  check_unbind_variable ("READLINE_POINT");
-  check_unbind_variable ("READLINE_MARK");
-  check_unbind_variable ("READLINE_ARGUMENT");
-  array_needs_making = 1;
+  unbind_readline_variables ();
+  discard_unwind_frame ("execute-unix-command");
 
   /* and restore the readline buffer and display after command execution. */
   /* If we clear the last line of the prompt above, redraw only that last
@@ -4523,15 +4581,29 @@ bash_execute_unix_command (int count, int key)
   return 0;
 }
 
+/* This only has to handle macros/shell commandsfrom print_unix_command_map */
+static void
+print_unix_command (const char *kseq, const char *value, int readable, const char *prefix)
+{
+  if (readable)
+    fprintf (rl_outstream, "\"%s%s\" \"%s\"\n", prefix ? prefix : "", kseq, value ? value : "");
+  else
+    fprintf (rl_outstream, "%s%s outputs %s\n", prefix ? prefix : "", kseq, value ? value : "");
+}
+
 int
 print_unix_command_map (void)
 {
   Keymap save, cmd_xmap;
+  rl_macro_print_func_t *old_printer;
 
   save = rl_get_keymap ();
   cmd_xmap = get_cmd_xmap_from_keymap (save);
   rl_set_keymap (cmd_xmap);
+  old_printer = rl_macro_display_hook;
+  rl_macro_display_hook = print_unix_command;
   rl_macro_dumper (1);
+  rl_macro_display_hook = old_printer;
   rl_set_keymap (save);
   return 0;
 }
@@ -4645,12 +4717,12 @@ bind_keyseq_to_unix_command (char *line)
 {
   Keymap kmap, cmd_xmap;
   char *kseq, *value;
-  int i, kstart;
+  int i, kstart, translate;
 
   kmap = rl_get_keymap ();
 
   /* We duplicate some of the work done by rl_parse_and_bind here, but
-     this code only has to handle `"keyseq"["]command["]' and can
+     this code only has to handle `"keyseq"[:][ \t]+["]command["]' and can
      generate an error for anything else. */
   i = isolate_sequence (line, 0, 1, &kstart);
   if (i < 0)
@@ -4659,16 +4731,26 @@ bind_keyseq_to_unix_command (char *line)
   /* Create the key sequence string to pass to rl_generic_bind */
   kseq = substring (line, kstart, i);
 
-  for ( ; line[i] && line[i] != ':'; i++)
+  /* Allow colon or whitespace to separate the key sequence and command string. */
+  for ( ; line[i] && line[i] != ':' && whitespace (line[i]) == 0; i++)
     ;
-  if (line[i] != ':')
+  if (line[i] != ':' && whitespace (line[i]) == 0)
     {
-      builtin_error (_("%s: missing colon separator"), line);
+      builtin_error (_("%s: missing separator"), line);
       FREE (kseq);
       return -1;
     }
 
-  i = isolate_sequence (line, i + 1, 0, &kstart);
+  /* If we have a whitespace separator we're going to call rl_macro_bind so
+     we get the readline-translated version of the value (backslash-escapes
+     handled, etc.) */
+  translate = line[i] != ':';
+
+  /* Kind of tricky. If we use whitespace as a delimiter, we can backslash-
+     quote double quotes and have them preserved in the value. However, we
+     still want to be able to auto-detect quoted strings and only require
+     them with whitespace delimiters. */
+  i = isolate_sequence (line, i + 1, translate, &kstart);
   if (i < 0)
     {
       FREE (kseq);
@@ -4680,7 +4762,10 @@ bind_keyseq_to_unix_command (char *line)
 
   /* Save the command to execute and the key sequence in the CMD_XMAP */
   cmd_xmap = get_cmd_xmap_from_keymap (kmap);
-  rl_generic_bind (ISMACR, kseq, value, cmd_xmap);
+  if (translate)
+    rl_macro_bind (kseq, value, cmd_xmap);
+  else
+    rl_generic_bind (ISMACR, kseq, value, cmd_xmap);
 
   /* and bind the key sequence in the current keymap to a function that
      understands how to execute from CMD_XMAP */