]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Recognize Qt4 plural forms format strings.
authorBruno Haible <bruno@clisp.org>
Wed, 28 Jan 2009 02:13:03 +0000 (02:13 +0000)
committerBruno Haible <bruno@clisp.org>
Tue, 23 Jun 2009 10:16:00 +0000 (12:16 +0200)
gettext-tools/src/ChangeLog
gettext-tools/src/FILES
gettext-tools/src/Makefile.am
gettext-tools/src/format-qt-plural.c [new file with mode: 0644]
gettext-tools/src/format.c
gettext-tools/src/format.h
gettext-tools/src/message.c
gettext-tools/src/message.h
gettext-tools/src/xgettext.c
gettext-tools/src/xgettext.h

index d738760fcc085050545ec09a69afcb90b3d43a05..f339d0c442c27741e75908220df8acc3e90ce97c 100644 (file)
@@ -1,3 +1,32 @@
+2009-01-27  Bruno Haible  <bruno@clisp.org>
+
+       * message.h (format_type): New enum value 'format_qt_plural'.
+       (NFORMATS): Increment.
+       * message.c (format_language): Add format_qt_plural entry.
+       (format_language_pretty): Likewise.
+       * format.h (formatstring_qt_plural): New declaration.
+       * format-qt-plural.c: New file.
+       * format.c (formatstring_parsers): Add formatstring_qt_plural.
+       * xgettext.h (struct flag_context_ty): Add fields is_format3,
+       pass_format3.
+       * xgettext.c (struct extractor_ty): Add field formatstring_parser3.
+       (inherited_context, flag_context_list_table_insert): Handle the new
+       flag_context_ty fields.
+       (xgettext_record_flag): Handle format_qt_plural.
+       (current_formatstring_parser3): New variable.
+       (extract_from_file): Initialize it.
+       (set_format_flags_from_context): Handle the new flag_context_ty fields.
+       (remember_a_message, remember_a_message_plural): Handle
+       current_formatstring_parser3. Avoid adding a c-format flag to a message
+       already flagged as qt-plural-format.
+       (arglist_parser_remember): Allow argnum1 and argnum2 in the call shape
+       to be the same.
+       (arglist_parser_done): Add special recognition of qt-plural-format
+       strings.
+       (language_to_extractor): Set the formatstring_parser3 in the result.
+       * Makefile.am (FORMAT_SOURCE): Add format-qt-plural.c.
+       * FILES: Update.
+
 2009-01-27  Bruno Haible  <bruno@clisp.org>
 
        * plural-table.c (plural_table): Put Turkish under nplurals=2.
index 764488966810eae07cb9866627e24e3e9e79d2a2..f786e06f013dc2af24637067ea6991760090ef55 100644 (file)
@@ -228,6 +228,7 @@ format-perl-brace.c    Format string handling for Perl, braced syntax.
 format-php.c           Format string handling for PHP.
 format-gcc-internal.c  Format string handling GCC internal.
 format-qt.c            Format string handling for Qt.
+format-qt-plural.c     Format string handling for Qt plural forms.
 format-kde.c           Format string handling for KDE.
 format-boost.c         Format string handling for Boost.
 format.c        Table of the language dependent format string handlers.
index 3409496aa8d29f01f140311e9ba8236f6b2fd408..834a84d81b4dcdd34fab35843a3a2891f65b0f43 100644 (file)
@@ -113,7 +113,8 @@ FORMAT_SOURCE += format-invalid.h \
 format-c.c format-sh.c format-python.c format-lisp.c format-elisp.c \
 format-librep.c format-scheme.c format-java.c format-csharp.c format-awk.c \
 format-pascal.c format-ycp.c format-tcl.c format-perl.c format-perl-brace.c \
-format-php.c format-gcc-internal.c format-qt.c format-kde.c format-boost.c
+format-php.c format-gcc-internal.c format-qt.c format-qt-plural.c \
+format-kde.c format-boost.c
 
 # libgettextsrc contains all code that is needed by at least two programs.
 libgettextsrc_la_SOURCES = \
diff --git a/gettext-tools/src/format-qt-plural.c b/gettext-tools/src/format-qt-plural.c
new file mode 100644 (file)
index 0000000..8b225fd
--- /dev/null
@@ -0,0 +1,194 @@
+/* Qt plural format strings.
+   Copyright (C) 2003-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2009.
+
+   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
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "xalloc.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Qt plural format strings are processed by QObject::tr and are documented in
+   qt-x11-opensource-src-4.3.1/doc/html/qobject.html#tr.
+   A directive
+     - starts with '%',
+     - is optionally followed by 'L' (a no-op),
+     - is followed by 'n'.
+   Every directive is replaced by the numeric argument N passed to QObject::tr.
+ */
+
+struct spec
+{
+  /* Number of format directives.  */
+  unsigned int directives;
+};
+
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+             char **invalid_reason)
+{
+  const char *const format_start = format;
+  struct spec spec;
+  struct spec *result;
+
+  spec.directives = 0;
+
+  for (; *format != '\0';)
+    if (*format++ == '%')
+      {
+       const char *dir_start = format - 1;
+
+       if (*format == 'L')
+         format++;
+       if (*format == 'n')
+         {
+           /* A directive.  */
+           FDI_SET (dir_start, FMTDIR_START);
+           spec.directives++;
+           FDI_SET (format, FMTDIR_END);
+
+           format++;
+         }
+      }
+
+  result = XMALLOC (struct spec);
+  *result = spec;
+  return result;
+}
+
+static void
+format_free (void *descr)
+{
+  struct spec *spec = (struct spec *) descr;
+
+  free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+  struct spec *spec = (struct spec *) descr;
+
+  return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+             formatstring_error_logger_t error_logger,
+             const char *pretty_msgstr)
+{
+  struct spec *spec1 = (struct spec *) msgid_descr;
+  struct spec *spec2 = (struct spec *) msgstr_descr;
+  bool err = false;
+
+  /* Check the argument is used.  */
+  if ((spec1->directives == 0 && spec2->directives > 0)
+      || (equality && spec1->directives > 0 && spec2->directives == 0))
+    {
+      if (error_logger)
+       error_logger (_("number of format specifications in 'msgid' and '%s' does not match"),
+                     pretty_msgstr);
+      err = true;
+    }
+
+  return err;
+}
+
+
+struct formatstring_parser formatstring_qt_plural =
+{
+  format_parse,
+  format_free,
+  format_get_number_of_directives,
+  NULL,
+  format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+   format_parse for strings read from standard input.  */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+  struct spec *spec = (struct spec *) descr;
+
+  if (spec == NULL)
+    {
+      printf ("INVALID");
+      return;
+    }
+
+  printf ("(");
+  if (spec->directives > 0)
+    printf ("*");
+  else
+    printf ("_");
+  printf (")");
+}
+
+int
+main ()
+{
+  for (;;)
+    {
+      char *line = NULL;
+      size_t line_size = 0;
+      int line_len;
+      char *invalid_reason;
+      void *descr;
+
+      line_len = getline (&line, &line_size, stdin);
+      if (line_len < 0)
+       break;
+      if (line_len > 0 && line[line_len - 1] == '\n')
+       line[--line_len] = '\0';
+
+      invalid_reason = NULL;
+      descr = format_parse (line, false, NULL, &invalid_reason);
+
+      format_print (descr);
+      printf ("\n");
+      if (descr == NULL)
+       printf ("%s\n", invalid_reason);
+
+      free (invalid_reason);
+      free (line);
+    }
+
+  return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-qt-plural.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
index 592a398dd36377024e1049c0326086fd06bbc223..ce50e971b02e219e164355c865ac625cc017fe43 100644 (file)
@@ -1,5 +1,5 @@
 /* Format strings.
-   Copyright (C) 2001-2008 Free Software Foundation, Inc.
+   Copyright (C) 2001-2009 Free Software Foundation, Inc.
    Written by Bruno Haible <haible@clisp.cons.org>, 2001.
 
    This program is free software: you can redistribute it and/or modify
@@ -54,6 +54,7 @@ struct formatstring_parser *formatstring_parsers[NFORMATS] =
   /* format_php */             &formatstring_php,
   /* format_gcc_internal */    &formatstring_gcc_internal,
   /* format_qt */              &formatstring_qt,
+  /* format_qt_plural */       &formatstring_qt_plural,
   /* format_kde */             &formatstring_kde,
   /* format_boost */           &formatstring_boost
 };
index 8de202ae0c185014adbe266dbb75172365494a00..412ecf6ab95a210243cf75a02098fafd4655ad55 100644 (file)
@@ -1,5 +1,5 @@
 /* Format strings.
-   Copyright (C) 2001-2008 Free Software Foundation, Inc.
+   Copyright (C) 2001-2009 Free Software Foundation, Inc.
    Written by Bruno Haible <haible@clisp.cons.org>, 2001.
 
    This program is free software: you can redistribute it and/or modify
@@ -115,6 +115,7 @@ extern DLL_VARIABLE struct formatstring_parser formatstring_perl_brace;
 extern DLL_VARIABLE struct formatstring_parser formatstring_php;
 extern DLL_VARIABLE struct formatstring_parser formatstring_gcc_internal;
 extern DLL_VARIABLE struct formatstring_parser formatstring_qt;
+extern DLL_VARIABLE struct formatstring_parser formatstring_qt_plural;
 extern DLL_VARIABLE struct formatstring_parser formatstring_kde;
 extern DLL_VARIABLE struct formatstring_parser formatstring_boost;
 
index 121240eac9bfe0620689db464ad16e5003d7eaa1..fdc2f9bd30f61010bb13dd20b8e0880a68000f1c 100644 (file)
@@ -1,5 +1,5 @@
 /* GNU gettext - internationalization aids
-   Copyright (C) 1995-1998, 2000-2008 Free Software Foundation, Inc.
+   Copyright (C) 1995-1998, 2000-2009 Free Software Foundation, Inc.
 
    This file was written by Peter Miller <millerp@canb.auug.org.au>
 
@@ -54,6 +54,7 @@ const char *const format_language[NFORMATS] =
   /* format_php */             "php",
   /* format_gcc_internal */    "gcc-internal",
   /* format_qt */              "qt",
+  /* format_qt_plursl */       "qt-plural",
   /* format_kde */             "kde",
   /* format_boost */           "boost"
 };
@@ -80,6 +81,7 @@ const char *const format_language_pretty[NFORMATS] =
   /* format_php */             "PHP",
   /* format_gcc_internal */    "GCC internal",
   /* format_qt */              "Qt",
+  /* format_qt_plural */       "Qt plural",
   /* format_kde */             "KDE",
   /* format_boost */           "Boost"
 };
index 6142055b25b1dc8eb8206bc0f1d5b478d483f073..16088dd26d4b339a07572cf0bbd45658a1ea1969 100644 (file)
@@ -1,5 +1,5 @@
 /* GNU gettext - internationalization aids
-   Copyright (C) 1995-1998, 2000-2008 Free Software Foundation, Inc.
+   Copyright (C) 1995-1998, 2000-2009 Free Software Foundation, Inc.
 
    This file was written by Peter Miller <millerp@canb.auug.org.au>
 
@@ -63,10 +63,11 @@ enum format_type
   format_php,
   format_gcc_internal,
   format_qt,
+  format_qt_plural,
   format_kde,
   format_boost
 };
-#define NFORMATS 22    /* Number of format_type enum values.  */
+#define NFORMATS 23    /* Number of format_type enum values.  */
 extern DLL_VARIABLE const char *const format_language[NFORMATS];
 extern DLL_VARIABLE const char *const format_language_pretty[NFORMATS];
 
index 8a0f6151fdc4a70e023c1638feb554ed44adf069..6466a4804207dee69d5a4d44b139ae5ddd8a573b 100644 (file)
@@ -252,6 +252,7 @@ struct extractor_ty
   flag_context_list_table_ty *flag_table;
   struct formatstring_parser *formatstring_parser1;
   struct formatstring_parser *formatstring_parser2;
+  struct formatstring_parser *formatstring_parser3;
 };
 
 
@@ -1249,6 +1250,11 @@ inherited_context (flag_context_ty outer_context,
       result.is_format2 = outer_context.is_format2;
       result.pass_format2 = false;
     }
+  if (result.pass_format3)
+    {
+      result.is_format3 = outer_context.is_format3;
+      result.pass_format3 = false;
+    }
   return result;
 }
 
@@ -1374,6 +1380,10 @@ flag_context_list_table_insert (flag_context_list_table_ty *table,
            list->flags.is_format2 = value;
            list->flags.pass_format2 = pass;
            break;
+         case 2:
+           list->flags.is_format3 = value;
+           list->flags.pass_format3 = pass;
+           break;
          default:
            abort ();
          }
@@ -1403,6 +1413,10 @@ flag_context_list_table_insert (flag_context_list_table_ty *table,
                list->flags.is_format2 = value;
                list->flags.pass_format2 = pass;
                break;
+             case 2:
+               list->flags.is_format3 = value;
+               list->flags.pass_format3 = pass;
+               break;
              default:
                abort ();
              }
@@ -1423,6 +1437,10 @@ flag_context_list_table_insert (flag_context_list_table_ty *table,
                list->flags.is_format2 = value;
                list->flags.pass_format2 = pass;
                break;
+             case 2:
+               list->flags.is_format3 = value;
+               list->flags.pass_format3 = pass;
+               break;
              default:
                abort ();
              }
@@ -1450,6 +1468,10 @@ flag_context_list_table_insert (flag_context_list_table_ty *table,
                list->flags.is_format2 = value;
                list->flags.pass_format2 = pass;
                break;
+             case 2:
+               list->flags.is_format3 = value;
+               list->flags.pass_format3 = pass;
+               break;
              default:
                abort ();
              }
@@ -1666,6 +1688,11 @@ xgettext_record_flag (const char *optionstring)
                                                    name_start, name_end,
                                                    argnum, value, pass);
                    break;
+                 case format_qt_plural:
+                   flag_context_list_table_insert (&flag_table_cxx_qt, 2,
+                                                   name_start, name_end,
+                                                   argnum, value, pass);
+                   break;
                  case format_kde:
                    flag_context_list_table_insert (&flag_table_cxx_kde, 1,
                                                    name_start, name_end,
@@ -1842,6 +1869,7 @@ error while opening \"%s\" for reading"), new_name);
    NULL if the language has no notion of format strings.  */
 static struct formatstring_parser *current_formatstring_parser1;
 static struct formatstring_parser *current_formatstring_parser2;
+static struct formatstring_parser *current_formatstring_parser3;
 
 
 static void
@@ -1861,6 +1889,7 @@ extract_from_file (const char *file_name, extractor_ty extractor,
 
   current_formatstring_parser1 = extractor.formatstring_parser1;
   current_formatstring_parser2 = extractor.formatstring_parser2;
+  current_formatstring_parser3 = extractor.formatstring_parser3;
   extractor.func (fp, real_file_name, logical_file_name, extractor.flag_table,
                  mdlp);
 
@@ -1966,7 +1995,9 @@ set_format_flags_from_context (enum is_format is_format[NFORMATS],
 {
   size_t i;
 
-  if (context.is_format1 != undecided || context.is_format2 != undecided)
+  if (context.is_format1 != undecided
+      || context.is_format2 != undecided
+      || context.is_format3 != undecided)
     for (i = 0; i < NFORMATS; i++)
       {
        if (is_format[i] == undecided)
@@ -1977,6 +2008,9 @@ set_format_flags_from_context (enum is_format is_format[NFORMATS],
            if (formatstring_parsers[i] == current_formatstring_parser2
                && context.is_format2 != undecided)
              is_format[i] = (enum is_format) context.is_format2;
+           if (formatstring_parsers[i] == current_formatstring_parser3
+               && context.is_format3 != undecided)
+             is_format[i] = (enum is_format) context.is_format3;
          }
        if (possible_format_p (is_format[i]))
          {
@@ -2237,14 +2271,17 @@ meta information, not the empty string.\n")));
     {
       if (is_format[i] == undecided
          && (formatstring_parsers[i] == current_formatstring_parser1
-             || formatstring_parsers[i] == current_formatstring_parser2)
+             || formatstring_parsers[i] == current_formatstring_parser2
+             || formatstring_parsers[i] == current_formatstring_parser3)
          /* But avoid redundancy: objc-format is stronger than c-format.  */
          && !(i == format_c && possible_format_p (is_format[format_objc]))
          && !(i == format_objc && possible_format_p (is_format[format_c]))
          /* Avoid flagging a string as c-format when it's known to be a
-            qt-format or kde-format or boost-format string.  */
+            qt-format or qt-plural-format or kde-format or boost-format
+            string.  */
          && !(i == format_c
               && (possible_format_p (is_format[format_qt])
+                  || possible_format_p (is_format[format_qt_plural])
                   || possible_format_p (is_format[format_kde])
                   || possible_format_p (is_format[format_boost]))))
        {
@@ -2360,7 +2397,8 @@ remember_a_message_plural (message_ty *mp, char *string,
         msgid_plural.  This is a heuristic.  */
       for (i = 0; i < NFORMATS; i++)
        if ((formatstring_parsers[i] == current_formatstring_parser1
-            || formatstring_parsers[i] == current_formatstring_parser2)
+            || formatstring_parsers[i] == current_formatstring_parser2
+            || formatstring_parsers[i] == current_formatstring_parser3)
            && (mp->is_format[i] == undecided || mp->is_format[i] == possible)
            /* But avoid redundancy: objc-format is stronger than c-format.  */
            && !(i == format_c
@@ -2368,9 +2406,10 @@ remember_a_message_plural (message_ty *mp, char *string,
            && !(i == format_objc
                 && possible_format_p (mp->is_format[format_c]))
            /* Avoid flagging a string as c-format when it's known to be a
-              qt-format or boost-format string.  */
+              qt-format or qt-plural-format or boost-format string.  */
            && !(i == format_c
                 && (possible_format_p (mp->is_format[format_qt])
+                    || possible_format_p (mp->is_format[format_qt_plural])
                     || possible_format_p (mp->is_format[format_kde])
                     || possible_format_p (mp->is_format[format_boost]))))
          {
@@ -2537,26 +2576,29 @@ arglist_parser_remember (struct arglist_parser *ap,
          /* Mark msgctxt as done.  */
          cp->argnumc = 0;
        }
-      else if (argnum == cp->argnum1)
-       {
-         cp->msgid = string;
-         cp->msgid_context = context;
-         cp->msgid_pos.file_name = file_name;
-         cp->msgid_pos.line_number = line_number;
-         cp->msgid_comment = add_reference (comment);
-         stored_string = true;
-         /* Mark msgid as done.  */
-         cp->argnum1 = 0;
-       }
-      else if (argnum == cp->argnum2)
+      else
        {
-         cp->msgid_plural = string;
-         cp->msgid_plural_context = context;
-         cp->msgid_plural_pos.file_name = file_name;
-         cp->msgid_plural_pos.line_number = line_number;
-         stored_string = true;
-         /* Mark msgid_plural as done.  */
-         cp->argnum2 = 0;
+         if (argnum == cp->argnum1)
+           {
+             cp->msgid = string;
+             cp->msgid_context = context;
+             cp->msgid_pos.file_name = file_name;
+             cp->msgid_pos.line_number = line_number;
+             cp->msgid_comment = add_reference (comment);
+             stored_string = true;
+             /* Mark msgid as done.  */
+             cp->argnum1 = 0;
+           }
+         if (argnum == cp->argnum2)
+           {
+             cp->msgid_plural = string;
+             cp->msgid_plural_context = context;
+             cp->msgid_plural_pos.file_name = file_name;
+             cp->msgid_plural_pos.line_number = line_number;
+             stored_string = true;
+             /* Mark msgid_plural as done.  */
+             cp->argnum2 = 0;
+           }
        }
     }
   /* Note: There is a memory leak here: When string was stored but is later
@@ -2802,15 +2844,33 @@ arglist_parser_done (struct arglist_parser *ap, int argnum)
                }
            }
 
-         mp = remember_a_message (ap->mlp, best_cp->msgctxt, best_cp->msgid,
-                                  best_cp->msgid_context,
-                                  &best_cp->msgid_pos,
-                                  best_cp->msgid_comment);
-         if (best_cp->msgid_plural != NULL)
-           remember_a_message_plural (mp, best_cp->msgid_plural,
-                                      best_cp->msgid_plural_context,
-                                      &best_cp->msgid_plural_pos,
-                                      NULL);
+         {
+           flag_context_ty msgid_context = best_cp->msgid_context;
+           flag_context_ty msgid_plural_context = best_cp->msgid_plural_context;
+
+           /* Special support for the 3-argument tr operator in Qt:
+              When --qt and --keyword=tr:1,1,2c,3t are specified, add to the
+              context the information that the argument is expeected to be a
+              qt-plural-format.  */
+           if (recognize_format_qt
+               && current_formatstring_parser3 == &formatstring_qt_plural
+               && best_cp->msgid_plural == best_cp->msgid)
+             {
+               msgid_context.is_format3 = yes_according_to_context;
+               msgid_plural_context.is_format3 = yes_according_to_context;
+             }
+
+           mp = remember_a_message (ap->mlp, best_cp->msgctxt, best_cp->msgid,
+                                    msgid_context,
+                                    &best_cp->msgid_pos,
+                                    best_cp->msgid_comment);
+           if (best_cp->msgid_plural != NULL)
+             remember_a_message_plural (mp, best_cp->msgid_plural,
+                                        msgid_plural_context,
+                                        &best_cp->msgid_plural_pos,
+                                        NULL);
+         }
+
          if (best_cp->xcomments.nitems > 0)
            {
              /* Add best_cp->xcomments to mp->comment_dot, unless already
@@ -3067,6 +3127,7 @@ language_to_extractor (const char *name)
        result.flag_table = tp->flag_table;
        result.formatstring_parser1 = tp->formatstring_parser1;
        result.formatstring_parser2 = tp->formatstring_parser2;
+       result.formatstring_parser3 = NULL;
 
        /* Handle --qt.  It's preferrable to handle this facility here rather
           than through an option --language=C++/Qt because the latter would
@@ -3075,6 +3136,7 @@ language_to_extractor (const char *name)
          {
            result.flag_table = &flag_table_cxx_qt;
            result.formatstring_parser2 = &formatstring_qt;
+           result.formatstring_parser3 = &formatstring_qt_plural;
          }
        /* Likewise for --kde.  */
        if (recognize_format_kde && strcmp (tp->name, "C++") == 0)
index 0d84c45631b80c709137fcf3c096d6c832d2ad38..a54b81c867c8bf5ef77fe194b5e7d02ee287b6a6 100644 (file)
@@ -1,5 +1,5 @@
 /* xgettext common functions.
-   Copyright (C) 2001-2003, 2005-2006, 2008 Free Software Foundation, Inc.
+   Copyright (C) 2001-2003, 2005-2006, 2008-2009 Free Software Foundation, Inc.
    Written by Peter Miller <millerp@canb.auug.org.au>
    and Bruno Haible <haible@clisp.cons.org>, 2001.
 
@@ -89,6 +89,9 @@ struct flag_context_ty
   /* Regarding the secondary formatstring type.  */
   /*enum is_format*/ unsigned int is_format2    : 3;
   /*bool*/           unsigned int pass_format2  : 1;
+  /* Regarding the tertiary formatstring type.  */
+  /*enum is_format*/ unsigned int is_format3    : 3;
+  /*bool*/           unsigned int pass_format3  : 1;
 };
 /* Null context.  */
 extern flag_context_ty null_context;