From 796875721aa31dab01eb3d14f0b86f6ae2d29ffd Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Mon, 2 Jun 2025 00:17:32 +0200 Subject: [PATCH] xgettext: C++: Improve heuristic for Boost format strings. * gettext-tools/src/format-boost.c (struct spec): Add field 'likely_intentional_directives'. (format_parse): Set it to the number of directives that don't contain a space. (format_is_unlikely_intentional): New function. (formatstring_boost): Use it. * gettext-tools/tests/format-boost-3: New file. * gettext-tools/tests/Makefile.am (TESTS): Add it. --- gettext-tools/src/format-boost.c | 26 +++++++++++++++-- gettext-tools/tests/Makefile.am | 2 +- gettext-tools/tests/format-boost-3 | 47 ++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) create mode 100755 gettext-tools/tests/format-boost-3 diff --git a/gettext-tools/src/format-boost.c b/gettext-tools/src/format-boost.c index eb7e2ece2..f704772b9 100644 --- a/gettext-tools/src/format-boost.c +++ b/gettext-tools/src/format-boost.c @@ -92,6 +92,11 @@ struct numbered_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 'boost-format' if they don't occur in a context that requires a format + string. */ + unsigned int likely_intentional_directives; unsigned int numbered_arg_count; struct numbered_arg *numbered; }; @@ -117,6 +122,7 @@ format_parse (const char *format, bool translated, char *fdi, struct spec *result; spec.directives = 0; + spec.likely_intentional_directives = 0; spec.numbered_arg_count = 0; spec.numbered = NULL; numbered_allocated = 0; @@ -127,6 +133,8 @@ format_parse (const char *format, bool translated, char *fdi, if (*format++ == '%') { /* A directive. */ + bool likely_intentional = true; + FDI_SET (format - 1, FMTDIR_START); spec.directives++; @@ -184,7 +192,11 @@ format_parse (const char *format, bool translated, char *fdi, || *format == '#' || *format == '0' || *format == '\'' || *format == '_' || *format == '=' || *format == 'h' || *format == 'l') - format++; + { + if (*format == ' ') + likely_intentional = false; + format++; + } else break; } @@ -499,6 +511,8 @@ format_parse (const char *format, bool translated, char *fdi, } } + if (likely_intentional) + spec.likely_intentional_directives++; FDI_SET (format - 1, FMTDIR_END); } @@ -582,6 +596,14 @@ format_get_number_of_directives (void *descr) return spec->directives; } +static bool +format_is_unlikely_intentional (void *descr) +{ + struct spec *spec = (struct spec *) descr; + + return spec->likely_intentional_directives == 0; +} + static bool format_check (void *msgid_descr, void *msgstr_descr, bool equality, formatstring_error_logger_t error_logger, void *error_logger_data, @@ -666,7 +688,7 @@ struct formatstring_parser formatstring_boost = format_parse, format_free, format_get_number_of_directives, - NULL, + format_is_unlikely_intentional, format_check }; diff --git a/gettext-tools/tests/Makefile.am b/gettext-tools/tests/Makefile.am index 11286f094..eff5a313d 100644 --- a/gettext-tools/tests/Makefile.am +++ b/gettext-tools/tests/Makefile.am @@ -196,7 +196,7 @@ TESTS = gettext-1 gettext-2 \ xgettext-ycp-stackovfl-1 xgettext-ycp-stackovfl-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-boost-1 format-boost-2 format-boost-3 \ 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 \ diff --git a/gettext-tools/tests/format-boost-3 b/gettext-tools/tests/format-boost-3 new file mode 100755 index 000000000..31a5b3252 --- /dev/null +++ b/gettext-tools/tests/format-boost-3 @@ -0,0 +1,47 @@ +#! /bin/sh +. "${srcdir=.}/init.sh"; path_prepend_ . ../src + +# Test heuristic recognition of Boost format strings like "100% complete". + +cat <<\EOF > f-bo-3.cc +gettext("Test 1a is 100% complete"); +format(gettext("Test 1b is 100% complete"), 120); +gettext("Test 2a from 0% complete to 100% complete"); +format(gettext("Test 2b from 0% complete to 100% complete"), 120, 121); +gettext("Test 3a of %s is 100% complete"); +format(gettext("Test 3b of %s is 100% complete"), "foo", 120); +EOF + +: ${XGETTEXT=xgettext} +${XGETTEXT} --boost --omit-header --no-location -d f-bo-3.tmp f-bo-3.cc || Exit 1 +LC_ALL=C tr -d '\r' < f-bo-3.tmp.po > f-bo-3.po || Exit 1 + +cat <<\EOF > f-bo-3.ok +msgid "Test 1a is 100% complete" +msgstr "" + +#, boost-format +msgid "Test 1b is 100% complete" +msgstr "" + +msgid "Test 2a from 0% complete to 100% complete" +msgstr "" + +#, boost-format +msgid "Test 2b from 0% complete to 100% complete" +msgstr "" + +#, c-format, boost-format +msgid "Test 3a of %s is 100% complete" +msgstr "" + +#, boost-format +msgid "Test 3b of %s is 100% complete" +msgstr "" +EOF + +: ${DIFF=diff} +${DIFF} f-bo-3.ok f-bo-3.po +result=$? + +exit $result -- 2.47.3