From: Bruno Haible Date: Sun, 1 Jun 2025 22:11:27 +0000 (+0200) Subject: xgettext: C: Improve heuristic for format strings. X-Git-Tag: v0.26~137 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cdc6870c776922b66cabf11f5bdd5a978a577384;p=thirdparty%2Fgettext.git xgettext: C: Improve heuristic for format strings. * gettext-tools/src/format-c-parse.h (struct spec): Add field 'likely_intentional_directives'. (format_parse_entrails): Set it to the number of directives that don't contain a space. * gettext-tools/src/format-c.c (format_is_unlikely_intentional): Also test the 'likely_intentional_directives' field. * gettext-tools/tests/format-c-6: New file. * gettext-tools/tests/Makefile.am (TESTS): Add it. --- diff --git a/gettext-tools/src/format-c-parse.h b/gettext-tools/src/format-c-parse.h index 0b52d4081..c0ea33395 100644 --- a/gettext-tools/src/format-c-parse.h +++ b/gettext-tools/src/format-c-parse.h @@ -142,6 +142,11 @@ struct unnumbered_arg struct spec { unsigned int directives; + /* We consider a directive as "likely intentional" if it does not contain a + space. This prevents xgettext from flagging strings like "100% complete" + as 'c-format' if they don't occur in a context that requires a format + string. */ + unsigned int likely_intentional_directives; unsigned int unnumbered_arg_count; struct unnumbered_arg *unnumbered; bool unlikely_intentional; @@ -182,6 +187,7 @@ format_parse_entrails (const char *format, bool translated, unsigned int allocated; spec.directives = 0; + spec.likely_intentional_directives = 0; spec.unnumbered_arg_count = 0; spec.unnumbered = NULL; spec.unlikely_intentional = false; @@ -202,6 +208,7 @@ format_parse_entrails (const char *format, bool translated, format_arg_type_t integer_size; /* Relevant for the conversion characters a, A, e, E, f, F, g, G. */ format_arg_type_t floatingpoint_size; + bool likely_intentional = true; FDI_SET (format - 1, FMTDIR_START); spec.directives++; @@ -236,7 +243,11 @@ format_parse_entrails (const char *format, bool translated, { if (*format == ' ' || *format == '+' || *format == '-' || *format == '#' || *format == '0' || *format == '\'') - format++; + { + if (*format == ' ') + likely_intentional = false; + format++; + } #if HANDLE_I_FLAG else if (translated && *format == 'I') { @@ -858,6 +869,8 @@ format_parse_entrails (const char *format, bool translated, } } + if (likely_intentional) + spec.likely_intentional_directives++; FDI_SET (format, FMTDIR_END); format++; diff --git a/gettext-tools/src/format-c.c b/gettext-tools/src/format-c.c index b30e1c6f0..7092b74bf 100644 --- a/gettext-tools/src/format-c.c +++ b/gettext-tools/src/format-c.c @@ -1,5 +1,5 @@ /* C format strings. - Copyright (C) 2001-2004, 2006-2007, 2009-2010, 2019, 2023 Free Software Foundation, Inc. + Copyright (C) 2001-2025 Free Software Foundation, Inc. Written by Bruno Haible , 2001. This program is free software: you can redistribute it and/or modify @@ -105,20 +105,20 @@ format_free (void *descr) free (spec); } -static bool -format_is_unlikely_intentional (void *descr) +static int +format_get_number_of_directives (void *descr) { struct spec *spec = (struct spec *) descr; - return spec->unlikely_intentional; + return spec->directives; } -static int -format_get_number_of_directives (void *descr) +static bool +format_is_unlikely_intentional (void *descr) { struct spec *spec = (struct spec *) descr; - return spec->directives; + return spec->likely_intentional_directives == 0 || spec->unlikely_intentional; } static bool diff --git a/gettext-tools/tests/Makefile.am b/gettext-tools/tests/Makefile.am index 7801c9631..11286f094 100644 --- a/gettext-tools/tests/Makefile.am +++ b/gettext-tools/tests/Makefile.am @@ -197,7 +197,7 @@ TESTS = gettext-1 gettext-2 \ xgettext-ycp-stackovfl-3 xgettext-ycp-stackovfl-4 \ format-awk-1 format-awk-2 format-awk-3 \ format-boost-1 format-boost-2 \ - format-c-1 format-c-2 format-c-3 format-c-4 format-c-5 \ + format-c-1 format-c-2 format-c-3 format-c-4 format-c-5 format-c-6 \ format-c++-brace-1 format-c++-brace-2 \ format-csharp-1 format-csharp-2 \ format-d-1 format-d-2 \ diff --git a/gettext-tools/tests/format-c-6 b/gettext-tools/tests/format-c-6 new file mode 100755 index 000000000..ac4a39968 --- /dev/null +++ b/gettext-tools/tests/format-c-6 @@ -0,0 +1,47 @@ +#! /bin/sh +. "${srcdir=.}/init.sh"; path_prepend_ . ../src + +# Test heuristic recognition of C format strings like "100% complete". + +cat <<\EOF > f-c-6.c +gettext("Test 1a is 100% complete"); +printf(gettext("Test 1b is 100% complete"), 120); +gettext("Test 2a from 0% complete to 100% complete"); +printf(gettext("Test 2b from 0% complete to 100% complete"), 120, 121); +gettext("Test 3a of %s is 100% complete"); +printf(gettext("Test 3b of %s is 100% complete"), "foo", 120); +EOF + +: ${XGETTEXT=xgettext} +${XGETTEXT} --omit-header --no-location -d f-c-6.tmp f-c-6.c || Exit 1 +LC_ALL=C tr -d '\r' < f-c-6.tmp.po > f-c-6.po || Exit 1 + +cat <<\EOF > f-c-6.ok +msgid "Test 1a is 100% complete" +msgstr "" + +#, c-format +msgid "Test 1b is 100% complete" +msgstr "" + +msgid "Test 2a from 0% complete to 100% complete" +msgstr "" + +#, c-format +msgid "Test 2b from 0% complete to 100% complete" +msgstr "" + +#, c-format +msgid "Test 3a of %s is 100% complete" +msgstr "" + +#, c-format +msgid "Test 3b of %s is 100% complete" +msgstr "" +EOF + +: ${DIFF=diff} +${DIFF} f-c-6.ok f-c-6.po +result=$? + +exit $result