1 BASH PATCH REPORT |
|
2 ================= |
|
3 |
|
4 Bash-Release: 4.2 |
|
5 Patch-ID: bash42-029 |
|
6 |
|
7 Bug-Reported-by: "Michael Kalisz" <[email protected]> |
|
8 Bug-Reference-ID: <[email protected]> |
|
9 Bug-Reference-URL: http://lists.gnu.org/archive/html/bug-bash/2011-02/msg00274.html |
|
10 |
|
11 Bug-Description: |
|
12 |
|
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. |
|
18 |
|
19 This patch introduces two things: |
|
20 |
|
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 |
|
23 completion; |
|
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. |
|
26 |
|
27 Patch (apply with `patch -p0'): |
|
28 |
|
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 |
|
32 *************** |
|
33 *** 122,125 **** |
|
34 --- 122,128 ---- |
|
35 static int bash_push_line __P((void)); |
|
36 |
|
37 + static rl_icppfunc_t *save_directory_hook __P((void)); |
|
38 + static void reset_directory_hook __P((rl_icppfunc_t *)); |
|
39 + |
|
40 static void cleanup_expansion_error __P((void)); |
|
41 static void maybe_make_readline_line __P((char *)); |
|
42 *************** |
|
43 *** 244,251 **** |
|
44 --- 247,261 ---- |
|
45 int dircomplete_spelling = 0; |
|
46 |
|
47 + /* Expand directory names during word/filename completion. */ |
|
48 + int dircomplete_expand = 0; |
|
49 + int dircomplete_expand_relpath = 0; |
|
50 + |
|
51 static char *bash_completer_word_break_characters = " \t\n\"'@><=;|&(:"; |
|
52 static char *bash_nohostname_word_break_characters = " \t\n\"'><=;|&(:"; |
|
53 /* )) */ |
|
54 |
|
55 + static const char *default_filename_quote_characters = " \t\n\\\"'@<>=;|&()#$`?*[!:{~"; /*}*/ |
|
56 + static char *custom_filename_quote_characters = 0; |
|
57 + |
|
58 static rl_hook_func_t *old_rl_startup_hook = (rl_hook_func_t *)NULL; |
|
59 |
|
60 *************** |
|
61 *** 502,506 **** |
|
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; |
|
65 |
|
66 rl_filename_rewrite_hook = bash_filename_rewrite_hook; |
|
67 --- 512,516 ---- |
|
68 /* Tell the completer that we might want to follow symbolic links or |
|
69 do other expansion on directory names. */ |
|
70 ! set_directory_hook (); |
|
71 |
|
72 rl_filename_rewrite_hook = bash_filename_rewrite_hook; |
|
73 *************** |
|
74 *** 530,534 **** |
|
75 |
|
76 /* characters that need to be quoted when appearing in filenames. */ |
|
77 ! rl_filename_quote_characters = " \t\n\\\"'@<>=;|&()#$`?*[!:{~"; /*}*/ |
|
78 |
|
79 rl_filename_quoting_function = bash_quote_filename; |
|
80 --- 540,544 ---- |
|
81 |
|
82 /* characters that need to be quoted when appearing in filenames. */ |
|
83 ! rl_filename_quote_characters = default_filename_quote_characters; |
|
84 |
|
85 rl_filename_quoting_function = bash_quote_filename; |
|
86 *************** |
|
87 *** 565,570 **** |
|
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; |
|
92 } |
|
93 |
|
94 --- 575,582 ---- |
|
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; |
|
99 + |
|
100 + set_directory_hook (); |
|
101 } |
|
102 |
|
103 *************** |
|
104 *** 1280,1283 **** |
|
105 --- 1292,1298 ---- |
|
106 rl_ignore_some_completions_function = filename_completion_ignore; |
|
107 |
|
108 + rl_filename_quote_characters = default_filename_quote_characters; |
|
109 + set_directory_hook (); |
|
110 + |
|
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 |
|
113 *************** |
|
114 *** 1592,1595 **** |
|
115 --- 1607,1616 ---- |
|
116 else |
|
117 { |
|
118 + if (dircomplete_expand && dot_or_dotdot (filename_hint)) |
|
119 + { |
|
120 + dircomplete_expand = 0; |
|
121 + set_directory_hook (); |
|
122 + dircomplete_expand = 1; |
|
123 + } |
|
124 mapping_over = 4; |
|
125 goto inner; |
|
126 *************** |
|
127 *** 1792,1795 **** |
|
128 --- 1813,1819 ---- |
|
129 inner: |
|
130 val = rl_filename_completion_function (filename_hint, istate); |
|
131 + if (mapping_over == 4 && dircomplete_expand) |
|
132 + set_directory_hook (); |
|
133 + |
|
134 istate = 1; |
|
135 |
|
136 *************** |
|
137 *** 2694,2697 **** |
|
138 --- 2718,2767 ---- |
|
139 } |
|
140 |
|
141 + /* Functions to save and restore the appropriate directory hook */ |
|
142 + /* This is not static so the shopt code can call it */ |
|
143 + void |
|
144 + set_directory_hook () |
|
145 + { |
|
146 + if (dircomplete_expand) |
|
147 + { |
|
148 + rl_directory_completion_hook = bash_directory_completion_hook; |
|
149 + rl_directory_rewrite_hook = (rl_icppfunc_t *)0; |
|
150 + } |
|
151 + else |
|
152 + { |
|
153 + rl_directory_rewrite_hook = bash_directory_completion_hook; |
|
154 + rl_directory_completion_hook = (rl_icppfunc_t *)0; |
|
155 + } |
|
156 + } |
|
157 + |
|
158 + static rl_icppfunc_t * |
|
159 + save_directory_hook () |
|
160 + { |
|
161 + rl_icppfunc_t *ret; |
|
162 + |
|
163 + if (dircomplete_expand) |
|
164 + { |
|
165 + ret = rl_directory_completion_hook; |
|
166 + rl_directory_completion_hook = (rl_icppfunc_t *)NULL; |
|
167 + } |
|
168 + else |
|
169 + { |
|
170 + ret = rl_directory_rewrite_hook; |
|
171 + rl_directory_rewrite_hook = (rl_icppfunc_t *)NULL; |
|
172 + } |
|
173 + |
|
174 + return ret; |
|
175 + } |
|
176 + |
|
177 + static void |
|
178 + restore_directory_hook (hookf) |
|
179 + rl_icppfunc_t *hookf; |
|
180 + { |
|
181 + if (dircomplete_expand) |
|
182 + rl_directory_completion_hook = hookf; |
|
183 + else |
|
184 + rl_directory_rewrite_hook = hookf; |
|
185 + } |
|
186 + |
|
187 /* Handle symbolic link references and other directory name |
|
188 expansions while hacking completion. This should return 1 if it modifies |
|
189 *************** |
|
190 *** 2703,2720 **** |
|
191 { |
|
192 char *local_dirname, *new_dirname, *t; |
|
193 ! int return_value, should_expand_dirname; |
|
194 WORD_LIST *wl; |
|
195 struct stat sb; |
|
196 |
|
197 ! return_value = should_expand_dirname = 0; |
|
198 local_dirname = *dirname; |
|
199 |
|
200 ! if (mbschr (local_dirname, '$')) |
|
201 ! should_expand_dirname = 1; |
|
202 else |
|
203 { |
|
204 t = mbschr (local_dirname, '`'); |
|
205 if (t && unclosed_pair (local_dirname, strlen (local_dirname), "`") == 0) |
|
206 ! should_expand_dirname = 1; |
|
207 } |
|
208 |
|
209 --- 2773,2801 ---- |
|
210 { |
|
211 char *local_dirname, *new_dirname, *t; |
|
212 ! int return_value, should_expand_dirname, nextch, closer; |
|
213 WORD_LIST *wl; |
|
214 struct stat sb; |
|
215 |
|
216 ! return_value = should_expand_dirname = nextch = closer = 0; |
|
217 local_dirname = *dirname; |
|
218 |
|
219 ! if (t = mbschr (local_dirname, '$')) |
|
220 ! { |
|
221 ! should_expand_dirname = '$'; |
|
222 ! nextch = t[1]; |
|
223 ! /* Deliberately does not handle the deprecated $[...] arithmetic |
|
224 ! expansion syntax */ |
|
225 ! if (nextch == '(') |
|
226 ! closer = ')'; |
|
227 ! else if (nextch == '{') |
|
228 ! closer = '}'; |
|
229 ! else |
|
230 ! nextch = 0; |
|
231 ! } |
|
232 else |
|
233 { |
|
234 t = mbschr (local_dirname, '`'); |
|
235 if (t && unclosed_pair (local_dirname, strlen (local_dirname), "`") == 0) |
|
236 ! should_expand_dirname = '`'; |
|
237 } |
|
238 |
|
239 *************** |
|
240 *** 2740,2743 **** |
|
241 --- 2821,2841 ---- |
|
242 dispose_words (wl); |
|
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) |
|
248 + { |
|
249 + int i, j, c; |
|
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++) |
|
253 + { |
|
254 + if (c == should_expand_dirname || c == nextch || c == closer) |
|
255 + continue; |
|
256 + custom_filename_quote_characters[j++] = c; |
|
257 + } |
|
258 + custom_filename_quote_characters[j] = '\0'; |
|
259 + rl_filename_quote_characters = custom_filename_quote_characters; |
|
260 + } |
|
261 } |
|
262 else |
|
263 *************** |
|
264 *** 2759,2762 **** |
|
265 --- 2857,2871 ---- |
|
266 } |
|
267 |
|
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? */ |
|
274 + |
|
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])) |
|
280 { |
|
281 *************** |
|
282 *** 2764,2767 **** |
|
283 --- 2873,2885 ---- |
|
284 int len1, len2; |
|
285 |
|
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 |
|
289 + temp1 = temp2+'/' |
|
290 + That is, |
|
291 + strcmp (temp1, temp2) == 0 |
|
292 + after adding a slash to temp2 below. It should be safe to not |
|
293 + change those. |
|
294 + */ |
|
295 t = get_working_directory ("symlink-hook"); |
|
296 temp1 = make_absolute (local_dirname, t); |
|
297 *************** |
|
298 *** 2798,2802 **** |
|
299 } |
|
300 } |
|
301 ! return_value |= STREQ (local_dirname, temp2) == 0; |
|
302 free (local_dirname); |
|
303 *dirname = temp2; |
|
304 --- 2916,2928 ---- |
|
305 } |
|
306 } |
|
307 ! |
|
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); |
|
317 *dirname = temp2; |
|
318 *************** |
|
319 *** 3003,3012 **** |
|
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\"\'"; |
|
330 --- 3129,3139 ---- |
|
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; |
|
335 + |
|
336 + orig_dir_func = save_directory_hook (); |
|
337 + |
|
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\"\'"; |
|
342 *************** |
|
343 *** 3016,3023 **** |
|
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; |
|
349 |
|
350 return r; |
|
351 } |
|
352 --- 3143,3151 ---- |
|
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; |
|
357 |
|
358 + restore_directory_hook (orig_dir_func); |
|
359 + |
|
360 return r; |
|
361 } |
|
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 |
|
365 *************** |
|
366 *** 34,41 **** |
|
367 --- 34,46 ---- |
|
368 extern int bash_re_edit __P((char *)); |
|
369 |
|
370 + extern void bashline_set_event_hook __P((void)); |
|
371 + extern void bashline_reset_event_hook __P((void)); |
|
372 + |
|
373 extern int bind_keyseq_to_unix_command __P((char *)); |
|
374 |
|
375 extern char **bash_default_completion __P((const char *, int, int, int, int)); |
|
376 |
|
377 + void set_directory_hook __P((void)); |
|
378 + |
|
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 |
|
384 *************** |
|
385 *** 62,65 **** |
|
386 --- 62,69 ---- |
|
387 #include "bashgetopt.h" |
|
388 |
|
389 + #if defined (READLINE) |
|
390 + # include "../bashline.h" |
|
391 + #endif |
|
392 + |
|
393 #if defined (HISTORY) |
|
394 # include "../bashhist.h" |
|
395 *************** |
|
396 *** 95,99 **** |
|
397 extern int no_empty_command_completion; |
|
398 extern int force_fignore; |
|
399 ! extern int dircomplete_spelling; |
|
400 |
|
401 extern int enable_hostname_completion __P((int)); |
|
402 --- 99,103 ---- |
|
403 extern int no_empty_command_completion; |
|
404 extern int force_fignore; |
|
405 ! extern int dircomplete_spelling, dircomplete_expand; |
|
406 |
|
407 extern int enable_hostname_completion __P((int)); |
|
408 *************** |
|
409 *** 122,125 **** |
|
410 --- 126,133 ---- |
|
411 #endif |
|
412 |
|
413 + #if defined (READLINE) |
|
414 + static int shopt_set_complete_direxpand __P((char *, int)); |
|
415 + #endif |
|
416 + |
|
417 static int shopt_login_shell; |
|
418 static int shopt_compat31; |
|
419 *************** |
|
420 *** 151,154 **** |
|
421 --- 159,163 ---- |
|
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 }, |
|
426 #endif |
|
427 *************** |
|
428 *** 536,539 **** |
|
429 --- 545,559 ---- |
|
430 } |
|
431 |
|
432 + #if defined (READLINE) |
|
433 + static int |
|
434 + shopt_set_complete_direxpand (option_name, mode) |
|
435 + char *option_name; |
|
436 + int mode; |
|
437 + { |
|
438 + set_directory_hook (); |
|
439 + return 0; |
|
440 + } |
|
441 + #endif |
|
442 + |
|
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 |
|
449 *************** |
|
450 *** 8949,8952 **** |
|
451 --- 8949,8962 ---- |
|
452 The default bash behavior remains as in previous versions. |
|
453 .TP 8 |
|
454 + .B direxpand |
|
455 + If set, |
|
456 + .B bash |
|
457 + replaces directory names with the results of word expansion when performing |
|
458 + filename completion. This changes the contents of the readline editing |
|
459 + buffer. |
|
460 + If not set, |
|
461 + .B bash |
|
462 + attempts to preserve what the user typed. |
|
463 + .TP 8 |
|
464 .B dirspell |
|
465 If set, |
|
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 |
|
469 *************** |
|
470 *** 4536,4539 **** |
|
471 --- 4536,4546 ---- |
|
472 The default Bash behavior remains as in previous versions. |
|
473 |
|
474 + @item direxpand |
|
475 + If set, Bash |
|
476 + replaces directory names with the results of word expansion when performing |
|
477 + filename completion. This changes the contents of the readline editing |
|
478 + buffer. |
|
479 + If not set, Bash attempts to preserve what the user typed. |
|
480 + |
|
481 @item dirspell |
|
482 If set, Bash |
|
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 |
|
486 *************** |
|
487 *** 13,16 **** |
|
488 --- 13,17 ---- |
|
489 shopt -u compat40 |
|
490 shopt -u compat41 |
|
491 + shopt -u direxpand |
|
492 shopt -u dirspell |
|
493 shopt -u dotglob |
|
494 *************** |
|
495 *** 69,72 **** |
|
496 --- 70,74 ---- |
|
497 shopt -u compat40 |
|
498 shopt -u compat41 |
|
499 + shopt -u direxpand |
|
500 shopt -u dirspell |
|
501 shopt -u dotglob |
|
502 *************** |
|
503 *** 102,105 **** |
|
504 --- 104,108 ---- |
|
505 compat40 off |
|
506 compat41 off |
|
507 + direxpand off |
|
508 dirspell off |
|
509 dotglob off |
|
510 *** ../bash-4.2-patched/patchlevel.h Sat Jun 12 20:14:48 2010 |
|
511 --- patchlevel.h Thu Feb 24 21:41:34 2011 |
|
512 *************** |
|
513 *** 26,30 **** |
|
514 looks for to find the patch level (for the sccs version string). */ |
|
515 |
|
516 ! #define PATCHLEVEL 28 |
|
517 |
|
518 #endif /* _PATCHLEVEL_H_ */ |
|
519 --- 26,30 ---- |
|
520 looks for to find the patch level (for the sccs version string). */ |
|
521 |
|
522 ! #define PATCHLEVEL 29 |
|
523 |
|
524 #endif /* _PATCHLEVEL_H_ */ |
|