• R/O
  • SSH
  • HTTPS

yash: 提交


Commit MetaInfo

修訂4093 (tree)
時間2020-09-29 00:21:00
作者magicant

Log Message

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.

Change Summary

差異

--- yash/trunk/NEWS (revision 4092)
+++ yash/trunk/NEWS (revision 4093)
@@ -13,6 +13,8 @@
1313 = When an expansion error occurs, the shell now immediately stops
1414 expansion rather than trying to expand the remaining part of the
1515 word.
16+ = When there are no positional parameters, "$@""$@" now expands to
17+ nothing rather than one empty field, as defined in POSIX.
1618 = Quote removal in arithmetic expansion has been modified to match
1719 the behavior defined in POSIX. It no longer allows things like
1820 $(("2" + \5)).
--- yash/trunk/expand.c (revision 4092)
+++ yash/trunk/expand.c (revision 4093)
@@ -43,34 +43,25 @@
4343 #include "yash.h"
4444
4545
46-/* data passed between expansion functions */
46+/* result of word expansion */
4747 struct expand_four_T {
4848 plist_T valuelist, cclist;
49- bool zeroword;
5049 };
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'. */
6255
6356 static plist_T expand_word(const wordunit_T *w,
6457 tildetype_T tilde, quoting_T quoting, escaping_T escaping)
6558 __attribute__((warn_unused_result));
6659 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)
6861 __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)
7465 __attribute__((nonnull));
7566
7667 static wchar_t *expand_tilde(const wchar_t **ss,
@@ -79,8 +70,7 @@
7970
8071 enum indextype_T { IDX_NONE, IDX_ALL, IDX_CONCAT, IDX_NUMBER, };
8172
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)
8474 __attribute__((nonnull));
8575 static enum indextype_T parse_indextype(const wchar_t *indexstr)
8676 __attribute__((nonnull,pure));
@@ -102,6 +92,11 @@
10292 __attribute__((nonnull,malloc,warn_unused_result));
10393 static void subst_length_each(void **slist)
10494 __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));
105100
106101 /* data used in brace expansion */
107102 struct brace_expand_T {
@@ -204,7 +199,7 @@
204199 bool expand_multiple(const wordunit_T *w, plist_T *list)
205200 {
206201 /* 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);
208203 if (expand.valuelist.contents == NULL) {
209204 maybe_exit_on_error();
210205 return false;
@@ -255,7 +250,7 @@
255250 tildetype_T tilde, quoting_T quoting, escaping_T escaping)
256251 {
257252 /* 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);
259254
260255 /* empty field removal & quote removal */
261256 if (expand.valuelist.contents != NULL)
@@ -343,59 +338,46 @@
343338
344339 /********** Four Expansions **********/
345340
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.
349343 * If successful, `valuelist' in the return value is the list of the resultant
350344 * fields, which are newly malloced wide strings, and `cclist' is the list of
351345 * 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.
352348 * If unsuccessful, `valuelist' and `cclist' are empty and have NULL `contents'.
353349 */
354350 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)
356352 {
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);
363357
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);
378361
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+
394366 bool indq = false; /* in a double quote? */
395367 bool first = true; /* is the first word unit? */
396368 const wchar_t *ss;
397369 wchar_t *s;
398370
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+
399381 for (; w != NULL; w = w->next, first = false) {
400382 switch (w->wu_type) {
401383 case WT_STRING:
@@ -403,8 +385,9 @@
403385 if (first && tilde != TT_NONE) {
404386 s = expand_tilde(&ss, w->next, tilde);
405387 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));
408391 }
409392 }
410393 while (*ss != L'\0') {
@@ -416,22 +399,36 @@
416399 case Q_INDQ: case Q_LITERAL:
417400 goto default_;
418401 }
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);
422419 break;
423420 case L'\'':
424421 if (quoting != Q_WORD || indq)
425422 goto default_;
426423
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);
429426
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);
432429
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);
435432 break;
436433 case L'\\':
437434 switch (quoting) {
@@ -451,12 +448,12 @@
451448 goto default_;
452449 }
453450
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);
456453 ss++;
457454 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);
460457 }
461458 break;
462459 case L':':
@@ -464,29 +461,36 @@
464461 goto default_;
465462
466463 /* 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);
469466 ss++;
470467 s = expand_tilde(&ss, w->next, tilde);
471468 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);
474471 }
475472 continue;
476473 default_:
477474 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));
480477 break;
481478 }
482479 ss++;
483480 }
484481 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);
490494 break;
491495 case WT_CMDSUB:
492496 s = exec_command_substitution(&w->wu_cmdsub);
@@ -497,22 +501,40 @@
497501 s = evaluate_arithmetic(s);
498502 cat_s:
499503 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 |
503507 (indq * CC_QUOTED) | (defaultcc & CC_QUOTED));
504508 break;
505509 }
506510 }
507511
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;
509530 }
510531
511532 /* Appends to `e->ccbuf' as many `c's as needed to match the length with
512533 * `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)
514536 {
515- sb_ccat_repeat(&e->ccbuf, c, e->valuebuf.length - e->ccbuf.length);
537+ sb_ccat_repeat(ccbuf, c, valuebuf->length - ccbuf->length);
516538 }
517539
518540 /* Performs tilde expansion.
@@ -582,10 +604,10 @@
582604 }
583605
584606 /* 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)
589611 {
590612 /* parse indices first */
591613 ssize_t startindex, endindex;
@@ -595,7 +617,7 @@
595617 } else {
596618 wchar_t *start = expand_single(p->pe_start, TT_NONE, Q_WORD, ES_NONE);
597619 if (start == NULL)
598- return false;
620+ goto failure1;
599621 indextype = parse_indextype(start);
600622 if (indextype != IDX_NONE) {
601623 startindex = 0, endindex = SSIZE_MAX;
@@ -602,10 +624,10 @@
602624 free(start);
603625 if (p->pe_end != NULL) {
604626 xerror(0, Ngt("the parameter index is invalid"));
605- return false;
627+ goto failure1;
606628 }
607629 } else if (!evaluate_index(start, &startindex)) {
608- return false;
630+ goto failure1;
609631 } else {
610632 if (p->pe_end == NULL) {
611633 endindex = (startindex == -1) ? SSIZE_MAX : startindex;
@@ -613,7 +635,7 @@
613635 wchar_t *end = expand_single(
614636 p->pe_end, TT_NONE, Q_WORD, ES_NONE);
615637 if (end == NULL || !evaluate_index(end, &endindex))
616- return false;
638+ goto failure1;
617639 }
618640 if (startindex == 0)
619641 startindex = SSIZE_MAX;
@@ -631,7 +653,7 @@
631653 if (p->pe_type & PT_NEST) {
632654 plist_T plist = expand_word(p->pe_nest, TT_NONE, Q_WORD, ES_NONE);
633655 if (plist.contents == NULL)
634- return false;
656+ goto failure1;
635657 v.type = (plist.length == 1) ? GV_SCALAR : GV_ARRAY;
636658 v.count = plist.length;
637659 v.values = pl_toary(&plist);
@@ -744,8 +766,8 @@
744766 if (unset) {
745767 subst:
746768 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));
749771 }
750772 break;
751773 case PT_ASSIGN:
@@ -754,34 +776,34 @@
754776 if (p->pe_type & PT_NEST) {
755777 xerror(0,
756778 Ngt("a nested parameter expansion cannot be assigned"));
757- return false;
779+ goto failure1;
758780 } else if (!is_name(p->pe_name)) {
759781 xerror(0, Ngt("cannot assign to parameter `%ls' "
760782 "in parameter expansion"),
761783 p->pe_name);
762- return false;
784+ goto failure1;
763785 } else if ((v.type == GV_ARRAY_CONCAT)
764786 || (v.type == GV_ARRAY && startindex + 1 != endindex)) {
765787 xerror(0, Ngt("the specified index does not support assignment "
766788 "in the parameter expansion of array `%ls'"),
767789 p->pe_name);
768- return false;
790+ goto failure1;
769791 }
770792 subst = expand_single(p->pe_subst, TT_SINGLE, substq, ES_NONE);
771793 if (subst == NULL)
772- return false;
794+ goto failure1;
773795 if (v.type != GV_ARRAY) {
774796 assert(v.type == GV_NOTFOUND || v.type == GV_SCALAR);
775797 if (!set_variable(
776798 p->pe_name, xwcsdup(subst), SCOPE_GLOBAL, false)) {
777799 free(subst);
778- return false;
800+ goto failure1;
779801 }
780802 } else {
781803 assert(0 <= startindex && (size_t) startindex <= v.count);
782804 if (!set_array_element(p->pe_name, startindex, xwcsdup(subst))){
783805 free(subst);
784- return false;
806+ goto failure1;
785807 }
786808 }
787809 values = xmallocn(2, sizeof *values);
@@ -792,17 +814,15 @@
792814 break;
793815 case PT_ERROR:
794816 if (unset) {
795- plfree(values, free);
796817 print_subst_as_error(p, substq);
797- return false;
818+ goto failure2;
798819 }
799820 break;
800821 }
801822
802823 if (unset && !shopt_unset) {
803- plfree(values, free);
804824 xerror(0, Ngt("parameter `%ls' is not set"), p->pe_name);
805- return false;
825+ goto failure2;
806826 }
807827
808828 /* PT_MATCH, PT_SUBST */
@@ -810,24 +830,19 @@
810830 switch (p->pe_type & PT_MASK) {
811831 case PT_MATCH:
812832 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;
817835 match_each(values, match, p->pe_type);
818836 free(match);
819837 break;
820838 case PT_SUBST:
821839 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;
826842 subst = expand_single(p->pe_subst, TT_SINGLE, Q_WORD, ES_NONE);
827843 if (subst == NULL) {
828844 free(match);
829- plfree(values, free);
830- return false;
845+ goto failure2;
831846 }
832847 subst_each(values, match, subst, p->pe_type);
833848 free(match);
@@ -843,30 +858,28 @@
843858 if (p->pe_type & PT_NUMBER)
844859 subst_length_each(values);
845860
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;
852862
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);
856865
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));
866874 }
867- free(values);
868875
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;
870883 }
871884
872885 /* Returns IDX_ALL, IDX_CONCAT, IDX_NUMBER if `indexstr' is L"@", L"*",
@@ -1088,7 +1101,51 @@
10881101 }
10891102 }
10901103
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);
10911117
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+
10921149 /********** Brace Expansions **********/
10931150
10941151 /* Performs brace expansion in each element of the specified array.
@@ -1796,10 +1853,7 @@
17961853 /* empty field removal */
17971854 if (e->valuelist.length == 1) {
17981855 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') {
18031857 pl_clear(&e->valuelist, free);
18041858 pl_clear(&e->cclist, free);
18051859 }
Show on old repository browser