From: Bruno Haible Date: Thu, 30 Nov 2023 15:01:50 +0000 (+0100) Subject: xgettext: Support modifying the flags of a message after it has been remembered. X-Git-Tag: v0.23~286 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6424bcbb6920ea0371183b23341d3cbe38b07573;p=thirdparty%2Fgettext.git xgettext: Support modifying the flags of a message after it has been remembered. * gettext-tools/src/xg-arglist-context.h (struct remembered_message_ty, struct remembered_message_list_ty): New types. (remembered_message_list_append): New declaration. (struct formatstring_region_ty): Add a 'remembered' field. * gettext-tools/src/xg-arglist-context.c (remembered_message_list_alloc, remembered_message_list_append, remembered_message_list_ref, remembered_message_list_unref): New functions. (the_null_context_region): Update. (inheriting_region): Inherit or allocate the 'remembered' fields. (unref_region): Unref the 'remembered' fields. * gettext-tools/src/xg-message.h (set_format_flag_from_context): New declaration. * gettext-tools/src/xg-message.c (validate_is_format): New function, extracted from set_format_flags_from_context. (set_format_flags_from_context): Change parameters. Invoke validate_is_format. (set_format_flag_from_context): New function. (remember_a_message, remember_a_message_plural): Update. --- diff --git a/gettext-tools/src/xg-arglist-context.c b/gettext-tools/src/xg-arglist-context.c index ff181d1c8..d3930a12f 100644 --- a/gettext-tools/src/xg-arglist-context.c +++ b/gettext-tools/src/xg-arglist-context.c @@ -208,6 +208,53 @@ flag_context_list_table_add (flag_context_list_table_ty *table, } +static struct remembered_message_list_ty * +remembered_message_list_alloc () +{ + struct remembered_message_list_ty *list = XMALLOC (struct remembered_message_list_ty); + list->refcount = 1; + list->item = NULL; + list->nitems = 0; + list->nitems_max = 0; + return list; +} + +void +remembered_message_list_append (struct remembered_message_list_ty *list, + struct remembered_message_ty element) +{ + if (list->nitems >= list->nitems_max) + { + size_t nbytes; + + list->nitems_max = list->nitems_max * 2 + 4; + nbytes = list->nitems_max * sizeof (struct remembered_message_ty); + list->item = xrealloc (list->item, nbytes); + } + list->item[list->nitems++] = element; +} + +static struct remembered_message_list_ty * +remembered_message_list_ref (struct remembered_message_list_ty *list) +{ + if (list != NULL) + list->refcount++; + return list; +} + +static void +remembered_message_list_unref (struct remembered_message_list_ty *list) +{ + if (list != NULL) + { + if (list->refcount > 1) + list->refcount--; + else + free (list); + } +} + + /* We don't need to remember messages that were processed in the null context region. Therefore the null context region can be a singleton. This reduces the number of needed calls to unref_region. */ @@ -215,10 +262,10 @@ static flag_region_ty const the_null_context_region = { 1, { - { undecided }, - { undecided }, - { undecided }, - { undecided } + { undecided, NULL }, + { undecided, NULL }, + { undecided, NULL }, + { undecided, NULL } } }; @@ -239,9 +286,23 @@ inheriting_region (flag_region_ty *outer_region, for (size_t fi = 0; fi < NXFORMATS; fi++) { if (modifier_context.for_formatstring[fi].pass_format) - region->for_formatstring[fi].is_format = outer_region->for_formatstring[fi].is_format; + { + region->for_formatstring[fi].is_format = outer_region->for_formatstring[fi].is_format; + region->for_formatstring[fi].remembered = + (current_formatstring_parser[fi] != NULL + ? (outer_region->for_formatstring[fi].remembered != NULL + ? remembered_message_list_ref (outer_region->for_formatstring[fi].remembered) + : remembered_message_list_alloc ()) + : NULL); + } else - region->for_formatstring[fi].is_format = modifier_context.for_formatstring[fi].is_format; + { + 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); + } } return region; @@ -265,6 +326,10 @@ unref_region (flag_region_ty *region) if (region->refcount > 1) region->refcount--; else - free (region); + { + for (size_t fi = 0; fi < NXFORMATS; fi++) + remembered_message_list_unref (region->for_formatstring[fi].remembered); + free (region); + } } } diff --git a/gettext-tools/src/xg-arglist-context.h b/gettext-tools/src/xg-arglist-context.h index 4127d7b64..00aefb1fe 100644 --- a/gettext-tools/src/xg-arglist-context.h +++ b/gettext-tools/src/xg-arglist-context.h @@ -85,11 +85,37 @@ extern void int argnum, enum is_format value, bool pass); +/* A set of arguments to pass to set_format_flag_from_context. */ +struct remembered_message_ty +{ + message_ty *mp; + bool plural; + lex_pos_ty pos; +}; + +/* A list of 'struct remembered_message_ty'. */ +struct remembered_message_list_ty +{ + unsigned int refcount; + struct remembered_message_ty *item; + size_t nitems; + size_t nitems_max; +}; + +/* Adds an element to a list of 'struct remembered_message_ty'. */ +extern void + remembered_message_list_append (struct remembered_message_list_ty *list, + struct remembered_message_ty element); + /* Context representing some flags w.r.t. a specific format string type, as effective in a region of the input file. */ struct formatstring_region_ty { 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 + in inheriting_region. */ + struct remembered_message_list_ty *remembered; }; /* A region of the input file, in which a given context is in effect, together diff --git a/gettext-tools/src/xg-message.c b/gettext-tools/src/xg-message.c index 8c7aad83f..b6c1ca6b4 100644 --- a/gettext-tools/src/xg-message.c +++ b/gettext-tools/src/xg-message.c @@ -44,52 +44,79 @@ pos->line_number); +/* Validates the modified value of mp->is_format[i]. */ +static void +validate_is_format (message_ty *mp, bool plural, lex_pos_ty *pos, size_t i) +{ + if (possible_format_p (mp->is_format[i])) + { + const char *string = (plural ? mp->msgid_plural : mp->msgid); + const char *pretty_msgstr = (plural ? "msgid_plural" : "msgid"); + struct formatstring_parser *parser = formatstring_parsers[i]; + char *invalid_reason = NULL; + void *descr = parser->parse (string, false, NULL, &invalid_reason); + + if (descr != NULL) + parser->free (descr); + else + { + /* The string is not a valid format string. */ + if (mp->is_format[i] != possible) + if_error (IF_SEVERITY_WARNING, + pos->file_name, pos->line_number, (size_t)(-1), true, + mp->is_format[i] == yes_according_to_context + ? _("Although being used in a format string position, the %s is not a valid %s format string. Reason: %s\n") + : _("Although declared as such, the %s is not a valid %s format string. Reason: %s\n"), + pretty_msgstr, format_language_pretty[i], + invalid_reason); + + mp->is_format[i] = impossible; + free (invalid_reason); + } + } +} + /* Update the is_format[] flags depending on the information given in the region's context. */ static void -set_format_flags_from_context (enum is_format is_format[NFORMATS], - flag_region_ty const *region, const char *string, - lex_pos_ty *pos, const char *pretty_msgstr) +set_format_flags_from_context (message_ty *mp, bool plural, lex_pos_ty *pos, + flag_region_ty const *region) { - bool some_undecided; - - some_undecided = false; + bool some_undecided = false; for (size_t fi = 0; fi < NXFORMATS; fi++) some_undecided |= (region->for_formatstring[fi].is_format != undecided); if (some_undecided) for (size_t i = 0; i < NFORMATS; i++) { - if (is_format[i] == undecided) + if (mp->is_format[i] == undecided) for (size_t fi = 0; fi < NXFORMATS; fi++) if (formatstring_parsers[i] == current_formatstring_parser[fi] && region->for_formatstring[fi].is_format != undecided) - is_format[i] = region->for_formatstring[fi].is_format; - if (possible_format_p (is_format[i])) - { - struct formatstring_parser *parser = formatstring_parsers[i]; - char *invalid_reason = NULL; - void *descr = parser->parse (string, false, NULL, &invalid_reason); - - if (descr != NULL) - parser->free (descr); - else - { - /* The string is not a valid format string. */ - if (is_format[i] != possible) - if_error (IF_SEVERITY_WARNING, - pos->file_name, pos->line_number, (size_t)(-1), true, - is_format[i] == yes_according_to_context - ? _("Although being used in a format string position, the %s is not a valid %s format string. Reason: %s\n") - : _("Although declared as such, the %s is not a valid %s format string. Reason: %s\n"), - pretty_msgstr, format_language_pretty[i], - invalid_reason); - - is_format[i] = impossible; - free (invalid_reason); - } - } + mp->is_format[i] = region->for_formatstring[fi].is_format; + validate_is_format (mp, plural, pos, i); } + + /* Prepare for doing the same thing in a delayed manner. + This is useful for methods named 'printf' on a class 'String'. */ + for (size_t fi = 0; fi < NXFORMATS; fi++) + if (current_formatstring_parser[fi] != NULL + && region->for_formatstring[fi].remembered != NULL) + remembered_message_list_append (region->for_formatstring[fi].remembered, + (struct remembered_message_ty) { mp, plural, *pos }); +} + +void +set_format_flag_from_context (message_ty *mp, bool plural, lex_pos_ty *pos, + size_t fi, flag_region_ty const *region) +{ + if (region->for_formatstring[fi].is_format != undecided) + for (size_t i = 0; i < NFORMATS; i++) + if (formatstring_parsers[i] == current_formatstring_parser[fi]) + { + mp->is_format[i] = region->for_formatstring[fi].is_format; + validate_is_format (mp, plural, pos, i); + } } @@ -336,7 +363,7 @@ meta information, not the empty string.\n")); /* Determine whether the context specifies that the msgid is a format string. */ - set_format_flags_from_context (mp->is_format, region, mp->msgid, pos, "msgid"); + set_format_flags_from_context (mp, false, pos, region); /* Ask the lexer for the comments it has seen. */ { @@ -559,8 +586,7 @@ remember_a_message_plural (message_ty *mp, char *string, bool is_utf8, /* Determine whether the context specifies that the msgid_plural is a format string. */ - set_format_flags_from_context (mp->is_format, region, mp->msgid_plural, - pos, "msgid_plural"); + set_format_flags_from_context (mp, true, pos, region); /* If it is not already decided, through programmer comments or the msgid, whether the msgid is a format string, examine the diff --git a/gettext-tools/src/xg-message.h b/gettext-tools/src/xg-message.h index 5ed538577..f43bb08de 100644 --- a/gettext-tools/src/xg-message.h +++ b/gettext-tools/src/xg-message.h @@ -76,6 +76,7 @@ extern void remember_a_message_plural (message_ty *mp, refcounted_string_list_ty *comment, bool comment_is_utf8); + /* The following functions are used by remember_a_message. Most extractors don't need to invoke them explicitly. */ @@ -92,6 +93,15 @@ extern void decide_do_wrap (message_ty *mp); 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. */ +extern void set_format_flag_from_context (message_ty *mp, bool plural, + lex_pos_ty *pos, + size_t fi, + flag_region_ty const *region); + + #ifdef __cplusplus } #endif