From: Bruno Haible Date: Sat, 27 Jul 2024 14:25:27 +0000 (+0200) Subject: xgettext: Improve support for string formatting through methods. X-Git-Tag: v0.23~226 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1d449499cbadd2f6b484c5ca371d4e0e193584a1;p=thirdparty%2Fgettext.git xgettext: Improve support for string formatting through methods. * gettext-tools/src/xg-arglist-context.h: Add comments. (struct formatstring_region_ty): Add 'pass_format' field. (struct flag_region_ty): Add subregion, nsubregions, nsubregions_max, inherit_from_parent_region fields. (new_sub_region): New declaration. (set_format_flag_on_region): New declaration. * gettext-tools/src/xg-arglist-context.c: Include xg-message.h. (the_null_context_region): Update initializer. (inheriting_region): Initialize the new fields. (new_sub_region): New function. (unref_region): Unreference also the subregions. (set_format_flag_on_region): New function. * gettext-tools/src/xg-message.h (set_format_flag_from_context): Fix typo in comment. --- diff --git a/gettext-tools/src/xg-arglist-context.c b/gettext-tools/src/xg-arglist-context.c index d3930a12f..5153f7b60 100644 --- a/gettext-tools/src/xg-arglist-context.c +++ b/gettext-tools/src/xg-arglist-context.c @@ -1,6 +1,6 @@ /* Keeping track of the flags that apply to a string extracted in a certain context. - Copyright (C) 2001-2018, 2023 Free Software Foundation, Inc. + Copyright (C) 2001-2024 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,6 +29,8 @@ #include "xmalloca.h" #include "verify.h" +#include "xg-message.h" + /* Null context. */ static flag_context_ty null_context = @@ -262,11 +264,13 @@ static flag_region_ty const the_null_context_region = { 1, { - { undecided, NULL }, - { undecided, NULL }, - { undecided, NULL }, - { undecided, NULL } - } + { true, undecided, NULL }, + { true, undecided, NULL }, + { true, undecided, NULL }, + { true, undecided, NULL } + }, + NULL, 0, 0, + true }; flag_region_ty * @@ -285,6 +289,7 @@ inheriting_region (flag_region_ty *outer_region, region->refcount = 1; for (size_t fi = 0; fi < NXFORMATS; fi++) { + region->for_formatstring[fi].pass_format = modifier_context.for_formatstring[fi].pass_format; if (modifier_context.for_formatstring[fi].pass_format) { region->for_formatstring[fi].is_format = outer_region->for_formatstring[fi].is_format; @@ -304,11 +309,58 @@ inheriting_region (flag_region_ty *outer_region, : NULL); } } + region->subregion = NULL; + region->nsubregions = 0; + region->nsubregions_max = 0; + region->inherit_from_parent_region = true; return region; } +flag_region_ty * +new_sub_region (flag_region_ty *outer_region, flag_context_ty modifier_context) +{ + /* Create the new region. */ + flag_region_ty *region = XMALLOC (flag_region_ty); + + region->refcount = 1; + for (size_t fi = 0; fi < NXFORMATS; fi++) + { + region->for_formatstring[fi].pass_format = modifier_context.for_formatstring[fi].pass_format; + if (modifier_context.for_formatstring[fi].pass_format) + region->for_formatstring[fi].is_format = outer_region->for_formatstring[fi].is_format; + else + region->for_formatstring[fi].is_format = modifier_context.for_formatstring[fi].is_format; + region->for_formatstring[fi].remembered = + (current_formatstring_parser[fi] != NULL + ? remembered_message_list_alloc () + : NULL); + } + region->subregion = NULL; + region->nsubregions = 0; + region->nsubregions_max = 0; + /* Set to true initially. Can be set to false later during the parsing. */ + region->inherit_from_parent_region = true; + + if (outer_region != &the_null_context_region) + { + /* Register it as child of outer_region. */ + if (outer_region->nsubregions >= outer_region->nsubregions_max) + { + size_t nbytes; + + outer_region->nsubregions_max = outer_region->nsubregions_max * 2 + 4; + nbytes = outer_region->nsubregions_max * sizeof (struct flag_region_ty *); + outer_region->subregion = xrealloc (outer_region->subregion, nbytes); + } + outer_region->subregion[outer_region->nsubregions++] = region; + region->refcount++; + } + + return region; +} + flag_region_ty * ref_region (flag_region_ty *region) { @@ -327,9 +379,40 @@ unref_region (flag_region_ty *region) region->refcount--; else { + for (size_t i = 0; i < region->nsubregions; i++) + unref_region (region->subregion[i]); + free (region->subregion); for (size_t fi = 0; fi < NXFORMATS; fi++) remembered_message_list_unref (region->for_formatstring[fi].remembered); free (region); } } } + + +void +set_format_flag_on_region (flag_region_ty *region, + size_t fi, enum is_format value) +{ + size_t i; + + /* First, on this region. */ + region->for_formatstring[fi].is_format = value; + struct remembered_message_list_ty *rmlp = + region->for_formatstring[fi].remembered; + for (i = 0; i < rmlp->nitems; i++) + { + struct remembered_message_ty *rmp = &rmlp->item[i]; + set_format_flag_from_context (rmp->mp, rmp->plural, &rmp->pos, + fi, region); + } + + /* Then, recurse through the sub-regions that inherit. */ + for (i = 0; i < region->nsubregions; i++) + { + flag_region_ty *sub_region = region->subregion[i]; + if (sub_region->inherit_from_parent_region + && sub_region->for_formatstring[fi].pass_format) + set_format_flag_on_region (sub_region, fi, value); + } +} diff --git a/gettext-tools/src/xg-arglist-context.h b/gettext-tools/src/xg-arglist-context.h index 00aefb1fe..e951a141b 100644 --- a/gettext-tools/src/xg-arglist-context.h +++ b/gettext-tools/src/xg-arglist-context.h @@ -1,6 +1,6 @@ /* Keeping track of the flags that apply to a string extracted in a certain context. - Copyright (C) 2001-2018, 2020, 2023 Free Software Foundation, Inc. + Copyright (C) 2001-2024 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,6 +29,17 @@ extern "C" { #endif +/* ========================================================================== */ + +/* The purpose of the format string flags is attach a flag such as 'c-format' + to a message, when appropriate. For example, when extracting (in C) + puts (_("foo")); + printf (_("foo"), _("bar")); + fprintf (fp, _("foo"), _("bar")); + - the context of puts establishes no flags, + - the context of printf establishes 'c-format' for the first argument, + - the context of fprintf establishes 'c-format' for the second argument. */ + /* Context representing some flags w.r.t. a specific format string type. */ struct formatstring_context_ty { @@ -53,6 +64,8 @@ struct flag_context_list_ty flag_context_list_ty *next; }; +/* -------------------------------------------------------------------------- */ + /* Iterator through a flag_context_list_ty. */ typedef struct flag_context_list_iterator_ty flag_context_list_iterator_ty; struct flag_context_list_iterator_ty @@ -60,13 +73,32 @@ struct flag_context_list_iterator_ty int argnum; /* current argument number, > 0 */ const flag_context_list_ty* head; /* tail of list */ }; + +/* The null context list iterator. + At each position, no flags are set. */ extern flag_context_list_iterator_ty null_context_list_iterator; + +/* The transparent context list iterator. + At each position, no flags are set but they are passed through from outside. + This transparent context list iterator is useful for parenthesized + expressions, because each of + printf (_("foo"), _("bar")); + printf ((_("foo")), _("bar")); + printf (((_("foo"))), _("bar")); + etc. + should extract "foo" with 'c-format' flag. */ extern flag_context_list_iterator_ty passthrough_context_list_iterator; + +/* Creates an iterator through an explicitly constructed list of contexts. */ extern flag_context_list_iterator_ty flag_context_list_iterator (flag_context_list_ty *list); + +/* Returns the context at the current position of the iterator, and advances + it to the next position. */ extern flag_context_ty flag_context_list_iterator_advance (flag_context_list_iterator_ty *iter); +/* ========================================================================== */ /* For nearly each backend, we have a separate table mapping a keyword to a flag_context_list_ty *. */ @@ -75,7 +107,7 @@ typedef hash_table /* char[] -> flag_context_list_ty * */ extern flag_context_list_ty * flag_context_list_table_lookup (flag_context_list_table_ty *flag_table, const void *key, size_t keylen); -/* Insert the pair (VALUE, PASS) as (is_format, pass_format) for the format +/* Inserts the pair (VALUE, PASS) as (is_format, pass_format) for the format string type FI in the flags of the element numbered ARGNUM of the list corresponding to NAME in the TABLE. */ extern void @@ -84,6 +116,40 @@ extern void const char *name_start, const char *name_end, int argnum, enum is_format value, bool pass); +/* ========================================================================== */ + +/* A region represents a portion of the input file and remembers the messages + that were encountered while processing this region. Typically a region + is not larger than a statement. Nested expressions correspond to nested + regions. + + For example, for the input + return m (printf(_("foo"), _("aaa")), _("bar").printf(_("bbb"))); + we have regions and sub-regions like this: + ---------------------------------------------------------------- + -------------------------- ------------------------- + -------- -------- -------- -------- + ----- ----- ----- ----- + return m (printf(_("foo"), _("aaa")), _("bar").printf(_("bbb"))); + + A. If a language has string formatting only through functions, the region + management is relatively simple: the list of remembered messages of a + sub-region can be shared with the list of remembered messages of the + parent region, because at the moment a message is seen, the flags that + apply are already known. + + B. If a language has string formatting through functions and through methods, + the region management is more complicated. At the moment a message is seen, + the flags that apply are not yet known. They become known only once the + method invocation (in the example above: '.printf') is seen. Therefore, + in this case, each region and sub-region stores their messages separately, + so that when the method invocation is seen, an invocation of + set_format_flag_on_region can set a flag on each of the remembered messages + a posteriori. + + In case A, regions are created through inheriting_region(). + In case B, regions are created through new_sub_region(). + */ /* A set of arguments to pass to set_format_flag_from_context. */ struct remembered_message_ty @@ -111,6 +177,7 @@ extern void as effective in a region of the input file. */ struct formatstring_region_ty { + bool pass_format; enum is_format is_format; /* Messages that were remembered in this context. This messages list is shared with sub-regions when pass_format was true @@ -125,17 +192,33 @@ struct flag_region_ty { unsigned int refcount; struct formatstring_region_ty for_formatstring[NXFORMATS]; + /* Any number of subregions. They represent disjoint sub-intervals + of this region. */ + struct flag_region_ty **subregion; + size_t nsubregions; + size_t nsubregions_max; + /* Whether this region, as a subregion, inherits its flags from its + parent region. */ + bool inherit_from_parent_region; }; /* Creates a region in which the null context is in effect. */ extern flag_region_ty * null_context_region (); -/* Creates a sub-region that inherits from an outer region. */ +/* Creates a sub-region that inherits from an outer region. + Only used in case A. */ extern flag_region_ty * inheriting_region (flag_region_ty *outer_region, flag_context_ty modifier_context); +/* Creates a sub-region that is prepared for inheriting from an outer region. + But whether it actually does so, can be changed as the parsing goes on. + Only used in case B. */ +extern flag_region_ty * + new_sub_region (flag_region_ty *outer_region, + flag_context_ty modifier_context); + /* Adds a reference to a region. Returns the region. */ extern flag_region_ty * ref_region (flag_region_ty *region); @@ -164,6 +247,14 @@ extern void unref_region (_prev_a); \ } while (0) +/* Changes the is_format[] flag for the given format string index FI + to VALUE, updating all remembered messages in REGION in the process. */ +extern void + set_format_flag_on_region (flag_region_ty *region, + size_t fi, enum is_format value); + +/* ========================================================================== */ + #ifdef __cplusplus } diff --git a/gettext-tools/src/xg-message.h b/gettext-tools/src/xg-message.h index f43bb08de..71e342c22 100644 --- a/gettext-tools/src/xg-message.h +++ b/gettext-tools/src/xg-message.h @@ -1,5 +1,5 @@ /* Extracting a message. Accumulating the message list. - Copyright (C) 2001-2023 Free Software Foundation, Inc. + Copyright (C) 2001-2024 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -95,7 +95,7 @@ extern void decide_syntax_check (message_ty *mp); /* Updates the is_format[] flag for the given format string index FI depending on the information given in the region's context. - This can be called after long after remember_a_message. */ + This can be called long after remember_a_message. */ extern void set_format_flag_from_context (message_ty *mp, bool plural, lex_pos_ty *pos, size_t fi,