]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
xgettext: Improve support for string formatting through methods.
authorBruno Haible <bruno@clisp.org>
Sat, 27 Jul 2024 14:25:27 +0000 (16:25 +0200)
committerBruno Haible <bruno@clisp.org>
Sat, 27 Jul 2024 14:25:27 +0000 (16:25 +0200)
* 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.

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

index d3930a12ff04d016a885309a0a9b0414a09468c6..5153f7b60ed046d97b07da40270ee1634e41e936 100644 (file)
@@ -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);
+    }
+}
index 00aefb1fe29875f2000a55ec9ab798261bbd878a..e951a141be28f3e4f5e97f84f2826b22238fbb8e 100644 (file)
@@ -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
 }
index f43bb08de8f41bb5d66cb5f9e1d3ea24953a383e..71e342c22afa1d7f077951dd291320bff93845c8 100644 (file)
@@ -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,