Correct escaping in quote removal in double bracket command
Unescaping the result of quote_removal(..., ES_QUOTED_HARD) is not the
same as quote_removal(..., ES_NONE).
@@ -77,18 +77,21 @@ | ||
77 | 77 | #if YASH_ENABLE_DOUBLE_BRACKET |
78 | 78 | static int eval_dbexp(const dbexp_T *e) |
79 | 79 | __attribute__((nonnull)); |
80 | -static inline wchar_t *expand_double_bracket_operand_escaped( | |
81 | - const wordunit_T *w) | |
82 | - __attribute__((nonnull,malloc,warn_unused_result)); | |
80 | +static inline cc_word_T expand_double_bracket_operand(const wordunit_T *w) | |
81 | + __attribute__((nonnull,warn_unused_result)); | |
83 | 82 | static inline wchar_t *expand_double_bracket_operand_unescaped( |
84 | 83 | const wordunit_T *w) |
85 | 84 | __attribute__((nonnull,malloc,warn_unused_result)); |
86 | -static bool test_triple_db( | |
87 | - const wchar_t *lhs, const wchar_t *op, const wchar_t *rhs_escaped) | |
85 | +static bool test_triple_db(const wchar_t *lhs, const wchar_t *op, | |
86 | + const wchar_t *rhsvalue, const char *rhscc) | |
88 | 87 | __attribute__((nonnull)); |
89 | -static bool test_triple_args( | |
90 | - const wchar_t *left, const wchar_t *op, const wchar_t *right) | |
88 | +static bool quote_removal_and_test_triple( | |
89 | + const wchar_t *lhs, const wchar_t *op, | |
90 | + const wchar_t *rhsvalue, const char *rhscc, escaping_T escaping) | |
91 | 91 | __attribute__((nonnull)); |
92 | +static bool quote_removal_and_pattern_matching( | |
93 | + const wchar_t *lhs, const wchar_t *rhsvalue, const char *rhscc) | |
94 | + __attribute__((nonnull)); | |
92 | 95 | #endif |
93 | 96 | |
94 | 97 |
@@ -725,7 +728,8 @@ | ||
725 | 728 | { |
726 | 729 | int lhs_result; |
727 | 730 | bool result; |
728 | - wchar_t *lhs = NULL, *rhs = NULL; | |
731 | + wchar_t *lhs = NULL; | |
732 | + cc_word_T rhs = { NULL, NULL }; | |
729 | 733 | |
730 | 734 | switch (e->type) { |
731 | 735 | case DBE_OR: |
@@ -746,25 +750,27 @@ | ||
746 | 750 | } |
747 | 751 | |
748 | 752 | case DBE_UNARY: |
749 | - rhs = expand_double_bracket_operand_unescaped(e->rhs.word); | |
750 | - if (rhs == NULL) | |
753 | + rhs.value = expand_double_bracket_operand_unescaped(e->rhs.word); | |
754 | + if (rhs.value == NULL) | |
751 | 755 | return Exit_TESTERROR; |
752 | - result = test_double((void *[]) { e->operator, rhs }); | |
756 | + result = test_double((void *[]) { e->operator, rhs.value }); | |
753 | 757 | break; |
754 | 758 | case DBE_BINARY: |
755 | 759 | lhs = expand_double_bracket_operand_unescaped(e->lhs.word); |
756 | 760 | if (lhs == NULL) |
757 | 761 | return Exit_TESTERROR; |
758 | - rhs = expand_double_bracket_operand_escaped(e->rhs.word); | |
759 | - if (rhs == NULL) | |
762 | + rhs = expand_double_bracket_operand(e->rhs.word); | |
763 | + if (rhs.value == NULL) { | |
764 | + free(lhs); | |
760 | 765 | return Exit_TESTERROR; |
761 | - result = test_triple_db(lhs, e->operator, rhs); | |
766 | + } | |
767 | + result = test_triple_db(lhs, e->operator, rhs.value, rhs.cc); | |
762 | 768 | break; |
763 | 769 | case DBE_STRING: |
764 | - rhs = expand_double_bracket_operand_unescaped(e->rhs.word); | |
765 | - if (rhs == NULL) | |
770 | + rhs.value = expand_double_bracket_operand_unescaped(e->rhs.word); | |
771 | + if (rhs.value == NULL) | |
766 | 772 | return Exit_TESTERROR; |
767 | - result = test_single((void *[]) { rhs }); | |
773 | + result = test_single((void *[]) { rhs.value }); | |
768 | 774 | break; |
769 | 775 | |
770 | 776 | default: |
@@ -772,17 +778,17 @@ | ||
772 | 778 | } |
773 | 779 | |
774 | 780 | free(lhs); |
775 | - free(rhs); | |
781 | + free(rhs.value); | |
782 | + free(rhs.cc); | |
776 | 783 | if (yash_error_message_count > 0) |
777 | 784 | return Exit_TESTERROR; |
778 | 785 | return result ? Exit_TRUE : Exit_FALSE; |
779 | 786 | } |
780 | 787 | |
781 | -/* Expands the operand of a primary. | |
782 | - * The result may contain backslash escapes. */ | |
783 | -wchar_t *expand_double_bracket_operand_escaped(const wordunit_T *w) | |
788 | +/* Expands the operand of a primary, but without quote removal. */ | |
789 | +cc_word_T expand_double_bracket_operand(const wordunit_T *w) | |
784 | 790 | { |
785 | - return expand_single(w, TT_SINGLE, Q_WORD, ES_QUOTED); | |
791 | + return expand_single_cc(w, TT_SINGLE, Q_WORD); | |
786 | 792 | } |
787 | 793 | |
788 | 794 | /* Expands the operand of a primary. |
@@ -793,10 +799,10 @@ | ||
793 | 799 | } |
794 | 800 | |
795 | 801 | /* Tests the specified three-token (binary) primary in the double-bracket |
796 | - * command. The left-hand-side must be given literal while the right-hand-side | |
797 | - * backslash-escaped. */ | |
798 | -bool test_triple_db( | |
799 | - const wchar_t *lhs, const wchar_t *op, const wchar_t *rhs_escaped) | |
802 | + * command. The left-hand-side must be given literal (with quote removal already | |
803 | + * performed) while the right-hand-side quoted (without quote removal). */ | |
804 | +bool test_triple_db(const wchar_t *lhs, const wchar_t *op, | |
805 | + const wchar_t *rhsvalue, const char *rhscc) | |
800 | 806 | { |
801 | 807 | /* Some string comparison primaries in the double-bracket command are |
802 | 808 | * different from those in the test built-in. */ |
@@ -804,30 +810,44 @@ | ||
804 | 810 | case L'=': |
805 | 811 | if (op[1] == L'~') { |
806 | 812 | assert(op[2] == L'\0'); |
807 | - return test_triple_args(lhs, op, rhs_escaped); | |
813 | + return quote_removal_and_test_triple( | |
814 | + lhs, op, rhsvalue, rhscc, ES_QUOTED); | |
808 | 815 | } |
809 | 816 | if (op[1] == L'\0' || (op[1] == L'=' && op[2] == L'\0')) |
810 | - return match_pattern(lhs, rhs_escaped); | |
817 | + return quote_removal_and_pattern_matching(lhs, rhsvalue, rhscc); | |
811 | 818 | break; |
812 | 819 | case L'!': |
813 | 820 | assert(op[1] == L'='); |
814 | 821 | if (op[2] == L'\0') |
815 | - return !match_pattern(lhs, rhs_escaped); | |
822 | + return | |
823 | + !quote_removal_and_pattern_matching(lhs, rhsvalue, rhscc); | |
816 | 824 | break; |
817 | 825 | } |
818 | 826 | |
819 | - wchar_t *rhs = unescape(rhs_escaped); | |
820 | - bool result = test_triple_args(lhs, op, rhs); | |
827 | + return quote_removal_and_test_triple(lhs, op, rhsvalue, rhscc, ES_NONE); | |
828 | +} | |
829 | + | |
830 | +/* Performs quote removal on the right hand side and then applies `test_triple'. | |
831 | + */ | |
832 | +bool quote_removal_and_test_triple( | |
833 | + const wchar_t *lhs, const wchar_t *op, | |
834 | + const wchar_t *rhsvalue, const char *rhscc, escaping_T escaping) | |
835 | +{ | |
836 | + wchar_t *rhs = quote_removal(rhsvalue, rhscc, escaping); | |
837 | + void *args[] = { (void *) lhs, (void *) op, (void *) rhs, }; | |
838 | + bool result = test_triple(args); | |
821 | 839 | free(rhs); |
822 | 840 | return result; |
823 | 841 | } |
824 | 842 | |
825 | -/* Tests the specified three-token expression. */ | |
826 | -bool test_triple_args( | |
827 | - const wchar_t *left, const wchar_t *op, const wchar_t *right) | |
843 | +/* Performs quote removal on the right hand side and then pattern matching. */ | |
844 | +bool quote_removal_and_pattern_matching( | |
845 | + const wchar_t *lhs, const wchar_t *rhsvalue, const char *rhscc) | |
828 | 846 | { |
829 | - void *args[] = { (void *) left, (void *) op, (void *) right, }; | |
830 | - return test_triple(args); | |
847 | + wchar_t *rhs = quote_removal(rhsvalue, rhscc, ES_QUOTED); | |
848 | + bool result = match_pattern(lhs, rhs); | |
849 | + free(rhs); | |
850 | + return result; | |
831 | 851 | } |
832 | 852 | |
833 | 853 | #endif /* YASH_ENABLE_DOUBLE_BRACKET */ |
@@ -142,9 +142,6 @@ | ||
142 | 142 | __attribute__((nonnull)); |
143 | 143 | static inline bool should_escape(char c, charcategory_T cc, escaping_T escaping) |
144 | 144 | __attribute__((const)); |
145 | -static wchar_t *quote_removal( | |
146 | - const wchar_t *restrict s, const char *restrict cc, escaping_T escaping) | |
147 | - __attribute__((nonnull,malloc,warn_unused_result)); | |
148 | 145 | static wchar_t *quote_removal_free( |
149 | 146 | wchar_t *restrict s, char *restrict cc, escaping_T escaping) |
150 | 147 | __attribute__((nonnull,malloc,warn_unused_result)); |
@@ -124,6 +124,9 @@ | ||
124 | 124 | __attribute__((nonnull)); |
125 | 125 | extern wchar_t *unquote(const wchar_t *s) |
126 | 126 | __attribute__((nonnull,malloc,warn_unused_result)); |
127 | +extern wchar_t *quote_removal( | |
128 | + const wchar_t *restrict s, const char *restrict cc, escaping_T escaping) | |
129 | + __attribute__((nonnull,malloc,warn_unused_result)); | |
127 | 130 | |
128 | 131 | extern wchar_t *parse_and_expand_string( |
129 | 132 | const wchar_t *s, const char *name, _Bool esc) |