]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Enable format string checking also for plural messages.
authorBruno Haible <bruno@clisp.org>
Mon, 17 Dec 2001 11:36:35 +0000 (11:36 +0000)
committerBruno Haible <bruno@clisp.org>
Sun, 21 Jun 2009 21:45:11 +0000 (23:45 +0200)
13 files changed:
src/ChangeLog
src/format-c.c
src/format-java.c
src/format-lisp.c
src/format-pascal.c
src/format-python.c
src/format-ycp.c
src/format.h
src/message.c
src/msgfmt.c
tests/ChangeLog
tests/Makefile.am
tests/msgfmt-10 [new file with mode: 0755]

index 627bfc1884ab31dbd7e5ca434c63287a3256d0fd..5a644eaa0871dc45870fb0bd1c4e85a5152069f0 100644 (file)
@@ -1,3 +1,19 @@
+2001-12-15  Bruno Haible  <bruno@clisp.org>
+
+       * format.h (struct formatstring_parser): Add 'equality' and
+       'pretty_msgstr' arguments to 'check' field.
+       * format-c.c (format_check): Add 'equality' and 'pretty_msgstr'
+       arguments.
+       * format-java.c (format_check): Likewise.
+       * format-lisp.c (format_check): Likewise.
+       * format-pascal.c (format_check): Likewise.
+       * format-python.c (format_check): Likewise.
+       * format-ycp.c (format_check): Likewise.
+       * msgfmt.c (check_pair): Check messages with plural forms as well.
+       * message.c (msgfmt_check_pair_fails): Add msgid_plural, msgstr_len
+       arguments. Check messages with plural forms as well.
+       (message_merge): Check messages with plural forms as well.
+
 2001-12-11  Bruno Haible  <bruno@clisp.org>
 
        * x-java.l (strip_ending_spaces): Fix isspace call.
index 5abf9a6ac472ca942dbe080e9db6c25fee4093e6..d8e4da227c6e7679ba95eed8d501a68dc212b868 100644 (file)
@@ -142,7 +142,8 @@ static void format_free PARAMS ((void *descr));
 static int format_get_number_of_directives PARAMS ((void *descr));
 static bool format_check PARAMS ((const lex_pos_ty *pos,
                                  void *msgid_descr, void *msgstr_descr,
-                                 bool noisy));
+                                 bool equality,
+                                 bool noisy, const char *pretty_msgstr));
 
 
 static int
@@ -561,11 +562,13 @@ format_get_number_of_directives (descr)
 }
 
 static bool
-format_check (pos, msgid_descr, msgstr_descr, noisy)
+format_check (pos, msgid_descr, msgstr_descr, equality, noisy, pretty_msgstr)
      const lex_pos_ty *pos;
      void *msgid_descr;
      void *msgstr_descr;
+     bool equality;
      bool noisy;
+     const char *pretty_msgstr;
 {
   struct spec *spec1 = (struct spec *) msgid_descr;
   struct spec *spec2 = (struct spec *) msgstr_descr;
@@ -573,27 +576,30 @@ format_check (pos, msgid_descr, msgstr_descr, noisy)
   unsigned int i;
 
   /* Check the argument types are the same.  */
-  if (spec1->unnumbered_arg_count != spec2->unnumbered_arg_count)
+  if (equality
+      ? spec1->unnumbered_arg_count != spec2->unnumbered_arg_count
+      : spec1->unnumbered_arg_count < spec2->unnumbered_arg_count)
     {
       if (noisy)
        {
          error_with_progname = false;
          error_at_line (0, 0, pos->file_name, pos->line_number,
-                        _("number of format specifications in 'msgid' and 'msgstr' does not match"));
+                        _("number of format specifications in 'msgid' and '%s' does not match"),
+                        pretty_msgstr);
          error_with_progname = true;
        }
       err = true;
     }
   else
-    for (i = 0; i < spec1->unnumbered_arg_count; i++)
+    for (i = 0; i < spec2->unnumbered_arg_count; i++)
       if (spec1->unnumbered[i].type != spec2->unnumbered[i].type)
        {
          if (noisy)
            {
              error_with_progname = false;
              error_at_line (0, 0, pos->file_name, pos->line_number,
-                            _("format specifications in 'msgid' and 'msgstr' for argument %u are not the same"),
-                            i + 1);
+                            _("format specifications in 'msgid' and '%s' for argument %u are not the same"),
+                            pretty_msgstr, i + 1);
              error_with_progname = true;
            }
          err = true;
index feaf66ac7c69af842b941e71b8e1b326ab188019..09174c015bd03d4c6cb60a3a5634be534cdb27bb 100644 (file)
@@ -27,7 +27,6 @@
 #include "format.h"
 #include "c-ctype.h"
 #include "xmalloc.h"
-#include "system.h"
 #include "error.h"
 #include "progname.h"
 #include "libgettext.h"
@@ -140,7 +139,8 @@ static void format_free PARAMS ((void *descr));
 static int format_get_number_of_directives PARAMS ((void *descr));
 static bool format_check PARAMS ((const lex_pos_ty *pos,
                                  void *msgid_descr, void *msgstr_descr,
-                                 bool noisy));
+                                 bool equality,
+                                 bool noisy, const char *pretty_msgstr));
 
 
 /* Quote handling:
@@ -622,11 +622,13 @@ format_get_number_of_directives (descr)
 }
 
 static bool
-format_check (pos, msgid_descr, msgstr_descr, noisy)
+format_check (pos, msgid_descr, msgstr_descr, equality, noisy, pretty_msgstr)
      const lex_pos_ty *pos;
      void *msgid_descr;
      void *msgstr_descr;
+     bool equality;
      bool noisy;
+     const char *pretty_msgstr;
 {
   struct spec *spec1 = (struct spec *) msgid_descr;
   struct spec *spec2 = (struct spec *) msgstr_descr;
@@ -634,17 +636,18 @@ format_check (pos, msgid_descr, msgstr_descr, noisy)
 
   if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
     {
-      unsigned int i;
-      unsigned int n = MAX (spec1->numbered_arg_count, spec2->numbered_arg_count);
+      unsigned int i, j;
+      unsigned int n1 = spec1->numbered_arg_count;
+      unsigned int n2 = spec2->numbered_arg_count;
 
       /* Check the argument names are the same.
         Both arrays are sorted.  We search for the first difference.  */
-      for (i = 0; i < n; i++)
+      for (i = 0, j = 0; i < n1 || j < n2; )
        {
-         int cmp = (i >= spec1->numbered_arg_count ? 1 :
-                    i >= spec2->numbered_arg_count ? -1 :
-                    spec1->numbered[i].number > spec2->numbered[i].number ? 1 :
-                    spec1->numbered[i].number < spec2->numbered[i].number ? -1 :
+         int cmp = (i >= n1 ? 1 :
+                    j >= n2 ? -1 :
+                    spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
+                    spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
                     0);
 
          if (cmp > 0)
@@ -653,8 +656,8 @@ format_check (pos, msgid_descr, msgstr_descr, noisy)
                {
                  error_with_progname = false;
                  error_at_line (0, 0, pos->file_name, pos->line_number,
-                                _("a format specification for argument {%u} doesn't exist in 'msgid'"),
-                                spec2->numbered[i].number);
+                                _("a format specification for argument {%u}, as in '%s', doesn't exist in 'msgid'"),
+                                spec2->numbered[j].number, pretty_msgstr);
                  error_with_progname = true;
                }
              err = true;
@@ -662,34 +665,50 @@ format_check (pos, msgid_descr, msgstr_descr, noisy)
            }
          else if (cmp < 0)
            {
-             if (noisy)
+             if (equality)
                {
-                 error_with_progname = false;
-                 error_at_line (0, 0, pos->file_name, pos->line_number,
-                                _("a format specification for argument {%u} doesn't exist in 'msgstr'"),
-                                spec1->numbered[i].number);
-                 error_with_progname = true;
+                 if (noisy)
+                   {
+                     error_with_progname = false;
+                     error_at_line (0, 0, pos->file_name, pos->line_number,
+                                    _("a format specification for argument {%u} doesn't exist in '%s'"),
+                                    spec1->numbered[i].number, pretty_msgstr);
+                     error_with_progname = true;
+                   }
+                 err = true;
+                 break;
                }
-             err = true;
-             break;
+             else
+               i++;
            }
+         else
+           j++, i++;
        }
       /* Check the argument types are the same.  */
       if (!err)
-       for (i = 0; i < spec2->numbered_arg_count; i++)
-         if (spec1->numbered[i].type != spec2->numbered[i].type)
-           {
-             if (noisy)
-               {
-                 error_with_progname = false;
-                 error_at_line (0, 0, pos->file_name, pos->line_number,
-                                _("format specifications in 'msgid' and 'msgstr' for argument {%u} are not the same"),
-                                spec2->numbered[i].number);
-                 error_with_progname = true;
-               }
-             err = true;
-             break;
-           }
+       for (i = 0, j = 0; j < n2; )
+         {
+           if (spec1->numbered[i].number == spec2->numbered[j].number)
+             {
+               if (spec1->numbered[i].type != spec2->numbered[j].type)
+                 {
+                   if (noisy)
+                     {
+                       error_with_progname = false;
+                       error_at_line (0, 0, pos->file_name, pos->line_number,
+                                      _("format specifications in 'msgid' and '%s' for argument {%u} are not the same"),
+                                      pretty_msgstr,
+                                      spec2->numbered[j].number);
+                       error_with_progname = true;
+                     }
+                   err = true;
+                   break;
+                 }
+               j++, i++;
+             }
+           else
+             i++;
+         }
     }
 
   return err;
index 26d7595b0d4b21d4c6a4c30fa24bff0a597b38b3..35e7510337e258f98038051c813e5bd6781fe274 100644 (file)
@@ -230,7 +230,8 @@ static void format_free PARAMS ((void *descr));
 static int format_get_number_of_directives PARAMS ((void *descr));
 static bool format_check PARAMS ((const lex_pos_ty *pos,
                                  void *msgid_descr, void *msgstr_descr,
-                                 bool noisy));
+                                 bool equality,
+                                 bool noisy, const char *pretty_msgstr));
 
 
 /* ======================= Verify a format_arg_list ======================= */
@@ -3356,26 +3357,53 @@ format_get_number_of_directives (descr)
 }
 
 static bool
-format_check (pos, msgid_descr, msgstr_descr, noisy)
+format_check (pos, msgid_descr, msgstr_descr, equality, noisy, pretty_msgstr)
      const lex_pos_ty *pos;
      void *msgid_descr;
      void *msgstr_descr;
+     bool equality;
      bool noisy;
+     const char *pretty_msgstr;
 {
   struct spec *spec1 = (struct spec *) msgid_descr;
   struct spec *spec2 = (struct spec *) msgstr_descr;
   bool err = false;
 
-  if (!equal_list (spec1->list, spec2->list))
+  if (equality)
     {
-      if (noisy)
+      if (!equal_list (spec1->list, spec2->list))
        {
-         error_with_progname = false;
-         error_at_line (0, 0, pos->file_name, pos->line_number,
-                        _("format specifications in 'msgid' and 'msgstr' are not equivalent"));
-         error_with_progname = true;
+         if (noisy)
+           {
+             error_with_progname = false;
+             error_at_line (0, 0, pos->file_name, pos->line_number,
+                            _("format specifications in 'msgid' and '%s' are not equivalent"),
+                            pretty_msgstr);
+             error_with_progname = true;
+           }
+         err = true;
+       }
+    }
+  else
+    {
+      struct format_arg_list *intersection =
+       make_intersected_list (copy_list (spec1->list),
+                              copy_list (spec2->list));
+
+      if (!(intersection != NULL
+           && (normalize_list (intersection),
+               equal_list (intersection, spec2->list))))
+       {
+         if (noisy)
+           {
+             error_with_progname = false;
+             error_at_line (0, 0, pos->file_name, pos->line_number,
+                            _("format specifications in '%s' are not a subset of those in 'msgid'"),
+                            pretty_msgstr);
+             error_with_progname = true;
+           }
+         err = true;
        }
-      err = true;
     }
 
   return err;
index de06431c6827b9d6bfa425e18335a161ecdf36d9..a3aa95785fda60513353848f3f7021992efc95ab 100644 (file)
@@ -26,7 +26,6 @@
 #include "format.h"
 #include "c-ctype.h"
 #include "xmalloc.h"
-#include "system.h"
 #include "error.h"
 #include "progname.h"
 #include "libgettext.h"
@@ -102,7 +101,8 @@ static void format_free PARAMS ((void *descr));
 static int format_get_number_of_directives PARAMS ((void *descr));
 static bool format_check PARAMS ((const lex_pos_ty *pos,
                                  void *msgid_descr, void *msgstr_descr,
-                                 bool noisy));
+                                 bool equality,
+                                 bool noisy, const char *pretty_msgstr));
 
 
 static int
@@ -366,11 +366,13 @@ format_get_number_of_directives (descr)
 }
 
 static bool
-format_check (pos, msgid_descr, msgstr_descr, noisy)
+format_check (pos, msgid_descr, msgstr_descr, equality, noisy, pretty_msgstr)
      const lex_pos_ty *pos;
      void *msgid_descr;
      void *msgstr_descr;
+     bool equality;
      bool noisy;
+     const char *pretty_msgstr;
 {
   struct spec *spec1 = (struct spec *) msgid_descr;
   struct spec *spec2 = (struct spec *) msgstr_descr;
@@ -378,17 +380,18 @@ format_check (pos, msgid_descr, msgstr_descr, noisy)
 
   if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
     {
-      unsigned int i;
-      unsigned int n = MAX (spec1->numbered_arg_count, spec2->numbered_arg_count);
+      unsigned int i, j;
+      unsigned int n1 = spec1->numbered_arg_count;
+      unsigned int n2 = spec2->numbered_arg_count;
 
       /* Check the argument names are the same.
         Both arrays are sorted.  We search for the first difference.  */
-      for (i = 0; i < n; i++)
+      for (i = 0, j = 0; i < n1 || j < n2; )
        {
-         int cmp = (i >= spec1->numbered_arg_count ? 1 :
-                    i >= spec2->numbered_arg_count ? -1 :
-                    spec1->numbered[i].number > spec2->numbered[i].number ? 1 :
-                    spec1->numbered[i].number < spec2->numbered[i].number ? -1 :
+         int cmp = (i >= n1 ? 1 :
+                    j >= n2 ? -1 :
+                    spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
+                    spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
                     0);
 
          if (cmp > 0)
@@ -397,8 +400,8 @@ format_check (pos, msgid_descr, msgstr_descr, noisy)
                {
                  error_with_progname = false;
                  error_at_line (0, 0, pos->file_name, pos->line_number,
-                                _("a format specification for argument {%u} doesn't exist in 'msgid'"),
-                                spec2->numbered[i].number);
+                                _("a format specification for argument {%u}, as in '%s', doesn't exist in 'msgid'"),
+                                spec2->numbered[j].number, pretty_msgstr);
                  error_with_progname = true;
                }
              err = true;
@@ -406,34 +409,50 @@ format_check (pos, msgid_descr, msgstr_descr, noisy)
            }
          else if (cmp < 0)
            {
-             if (noisy)
+             if (equality)
                {
-                 error_with_progname = false;
-                 error_at_line (0, 0, pos->file_name, pos->line_number,
-                                _("a format specification for argument {%u} doesn't exist in 'msgstr'"),
-                                spec1->numbered[i].number);
-                 error_with_progname = true;
+                 if (noisy)
+                   {
+                     error_with_progname = false;
+                     error_at_line (0, 0, pos->file_name, pos->line_number,
+                                    _("a format specification for argument {%u} doesn't exist in '%s'"),
+                                    spec1->numbered[i].number, pretty_msgstr);
+                     error_with_progname = true;
+                   }
+                 err = true;
+                 break;
                }
-             err = true;
-             break;
+             else
+               i++;
            }
+         else
+           j++, i++;
        }
       /* Check the argument types are the same.  */
       if (!err)
-       for (i = 0; i < spec2->numbered_arg_count; i++)
-         if (spec1->numbered[i].type != spec2->numbered[i].type)
-           {
-             if (noisy)
-               {
-                 error_with_progname = false;
-                 error_at_line (0, 0, pos->file_name, pos->line_number,
-                                _("format specifications in 'msgid' and 'msgstr' for argument {%u} are not the same"),
-                                spec2->numbered[i].number);
-                 error_with_progname = true;
-               }
-             err = true;
-             break;
-           }
+       for (i = 0, j = 0; j < n2; )
+         {
+           if (spec1->numbered[i].number == spec2->numbered[j].number)
+             {
+               if (spec1->numbered[i].type != spec2->numbered[j].type)
+                 {
+                   if (noisy)
+                     {
+                       error_with_progname = false;
+                       error_at_line (0, 0, pos->file_name, pos->line_number,
+                                      _("format specifications in 'msgid' and '%s' for argument {%u} are not the same"),
+                                      pretty_msgstr,
+                                      spec2->numbered[j].number);
+                       error_with_progname = true;
+                     }
+                   err = true;
+                   break;
+                 }
+               j++, i++;
+             }
+           else
+             i++;
+         }
     }
 
   return err;
index ab47d10c507cc8ec65031a9ccae03acc8d7207ac..e0648096ba0f325a77a21152a6355834523d2244 100644 (file)
@@ -26,7 +26,6 @@
 
 #include "format.h"
 #include "xmalloc.h"
-#include "system.h"
 #include "error.h"
 #include "progname.h"
 #include "libgettext.h"
@@ -110,7 +109,8 @@ static void format_free PARAMS ((void *descr));
 static int format_get_number_of_directives PARAMS ((void *descr));
 static bool format_check PARAMS ((const lex_pos_ty *pos,
                                  void *msgid_descr, void *msgstr_descr,
-                                 bool noisy));
+                                 bool equality,
+                                 bool noisy, const char *pretty_msgstr));
 
 
 static int
@@ -376,11 +376,13 @@ format_get_number_of_directives (descr)
 }
 
 static bool
-format_check (pos, msgid_descr, msgstr_descr, noisy)
+format_check (pos, msgid_descr, msgstr_descr, equality, noisy, pretty_msgstr)
      const lex_pos_ty *pos;
      void *msgid_descr;
      void *msgstr_descr;
+     bool equality;
      bool noisy;
+     const char *pretty_msgstr;
 {
   struct spec *spec1 = (struct spec *) msgid_descr;
   struct spec *spec2 = (struct spec *) msgstr_descr;
@@ -392,7 +394,8 @@ format_check (pos, msgid_descr, msgstr_descr, noisy)
        {
          error_with_progname = false;
          error_at_line (0, 0, pos->file_name, pos->line_number,
-                        _("format specifications in 'msgid' expect a mapping, those in 'msgstr' expect a tuple"));
+                        _("format specifications in 'msgid' expect a mapping, those in '%s' expect a tuple"),
+                        pretty_msgstr);
          error_with_progname = true;
        }
       err = true;
@@ -403,7 +406,8 @@ format_check (pos, msgid_descr, msgstr_descr, noisy)
        {
          error_with_progname = false;
          error_at_line (0, 0, pos->file_name, pos->line_number,
-                        _("format specifications in 'msgid' expect a tuple, those in 'msgstr' expect a mapping"));
+                        _("format specifications in 'msgid' expect a tuple, those in '%s' expect a mapping"),
+                        pretty_msgstr);
          error_with_progname = true;
        }
       err = true;
@@ -412,16 +416,17 @@ format_check (pos, msgid_descr, msgstr_descr, noisy)
     {
       if (spec1->named_arg_count + spec2->named_arg_count > 0)
        {
-         unsigned int i;
-         unsigned int n = MAX (spec1->named_arg_count, spec2->named_arg_count);
+         unsigned int i, j;
+         unsigned int n1 = spec1->named_arg_count;
+         unsigned int n2 = spec2->named_arg_count;
 
          /* Check the argument names are the same.
             Both arrays are sorted.  We search for the first difference.  */
-         for (i = 0; i < n; i++)
+         for (i = 0, j = 0; i < n1 || j < n2; )
            {
-             int cmp = (i >= spec1->named_arg_count ? 1 :
-                        i >= spec2->named_arg_count ? -1 :
-                        strcmp (spec1->named[i].name, spec2->named[i].name));
+             int cmp = (i >= n1 ? 1 :
+                        j >= n2 ? -1 :
+                        strcmp (spec1->named[i].name, spec2->named[j].name));
 
              if (cmp > 0)
                {
@@ -429,8 +434,8 @@ format_check (pos, msgid_descr, msgstr_descr, noisy)
                    {
                      error_with_progname = false;
                      error_at_line (0, 0, pos->file_name, pos->line_number,
-                                    _("a format specification for argument '%s' doesn't exist in 'msgid'"),
-                                    spec2->named[i].name);
+                                    _("a format specification for argument '%s', as in '%s', doesn't exist in 'msgid'"),
+                                    spec2->named[j].name, pretty_msgstr);
                      error_with_progname = true;
                    }
                  err = true;
@@ -438,34 +443,51 @@ format_check (pos, msgid_descr, msgstr_descr, noisy)
                }
              else if (cmp < 0)
                {
-                 if (noisy)
+                 if (equality)
                    {
-                     error_with_progname = false;
-                     error_at_line (0, 0, pos->file_name, pos->line_number,
-                                    _("a format specification for argument '%s' doesn't exist in 'msgstr'"),
-                                    spec1->named[i].name);
-                     error_with_progname = true;
+                     if (noisy)
+                       {
+                         error_with_progname = false;
+                         error_at_line (0, 0, pos->file_name, pos->line_number,
+                                        _("a format specification for argument '%s' doesn't exist in '%s'"),
+                                        spec1->named[i].name, pretty_msgstr);
+                         error_with_progname = true;
+                       }
+                     err = true;
+                     break;
                    }
-                 err = true;
-                 break;
+                 else
+                   i++;
                }
+             else
+               j++, i++;
            }
          /* Check the argument types are the same.  */
          if (!err)
-           for (i = 0; i < spec2->named_arg_count; i++)
-             if (spec1->named[i].type != spec2->named[i].type)
-               {
-                 if (noisy)
-                   {
-                     error_with_progname = false;
-                     error_at_line (0, 0, pos->file_name, pos->line_number,
-                                    _("format specifications in 'msgid' and 'msgstr' for argument '%s' are not the same"),
-                                    spec2->named[i].name);
-                     error_with_progname = true;
-                   }
-                 err = true;
-                 break;
-               }
+           for (i = 0, j = 0; j < n2; )
+             {
+               if (strcmp (spec1->named[i].name, spec2->named[j].name) == 0)
+                 {
+                   if (spec1->named[i].type != spec2->named[j].type)
+                     {
+                       if (noisy)
+                         {
+                           error_with_progname = false;
+                           error_at_line (0, 0, pos->file_name,
+                                          pos->line_number,
+                                          _("format specifications in 'msgid' and '%s' for argument '%s' are not the same"),
+                                          pretty_msgstr,
+                                          spec2->named[j].name);
+                           error_with_progname = true;
+                         }
+                       err = true;
+                       break;
+                     }
+                   j++, i++;
+                 }
+               else
+                 i++;
+             }
        }
 
       if (spec1->unnamed_arg_count + spec2->unnamed_arg_count > 0)
@@ -473,27 +495,30 @@ format_check (pos, msgid_descr, msgstr_descr, noisy)
          unsigned int i;
 
          /* Check the argument types are the same.  */
-         if (spec1->unnamed_arg_count != spec2->unnamed_arg_count)
+         if (equality
+             ? spec1->unnamed_arg_count != spec2->unnamed_arg_count
+             : spec1->unnamed_arg_count < spec2->unnamed_arg_count)
            {
              if (noisy)
                {
                  error_with_progname = false;
                  error_at_line (0, 0, pos->file_name, pos->line_number,
-                                _("number of format specifications in 'msgid' and 'msgstr' does not match"));
+                                _("number of format specifications in 'msgid' and '%s' does not match"),
+                                pretty_msgstr);
                  error_with_progname = true;
                }
              err = true;
            }
          else
-           for (i = 0; i < spec1->unnamed_arg_count; i++)
+           for (i = 0; i < spec2->unnamed_arg_count; i++)
              if (spec1->unnamed[i].type != spec2->unnamed[i].type)
                {
                  if (noisy)
                    {
                      error_with_progname = false;
                      error_at_line (0, 0, pos->file_name, pos->line_number,
-                                    _("format specifications in 'msgid' and 'msgstr' for argument %u are not the same"),
-                                    i + 1);
+                                    _("format specifications in 'msgid' and '%s' for argument %u are not the same"),
+                                    pretty_msgstr, i + 1);
                      error_with_progname = true;
                    }
                  err = true;
index dbd9e6a352740760a67705eb057ba2075cd60234..494effd09ac5cd6344e83faa609f3d5c84662744 100644 (file)
@@ -53,7 +53,8 @@ static void format_free PARAMS ((void *descr));
 static int format_get_number_of_directives PARAMS ((void *descr));
 static bool format_check PARAMS ((const lex_pos_ty *pos,
                                  void *msgid_descr, void *msgstr_descr,
-                                 bool noisy));
+                                 bool equality,
+                                 bool noisy, const char *pretty_msgstr));
 
 
 static void *
@@ -115,11 +116,13 @@ format_get_number_of_directives (descr)
 }
 
 static bool
-format_check (pos, msgid_descr, msgstr_descr, noisy)
+format_check (pos, msgid_descr, msgstr_descr, equality, noisy, pretty_msgstr)
      const lex_pos_ty *pos;
      void *msgid_descr;
      void *msgstr_descr;
+     bool equality;
      bool noisy;
+     const char *pretty_msgstr;
 {
   struct spec *spec1 = (struct spec *) msgid_descr;
   struct spec *spec2 = (struct spec *) msgstr_descr;
@@ -131,16 +134,16 @@ format_check (pos, msgid_descr, msgstr_descr, noisy)
       bool arg_used1 = (i < spec1->arg_count && spec1->args_used[i]);
       bool arg_used2 = (i < spec2->arg_count && spec2->args_used[i]);
 
-      if (arg_used1 != arg_used2)
+      if (equality ? (arg_used1 != arg_used2) : (!arg_used1 && arg_used2))
        {
          if (noisy)
            {
              error_with_progname = false;
              error_at_line (0, 0, pos->file_name, pos->line_number,
                             arg_used1
-                            ? _("a format specification for argument %u doesn't exist in 'msgstr'")
-                            : _("a format specification for argument %u doesn't exist in 'msgid'"),
-                            i + 1);
+                            ? _("a format specification for argument %u doesn't exist in '%s'")
+                            : _("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"),
+                            i + 1, pretty_msgstr);
              error_with_progname = true;
            }
          err = true;
index cb925ff6c90c036d0356c7f8e90be1e40a781a82..43cbbcd322d99d5b066a9aeb292716cdd6b852df 100644 (file)
@@ -42,12 +42,15 @@ struct formatstring_parser
   int (*get_number_of_directives) PARAMS ((void *descr));
 
   /* Verify that the argument types/names in msgid_descr and those in
-     msgstr_descr are the same.  If not, signal an error using
+     msgstr_descr are the same (if equality=true), or (if equality=false)
+     that those of msgid_descr extend those of msgstr_descr (i.e.
+     msgstr_descr may omit some of the arguments of msgid_descr).
+     If not, signal an error using
        error_with_progname = false;
        error_at_line (0, 0, pos->file_name, pos->line_number, ...);
        error_with_progname = true;
      (but only if noisy=true) and return true.  Otherwise return false.  */
-  bool (*check) PARAMS ((const lex_pos_ty *pos, void *msgid_descr, void *msgstr_descr, bool noisy));
+  bool (*check) PARAMS ((const lex_pos_ty *pos, void *msgid_descr, void *msgstr_descr, bool equality, bool noisy, const char *pretty_msgstr));
 };
 
 /* Format string parsers, each defined in its own file.  */
index 2b6c39c38fb5320a66659219522dbb69d2558c04..b6d37512d6a6debe913f866bc590d1b07b999286 100644 (file)
@@ -39,7 +39,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
    function argument counts despite of K&R C function definition syntax.  */
 static bool msgfmt_check_pair_fails PARAMS ((const lex_pos_ty *pos,
                                             const char *msgid,
-                                            const char *msgstr, size_t i));
+                                            const char *msgid_plural,
+                                            const char *msgstr,
+                                            size_t msgstr_len, size_t fmt));
 static message_ty *message_list_search_fuzzy_inner PARAMS ((
        message_list_ty *mlp, const char *msgid, double *best_weight_p));
 
@@ -213,32 +215,44 @@ message_copy (mp)
 
 
 static bool
-msgfmt_check_pair_fails (pos, msgid, msgstr, i)
+msgfmt_check_pair_fails (pos, msgid, msgid_plural, msgstr, msgstr_len, fmt)
      const lex_pos_ty *pos;
      const char *msgid;
+     const char *msgid_plural;
      const char *msgstr;
-     size_t i;
+     size_t msgstr_len;
+     size_t fmt;
 {
   bool failure;
-  struct formatstring_parser *parser = formatstring_parsers[i];
-  void *msgid_descr = parser->parse (msgid);
+  struct formatstring_parser *parser = formatstring_parsers[fmt];
+  void *msgid_descr =
+    parser->parse (msgid_plural != NULL ? msgid_plural : msgid);
 
+  failure = false;
   if (msgid_descr != NULL)
     {
-      void *msgstr_descr = parser->parse (msgstr);
+      const char *p_end = msgstr + msgstr_len;
+      const char *p;
 
-      if (msgstr_descr != NULL)
+      for (p = msgstr; p < p_end; p += strlen (p) + 1)
        {
-         failure = parser->check (pos, msgid_descr, msgstr_descr, false);
-         parser->free (msgstr_descr);
+         void *msgstr_descr = parser->parse (msgstr);
+
+         if (msgstr_descr != NULL)
+           {
+             failure = parser->check (pos, msgid_descr, msgstr_descr,
+                                      msgid_plural == NULL, false, NULL);
+             parser->free (msgstr_descr);
+           }
+         else
+           failure = true;
+
+         if (failure)
+           break;
        }
-      else
-       failure = true;
 
       parser->free (msgid_descr);
     }
-  else
-    failure = false;
 
   return failure;
 }
@@ -455,10 +469,10 @@ message_merge (def, ref)
         2. msgmerge must not transform a PO file which passes "msgfmt -c"
            into a PO file which doesn't.  */
       if (!result->is_fuzzy
-         && ref->msgid_plural == NULL
          && possible_format_p (ref->is_format[i])
          && !possible_format_p (def->is_format[i])
-         && msgfmt_check_pair_fails (&def->pos, ref->msgid, msgstr, i))
+         && msgfmt_check_pair_fails (&def->pos, ref->msgid, ref->msgid_plural,
+                                     msgstr, msgstr_len, i))
        result->is_fuzzy = true;
     }
 
index 01794a2b22b0a511ae0f31f1c9ea0fbb47e9620e..e8a40b64d8aa50dcc31bb5c2652670ed27391254 100644 (file)
@@ -1043,7 +1043,7 @@ check_pair (msgid, msgid_pos, msgid_plural, msgstr, msgstr_len, msgstr_pos,
       exit_status = EXIT_FAILURE;
     }
 
-  if (check_format_strings && msgid_plural == NULL)
+  if (check_format_strings)
     /* Test 3: Check whether both formats strings contain the same number
        of format specifications.
        We check only those messages for which the msgid's is_format flag
@@ -1067,30 +1067,48 @@ check_pair (msgid, msgid_pos, msgid_plural, msgstr, msgstr_len, msgstr_pos,
                arguments that are used by other translations.  */
 
          struct formatstring_parser *parser = formatstring_parsers[i];
-         void *msgid_descr = parser->parse (msgid);
+         void *msgid_descr =
+           parser->parse (msgid_plural != NULL ? msgid_plural : msgid);
 
          if (msgid_descr != NULL)
            {
-             void *msgstr_descr = parser->parse (msgstr);
+             char buf[18+1];
+             const char *pretty_msgstr = "msgstr";
+             const char *p_end = msgstr + msgstr_len;
+             const char *p;
 
-             if (msgstr_descr != NULL)
+             for (p = msgstr, j = 0; p < p_end; p += strlen (p) + 1, j++)
                {
-                 if (parser->check (msgid_pos, msgid_descr, msgstr_descr,
-                                    true))
-                   exit_status = EXIT_FAILURE;
-
-                 parser->free (msgstr_descr);
-               }
-             else
-               {
-                 error_with_progname = false;
-                 error_at_line (0, 0, msgid_pos->file_name,
-                                msgid_pos->line_number,
-                                _("\
-'msgstr' is not a valid %s format string, unlike 'msgid'"),
-                                format_language_pretty[i]);
-                 error_with_progname = true;
-                 exit_status = EXIT_FAILURE;
+                 void *msgstr_descr;
+
+                 if (msgid_plural != NULL)
+                   {
+                     sprintf (buf, "msgstr[%u]", j);
+                     pretty_msgstr = buf;
+                   }
+
+                 msgstr_descr = parser->parse (p);
+
+                 if (msgstr_descr != NULL)
+                   {
+                     if (parser->check (msgid_pos, msgid_descr, msgstr_descr,
+                                        msgid_plural == NULL,
+                                        true, pretty_msgstr))
+                       exit_status = EXIT_FAILURE;
+
+                     parser->free (msgstr_descr);
+                   }
+                 else
+                   {
+                     error_with_progname = false;
+                     error_at_line (0, 0, msgid_pos->file_name,
+                                    msgid_pos->line_number,
+                                    _("\
+'%s' is not a valid %s format string, unlike 'msgid'"),
+                                    pretty_msgstr, format_language_pretty[i]);
+                     error_with_progname = true;
+                     exit_status = EXIT_FAILURE;
+                   }
                }
 
              parser->free (msgid_descr);
index f11dc471e3dce3fa22f286875b1c040defd51e13..d4bc1e16d39aaaaf212be8e6653ccfd8ca47507d 100644 (file)
@@ -1,3 +1,8 @@
+2001-12-15  Bruno Haible  <bruno@clisp.org>
+
+       * msgfmt-10: New file.
+       * Makefile.am (TESTS): Add it.
+
 2001-12-11  Bruno Haible  <bruno@clisp.org>
 
        * lang-c++: Don't use ostream::form, it's a g++ 2.x extension not
index a80860e378f0a3ce7f0f15cf40da514ff5c3ba72..23d2bb483a14493e6455e8c86774eaae6a372c8b 100644 (file)
@@ -34,7 +34,7 @@ TESTS = gettext-1 gettext-2 \
        msgexec-1 msgexec-2 \
        msgfilter-1 msgfilter-2 \
        msgfmt-1 msgfmt-2 msgfmt-3 msgfmt-4 msgfmt-5 msgfmt-6 msgfmt-7 \
-       msgfmt-8 msgfmt-9 \
+       msgfmt-8 msgfmt-9 msgfmt-10 \
        msggrep-1 msggrep-2 msggrep-3 msggrep-4 \
        msgmerge-1 msgmerge-2 msgmerge-3 msgmerge-4 msgmerge-5 msgmerge-6 \
        msgmerge-7 msgmerge-8 msgmerge-9 msgmerge-10 msgmerge-11 msgmerge-12 \
diff --git a/tests/msgfmt-10 b/tests/msgfmt-10
new file mode 100755 (executable)
index 0000000..2945273
--- /dev/null
@@ -0,0 +1,87 @@
+#! /bin/sh
+
+# Test format string checking in plural entries.
+
+tmpfiles=""
+trap 'rm -fr $tmpfiles' 1 2 3 15
+
+tmpfiles="mf-test10.po1"
+cat <<EOF > mf-test10.po1
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR Free Software Foundation, Inc.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU bison\n"
+"PO-Revision-Date: 2001-04-05 19:47+0200\n"
+"Last-Translator: ABC DEF <abc@gnu.uucp>\n"
+"Language-Team: test <test@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-9\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/reduce.c:511
+#, c-format
+msgid "%d useless nonterminal"
+msgid_plural "%d useless nonterminals"
+msgstr[0] "1 yararsýz deðiþken simge"
+msgstr[1] "%d yararsýz deðiþken simges"
+
+#: src/reduce.c:520
+#, c-format
+msgid "one useless rule"
+msgid_plural "%d useless rules"
+msgstr[0] "%d yararsýz kural"
+msgstr[1] "%d yararsýz kurals"
+EOF
+
+: ${MSGFMT=msgfmt}
+${MSGFMT} --check mf-test10.po1 -o /dev/null || \
+  { rm -fr $tmpfiles; exit 1; }
+
+tmpfiles="mf-test10.po2"
+cat <<EOF > mf-test10.po2
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR Free Software Foundation, Inc.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU bison\n"
+"PO-Revision-Date: 2001-04-05 19:47+0200\n"
+"Last-Translator: ABC DEF <abc@gnu.uucp>\n"
+"Language-Team: test <test@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-9\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/reduce.c:520
+#, c-format
+msgid "one useless rule"
+msgid_plural "%d useless rules"
+msgstr[0] "%d yararsýz kural"
+msgstr[1] "%d yararsýz kural%s"
+EOF
+
+tmpfiles="$tmpfiles mf-test10.err"
+: ${MSGFMT=msgfmt}
+LC_MESSAGES=C LC_ALL= \
+${MSGFMT} --check mf-test10.po2 -o /dev/null \
+    2> mf-test10.err
+
+tmpfiles="$tmpfiles mf-test10.ok"
+cat << EOF > mf-test10.ok
+mf-test10.po2:18: number of format specifications in 'msgid' and 'msgstr[1]' does not match
+msgfmt: found 1 fatal error
+EOF
+
+: ${DIFF=diff}
+${DIFF} mf-test10.ok mf-test10.err
+result=$?
+
+rm -fr $tmpfiles
+
+exit $result