]>
Commit | Line | Data |
---|---|---|
e5ec3c9c ER |
1 | BASH PATCH REPORT |
2 | ================= | |
3 | ||
4 | Bash-Release: 4.2 | |
5 | Patch-ID: bash42-029 | |
6 | ||
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 | |
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_ */ |