]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
xgettext: Support modifying the flags of a message after it has been remembered.
authorBruno Haible <bruno@clisp.org>
Thu, 30 Nov 2023 15:01:50 +0000 (16:01 +0100)
committerBruno Haible <bruno@clisp.org>
Thu, 30 Nov 2023 16:15:42 +0000 (17:15 +0100)
* 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.

gettext-tools/src/xg-arglist-context.c
gettext-tools/src/xg-arglist-context.h
gettext-tools/src/xg-message.c
gettext-tools/src/xg-message.h

index ff181d1c8975c3e4499812244853319287780ef8..d3930a12ff04d016a885309a0a9b0414a09468c6 100644 (file)
@@ -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);
+        }
     }
 }
index 4127d7b64fdcc187ab1b81914dd480803d70eeb1..00aefb1fe29875f2000a55ec9ab798261bbd878a 100644 (file)
@@ -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
index 8c7aad83f566d28b9d4a3f94f8dc1a2ae1defab1..b6c1ca6b4d240ecbd23cc4dcde2140de90773faa 100644 (file)
                                          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
index 5ed5385771d06b2cea48d4523817f778ababebce..f43bb08de8f41bb5d66cb5f9e1d3ea24953a383e 100644 (file)
@@ -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