Allow expanding "$@""$@" to nothing (#40719)
When there are no positional parameters, "$@" should expand to zero
fields. Two times zero fields should be zero fields.
This commit modifies many parts of word expansion algorithm. The most
important change is in the way the double-quotes are removed after the
expansion of $@. In the previous implementation, the double-quotes were
removed in empty field removal, which is done after field splitting.
That behavior more closely resembled the order of expansion steps as
defined in POSIX, but it removed only one pair of double-quotes.
To remove correct number of double-quotes, we now remove them in the
expand_four function, just when expand_param returns zero fields.
To count the number of expanded fields returned from expand_param in
expand_four, expand_param now needs to return the expanded fields
directly. In doing so, the expand_four_T structure is simplified, the
expand_four_inner_T structure is removed, and the expand_four and
expand_param functions return expand_four_T as a return value instead of
an output parameter.
@@ -13,6 +13,8 @@ | ||
13 | 13 | = When an expansion error occurs, the shell now immediately stops |
14 | 14 | expansion rather than trying to expand the remaining part of the |
15 | 15 | word. |
16 | + = When there are no positional parameters, "$@""$@" now expands to | |
17 | + nothing rather than one empty field, as defined in POSIX. | |
16 | 18 | = Quote removal in arithmetic expansion has been modified to match |
17 | 19 | the behavior defined in POSIX. It no longer allows things like |
18 | 20 | $(("2" + \5)). |
@@ -43,34 +43,25 @@ | ||
43 | 43 | #include "yash.h" |
44 | 44 | |
45 | 45 | |
46 | -/* data passed between expansion functions */ | |
46 | +/* result of word expansion */ | |
47 | 47 | struct expand_four_T { |
48 | 48 | plist_T valuelist, cclist; |
49 | - bool zeroword; | |
50 | 49 | }; |
51 | -struct expand_four_inner_T { | |
52 | - struct expand_four_T e; | |
53 | - xwcsbuf_T valuebuf; | |
54 | - xstrbuf_T ccbuf; | |
55 | -}; | |
56 | -/* If expansion yields multiple fields, all the fields are added to `valuelist' | |
57 | - * except that the last field remains in `valuebuf'. Character categories | |
58 | - * (charcategory_T) corresponding to the characters in `valuelist' and | |
59 | - * `valuebuf' are cast to char and added to `cclist' and `ccbuf' accordingly. */ | |
60 | -/* When "$@" appears during expansion and there is no positional parameter, the | |
61 | - * `zeroword' flag is set so that the quoted empty word can be removed later. */ | |
50 | +/* `valuelist' is a list of pointers to newly malloced wide strings that contain | |
51 | + * the expanded word value. `cclist' is a list of pointers to newly malloced | |
52 | + * single-byte strings whose values are charcategory_T cast to char. `cclist' | |
53 | + * must have as many strings as `valuelist' and each string in `cclist' must | |
54 | + * have the same length as the corresponding wide string in `valuelist'. */ | |
62 | 55 | |
63 | 56 | static plist_T expand_word(const wordunit_T *w, |
64 | 57 | tildetype_T tilde, quoting_T quoting, escaping_T escaping) |
65 | 58 | __attribute__((warn_unused_result)); |
66 | 59 | static struct expand_four_T expand_four(const wordunit_T *restrict w, |
67 | - tildetype_T tilde, quoting_T quoting) | |
60 | + tildetype_T tilde, quoting_T quoting, charcategory_T defaultcc) | |
68 | 61 | __attribute__((warn_unused_result)); |
69 | -static bool expand_four_inner(const wordunit_T *restrict w, tildetype_T tilde, | |
70 | - quoting_T quoting, charcategory_T defaultcc, | |
71 | - struct expand_four_inner_T *restrict e) | |
72 | - __attribute__((nonnull(5))); | |
73 | -static void fill_ccbuf(struct expand_four_inner_T *e, charcategory_T c) | |
62 | +static inline void fill_ccbuf( | |
63 | + const xwcsbuf_T *restrict valuebuf, xstrbuf_T *restrict ccbuf, | |
64 | + charcategory_T c) | |
74 | 65 | __attribute__((nonnull)); |
75 | 66 | |
76 | 67 | static wchar_t *expand_tilde(const wchar_t **ss, |
@@ -79,8 +70,7 @@ | ||
79 | 70 | |
80 | 71 | enum indextype_T { IDX_NONE, IDX_ALL, IDX_CONCAT, IDX_NUMBER, }; |
81 | 72 | |
82 | -static bool expand_param(const paramexp_T *restrict p, bool indq, | |
83 | - struct expand_four_inner_T *restrict e) | |
73 | +static struct expand_four_T expand_param(const paramexp_T *p, bool indq) | |
84 | 74 | __attribute__((nonnull)); |
85 | 75 | static enum indextype_T parse_indextype(const wchar_t *indexstr) |
86 | 76 | __attribute__((nonnull,pure)); |
@@ -102,6 +92,11 @@ | ||
102 | 92 | __attribute__((nonnull,malloc,warn_unused_result)); |
103 | 93 | static void subst_length_each(void **slist) |
104 | 94 | __attribute__((nonnull)); |
95 | +static void merge_expand_four( | |
96 | + struct expand_four_T *restrict from, | |
97 | + struct expand_four_T *restrict to, | |
98 | + xwcsbuf_T *restrict valuebuf, xstrbuf_T *restrict ccbuf) | |
99 | + __attribute__((nonnull)); | |
105 | 100 | |
106 | 101 | /* data used in brace expansion */ |
107 | 102 | struct brace_expand_T { |
@@ -204,7 +199,7 @@ | ||
204 | 199 | bool expand_multiple(const wordunit_T *w, plist_T *list) |
205 | 200 | { |
206 | 201 | /* four expansions (w -> valuelist) */ |
207 | - struct expand_four_T expand = expand_four(w, TT_SINGLE, Q_WORD); | |
202 | + struct expand_four_T expand = expand_four(w, TT_SINGLE, Q_WORD, CC_LITERAL); | |
208 | 203 | if (expand.valuelist.contents == NULL) { |
209 | 204 | maybe_exit_on_error(); |
210 | 205 | return false; |
@@ -255,7 +250,7 @@ | ||
255 | 250 | tildetype_T tilde, quoting_T quoting, escaping_T escaping) |
256 | 251 | { |
257 | 252 | /* four expansions */ |
258 | - struct expand_four_T expand = expand_four(w, tilde, quoting); | |
253 | + struct expand_four_T expand = expand_four(w, tilde, quoting, CC_LITERAL); | |
259 | 254 | |
260 | 255 | /* empty field removal & quote removal */ |
261 | 256 | if (expand.valuelist.contents != NULL) |
@@ -343,59 +338,46 @@ | ||
343 | 338 | |
344 | 339 | /********** Four Expansions **********/ |
345 | 340 | |
346 | -/* Performs the four expansions in the specified single word. | |
347 | - * The four expansions are tilde expansion, parameter expansion, command | |
348 | - * substitution, and arithmetic expansion. | |
341 | +/* Performs the four expansions, i.e., tilde expansion, parameter expansion, | |
342 | + * command substitution, and arithmetic expansion. | |
349 | 343 | * If successful, `valuelist' in the return value is the list of the resultant |
350 | 344 | * fields, which are newly malloced wide strings, and `cclist' is the list of |
351 | 345 | * the corresponding charcategory_T strings, which are also newly malloced. |
346 | + * Usually this function produces one or more fields, but it may produce zero | |
347 | + * fields if "$@" is expanded with no positional parameters. | |
352 | 348 | * If unsuccessful, `valuelist' and `cclist' are empty and have NULL `contents'. |
353 | 349 | */ |
354 | 350 | struct expand_four_T expand_four(const wordunit_T *restrict w, |
355 | - tildetype_T tilde, quoting_T quoting) | |
351 | + tildetype_T tilde, quoting_T quoting, charcategory_T defaultcc) | |
356 | 352 | { |
357 | - struct expand_four_inner_T e; | |
358 | - pl_init(&e.e.valuelist); | |
359 | - pl_init(&e.e.cclist); | |
360 | - wb_init(&e.valuebuf); | |
361 | - sb_init(&e.ccbuf); | |
362 | - e.e.zeroword = false; | |
353 | + /* lists to insert the final results into */ | |
354 | + struct expand_four_T e; | |
355 | + pl_init(&e.valuelist); | |
356 | + pl_init(&e.cclist); | |
363 | 357 | |
364 | - if (expand_four_inner(w, tilde, quoting, CC_LITERAL, &e)) { | |
365 | - assert(e.e.valuelist.length == e.e.cclist.length); | |
366 | - assert(e.valuebuf.length == e.ccbuf.length); | |
367 | - pl_add(&e.e.valuelist, wb_towcs(&e.valuebuf)); | |
368 | - pl_add(&e.e.cclist, sb_tostr(&e.ccbuf)); | |
369 | - } else { | |
370 | - plfree(pl_toary(&e.e.valuelist), free); | |
371 | - plfree(pl_toary(&e.e.cclist), free); | |
372 | - wb_destroy(&e.valuebuf); | |
373 | - sb_destroy(&e.ccbuf); | |
374 | - e.e.valuelist.contents = e.e.cclist.contents = NULL; | |
375 | - } | |
376 | - return e.e; | |
377 | -} | |
358 | + /* intermediate value of the currently expanded word */ | |
359 | + xwcsbuf_T valuebuf; | |
360 | + wb_init(&valuebuf); | |
378 | 361 | |
379 | -/* Performs the four expansions in the specified single word. | |
380 | - * The four expansions are tilde expansion, parameter expansion, command | |
381 | - * substitution, and arithmetic expansion. | |
382 | - * The lists and buffers in `e' must have been initialized before calling this | |
383 | - * function. If the expansion yields a single field, the result is appended to | |
384 | - * `e->valuebuf'. If more than one field result, all but the last field are | |
385 | - * appended to `e->valuelist' as newly malloced wide strings and the last field | |
386 | - * remains in `e->valuebuf'. The corresponding charcategory_T strings are added | |
387 | - * to `e->cclist' and `e->ccbuf', having the same count and length as | |
388 | - * `e->valuelist' and `e->valuebuf'. | |
389 | - * The return value is true iff successful. */ | |
390 | -bool expand_four_inner(const wordunit_T *restrict w, tildetype_T tilde, | |
391 | - quoting_T quoting, charcategory_T defaultcc, | |
392 | - struct expand_four_inner_T *restrict e) | |
393 | -{ | |
362 | + /* charcategory_T string corresponding to `valuebuf' */ | |
363 | + xstrbuf_T ccbuf; | |
364 | + sb_init(&ccbuf); | |
365 | + | |
394 | 366 | bool indq = false; /* in a double quote? */ |
395 | 367 | bool first = true; /* is the first word unit? */ |
396 | 368 | const wchar_t *ss; |
397 | 369 | wchar_t *s; |
398 | 370 | |
371 | + /* When there are no positional parameters, expansion of ${@} contained in | |
372 | + * double-quotes is very special: The result is no fields. To handle this | |
373 | + * correctly, we do the following: | |
374 | + * 1. Remove the surrounding double-quotes if a parameter expansion inside | |
375 | + * them happens to expand to no fields. | |
376 | + * 2. Remove the empty field so that the final result is no fields rather | |
377 | + * than one empty field. | |
378 | + * `removedq' is for step 1 and `removeempty' for step 2. */ | |
379 | + bool removedq = false, removeempty = false; | |
380 | + | |
399 | 381 | for (; w != NULL; w = w->next, first = false) { |
400 | 382 | switch (w->wu_type) { |
401 | 383 | case WT_STRING: |
@@ -403,8 +385,9 @@ | ||
403 | 385 | if (first && tilde != TT_NONE) { |
404 | 386 | s = expand_tilde(&ss, w->next, tilde); |
405 | 387 | if (s != NULL) { |
406 | - wb_catfree(&e->valuebuf, s); | |
407 | - fill_ccbuf(e, CC_HARD_EXPANSION | (defaultcc & CC_QUOTED)); | |
388 | + wb_catfree(&valuebuf, s); | |
389 | + fill_ccbuf(&valuebuf, &ccbuf, | |
390 | + CC_HARD_EXPANSION | (defaultcc & CC_QUOTED)); | |
408 | 391 | } |
409 | 392 | } |
410 | 393 | while (*ss != L'\0') { |
@@ -416,22 +399,36 @@ | ||
416 | 399 | case Q_INDQ: case Q_LITERAL: |
417 | 400 | goto default_; |
418 | 401 | } |
419 | - indq = !indq; | |
420 | - wb_wccat(&e->valuebuf, L'"'); | |
421 | - sb_ccat(&e->ccbuf, defaultcc | CC_QUOTATION); | |
402 | + if (!indq) { | |
403 | + indq = true; /* entering a quotation */ | |
404 | + removedq = false; | |
405 | + } else { | |
406 | + indq = false; /* leaving a quotation */ | |
407 | + if (removedq && e.valuelist.length == 0 && | |
408 | + wcscmp(valuebuf.contents, L"\"") == 0 && | |
409 | + (ccbuf.contents[0] & CC_QUOTATION)) { | |
410 | + /* remove the corresponding opening double-quote */ | |
411 | + wb_clear(&valuebuf); | |
412 | + sb_clear(&ccbuf); | |
413 | + removeempty = true; | |
414 | + break; /* and ignore the closing double-quote */ | |
415 | + } | |
416 | + } | |
417 | + wb_wccat(&valuebuf, L'"'); | |
418 | + sb_ccat(&ccbuf, defaultcc | CC_QUOTATION); | |
422 | 419 | break; |
423 | 420 | case L'\'': |
424 | 421 | if (quoting != Q_WORD || indq) |
425 | 422 | goto default_; |
426 | 423 | |
427 | - wb_wccat(&e->valuebuf, L'\''); | |
428 | - sb_ccat(&e->ccbuf, defaultcc | CC_QUOTATION); | |
424 | + wb_wccat(&valuebuf, L'\''); | |
425 | + sb_ccat(&ccbuf, defaultcc | CC_QUOTATION); | |
429 | 426 | |
430 | - add_sq(&ss, &e->valuebuf, false); | |
431 | - fill_ccbuf(e, defaultcc | CC_QUOTED); | |
427 | + add_sq(&ss, &valuebuf, false); | |
428 | + fill_ccbuf(&valuebuf, &ccbuf, defaultcc | CC_QUOTED); | |
432 | 429 | |
433 | - wb_wccat(&e->valuebuf, L'\''); | |
434 | - sb_ccat(&e->ccbuf, defaultcc | CC_QUOTATION); | |
430 | + wb_wccat(&valuebuf, L'\''); | |
431 | + sb_ccat(&ccbuf, defaultcc | CC_QUOTATION); | |
435 | 432 | break; |
436 | 433 | case L'\\': |
437 | 434 | switch (quoting) { |
@@ -451,12 +448,12 @@ | ||
451 | 448 | goto default_; |
452 | 449 | } |
453 | 450 | |
454 | - wb_wccat(&e->valuebuf, L'\\'); | |
455 | - sb_ccat(&e->ccbuf, defaultcc | CC_QUOTATION); | |
451 | + wb_wccat(&valuebuf, L'\\'); | |
452 | + sb_ccat(&ccbuf, defaultcc | CC_QUOTATION); | |
456 | 453 | ss++; |
457 | 454 | if (*ss != L'\0') { |
458 | - wb_wccat(&e->valuebuf, *ss); | |
459 | - sb_ccat(&e->ccbuf, defaultcc | CC_QUOTED); | |
455 | + wb_wccat(&valuebuf, *ss); | |
456 | + sb_ccat(&ccbuf, defaultcc | CC_QUOTED); | |
460 | 457 | } |
461 | 458 | break; |
462 | 459 | case L':': |
@@ -464,29 +461,36 @@ | ||
464 | 461 | goto default_; |
465 | 462 | |
466 | 463 | /* perform tilde expansion after a colon */ |
467 | - wb_wccat(&e->valuebuf, L':'); | |
468 | - sb_ccat(&e->ccbuf, defaultcc); | |
464 | + wb_wccat(&valuebuf, L':'); | |
465 | + sb_ccat(&ccbuf, defaultcc); | |
469 | 466 | ss++; |
470 | 467 | s = expand_tilde(&ss, w->next, tilde); |
471 | 468 | if (s != NULL) { |
472 | - wb_catfree(&e->valuebuf, s); | |
473 | - fill_ccbuf(e, CC_HARD_EXPANSION); | |
469 | + wb_catfree(&valuebuf, s); | |
470 | + fill_ccbuf(&valuebuf, &ccbuf, CC_HARD_EXPANSION); | |
474 | 471 | } |
475 | 472 | continue; |
476 | 473 | default_: |
477 | 474 | default: |
478 | - wb_wccat(&e->valuebuf, *ss); | |
479 | - sb_ccat(&e->ccbuf, defaultcc | (indq * CC_QUOTED)); | |
475 | + wb_wccat(&valuebuf, *ss); | |
476 | + sb_ccat(&ccbuf, defaultcc | (indq * CC_QUOTED)); | |
480 | 477 | break; |
481 | 478 | } |
482 | 479 | ss++; |
483 | 480 | } |
484 | 481 | break; |
485 | - case WT_PARAM: | |
486 | - if (!expand_param(w->wu_param, | |
487 | - indq || quoting == Q_LITERAL || (defaultcc & CC_QUOTED), | |
488 | - e)) | |
489 | - return false; | |
482 | + case WT_PARAM:; | |
483 | + struct expand_four_T e2 = expand_param(w->wu_param, | |
484 | + indq || quoting == Q_LITERAL || (defaultcc & CC_QUOTED)); | |
485 | + if (e2.valuelist.contents == NULL) | |
486 | + goto failure; | |
487 | + if (e2.valuelist.length == 0) { | |
488 | + if (indq) | |
489 | + removedq = true; | |
490 | + else | |
491 | + removeempty = true; | |
492 | + } | |
493 | + merge_expand_four(&e2, &e, &valuebuf, &ccbuf); | |
490 | 494 | break; |
491 | 495 | case WT_CMDSUB: |
492 | 496 | s = exec_command_substitution(&w->wu_cmdsub); |
@@ -497,22 +501,40 @@ | ||
497 | 501 | s = evaluate_arithmetic(s); |
498 | 502 | cat_s: |
499 | 503 | if (s == NULL) |
500 | - return false; | |
501 | - wb_catfree(&e->valuebuf, s); | |
502 | - fill_ccbuf(e, CC_SOFT_EXPANSION | | |
504 | + goto failure; | |
505 | + wb_catfree(&valuebuf, s); | |
506 | + fill_ccbuf(&valuebuf, &ccbuf, CC_SOFT_EXPANSION | | |
503 | 507 | (indq * CC_QUOTED) | (defaultcc & CC_QUOTED)); |
504 | 508 | break; |
505 | 509 | } |
506 | 510 | } |
507 | 511 | |
508 | - return true; | |
512 | + /* empty field removal */ | |
513 | + if (removeempty && e.valuelist.length == 0 && valuebuf.length == 0) { | |
514 | + wb_destroy(&valuebuf); | |
515 | + sb_destroy(&ccbuf); | |
516 | + } else { | |
517 | + pl_add(&e.valuelist, wb_towcs(&valuebuf)); | |
518 | + pl_add(&e.cclist, sb_tostr(&ccbuf)); | |
519 | + } | |
520 | + | |
521 | + return e; | |
522 | + | |
523 | +failure: | |
524 | + sb_destroy(&ccbuf); | |
525 | + wb_destroy(&valuebuf); | |
526 | + plfree(pl_toary(&e.cclist), free); | |
527 | + plfree(pl_toary(&e.valuelist), free); | |
528 | + e.valuelist.contents = e.cclist.contents = NULL; | |
529 | + return e; | |
509 | 530 | } |
510 | 531 | |
511 | 532 | /* Appends to `e->ccbuf' as many `c's as needed to match the length with |
512 | 533 | * `e->valuebuf'. */ |
513 | -void fill_ccbuf(struct expand_four_inner_T *e, charcategory_T c) | |
534 | +void fill_ccbuf(const xwcsbuf_T *restrict valuebuf, xstrbuf_T *restrict ccbuf, | |
535 | + charcategory_T c) | |
514 | 536 | { |
515 | - sb_ccat_repeat(&e->ccbuf, c, e->valuebuf.length - e->ccbuf.length); | |
537 | + sb_ccat_repeat(ccbuf, c, valuebuf->length - ccbuf->length); | |
516 | 538 | } |
517 | 539 | |
518 | 540 | /* Performs tilde expansion. |
@@ -582,10 +604,10 @@ | ||
582 | 604 | } |
583 | 605 | |
584 | 606 | /* Performs parameter expansion. |
585 | - * The result is put in `e'. | |
586 | - * Returns true iff successful. */ | |
587 | -bool expand_param(const paramexp_T *restrict p, bool indq, | |
588 | - struct expand_four_inner_T *restrict e) | |
607 | + * If successful, the return value contains valid lists of pointers to newly | |
608 | + * malloced strings. Note that the lists may contain no strings. | |
609 | + * If unsuccessful, the lists have NULL `contents'. */ | |
610 | +struct expand_four_T expand_param(const paramexp_T *p, bool indq) | |
589 | 611 | { |
590 | 612 | /* parse indices first */ |
591 | 613 | ssize_t startindex, endindex; |
@@ -595,7 +617,7 @@ | ||
595 | 617 | } else { |
596 | 618 | wchar_t *start = expand_single(p->pe_start, TT_NONE, Q_WORD, ES_NONE); |
597 | 619 | if (start == NULL) |
598 | - return false; | |
620 | + goto failure1; | |
599 | 621 | indextype = parse_indextype(start); |
600 | 622 | if (indextype != IDX_NONE) { |
601 | 623 | startindex = 0, endindex = SSIZE_MAX; |
@@ -602,10 +624,10 @@ | ||
602 | 624 | free(start); |
603 | 625 | if (p->pe_end != NULL) { |
604 | 626 | xerror(0, Ngt("the parameter index is invalid")); |
605 | - return false; | |
627 | + goto failure1; | |
606 | 628 | } |
607 | 629 | } else if (!evaluate_index(start, &startindex)) { |
608 | - return false; | |
630 | + goto failure1; | |
609 | 631 | } else { |
610 | 632 | if (p->pe_end == NULL) { |
611 | 633 | endindex = (startindex == -1) ? SSIZE_MAX : startindex; |
@@ -613,7 +635,7 @@ | ||
613 | 635 | wchar_t *end = expand_single( |
614 | 636 | p->pe_end, TT_NONE, Q_WORD, ES_NONE); |
615 | 637 | if (end == NULL || !evaluate_index(end, &endindex)) |
616 | - return false; | |
638 | + goto failure1; | |
617 | 639 | } |
618 | 640 | if (startindex == 0) |
619 | 641 | startindex = SSIZE_MAX; |
@@ -631,7 +653,7 @@ | ||
631 | 653 | if (p->pe_type & PT_NEST) { |
632 | 654 | plist_T plist = expand_word(p->pe_nest, TT_NONE, Q_WORD, ES_NONE); |
633 | 655 | if (plist.contents == NULL) |
634 | - return false; | |
656 | + goto failure1; | |
635 | 657 | v.type = (plist.length == 1) ? GV_SCALAR : GV_ARRAY; |
636 | 658 | v.count = plist.length; |
637 | 659 | v.values = pl_toary(&plist); |
@@ -744,8 +766,8 @@ | ||
744 | 766 | if (unset) { |
745 | 767 | subst: |
746 | 768 | plfree(values, free); |
747 | - return expand_four_inner(p->pe_subst, TT_SINGLE, substq, | |
748 | - CC_SOFT_EXPANSION | (indq * CC_QUOTED), e); | |
769 | + return expand_four(p->pe_subst, TT_SINGLE, substq, | |
770 | + CC_SOFT_EXPANSION | (indq * CC_QUOTED)); | |
749 | 771 | } |
750 | 772 | break; |
751 | 773 | case PT_ASSIGN: |
@@ -754,34 +776,34 @@ | ||
754 | 776 | if (p->pe_type & PT_NEST) { |
755 | 777 | xerror(0, |
756 | 778 | Ngt("a nested parameter expansion cannot be assigned")); |
757 | - return false; | |
779 | + goto failure1; | |
758 | 780 | } else if (!is_name(p->pe_name)) { |
759 | 781 | xerror(0, Ngt("cannot assign to parameter `%ls' " |
760 | 782 | "in parameter expansion"), |
761 | 783 | p->pe_name); |
762 | - return false; | |
784 | + goto failure1; | |
763 | 785 | } else if ((v.type == GV_ARRAY_CONCAT) |
764 | 786 | || (v.type == GV_ARRAY && startindex + 1 != endindex)) { |
765 | 787 | xerror(0, Ngt("the specified index does not support assignment " |
766 | 788 | "in the parameter expansion of array `%ls'"), |
767 | 789 | p->pe_name); |
768 | - return false; | |
790 | + goto failure1; | |
769 | 791 | } |
770 | 792 | subst = expand_single(p->pe_subst, TT_SINGLE, substq, ES_NONE); |
771 | 793 | if (subst == NULL) |
772 | - return false; | |
794 | + goto failure1; | |
773 | 795 | if (v.type != GV_ARRAY) { |
774 | 796 | assert(v.type == GV_NOTFOUND || v.type == GV_SCALAR); |
775 | 797 | if (!set_variable( |
776 | 798 | p->pe_name, xwcsdup(subst), SCOPE_GLOBAL, false)) { |
777 | 799 | free(subst); |
778 | - return false; | |
800 | + goto failure1; | |
779 | 801 | } |
780 | 802 | } else { |
781 | 803 | assert(0 <= startindex && (size_t) startindex <= v.count); |
782 | 804 | if (!set_array_element(p->pe_name, startindex, xwcsdup(subst))){ |
783 | 805 | free(subst); |
784 | - return false; | |
806 | + goto failure1; | |
785 | 807 | } |
786 | 808 | } |
787 | 809 | values = xmallocn(2, sizeof *values); |
@@ -792,17 +814,15 @@ | ||
792 | 814 | break; |
793 | 815 | case PT_ERROR: |
794 | 816 | if (unset) { |
795 | - plfree(values, free); | |
796 | 817 | print_subst_as_error(p, substq); |
797 | - return false; | |
818 | + goto failure2; | |
798 | 819 | } |
799 | 820 | break; |
800 | 821 | } |
801 | 822 | |
802 | 823 | if (unset && !shopt_unset) { |
803 | - plfree(values, free); | |
804 | 824 | xerror(0, Ngt("parameter `%ls' is not set"), p->pe_name); |
805 | - return false; | |
825 | + goto failure2; | |
806 | 826 | } |
807 | 827 | |
808 | 828 | /* PT_MATCH, PT_SUBST */ |
@@ -810,24 +830,19 @@ | ||
810 | 830 | switch (p->pe_type & PT_MASK) { |
811 | 831 | case PT_MATCH: |
812 | 832 | match = expand_single(p->pe_match, TT_SINGLE, Q_WORD, ES_QUOTED); |
813 | - if (match == NULL) { | |
814 | - plfree(values, free); | |
815 | - return false; | |
816 | - } | |
833 | + if (match == NULL) | |
834 | + goto failure2; | |
817 | 835 | match_each(values, match, p->pe_type); |
818 | 836 | free(match); |
819 | 837 | break; |
820 | 838 | case PT_SUBST: |
821 | 839 | match = expand_single(p->pe_match, TT_SINGLE, Q_WORD, ES_QUOTED); |
822 | - if (match == NULL) { | |
823 | - plfree(values, free); | |
824 | - return false; | |
825 | - } | |
840 | + if (match == NULL) | |
841 | + goto failure2; | |
826 | 842 | subst = expand_single(p->pe_subst, TT_SINGLE, Q_WORD, ES_NONE); |
827 | 843 | if (subst == NULL) { |
828 | 844 | free(match); |
829 | - plfree(values, free); | |
830 | - return false; | |
845 | + goto failure2; | |
831 | 846 | } |
832 | 847 | subst_each(values, match, subst, p->pe_type); |
833 | 848 | free(match); |
@@ -843,30 +858,28 @@ | ||
843 | 858 | if (p->pe_type & PT_NUMBER) |
844 | 859 | subst_length_each(values); |
845 | 860 | |
846 | - /* add the elements of `values' to `e->valuelist' */ | |
847 | - if (values[0] == NULL) { | |
848 | - if (indq) | |
849 | - e->e.zeroword = true; | |
850 | - } else { | |
851 | - charcategory_T cc = CC_SOFT_EXPANSION | (indq * CC_QUOTED); | |
861 | + struct expand_four_T e; | |
852 | 862 | |
853 | - /* add the first element */ | |
854 | - wb_catfree(&e->valuebuf, values[0]); | |
855 | - fill_ccbuf(e, cc); | |
863 | + pl_initwith(&e.valuelist, values, plcount(values)); | |
864 | + pl_initwithmax(&e.cclist, e.valuelist.length); | |
856 | 865 | |
857 | - /* add the other elements */ | |
858 | - for (size_t i = 1; values[i] != NULL; i++) { | |
859 | - pl_add(&e->e.valuelist, wb_towcs(&e->valuebuf)); | |
860 | - pl_add(&e->e.cclist, sb_tostr(&e->ccbuf)); | |
861 | - | |
862 | - wb_initwith(&e->valuebuf, values[i]); | |
863 | - sb_initwithmax(&e->ccbuf, e->valuebuf.length); | |
864 | - fill_ccbuf(e, cc); | |
865 | - } | |
866 | + /* create `e.cclist' contents */ | |
867 | + charcategory_T cc = CC_SOFT_EXPANSION | (indq * CC_QUOTED); | |
868 | + for (size_t i = 0; i < e.valuelist.length; i++) { | |
869 | + size_t n = wcslen(e.valuelist.contents[i]); | |
870 | + xstrbuf_T ccbuf; | |
871 | + sb_initwithmax(&ccbuf, n); | |
872 | + sb_ccat_repeat(&ccbuf, cc, n); | |
873 | + pl_add(&e.cclist, sb_tostr(&ccbuf)); | |
866 | 874 | } |
867 | - free(values); | |
868 | 875 | |
869 | - return true; | |
876 | + return e; | |
877 | + | |
878 | +failure2: | |
879 | + plfree(values, free); | |
880 | +failure1: | |
881 | + e.valuelist.contents = e.cclist.contents = NULL; | |
882 | + return e; | |
870 | 883 | } |
871 | 884 | |
872 | 885 | /* Returns IDX_ALL, IDX_CONCAT, IDX_NUMBER if `indexstr' is L"@", L"*", |
@@ -1088,7 +1101,51 @@ | ||
1088 | 1101 | } |
1089 | 1102 | } |
1090 | 1103 | |
1104 | +/* Merge a result of `expand_param' into another expand_four_T value. | |
1105 | + * All the values in `from->valuelist' and `from->cclist' except the last one | |
1106 | + * are moved into `to->valuelist' and `to->cclist', respectively, and the last | |
1107 | + * one is left in `valuebuf' and `ccbuf'. | |
1108 | + * `from->valuelist' and `from->cclist' are destroyed in this function. */ | |
1109 | +void merge_expand_four( | |
1110 | + struct expand_four_T *restrict from, | |
1111 | + struct expand_four_T *restrict to, | |
1112 | + xwcsbuf_T *restrict valuebuf, xstrbuf_T *restrict ccbuf) | |
1113 | +{ | |
1114 | + assert(from->valuelist.length == from->cclist.length); | |
1115 | + assert(to->valuelist.length == to->cclist.length); | |
1116 | + assert(valuebuf->length == ccbuf->length); | |
1091 | 1117 | |
1118 | + if (from->valuelist.length > 0) { | |
1119 | + /* add the first element */ | |
1120 | + wb_catfree(valuebuf, from->valuelist.contents[0]); | |
1121 | + sb_ncat_force(ccbuf, from->cclist.contents[0], | |
1122 | + valuebuf->length - ccbuf->length); | |
1123 | + free(from->cclist.contents[0]); | |
1124 | + | |
1125 | + if (from->valuelist.length > 1) { | |
1126 | + pl_add(&to->valuelist, wb_towcs(valuebuf)); | |
1127 | + pl_add(&to->cclist, sb_tostr(ccbuf)); | |
1128 | + | |
1129 | + /* add the other elements but last */ | |
1130 | + pl_ncat(&to->valuelist, | |
1131 | + &from->valuelist.contents[1], from->valuelist.length - 2); | |
1132 | + pl_ncat(&to->cclist, | |
1133 | + &from->cclist.contents[1], from->cclist.length - 2); | |
1134 | + | |
1135 | + /* add the last element */ | |
1136 | + size_t i = from->valuelist.length - 1; | |
1137 | + wb_initwith(valuebuf, from->valuelist.contents[i]); | |
1138 | + sb_initwithmax(ccbuf, valuebuf->length); | |
1139 | + sb_ncat_force(ccbuf, from->cclist.contents[i], valuebuf->length); | |
1140 | + free(from->cclist.contents[i]); | |
1141 | + } | |
1142 | + } | |
1143 | + | |
1144 | + pl_destroy(&from->valuelist); | |
1145 | + pl_destroy(&from->cclist); | |
1146 | +} | |
1147 | + | |
1148 | + | |
1092 | 1149 | /********** Brace Expansions **********/ |
1093 | 1150 | |
1094 | 1151 | /* Performs brace expansion in each element of the specified array. |
@@ -1796,10 +1853,7 @@ | ||
1796 | 1853 | /* empty field removal */ |
1797 | 1854 | if (e->valuelist.length == 1) { |
1798 | 1855 | const wchar_t *field = e->valuelist.contents[0]; |
1799 | - const char *cc = e->cclist.contents[0]; | |
1800 | - if (field[0] == L'\0' || | |
1801 | - (e->zeroword && wcscmp(field, L"\"\"") == 0 && | |
1802 | - (cc[0] & cc[1] & CC_QUOTATION))) { | |
1856 | + if (field[0] == L'\0') { | |
1803 | 1857 | pl_clear(&e->valuelist, free); |
1804 | 1858 | pl_clear(&e->cclist, free); |
1805 | 1859 | } |