Holger Hoffstätte wrote: > - reconfigure the project: > cd mytests && ./configure --disable-nls CFLAGS="-pipe > -Werror=format-security -O"
Did this work for you? I get errors cc1: error: ‘-Wformat-security’ ignored without ‘-Wformat’ [-Werror=format-security] > - make will fail with an error like: > clean-temp.c: In function 'create_temp_dir': > clean-temp.c:234:7: error: format not a string literal and no format > arguments [-Werror=format-security] > 234 | error (0, errno, > | ^~~~~ > clean-temp.c:234:7: error: format not a string literal and no format > arguments [-Werror=format-security] > > The errors cannot be reproduced when nls is enabled Indeed, the warnings do not appear when - using gcc (as opposed to clang), AND - in C mode (as opposed to C++ mode), AND - the option -fno-builtin is not specified. > which suggests > an implementation difference in the generated error() macros depending > on the nls feature. Nope, the difference is with the function 'gettext' (a gcc built-in) vs. the macro 'gettext'. One needs to rely on the translation checking of 'msgfmt -c'. Then it is irrelevant whether a format string takes 0 parameters or a positive number of parameters. Thus the "and no format arguments" part of the warning is pointless when it comes to *gettext invocations. This patch fixes the warnings. 2025-05-28 Bruno Haible <br...@clisp.org> gettext-h: Avoid gcc -Wformat-security warnings with --disable-nls. Reported by Holger Hoffstätte <hol...@applied-asynchrony.com> in <https://lists.gnu.org/archive/html/bug-gnulib/2025-05/msg00225.html>. * lib/gettext.h (gettext, dgettext, dcgettext): With gcc in C mode, define these as inline functions. * lib/sigpipe-die.c (sigpipe_die): Use translated string as a format string, relying on the format string checking done by 'msgfmt -c'. * lib/xmemcoll.c (collate_error): Revert commit from 2025-01-17. * lib/xprintf.c (xvprintf, xvfprintf): Likewise. * lib/openat-die.c (openat_save_fail, openat_restore_fail): Revert commit from 2024-12-10. diff --git a/lib/gettext.h b/lib/gettext.h index ea0c27e000..bb3d9755de 100644 --- a/lib/gettext.h +++ b/lib/gettext.h @@ -59,18 +59,43 @@ # endif # endif -/* Disabled NLS. - The casts to 'const char *' serve the purpose of producing warnings - for invalid uses of the value returned from these functions. - On pre-ANSI systems without 'const', the config.h file is supposed to - contain "#define const". */ -# undef gettext -# define gettext(Msgid) ((const char *) (Msgid)) -# undef dgettext -# define dgettext(Domainname, Msgid) ((void) (Domainname), gettext (Msgid)) -# undef dcgettext -# define dcgettext(Domainname, Msgid, Category) \ - ((void) (Category), dgettext (Domainname, Msgid)) +/* Disabled NLS. */ +# if defined __GNUC__ && !defined __clang__ && !defined __cplusplus +/* Use inline functions, to avoid warnings + warning: format not a string literal and no format arguments + that don't occur with enabled NLS. */ +__attribute__ ((__always_inline__, __gnu_inline__)) extern inline +const char * +gettext (const char *msgid) +{ + return msgid; +} +__attribute__ ((__always_inline__, __gnu_inline__)) extern inline +const char * +dgettext (const char *domain, const char *msgid) +{ + (void) domain; + return msgid; +} +__attribute__ ((__always_inline__, __gnu_inline__)) extern inline +const char * +dcgettext (const char *domain, const char *msgid, int category) +{ + (void) domain; + (void) category; + return msgid; +} +# else +/* The casts to 'const char *' serve the purpose of producing warnings + for invalid uses of the value returned from these functions. */ +# undef gettext +# define gettext(Msgid) ((const char *) (Msgid)) +# undef dgettext +# define dgettext(Domainname, Msgid) ((void) (Domainname), gettext (Msgid)) +# undef dcgettext +# define dcgettext(Domainname, Msgid, Category) \ + ((void) (Category), dgettext (Domainname, Msgid)) +# endif # undef ngettext # define ngettext(Msgid1, Msgid2, N) \ ((N) == 1 \ diff --git a/lib/openat-die.c b/lib/openat-die.c index 3fbb5d86e4..79a5b23bc8 100644 --- a/lib/openat-die.c +++ b/lib/openat-die.c @@ -34,7 +34,7 @@ _Noreturn void openat_save_fail (int errnum) { #ifndef GNULIB_LIBPOSIX - error (exit_failure, errnum, "%s", + error (exit_failure, errnum, _("unable to record current working directory")); #endif /* _Noreturn cannot be applied to error, since it returns @@ -53,7 +53,7 @@ _Noreturn void openat_restore_fail (int errnum) { #ifndef GNULIB_LIBPOSIX - error (exit_failure, errnum, "%s", + error (exit_failure, errnum, _("failed to return to initial working directory")); #endif diff --git a/lib/sigpipe-die.c b/lib/sigpipe-die.c index 006f3b1636..ddd05cd49c 100644 --- a/lib/sigpipe-die.c +++ b/lib/sigpipe-die.c @@ -33,7 +33,7 @@ void sigpipe_die (void) { - error (exit_failure, 0, "%s", + error (exit_failure, 0, _("error writing to a closed pipe or socket")); /* Ensure that this function really does not return. */ diff --git a/lib/xmemcoll.c b/lib/xmemcoll.c index c0ac7c4863..d1b141c9e0 100644 --- a/lib/xmemcoll.c +++ b/lib/xmemcoll.c @@ -36,8 +36,8 @@ collate_error (int collation_errno, char const *s1, size_t s1len, char const *s2, size_t s2len) { - error (0, collation_errno, "%s", _("string comparison failed")); - error (0, 0, "%s", _("Set LC_ALL='C' to work around the problem.")); + error (0, collation_errno, _("string comparison failed")); + error (0, 0, _("Set LC_ALL='C' to work around the problem.")); error (exit_failure, 0, _("The strings compared were %s and %s."), quotearg_n_style_mem (0, locale_quoting_style, s1, s1len), diff --git a/lib/xprintf.c b/lib/xprintf.c index 4d9a3e5e4d..790af5320b 100644 --- a/lib/xprintf.c +++ b/lib/xprintf.c @@ -45,7 +45,7 @@ xvprintf (char const *restrict format, va_list args) { off64_t retval = vzprintf (format, args); if (retval < 0 && ! ferror (stdout)) - error (exit_failure, errno, "%s", _("cannot perform formatted output")); + error (exit_failure, errno, _("cannot perform formatted output")); return retval; } @@ -67,7 +67,7 @@ xvfprintf (FILE *restrict stream, char const *restrict format, va_list args) { off64_t retval = vfzprintf (stream, format, args); if (retval < 0 && ! ferror (stream)) - error (exit_failure, errno, "%s", _("cannot perform formatted output")); + error (exit_failure, errno, _("cannot perform formatted output")); return retval; }