7 Bug-Reported-by: "Michael Kalisz" <michael@kalisz.homelinux.net>
8 Bug-Reference-ID: <50241.78.69.11.112.1298585641.squirrel@kalisz.homelinux.net>
9 Bug-Reference-URL: http://lists.gnu.org/archive/html/bug-bash/2011-02/msg00274.html
13 Bash-4.2 tries to leave completed directory names as the user typed them,
14 without expanding them to a full pathname. One effect of this is that
15 shell variables used in pathnames being completed (e.g., $HOME) are left
16 unchanged, but the `$' is quoted by readline because it is a special
17 character to the shell.
19 This patch introduces two things:
21 1. A new shell option, `direxpand', which, if set, attempts to emulate the
22 bash-4.1 behavior of expanding words to full pathnames during
24 2. A set of heuristics that reduce the number of times special characters
25 such as `$' are quoted when the directory name is not expanded.
27 Patch (apply with `patch -p0'):
29 diff -NrC 2 ../bash-4.2-patched/bashline.c ./bashline.c
30 *** ../bash-4.2-patched/bashline.c 2011-01-16 15:32:47.000000000 -0500
31 --- ./bashline.c 2012-05-07 16:27:18.000000000 -0400
35 static int bash_push_line __P((void));
37 + static rl_icppfunc_t *save_directory_hook __P((void));
38 + static void reset_directory_hook __P((rl_icppfunc_t *));
40 static void cleanup_expansion_error __P((void));
41 static void maybe_make_readline_line __P((char *));
45 int dircomplete_spelling = 0;
47 + /* Expand directory names during word/filename completion. */
48 + int dircomplete_expand = 0;
49 + int dircomplete_expand_relpath = 0;
51 static char *bash_completer_word_break_characters = " \t\n\"'@><=;|&(:";
52 static char *bash_nohostname_word_break_characters = " \t\n\"'><=;|&(:";
55 + static const char *default_filename_quote_characters = " \t\n\\\"'@<>=;|&()#$`?*[!:{~"; /*}*/
56 + static char *custom_filename_quote_characters = 0;
58 static rl_hook_func_t *old_rl_startup_hook = (rl_hook_func_t *)NULL;
62 /* Tell the completer that we might want to follow symbolic links or
63 do other expansion on directory names. */
64 ! rl_directory_rewrite_hook = bash_directory_completion_hook;
66 rl_filename_rewrite_hook = bash_filename_rewrite_hook;
68 /* Tell the completer that we might want to follow symbolic links or
69 do other expansion on directory names. */
70 ! set_directory_hook ();
72 rl_filename_rewrite_hook = bash_filename_rewrite_hook;
76 /* characters that need to be quoted when appearing in filenames. */
77 ! rl_filename_quote_characters = " \t\n\\\"'@<>=;|&()#$`?*[!:{~"; /*}*/
79 rl_filename_quoting_function = bash_quote_filename;
82 /* characters that need to be quoted when appearing in filenames. */
83 ! rl_filename_quote_characters = default_filename_quote_characters;
85 rl_filename_quoting_function = bash_quote_filename;
88 rl_attempted_completion_function = attempt_shell_completion;
89 rl_completion_entry_function = NULL;
90 - rl_directory_rewrite_hook = bash_directory_completion_hook;
91 rl_ignore_some_completions_function = filename_completion_ignore;
95 rl_attempted_completion_function = attempt_shell_completion;
96 rl_completion_entry_function = NULL;
97 rl_ignore_some_completions_function = filename_completion_ignore;
98 + rl_filename_quote_characters = default_filename_quote_characters;
100 + set_directory_hook ();
106 rl_ignore_some_completions_function = filename_completion_ignore;
108 + rl_filename_quote_characters = default_filename_quote_characters;
109 + set_directory_hook ();
111 /* Determine if this could be a command word. It is if it appears at
112 the start of the line (ignoring preceding whitespace), or if it
118 + if (dircomplete_expand && dot_or_dotdot (filename_hint))
120 + dircomplete_expand = 0;
121 + set_directory_hook ();
122 + dircomplete_expand = 1;
130 val = rl_filename_completion_function (filename_hint, istate);
131 + if (mapping_over == 4 && dircomplete_expand)
132 + set_directory_hook ();
141 + /* Functions to save and restore the appropriate directory hook */
142 + /* This is not static so the shopt code can call it */
144 + set_directory_hook ()
146 + if (dircomplete_expand)
148 + rl_directory_completion_hook = bash_directory_completion_hook;
149 + rl_directory_rewrite_hook = (rl_icppfunc_t *)0;
153 + rl_directory_rewrite_hook = bash_directory_completion_hook;
154 + rl_directory_completion_hook = (rl_icppfunc_t *)0;
158 + static rl_icppfunc_t *
159 + save_directory_hook ()
161 + rl_icppfunc_t *ret;
163 + if (dircomplete_expand)
165 + ret = rl_directory_completion_hook;
166 + rl_directory_completion_hook = (rl_icppfunc_t *)NULL;
170 + ret = rl_directory_rewrite_hook;
171 + rl_directory_rewrite_hook = (rl_icppfunc_t *)NULL;
178 + restore_directory_hook (hookf)
179 + rl_icppfunc_t *hookf;
181 + if (dircomplete_expand)
182 + rl_directory_completion_hook = hookf;
184 + rl_directory_rewrite_hook = hookf;
187 /* Handle symbolic link references and other directory name
188 expansions while hacking completion. This should return 1 if it modifies
192 char *local_dirname, *new_dirname, *t;
193 ! int return_value, should_expand_dirname;
197 ! return_value = should_expand_dirname = 0;
198 local_dirname = *dirname;
200 ! if (mbschr (local_dirname, '$'))
201 ! should_expand_dirname = 1;
204 t = mbschr (local_dirname, '`');
205 if (t && unclosed_pair (local_dirname, strlen (local_dirname), "`") == 0)
206 ! should_expand_dirname = 1;
211 char *local_dirname, *new_dirname, *t;
212 ! int return_value, should_expand_dirname, nextch, closer;
216 ! return_value = should_expand_dirname = nextch = closer = 0;
217 local_dirname = *dirname;
219 ! if (t = mbschr (local_dirname, '$'))
221 ! should_expand_dirname = '$';
223 ! /* Deliberately does not handle the deprecated $[...] arithmetic
224 ! expansion syntax */
227 ! else if (nextch == '{')
234 t = mbschr (local_dirname, '`');
235 if (t && unclosed_pair (local_dirname, strlen (local_dirname), "`") == 0)
236 ! should_expand_dirname = '`';
243 local_dirname = *dirname;
244 + /* XXX - change rl_filename_quote_characters here based on
245 + should_expand_dirname/nextch/closer. This is the only place
246 + custom_filename_quote_characters is modified. */
247 + if (rl_filename_quote_characters && *rl_filename_quote_characters)
250 + i = strlen (default_filename_quote_characters);
251 + custom_filename_quote_characters = xrealloc (custom_filename_quote_characters, i+1);
252 + for (i = j = 0; c = default_filename_quote_characters[i]; i++)
254 + if (c == should_expand_dirname || c == nextch || c == closer)
256 + custom_filename_quote_characters[j++] = c;
258 + custom_filename_quote_characters[j] = '\0';
259 + rl_filename_quote_characters = custom_filename_quote_characters;
268 + /* no_symbolic_links == 0 -> use (default) logical view of the file system.
269 + local_dirname[0] == '.' && local_dirname[1] == '/' means files in the
270 + current directory (./).
271 + local_dirname[0] == '.' && local_dirname[1] == 0 means relative pathnames
272 + in the current directory (e.g., lib/sh).
273 + XXX - should we do spelling correction on these? */
275 + /* This is test as it was in bash-4.2: skip relative pathnames in current
276 + directory. Change test to
277 + (local_dirname[0] != '.' || (local_dirname[1] && local_dirname[1] != '/'))
278 + if we want to skip paths beginning with ./ also. */
279 if (no_symbolic_links == 0 && (local_dirname[0] != '.' || local_dirname[1]))
286 + /* If we have a relative path
287 + (local_dirname[0] != '/' && local_dirname[0] != '.')
288 + that is canonical after appending it to the current directory, then
291 + strcmp (temp1, temp2) == 0
292 + after adding a slash to temp2 below. It should be safe to not
295 t = get_working_directory ("symlink-hook");
296 temp1 = make_absolute (local_dirname, t);
301 ! return_value |= STREQ (local_dirname, temp2) == 0;
302 free (local_dirname);
308 ! /* dircomplete_expand_relpath == 0 means we want to leave relative
309 ! pathnames that are unchanged by canonicalization alone.
310 ! *local_dirname != '/' && *local_dirname != '.' == relative pathname
311 ! (consistent with general.c:absolute_pathname())
312 ! temp1 == temp2 (after appending a slash to temp2) means the pathname
313 ! is not changed by canonicalization as described above. */
314 ! if (dircomplete_expand_relpath || ((local_dirname[0] != '/' && local_dirname[0] != '.') && STREQ (temp1, temp2) == 0))
315 ! return_value |= STREQ (local_dirname, temp2) == 0;
316 free (local_dirname);
320 orig_func = rl_completion_entry_function;
321 orig_attempt_func = rl_attempted_completion_function;
322 - orig_dir_func = rl_directory_rewrite_hook;
323 orig_ignore_func = rl_ignore_some_completions_function;
324 orig_rl_completer_word_break_characters = rl_completer_word_break_characters;
325 rl_completion_entry_function = rl_filename_completion_function;
326 rl_attempted_completion_function = (rl_completion_func_t *)NULL;
327 - rl_directory_rewrite_hook = (rl_icppfunc_t *)NULL;
328 rl_ignore_some_completions_function = filename_completion_ignore;
329 rl_completer_word_break_characters = " \t\n\"\'";
331 orig_func = rl_completion_entry_function;
332 orig_attempt_func = rl_attempted_completion_function;
333 orig_ignore_func = rl_ignore_some_completions_function;
334 orig_rl_completer_word_break_characters = rl_completer_word_break_characters;
336 + orig_dir_func = save_directory_hook ();
338 rl_completion_entry_function = rl_filename_completion_function;
339 rl_attempted_completion_function = (rl_completion_func_t *)NULL;
340 rl_ignore_some_completions_function = filename_completion_ignore;
341 rl_completer_word_break_characters = " \t\n\"\'";
344 rl_completion_entry_function = orig_func;
345 rl_attempted_completion_function = orig_attempt_func;
346 - rl_directory_rewrite_hook = orig_dir_func;
347 rl_ignore_some_completions_function = orig_ignore_func;
348 rl_completer_word_break_characters = orig_rl_completer_word_break_characters;
353 rl_completion_entry_function = orig_func;
354 rl_attempted_completion_function = orig_attempt_func;
355 rl_ignore_some_completions_function = orig_ignore_func;
356 rl_completer_word_break_characters = orig_rl_completer_word_break_characters;
358 + restore_directory_hook (orig_dir_func);
362 diff -NrC 2 ../bash-4.2-patched/bashline.h ./bashline.h
363 *** ../bash-4.2-patched/bashline.h 2009-01-04 14:32:22.000000000 -0500
364 --- ./bashline.h 2012-05-07 16:27:18.000000000 -0400
368 extern int bash_re_edit __P((char *));
370 + extern void bashline_set_event_hook __P((void));
371 + extern void bashline_reset_event_hook __P((void));
373 extern int bind_keyseq_to_unix_command __P((char *));
375 extern char **bash_default_completion __P((const char *, int, int, int, int));
377 + void set_directory_hook __P((void));
379 /* Used by programmable completion code. */
380 extern char *command_word_completion_function __P((const char *, int));
381 diff -NrC 2 ../bash-4.2-patched/builtins/shopt.def ./builtins/shopt.def
382 *** ../bash-4.2-patched/builtins/shopt.def 2010-07-02 22:42:44.000000000 -0400
383 --- ./builtins/shopt.def 2012-05-07 16:27:18.000000000 -0400
387 #include "bashgetopt.h"
389 + #if defined (READLINE)
390 + # include "../bashline.h"
393 #if defined (HISTORY)
394 # include "../bashhist.h"
397 extern int no_empty_command_completion;
398 extern int force_fignore;
399 ! extern int dircomplete_spelling;
401 extern int enable_hostname_completion __P((int));
403 extern int no_empty_command_completion;
404 extern int force_fignore;
405 ! extern int dircomplete_spelling, dircomplete_expand;
407 extern int enable_hostname_completion __P((int));
413 + #if defined (READLINE)
414 + static int shopt_set_complete_direxpand __P((char *, int));
417 static int shopt_login_shell;
418 static int shopt_compat31;
422 { "compat41", &shopt_compat41, set_compatibility_level },
423 #if defined (READLINE)
424 + { "direxpand", &dircomplete_expand, shopt_set_complete_direxpand },
425 { "dirspell", &dircomplete_spelling, (shopt_set_func_t *)NULL },
432 + #if defined (READLINE)
434 + shopt_set_complete_direxpand (option_name, mode)
438 + set_directory_hook ();
443 #if defined (RESTRICTED_SHELL)
444 /* Don't allow the value of restricted_shell to be modified. */
445 Binary files ../bash-4.2-patched/doc/._bashref.pdf and ./doc/._bashref.pdf differ
446 diff -NrC 2 ../bash-4.2-patched/doc/bash.1 ./doc/bash.1
447 *** ../bash-4.2-patched/doc/bash.1 2011-01-16 15:31:39.000000000 -0500
448 --- ./doc/bash.1 2012-05-07 16:27:18.000000000 -0400
452 The default bash behavior remains as in previous versions.
457 + replaces directory names with the results of word expansion when performing
458 + filename completion. This changes the contents of the readline editing
462 + attempts to preserve what the user typed.
466 diff -NrC 2 ../bash-4.2-patched/doc/bashref.texi ./doc/bashref.texi
467 *** ../bash-4.2-patched/doc/bashref.texi 2011-01-16 15:31:57.000000000 -0500
468 --- ./doc/bashref.texi 2012-05-07 16:27:18.000000000 -0400
472 The default Bash behavior remains as in previous versions.
476 + replaces directory names with the results of word expansion when performing
477 + filename completion. This changes the contents of the readline editing
479 + If not set, Bash attempts to preserve what the user typed.
483 diff -NrC 2 ../bash-4.2-patched/tests/shopt.right ./tests/shopt.right
484 *** ../bash-4.2-patched/tests/shopt.right 2010-07-02 23:36:30.000000000 -0400
485 --- ./tests/shopt.right 2012-05-07 16:27:18.000000000 -0400
510 *** ../bash-4.2-patched/patchlevel.h Sat Jun 12 20:14:48 2010
511 --- patchlevel.h Thu Feb 24 21:41:34 2011
514 looks for to find the patch level (for the sccs version string). */
516 ! #define PATCHLEVEL 28
518 #endif /* _PATCHLEVEL_H_ */
520 looks for to find the patch level (for the sccs version string). */
522 ! #define PATCHLEVEL 29
524 #endif /* _PATCHLEVEL_H_ */