]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Strengthen gcc-internal-format: Check that %< %>, %r %R, %{ %} come in pairs.
authorBruno Haible <bruno@clisp.org>
Thu, 19 Jun 2025 11:35:55 +0000 (13:35 +0200)
committerBruno Haible <bruno@clisp.org>
Thu, 19 Jun 2025 11:35:55 +0000 (13:35 +0200)
* 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.

gettext-tools/src/format-gcc-internal.c
gettext-tools/tests/format-gcc-internal-1
gettext-tools/tests/format-gcc-internal-2

index 535a0005ca028b1466efdb9d329f5a1b7eda78a7..7c9d3ca1240001a88ea86fb37c382b787ff13b4f 100644 (file)
@@ -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;
index 482e8f40491732c44e8f6e13bb457aea1684909b..590cf876fe4476b54c43068b435c1d185d86eb48 100755 (executable)
@@ -7,20 +7,26 @@ cat <<\EOF > f-gi-1.data
 # Valid: no argument
 "abc%%"
 # Valid: no argument
+"abc%<def%>"
+# 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"
index 5114a4f56257c1e38226c95094dd53fc3b702e0d..fc920220d83088151dbc10bcd1d2fdbae16ee9c3 100755 (executable)
@@ -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%<def"
-msgstr "xyz"
-# Valid: %> doesn't count
-msgid  "abc%>def"
+# Valid: %< %> doesn't count
+msgid  "abc%<def%>ghi"
 msgstr "xyz"
+# Invalid: use of %< without %>
+msgid  "abc%<def%>ghi"
+msgstr "xyz%<"
+# Invalid: use of %> without %<
+msgid  "abc%<def%>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"