Correct escaping in quote removal in expand_single_with_glob
Unescaping the result of quote_removal(..., ES_QUOTED_HARD) is not the
same as quote_removal(..., ES_NONE).
@@ -320,12 +320,13 @@ | ||
320 | 320 | return quote_removal_free(e.value, e.cc, escaping); |
321 | 321 | } |
322 | 322 | |
323 | -/* Expands a single word: the four expansions, glob, quote removal and unescape. | |
324 | - * This function doesn't perform brace expansion and field splitting. | |
325 | - * If the result of glob is more than one word, | |
326 | - * - returns the pre-glob pattern string if in the POSIXly correct mode | |
327 | - * - treats as an error otherwise. | |
328 | - * If the "glob" shell option is off, glob is not performed. | |
323 | +/* Expands a single word: the four expansions, pathname expansion, and quote | |
324 | + * removal. | |
325 | + * This function doesn't perform brace expansion or field splitting. | |
326 | + * If the result of pathname expansion is more than one word, this function | |
327 | + * - returns the original pattern string if in the POSIXly correct mode | |
328 | + * - treats it as an error otherwise. | |
329 | + * If the "glob" shell option is off, pathname expansion is not performed. | |
329 | 330 | * The "nullglob" shell option is ignored. |
330 | 331 | * If successful, the resulting word is returned as a newly malloced string. |
331 | 332 | * On error, an error message is printed and NULL is returned. |
@@ -332,51 +333,62 @@ | ||
332 | 333 | * On error in a non-interactive shell, the shell exits. */ |
333 | 334 | char *expand_single_with_glob(const wordunit_T *arg) |
334 | 335 | { |
335 | - wchar_t *exp = expand_single(arg, TT_SINGLE, Q_WORD, ES_QUOTED_HARD); | |
336 | - char *result; | |
336 | + cc_word_T e = expand_single_cc(arg, TT_SINGLE, Q_WORD); | |
337 | + if (e.value == NULL) | |
338 | + goto return_null; | |
337 | 339 | |
338 | - if (exp == NULL) | |
339 | - return NULL; | |
340 | + if (!shopt_glob) | |
341 | + goto quote_removal; | |
340 | 342 | |
341 | - /* glob */ | |
342 | - if (shopt_glob && is_pathname_matching_pattern(exp)) { | |
343 | - plist_T list; | |
344 | - bool ok; | |
343 | + wchar_t *pattern = quote_removal(e.value, e.cc, ES_QUOTED_HARD); | |
344 | + if (!is_pathname_matching_pattern(pattern)) { | |
345 | + free(pattern); | |
346 | + goto quote_removal; | |
347 | + } | |
345 | 348 | |
346 | - pl_init(&list); | |
347 | - set_interruptible_by_sigint(true); | |
348 | - ok = wglob(exp, get_wglobflags(), &list); | |
349 | - set_interruptible_by_sigint(false); | |
350 | - if (!ok) { | |
351 | - free(exp); | |
352 | - plfree(pl_toary(&list), free); | |
353 | - xerror(EINTR, Ngt("redirection")); | |
354 | - result = NULL; | |
355 | - } else if (list.length == 1) { | |
356 | - free(exp); | |
357 | - result = realloc_wcstombs(list.contents[0]); | |
358 | - if (result == NULL) | |
359 | - xerror(EILSEQ, Ngt("redirection")); | |
360 | - pl_destroy(&list); | |
361 | - } else { | |
362 | - plfree(pl_toary(&list), free); | |
363 | - if (posixly_correct) { | |
364 | - goto noglob; | |
365 | - } else { | |
366 | - exp = unescapefree(exp); | |
367 | - xerror(0, Ngt("filename `%ls' matches more than one file"), | |
368 | - exp); | |
369 | - free(exp); | |
370 | - result = NULL; | |
371 | - } | |
349 | + plist_T globresults; | |
350 | + bool ok; | |
351 | + | |
352 | + /* perform pathname expansion */ | |
353 | + pl_init(&globresults); | |
354 | + set_interruptible_by_sigint(true); | |
355 | + ok = wglob(pattern, get_wglobflags(), &globresults); | |
356 | + set_interruptible_by_sigint(false); | |
357 | + free(pattern); | |
358 | + if (!ok) { | |
359 | + plfree(pl_toary(&globresults), free); | |
360 | + xerror(EINTR, Ngt("redirection")); | |
361 | + goto return_null; | |
362 | + } | |
363 | + | |
364 | + /* examine the expansion results */ | |
365 | + wchar_t *wresult; | |
366 | + if (globresults.length == 1) { | |
367 | + wresult = globresults.contents[0]; | |
368 | + pl_destroy(&globresults); | |
369 | + } else { | |
370 | + plfree(pl_toary(&globresults), free); | |
371 | + if (!posixly_correct) { | |
372 | + wchar_t *word = quote_removal(e.value, e.cc, ES_NONE); | |
373 | + xerror(0, Ngt("filename `%ls' matches more than one file"), word); | |
374 | + free(word); | |
375 | + goto return_null; | |
372 | 376 | } |
373 | - } else { | |
374 | -noglob: | |
375 | - result = realloc_wcstombs(unescapefree(exp)); | |
376 | - if (result == NULL) | |
377 | - xerror(EILSEQ, Ngt("redirection")); | |
377 | +quote_removal: | |
378 | + wresult = quote_removal(e.value, e.cc, ES_NONE); | |
378 | 379 | } |
379 | - return result; | |
380 | + | |
381 | + char *mbresult = realloc_wcstombs(wresult); | |
382 | + if (mbresult == NULL) | |
383 | + xerror(EILSEQ, Ngt("redirection")); | |
384 | + free(e.value); | |
385 | + free(e.cc); | |
386 | + return mbresult; | |
387 | + | |
388 | +return_null: | |
389 | + free(e.value); | |
390 | + free(e.cc); | |
391 | + return NULL; | |
380 | 392 | } |
381 | 393 | |
382 | 394 |
@@ -1746,6 +1758,7 @@ | ||
1746 | 1758 | return wb_towcs(&buf); |
1747 | 1759 | } |
1748 | 1760 | |
1761 | +#if 0 | |
1749 | 1762 | /* Same as `unescape', except that the first argument is freed. */ |
1750 | 1763 | wchar_t *unescapefree(wchar_t *s) |
1751 | 1764 | { |
@@ -1756,6 +1769,7 @@ | ||
1756 | 1769 | free(s); |
1757 | 1770 | return result; |
1758 | 1771 | } |
1772 | +#endif | |
1759 | 1773 | |
1760 | 1774 | /* Quotes the specified string using backslashes and single-quotes. The result |
1761 | 1775 | * is suitable for re-parsing as a shell command word that would expand to the |