xgettext: awk: Improve heuristic for format strings.

* gettext-tools/src/format-awk.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_awk): Use it.
* gettext-tools/tests/format-awk-3: New file.
* gettext-tools/tests/Makefile.am (TESTS): Add it.
This commit is contained in:
Bruno Haible 2025-06-01 19:53:09 +02:00
parent a7c6eeefd6
commit b330ce7804
3 changed files with 71 additions and 3 deletions

View File

@ -71,6 +71,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 'awk-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;
};
@ -96,6 +101,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;
@ -108,6 +114,7 @@ format_parse (const char *format, bool translated, char *fdi,
/* A directive. */
unsigned int number = 0;
enum format_arg_type type;
bool likely_intentional = true;
FDI_SET (format - 1, FMTDIR_START);
spec.directives++;
@ -140,7 +147,11 @@ format_parse (const char *format, bool translated, char *fdi,
/* Parse flags. */
while (*format == ' ' || *format == '+' || *format == '-'
|| *format == '#' || *format == '0')
format++;
{
if (*format == ' ')
likely_intentional = false;
format++;
}
/* Parse width. */
if (*format == '*')
@ -390,6 +401,8 @@ format_parse (const char *format, bool translated, char *fdi,
}
}
if (likely_intentional)
spec.likely_intentional_directives++;
FDI_SET (format, FMTDIR_END);
format++;
@ -473,6 +486,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,
@ -557,7 +578,7 @@ struct formatstring_parser formatstring_awk =
format_parse,
format_free,
format_get_number_of_directives,
NULL,
format_is_unlikely_intentional,
format_check
};

View File

@ -195,7 +195,7 @@ TESTS = gettext-1 gettext-2 \
xgettext-ycp-1 xgettext-ycp-2 xgettext-ycp-3 xgettext-ycp-4 \
xgettext-ycp-stackovfl-1 xgettext-ycp-stackovfl-2 \
xgettext-ycp-stackovfl-3 xgettext-ycp-stackovfl-4 \
format-awk-1 format-awk-2 \
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++-brace-1 format-c++-brace-2 \

View File

@ -0,0 +1,47 @@
#! /bin/sh
. "${srcdir=.}/init.sh"; path_prepend_ . ../src
# Test heuristic recognition of awk format strings like "100% complete".
cat <<\EOF > f-a-3.awk
dcgettext("Test 1a is 100% complete");
printf dcgettext("Test 1b is 100% complete"), 120;
dcgettext("Test 2a from 0% complete to 100% complete");
printf dcgettext("Test 2b from 0% complete to 100% complete"), 120, 121;
dcgettext("Test 3a of %s is 100% complete");
printf dcgettext("Test 3b of %s is 100% complete"), "foo", 120;
EOF
: ${XGETTEXT=xgettext}
${XGETTEXT} --omit-header --no-location -d f-a-3.tmp f-a-3.awk || Exit 1
LC_ALL=C tr -d '\r' < f-a-3.tmp.po > f-a-3.po || Exit 1
cat <<\EOF > f-a-3.ok
msgid "Test 1a is 100% complete"
msgstr ""
#, awk-format
msgid "Test 1b is 100% complete"
msgstr ""
msgid "Test 2a from 0% complete to 100% complete"
msgstr ""
#, awk-format
msgid "Test 2b from 0% complete to 100% complete"
msgstr ""
#, awk-format
msgid "Test 3a of %s is 100% complete"
msgstr ""
#, awk-format
msgid "Test 3b of %s is 100% complete"
msgstr ""
EOF
: ${DIFF=diff}
${DIFF} f-a-3.ok f-a-3.po
result=$?
exit $result