From: Bruno Haible Date: Thu, 19 Jun 2025 11:35:55 +0000 (+0200) Subject: Strengthen gcc-internal-format: Check that %< %>, %r %R, %{ %} come in pairs. X-Git-Tag: v0.26~103 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=790fc440dfaf588b7c7076f6caf2bd86d670ad80;p=thirdparty%2Fgettext.git Strengthen gcc-internal-format: Check that %< %>, %r %R, %{ %} come in pairs. * gettext-tools/src/format-gcc-internal.c (format_parse): New local variables in_quote_group, in_color_group, in_url_group. Add pairing checks for %< %>, %r %R, %{ %}. * gettext-tools/tests/format-gcc-internal-1: Update. * gettext-tools/tests/format-gcc-internal-2: Update. --- diff --git a/gettext-tools/src/format-gcc-internal.c b/gettext-tools/src/format-gcc-internal.c index 535a0005c..7c9d3ca12 100644 --- a/gettext-tools/src/format-gcc-internal.c +++ b/gettext-tools/src/format-gcc-internal.c @@ -202,6 +202,9 @@ format_parse (const char *format, bool translated, char *fdi, struct spec spec; unsigned int numbered_allocated; unsigned int unnumbered_arg_count; + unsigned int in_quote_group; + unsigned int in_color_group; + unsigned int in_url_group; struct spec *result; spec.directives = 0; @@ -211,6 +214,9 @@ format_parse (const char *format, bool translated, char *fdi, spec.uses_current_locus = false; numbered_allocated = 0; unnumbered_arg_count = 0; + in_quote_group = 0; + in_color_group = 0; + in_url_group = 0; for (; *format != '\0';) /* Invariant: spec.numbered_arg_count == 0 || unnumbered_arg_count == 0. */ @@ -220,9 +226,48 @@ format_parse (const char *format, bool translated, char *fdi, FDI_SET (format - 1, FMTDIR_START); spec.directives++; - if (*format == '%' || *format == '<' || *format == '>' - || *format == '}' || *format == '\'' || *format == 'R') + if (*format == '%' || *format == '\'') ; + else if (*format == '<') + { + if (in_quote_group) + { + *invalid_reason = xasprintf (_("The directive number %u opens a quote group, but the previous one is not terminated."), spec.directives); + FDI_SET (format, FMTDIR_ERROR); + goto bad_format; + } + in_quote_group = spec.directives; + } + else if (*format == '>') + { + if (!in_quote_group) + { + *invalid_reason = xasprintf (_("The directive number %u does not match a preceding '%%%c'."), spec.directives, '<'); + FDI_SET (format, FMTDIR_ERROR); + goto bad_format; + } + in_quote_group = 0; + } + else if (*format == 'R') + { + if (!in_color_group) + { + *invalid_reason = xasprintf (_("The directive number %u does not match a preceding '%%%c'."), spec.directives, 'r'); + FDI_SET (format, FMTDIR_ERROR); + goto bad_format; + } + in_color_group = 0; + } + else if (*format == '}') + { + if (!in_url_group) + { + *invalid_reason = xasprintf (_("The directive number %u does not match a preceding '%%%c'."), spec.directives, '{'); + FDI_SET (format, FMTDIR_ERROR); + goto bad_format; + } + in_url_group = 0; + } else if (*format == 'm') spec.uses_err_no = true; else if (*format == 'C') @@ -482,9 +527,27 @@ format_parse (const char *format, bool translated, char *fdi, else if (*format == 'Z') type = FAT_INT_ARRAY_PART1; else if (*format == 'r') - type = FAT_COLOR; + { + if (in_color_group) + { + *invalid_reason = xasprintf (_("The directive number %u opens a color group, but the previous one is not terminated."), spec.directives); + FDI_SET (format, FMTDIR_ERROR); + goto bad_format; + } + in_color_group = spec.directives; + type = FAT_COLOR; + } else if (*format == '{') - type = FAT_URL; + { + if (in_url_group) + { + *invalid_reason = xasprintf (_("The directive number %u opens a URL group, but the previous one is not terminated."), spec.directives); + FDI_SET (format, FMTDIR_ERROR); + goto bad_format; + } + in_url_group = spec.directives; + type = FAT_URL; + } else if (*format == 'D') type = FAT_TREE | FAT_TREE_DECL; else if (*format == 'F') @@ -604,6 +667,22 @@ format_parse (const char *format, bool translated, char *fdi, format++; } + if (in_quote_group) + { + *invalid_reason = xasprintf (_("The quote group opened by the directive number %u is not terminated."), in_quote_group); + goto bad_format; + } + if (in_color_group) + { + *invalid_reason = xasprintf (_("The color group opened by the directive number %u is not terminated."), in_color_group); + goto bad_format; + } + if (in_url_group) + { + *invalid_reason = xasprintf (_("The URL group opened by the directive number %u is not terminated."), in_url_group); + goto bad_format; + } + /* Convert the unnumbered argument array to numbered arguments. */ if (unnumbered_arg_count > 0) spec.numbered_arg_count = unnumbered_arg_count; diff --git a/gettext-tools/tests/format-gcc-internal-1 b/gettext-tools/tests/format-gcc-internal-1 index 482e8f404..590cf876f 100755 --- a/gettext-tools/tests/format-gcc-internal-1 +++ b/gettext-tools/tests/format-gcc-internal-1 @@ -7,20 +7,26 @@ cat <<\EOF > f-gi-1.data # Valid: no argument "abc%%" # Valid: no argument +"abc%" +# Invalid: unterminated quote group "abc%<" -# Valid: no argument +# Invalid: %> without %< "abc%>" -# Valid: no argument +# Invalid: %} without %{ "abc%}" # Valid: no argument "abc%'" -# Valid: no argument +# Invalid: %R without %r "abc%R" # Valid: no argument "abc%m" # Valid: one color argument +"abc%rdef%R" +# Invalid: unterminated color group "abc%r" # Valid: one URL argument +"abc%{def%}" +# Invalid: unterminated URL group "abc%{" # Valid: one character argument "abc%c" diff --git a/gettext-tools/tests/format-gcc-internal-2 b/gettext-tools/tests/format-gcc-internal-2 index 5114a4f56..fc920220d 100755 --- a/gettext-tools/tests/format-gcc-internal-2 +++ b/gettext-tools/tests/format-gcc-internal-2 @@ -7,12 +7,21 @@ cat <<\EOF > f-gi-2.data # Valid: %% doesn't count msgid "abc%%def" msgstr "xyz" -# Valid: %< doesn't count -msgid "abc% doesn't count -msgid "abc%>def" +# Valid: %< %> doesn't count +msgid "abc%ghi" msgstr "xyz" +# Invalid: use of %< without %> +msgid "abc%ghi" +msgstr "xyz%<" +# Invalid: use of %> without %< +msgid "abc%ghi" +msgstr "xyz%>" +# Invalid: use of %r without %R +msgid "abc%rdef%Rghi" +msgstr "xyz%r" +# Invalid: use of %{ without %} +msgid "abc%{def%}ghi" +msgstr "xyz%{" # Valid: %' doesn't count msgid "abc%'def" msgstr "xyz" @@ -71,157 +80,157 @@ msgstr "xyz%u" msgid "abc%u" msgstr "xyz%x" # Invalid: type incompatibility -msgid "abc%r" -msgstr "xyz%{" +msgid "abc%r%R" +msgstr "xyz%{%}" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%c" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%s" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%i" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%u" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%f" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%p" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%@" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%e" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%Z" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%D" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%E" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%F" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%T" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%V" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%v" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%A" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%H" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%I" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%O" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%P" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%Q" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%S" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%X" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%C" # Invalid: type incompatibility -msgid "abc%r" +msgid "abc%r%R" msgstr "xyz%L" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%c" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%s" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%i" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%u" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%f" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%p" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%@" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%e" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%Z" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%D" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%E" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%F" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%T" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%V" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%v" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%A" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%H" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%I" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%O" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%P" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%Q" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%S" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%X" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%C" # Invalid: type incompatibility -msgid "abc%{" +msgid "abc%{%}" msgstr "xyz%L" # Invalid: type incompatibility msgid "abc%c"