]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Add support for Shell printf format strings, part 3.
authorBruno Haible <bruno@clisp.org>
Mon, 23 Jun 2025 19:20:02 +0000 (21:20 +0200)
committerBruno Haible <bruno@clisp.org>
Mon, 23 Jun 2025 19:20:02 +0000 (21:20 +0200)
* gettext-tools/src/format-invalid.h (INVALID_FLAG_FOR): New macro.
* gettext-tools/src/format-java-printf.c (INVALID_FLAG_FOR): Remove macro.
* gettext-tools/src/format-sh-printf.c (format_parse): Reject format strings
with invalid flag+specifier combinations.
* gettext-tools/tests/format-sh-printf-1: Add more test cases.
* gettext-tools/doc/gettext.texi (sh-format): Mention the ' flag.

gettext-tools/doc/gettext.texi
gettext-tools/src/format-invalid.h
gettext-tools/src/format-java-printf.c
gettext-tools/src/format-sh-printf.c
gettext-tools/tests/format-sh-printf-1

index ce2c0f09d5f707d329165118e6eb9a81b30b8c6d..fcbb421308e1b28a6baf2dc633efb0a7cb1ee59e 100644 (file)
@@ -10266,6 +10266,9 @@ but without the obsolescent @code{b} conversion specifier.
 Extensions by the GNU coreutils @samp{printf} command
 (@url{https://www.gnu.org/software/coreutils/manual/html_node/printf-invocation.html})
 are not supported:
+use of the @samp{'} flag in the
+@code{%i}, @code{%d}, @code{%u}, @code{%f}, @code{%F}, @code{%g}, @code{%G}
+directives;
 use of @samp{*} or @samp{*@var{m}$} as width or precision;
 use of size specifiers @code{h}, @code{l}, @code{j}, @code{z}, @code{t} (ignored);
 and the escape sequences @code{\c},
index 763ca5988845f55c6f0039f0800a1c072e8739b3..c38daf5ab54327d551fb4be4f8e3c1d255024d17 100644 (file)
@@ -39,5 +39,8 @@
    ? xasprintf (_("In the directive number %u, the character '%c' is not a valid conversion specifier."), directive_number, conv_char) \
    : xasprintf (_("The character that terminates the directive number %u is not a valid conversion specifier."), directive_number))
 
+#define INVALID_FLAG_FOR(directive_number,flag_char,conv_char) \
+  xasprintf (_("In the directive number %u, the flag '%c' is invalid for the conversion '%c'."), directive_number, flag_char, conv_char)
+
 #define INVALID_INCOMPATIBLE_ARG_TYPES(arg_number) \
   xasprintf (_("The string refers to argument number %u in incompatible ways."), arg_number)
index a22bbf50e543bc2ebf510374e5dd2b79b7b1a4e8..99f476941053c87411cde3804ab73198a709b0d9 100644 (file)
@@ -131,9 +131,6 @@ numbered_arg_compare (const void *p1, const void *p2)
 #define INVALID_LAST_ARG(directive_number) \
   xasprintf (_("In the directive number %u, the reference to the argument of the previous directive is invalid."), directive_number)
 
-#define INVALID_FLAG_FOR(directive_number,flag_char,conv_char) \
-  xasprintf (_("In the directive number %u, the flag '%c' is invalid for the conversion '%c'."), directive_number, flag_char, conv_char)
-
 #define INVALID_WIDTH_FOR(directive_number,conv_char) \
   xasprintf (_("In the directive number %u, a width is invalid for the conversion '%c'."), directive_number, conv_char)
 
index bcaf2d1884397e8ed1570ca510c061b2edcfd1c4..feb2b930918236cb278bc76d5a041bb37e3b76da 100644 (file)
@@ -65,6 +65,9 @@
        - 'u', 'o', 'x', 'X', that need an unsigned integer argument,
        - [optional in POSIX, but supported here:] 'e', 'E', 'f', 'F', 'g', 'G',
          'a', 'A', that need a floating-point argument.
+   Some flag+specifier combinations are invalid:
+     - The '#' flag with the specifiers 'c', 's', 'i', 'd', 'u'.
+     - The '0' flag with the specifiers 'c', 's'.
    Additionally there is the directive '%%', which takes no argument.
    Numbered ('%m$' or '*m$') and unnumbered argument specifications cannot
    be used in the same string.
@@ -172,11 +175,17 @@ format_parse (const char *format, bool translated, char *fdi,
               }
 
             /* Parse flags.  */
+            bool have_hash_flag = false;
+            bool have_zero_flag = false;
             while (*format == ' ' || *format == '+' || *format == '-'
                    || *format == '#' || *format == '0')
               {
                 if (*format == ' ')
                   likely_intentional = false;
+                if (*format == '#')
+                  have_hash_flag = true;
+                if (*format == '0')
+                  have_zero_flag = true;
                 format++;
               }
 
@@ -228,6 +237,23 @@ format_parse (const char *format, bool translated, char *fdi,
                 goto bad_format;
               }
 
+            if (have_hash_flag
+                && (*format == 'c' || *format == 's'
+                    || *format == 'i' || *format == 'd' || *format == 'u'))
+              {
+                *invalid_reason =
+                  INVALID_FLAG_FOR (spec.directives, '#', *format);
+                FDI_SET (format, FMTDIR_ERROR);
+                goto bad_format;
+              }
+            if (have_zero_flag && (*format == 'c' || *format == 's'))
+              {
+                *invalid_reason =
+                  INVALID_FLAG_FOR (spec.directives, '0', *format);
+                FDI_SET (format, FMTDIR_ERROR);
+                goto bad_format;
+              }
+
             if (number)
               {
                 /* Numbered argument.  */
index ee76814a03ecc1d8d61271f92704c6ba09652e83..e304f232000097bf85cfd2cf14ae587418529689 100755 (executable)
@@ -103,6 +103,28 @@ LC_ALL=C sed -e "$escape_backslashes" <<\EOF > f-sp-1.data
 "abc%3$*2$.*1$g"
 # Invalid: zero
 "abc%2$*0$.*1$g"
+# Invalid: flag not valid
+"abc%'d"
+# Invalid: flag not valid
+"abc%'u"
+# Invalid: flag not valid
+"abc%'f"
+# Invalid: flag not valid
+"abc%'g"
+# Invalid: flag not valid for specifier
+"abc%#c"
+# Invalid: flag not valid for specifier
+"abc%#s"
+# Invalid: flag not valid for specifier
+"abc%#i"
+# Invalid: flag not valid for specifier
+"abc%#d"
+# Invalid: flag not valid for specifier
+"abc%#u"
+# Invalid: flag not valid for specifier
+"abc%0c"
+# Invalid: flag not valid for specifier
+"abc%0s"
 # Valid: escape sequence
 "abc%%def\\"
 # Valid: escape sequence