]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Better error message when an msgstr string is an invalid format string.
authorBruno Haible <bruno@clisp.org>
Mon, 24 Feb 2003 10:54:10 +0000 (10:54 +0000)
committerBruno Haible <bruno@clisp.org>
Tue, 23 Jun 2009 10:10:14 +0000 (12:10 +0200)
20 files changed:
gettext-tools/po/ChangeLog
gettext-tools/po/POTFILES.in
gettext-tools/src/ChangeLog
gettext-tools/src/Makefile.am
gettext-tools/src/format-awk.c
gettext-tools/src/format-c.c
gettext-tools/src/format-elisp.c
gettext-tools/src/format-invalid.h [new file with mode: 0644]
gettext-tools/src/format-java.c
gettext-tools/src/format-librep.c
gettext-tools/src/format-lisp.c
gettext-tools/src/format-pascal.c
gettext-tools/src/format-php.c
gettext-tools/src/format-python.c
gettext-tools/src/format-tcl.c
gettext-tools/src/format-ycp.c
gettext-tools/src/format.h
gettext-tools/src/msgfmt.c
gettext-tools/src/msgmerge.c
gettext-tools/src/xgettext.c

index 5a929fc239e32e2ac0e21809c06e1131948dbc93..841ddd6d8fedfcadd8b41510d72e6d192b029853 100644 (file)
@@ -1,3 +1,8 @@
+2003-02-23  Bruno Haible  <bruno@clisp.org>
+
+       * POTFILES.in: Add src/format-invalid.h. Replace src/po-gram-gen.c
+       with src/po-gram-gen.y.
+
 2003-02-16  Bruno Haible  <bruno@clisp.org>
 
        * Makevars.template: New file.
index 46ae65ada5ceb6df6247bbe093f254762936b803..787a1d162cb1ce26e7d0544a2c6e61fb76d6cfc3 100644 (file)
@@ -26,6 +26,7 @@ src/file-list.c
 src/format-awk.c
 src/format-c.c
 src/format-elisp.c
+src/format-invalid.h
 src/format-java.c
 src/format-librep.c
 src/format-lisp.c
@@ -54,7 +55,7 @@ src/msgunfmt.c
 src/msguniq.c
 src/open-po.c
 src/po-charset.c
-src/po-gram-gen.c
+src/po-gram-gen.y
 src/po-lex.h
 src/po-lex.c
 src/read-java.c
index 439703e7c9a943789f7b8f2b86ecfc7533f53f87..2fba9b5d66df35e653506ca8791bc4954106ae97 100644 (file)
@@ -1,3 +1,44 @@
+2003-02-23  Bruno Haible  <bruno@clisp.org>
+
+       Improve error messages for invalid format strings.
+       * format-invalid.h: New file.
+       * format.h (struct formatstring_parser): Add invalid_reason argument
+       to 'parse' field.
+       * format-awk.c: Include c-ctype.h, xerror.h, format-invalid.h.
+       (format_parse): Add invalid_reason argument.
+       * format-c.c: Include c-ctype.h, xerror.h, format-invalid.h.
+       (INVALID_C99_MACRO): New macro.
+       (format_parse): Add invalid_reason argument.
+       (get_c99_format_directives): Update.
+       * format-elisp.c: Include c-ctype.h, xerror.h, format-invalid.h.
+       (format_parse): Add invalid_reason argument.
+       * format-java.c: Include xerror.h, format-invalid.h.
+       (message_format_parse, choice_format_parse, format_parse): Add
+       invalid_reason argument.
+       (choice_format_parse): Return false if a choice contains an empty
+       number part.
+       * format-librep.c: Include c-ctype.h, xerror.h, format-invalid.h.
+       (format_parse): Add invalid_reason argument.
+       * format-lisp.c: Include xerror.h, format-invalid.h.
+       (check_params, nocheck_params): Add directives, invalid_reason
+       arguments.
+       (parse_upto, format_parse): Add invalid_reason argument.
+       * format-pascal.c: Include xerror.h, format-invalid.h.
+       (format_parse): Add invalid_reason argument.
+       * format-php.c: Include c-ctype.h, xerror.h, format-invalid.h.
+       (format_parse): Add invalid_reason argument.
+       * format-python.c: Include c-ctype.h, xerror.h, format-invalid.h.
+       (INVALID_MIXES_NAMED_UNNAMED): New macro.
+       (format_parse): Add invalid_reason argument.
+       * format-tcl.c: Include c-ctype.h, xerror.h, format-invalid.h.
+       (format_parse): Add invalid_reason argument.
+       * format-ycp.c: Include c-ctype.h, xerror.h, format-invalid.h.
+       (format_parse): Add invalid_reason argument.
+       * msgfmt.c (check_pair): Output invalid_reason returned for msgstr.
+       * msgmerge.c (msgfmt_check_pair_fails): Update.
+       * xgettext.c (remember_a_message, remember_a_message_plural): Update.
+       * Makefile.am (FORMAT_SOURCE): Add format-invalid.h.
+
 2003-02-22  Bruno Haible  <bruno@clisp.org>
 
        * x-python.c (init_keywords): Add u*gettext variants and plural
index 6dcce157568a87c7b19c4abf37c0380de5d4dbdb..14c73c09afade93cc2cadcff888b3fff68184ee1 100644 (file)
@@ -87,7 +87,7 @@ po.c po-lex.c po-gram-gen.y po-hash-gen.y po-charset.c \
 open-po.c dir-list.c str-list.c
 
 # xgettext and msgfmt deal with format strings.
-FORMAT_SOURCE = format.c \
+FORMAT_SOURCE = format.c format-invalid.h \
 format-c.c format-python.c format-lisp.c format-elisp.c format-librep.c \
 format-java.c format-awk.c format-pascal.c format-ycp.c format-tcl.c \
 format-php.c
index 0c8bbd51cfbff8296ef5fc06f4f077db2b941232..273cea403dd3241664209d49cb2a6bb22c8f9834 100644 (file)
@@ -1,5 +1,5 @@
 /* awk format strings.
-   Copyright (C) 2001-2002 Free Software Foundation, Inc.
+   Copyright (C) 2001-2003 Free Software Foundation, Inc.
    Written by Bruno Haible <haible@clisp.cons.org>, 2002.
 
    This program is free software; you can redistribute it and/or modify
 #include <stdlib.h>
 
 #include "format.h"
+#include "c-ctype.h"
 #include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
 #include "error.h"
 #include "progname.h"
 #include "gettext.h"
@@ -93,7 +96,7 @@ numbered_arg_compare (const void *p1, const void *p2)
 }
 
 static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
 {
   struct spec spec;
   unsigned int unnumbered_arg_count;
@@ -129,7 +132,10 @@ format_parse (const char *format)
            if (*f == '$')
              {
                if (m == 0)
-                 goto bad_format;
+                 {
+                   *invalid_reason = INVALID_ARGNO_0 (spec.directives);
+                   goto bad_format;
+                 }
                number = m;
                format = ++f;
              }
@@ -162,7 +168,11 @@ format_parse (const char *format)
                if (*f == '$')
                  {
                    if (m == 0)
-                     goto bad_format;
+                     {
+                       *invalid_reason =
+                         INVALID_WIDTH_ARGNO_0 (spec.directives);
+                       goto bad_format;
+                     }
                    width_number = m;
                    format = ++f;
                  }
@@ -174,7 +184,10 @@ format_parse (const char *format)
 
                /* Numbered and unnumbered specifications are exclusive.  */
                if (unnumbered_arg_count > 0)
-                 goto bad_format;
+                 {
+                   *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+                   goto bad_format;
+                 }
 
                if (spec.allocated == spec.numbered_arg_count)
                  {
@@ -191,7 +204,10 @@ format_parse (const char *format)
 
                /* Numbered and unnumbered specifications are exclusive.  */
                if (spec.numbered_arg_count > 0)
-                 goto bad_format;
+                 {
+                   *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+                   goto bad_format;
+                 }
 
                if (spec.allocated == unnumbered_arg_count)
                  {
@@ -234,7 +250,11 @@ format_parse (const char *format)
                    if (*f == '$')
                      {
                        if (m == 0)
-                         goto bad_format;
+                         {
+                           *invalid_reason =
+                             INVALID_PRECISION_ARGNO_0 (spec.directives);
+                           goto bad_format;
+                         }
                        precision_number = m;
                        format = ++f;
                      }
@@ -246,7 +266,10 @@ format_parse (const char *format)
 
                    /* Numbered and unnumbered specifications are exclusive.  */
                    if (unnumbered_arg_count > 0)
-                     goto bad_format;
+                     {
+                       *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+                       goto bad_format;
+                     }
 
                    if (spec.allocated == spec.numbered_arg_count)
                      {
@@ -263,7 +286,10 @@ format_parse (const char *format)
 
                    /* Numbered and unnumbered specifications are exclusive.  */
                    if (spec.numbered_arg_count > 0)
-                     goto bad_format;
+                     {
+                       *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+                       goto bad_format;
+                     }
 
                    if (spec.allocated == unnumbered_arg_count)
                      {
@@ -302,6 +328,10 @@ format_parse (const char *format)
            type = FAT_FLOAT;
            break;
          default:
+           *invalid_reason =
+             (*format == '\0'
+              ? INVALID_UNTERMINATED_DIRECTIVE ()
+              : INVALID_CONVERSION_SPECIFIER (spec.directives, *format));
            goto bad_format;
          }
 
@@ -313,7 +343,10 @@ format_parse (const char *format)
 
                /* Numbered and unnumbered specifications are exclusive.  */
                if (unnumbered_arg_count > 0)
-                 goto bad_format;
+                 {
+                   *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+                   goto bad_format;
+                 }
 
                if (spec.allocated == spec.numbered_arg_count)
                  {
@@ -330,7 +363,10 @@ format_parse (const char *format)
 
                /* Numbered and unnumbered specifications are exclusive.  */
                if (spec.numbered_arg_count > 0)
-                 goto bad_format;
+                 {
+                   *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+                   goto bad_format;
+                 }
 
                if (spec.allocated == unnumbered_arg_count)
                  {
@@ -370,8 +406,14 @@ format_parse (const char *format)
            if (type1 == type2)
              type_both = type1;
            else
-             /* Incompatible types.  */
-             type_both = FAT_NONE, err = true;
+             {
+               /* Incompatible types.  */
+               type_both = FAT_NONE;
+               if (!err)
+                 *invalid_reason =
+                   INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
+               err = true;
+             }
 
            spec.numbered[j-1].type = type_both;
          }
@@ -386,6 +428,7 @@ format_parse (const char *format)
          }
       spec.numbered_arg_count = j;
       if (err)
+       /* *invalid_reason has already been set above.  */
        goto bad_format;
     }
 
@@ -579,17 +622,26 @@ main ()
   for (;;)
     {
       char *line = NULL;
-      size_t line_len = 0;
+      size_t line_size = 0;
+      int line_len;
+      char *invalid_reason;
       void *descr;
 
-      if (getline (&line, &line_len, stdin) < 0)
+      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';
 
-      descr = format_parse (line);
+      invalid_reason = NULL;
+      descr = format_parse (line, &invalid_reason);
 
       format_print (descr);
       printf ("\n");
+      if (descr == NULL)
+       printf ("%s\n", invalid_reason);
 
+      free (invalid_reason);
       free (line);
     }
 
index 81300ca2ebe6e54ffbbd5179266375a18b06c233..4eb3c2cbe5053139fd60368d166f04cdbcde680f 100644 (file)
 #include <stdlib.h>
 
 #include "format.h"
+#include "c-ctype.h"
 #include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
 #include "error.h"
 #include "progname.h"
 #include "gettext.h"
@@ -169,8 +172,11 @@ numbered_arg_compare (const void *p1, const void *p2)
   return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
 }
 
+#define INVALID_C99_MACRO(directive_number) \
+  xasprintf (_("In the directive number %u, the token after '<' is not the name of a format specifier macro. The valid macro names are listed in ISO C 99 section 7.8.1."), directive_number)
+
 static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
 {
   struct spec spec;
   unsigned int numbered_arg_count;
@@ -211,7 +217,10 @@ format_parse (const char *format)
            if (*f == '$')
              {
                if (m == 0)
-                 goto bad_format;
+                 {
+                   *invalid_reason = INVALID_ARGNO_0 (spec.directives);
+                   goto bad_format;
+                 }
                number = m;
                format = ++f;
              }
@@ -244,7 +253,11 @@ format_parse (const char *format)
                if (*f == '$')
                  {
                    if (m == 0)
-                     goto bad_format;
+                     {
+                       *invalid_reason =
+                         INVALID_WIDTH_ARGNO_0 (spec.directives);
+                       goto bad_format;
+                     }
                    width_number = m;
                    format = ++f;
                  }
@@ -256,7 +269,10 @@ format_parse (const char *format)
 
                /* Numbered and unnumbered specifications are exclusive.  */
                if (spec.unnumbered_arg_count > 0)
-                 goto bad_format;
+                 {
+                   *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+                   goto bad_format;
+                 }
 
                if (spec.allocated == numbered_arg_count)
                  {
@@ -273,7 +289,10 @@ format_parse (const char *format)
 
                /* Numbered and unnumbered specifications are exclusive.  */
                if (numbered_arg_count > 0)
-                 goto bad_format;
+                 {
+                   *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+                   goto bad_format;
+                 }
 
                if (spec.allocated == spec.unnumbered_arg_count)
                  {
@@ -315,7 +334,11 @@ format_parse (const char *format)
                    if (*f == '$')
                      {
                        if (m == 0)
-                         goto bad_format;
+                         {
+                           *invalid_reason =
+                             INVALID_PRECISION_ARGNO_0 (spec.directives);
+                           goto bad_format;
+                         }
                        precision_number = m;
                        format = ++f;
                      }
@@ -327,7 +350,10 @@ format_parse (const char *format)
 
                    /* Numbered and unnumbered specifications are exclusive.  */
                    if (spec.unnumbered_arg_count > 0)
-                     goto bad_format;
+                     {
+                       *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+                       goto bad_format;
+                     }
 
                    if (spec.allocated == numbered_arg_count)
                      {
@@ -344,7 +370,10 @@ format_parse (const char *format)
 
                    /* Numbered and unnumbered specifications are exclusive.  */
                    if (numbered_arg_count > 0)
-                     goto bad_format;
+                     {
+                       *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+                       goto bad_format;
+                     }
 
                    if (spec.allocated == spec.unnumbered_arg_count)
                      {
@@ -376,13 +405,22 @@ format_parse (const char *format)
               P R I { d | i | o | u | x | X }
               { { | LEAST | FAST } { 8 | 16 | 32 | 64 } | MAX | PTR }  */
            if (*format != 'P')
-             goto bad_format;
+             {
+               *invalid_reason = INVALID_C99_MACRO (spec.directives);
+               goto bad_format;
+             }
            format++;
            if (*format != 'R')
-             goto bad_format;
+             {
+               *invalid_reason = INVALID_C99_MACRO (spec.directives);
+               goto bad_format;
+             }
            format++;
            if (*format != 'I')
-             goto bad_format;
+             {
+               *invalid_reason = INVALID_C99_MACRO (spec.directives);
+               goto bad_format;
+             }
            format++;
 
            switch (*format)
@@ -394,6 +432,7 @@ format_parse (const char *format)
                type = FAT_INTEGER | FAT_UNSIGNED;
                break;
              default:
+               *invalid_reason = INVALID_C99_MACRO (spec.directives);
                goto bad_format;
              }
            format++;
@@ -435,7 +474,10 @@ format_parse (const char *format)
                        format += 2;
                      }
                    else
-                     goto bad_format;
+                     {
+                       *invalid_reason = INVALID_C99_MACRO (spec.directives);
+                       goto bad_format;
+                     }
                  }
                else if (format[0] == 'F' && format[1] == 'A'
                         && format[2] == 'S' && format[3] == 'T')
@@ -462,7 +504,10 @@ format_parse (const char *format)
                        format += 2;
                      }
                    else
-                     goto bad_format;
+                     {
+                       *invalid_reason = INVALID_C99_MACRO (spec.directives);
+                       goto bad_format;
+                     }
                  }
                else
                  {
@@ -487,12 +532,19 @@ format_parse (const char *format)
                        format += 2;
                      }
                    else
-                     goto bad_format;
+                     {
+                       *invalid_reason = INVALID_C99_MACRO (spec.directives);
+                       goto bad_format;
+                     }
                  }
              }
 
            if (*format != '>')
-             goto bad_format;
+             {
+               *invalid_reason =
+                 xasprintf (_("In the directive number %u, the token after '<' is not followed by '>'."), spec.directives);
+               goto bad_format;
+             }
 
            spec.c99_directives[2 * spec.c99_directives_count + 1] = format;
            spec.c99_directives_count++;
@@ -578,6 +630,10 @@ format_parse (const char *format)
                type |= (size & FAT_SIZE_MASK);
                break;
              default:
+               *invalid_reason =
+                 (*format == '\0'
+                  ? INVALID_UNTERMINATED_DIRECTIVE ()
+                  : INVALID_CONVERSION_SPECIFIER (spec.directives, *format));
                goto bad_format;
              }
          }
@@ -590,7 +646,10 @@ format_parse (const char *format)
 
                /* Numbered and unnumbered specifications are exclusive.  */
                if (spec.unnumbered_arg_count > 0)
-                 goto bad_format;
+                 {
+                   *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+                   goto bad_format;
+                 }
 
                if (spec.allocated == numbered_arg_count)
                  {
@@ -607,7 +666,10 @@ format_parse (const char *format)
 
                /* Numbered and unnumbered specifications are exclusive.  */
                if (numbered_arg_count > 0)
-                 goto bad_format;
+                 {
+                   *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+                   goto bad_format;
+                 }
 
                if (spec.allocated == spec.unnumbered_arg_count)
                  {
@@ -643,8 +705,14 @@ format_parse (const char *format)
            if (type1 == type2)
              type_both = type1;
            else
-             /* Incompatible types.  */
-             type_both = FAT_NONE, err = true;
+             {
+               /* Incompatible types.  */
+               type_both = FAT_NONE;
+               if (!err)
+                 *invalid_reason =
+                   INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
+               err = true;
+             }
 
            numbered[j-1].type = type_both;
          }
@@ -659,6 +727,7 @@ format_parse (const char *format)
          }
       numbered_arg_count = j;
       if (err)
+       /* *invalid_reason has already been set above.  */
        goto bad_format;
     }
 
@@ -670,7 +739,11 @@ format_parse (const char *format)
 
       for (i = 0; i < numbered_arg_count; i++)
        if (numbered[i].number != i + 1)
-         goto bad_format;
+         {
+           *invalid_reason =
+             xasprintf (_("The string refers to argument number %u but ignores argument number %u."), numbered[i].number, i + 1);
+           goto bad_format;
+         }
 
       /* So now the numbered arguments array is equivalent to a sequence
         of unnumbered arguments.  */
@@ -773,7 +846,8 @@ void
 get_c99_format_directives (const char *string,
                           struct interval **intervalsp, size_t *lengthp)
 {
-  struct spec *descr = (struct spec *) format_parse (string);
+  char *invalid_reason = NULL;
+  struct spec *descr = (struct spec *) format_parse (string, &invalid_reason);
 
   if (descr != NULL && descr->c99_directives_count > 0)
     {
@@ -798,6 +872,8 @@ get_c99_format_directives (const char *string,
 
   if (descr != NULL)
     format_free (descr);
+  else
+    free (invalid_reason);
 }
 
 
@@ -928,17 +1004,26 @@ main ()
   for (;;)
     {
       char *line = NULL;
-      size_t line_len = 0;
+      size_t line_size = 0;
+      int line_len;
+      char *invalid_reason;
       void *descr;
 
-      if (getline (&line, &line_len, stdin) < 0)
+      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';
 
-      descr = format_parse (line);
+      invalid_reason = NULL;
+      descr = format_parse (line, &invalid_reason);
 
       format_print (descr);
       printf ("\n");
+      if (descr == NULL)
+       printf ("%s\n", invalid_reason);
 
+      free (invalid_reason);
       free (line);
     }
 
index b121a4c0e9fc22870021bda6965c3c034db1d33d..f575e3a871135c4dfa147c21978a51ec2fca6de3 100644 (file)
@@ -1,5 +1,5 @@
 /* Emacs Lisp format strings.
-   Copyright (C) 2001-2002 Free Software Foundation, Inc.
+   Copyright (C) 2001-2003 Free Software Foundation, Inc.
    Written by Bruno Haible <haible@clisp.cons.org>, 2002.
 
    This program is free software; you can redistribute it and/or modify
 #include <stdlib.h>
 
 #include "format.h"
+#include "c-ctype.h"
 #include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
 #include "error.h"
 #include "progname.h"
 #include "gettext.h"
@@ -94,7 +97,7 @@ numbered_arg_compare (const void *p1, const void *p2)
 }
 
 static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
 {
   struct spec spec;
   struct spec *result;
@@ -206,6 +209,10 @@ format_parse (const char *format)
            type = FAT_OBJECT;
            break;
          default:
+           *invalid_reason =
+             (*format == '\0'
+              ? INVALID_UNTERMINATED_DIRECTIVE ()
+              : INVALID_CONVERSION_SPECIFIER (spec.directives, *format));
            goto bad_format;
          }
 
@@ -247,8 +254,14 @@ format_parse (const char *format)
            if (type1 == type2)
              type_both = type1;
            else
-             /* Incompatible types.  */
-             type_both = FAT_NONE, err = true;
+             {
+               /* Incompatible types.  */
+               type_both = FAT_NONE;
+               if (!err)
+                 *invalid_reason =
+                   INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
+               err = true;
+             }
 
            spec.numbered[j-1].type = type_both;
          }
@@ -263,6 +276,7 @@ format_parse (const char *format)
          }
       spec.numbered_arg_count = j;
       if (err)
+       /* *invalid_reason has already been set above.  */
        goto bad_format;
     }
 
@@ -456,17 +470,26 @@ main ()
   for (;;)
     {
       char *line = NULL;
-      size_t line_len = 0;
+      size_t line_size = 0;
+      int line_len;
+      char *invalid_reason;
       void *descr;
 
-      if (getline (&line, &line_len, stdin) < 0)
+      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';
 
-      descr = format_parse (line);
+      invalid_reason = NULL;
+      descr = format_parse (line, &invalid_reason);
 
       format_print (descr);
       printf ("\n");
+      if (descr == NULL)
+       printf ("%s\n", invalid_reason);
 
+      free (invalid_reason);
       free (line);
     }
 
diff --git a/gettext-tools/src/format-invalid.h b/gettext-tools/src/format-invalid.h
new file mode 100644 (file)
index 0000000..8e98a62
--- /dev/null
@@ -0,0 +1,41 @@
+/* Common reasons that make a format string invalid.
+   Copyright (C) 2003 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+   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 2, 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, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* These macros return freshly allocated error message strings, intended
+   to be stored in *invalid_reason.  */
+
+#define INVALID_UNTERMINATED_DIRECTIVE() \
+  xstrdup (_("The string ends in the middle of a directive."))
+
+#define INVALID_MIXES_NUMBERED_UNNUMBERED() \
+  xstrdup (_("The string refers to arguments both through absolute argument numbers and through unnumbered argument specifications."))
+
+#define INVALID_ARGNO_0(directive_number) \
+  xasprintf (_("In the directive number %u, the argument number 0 is not a positive integer."), directive_number)
+#define INVALID_WIDTH_ARGNO_0(directive_number) \
+  xasprintf (_("In the directive number %u, the width's argument number 0 is not a positive integer."), directive_number)
+#define INVALID_PRECISION_ARGNO_0(directive_number) \
+  xasprintf (_("In the directive number %u, the precision's argument number 0 is not a positive integer."), directive_number)
+
+#define INVALID_CONVERSION_SPECIFIER(directive_number,conv_char) \
+  (c_isprint (conv_char) \
+   ? xasprintf (_("In the directive number %u, the character '%c' is not a valid conversion specifier."), directive_number, conv_char) \
+   : xasprintf (_("The character that terminates the directive number %u is not a valid conversion specifier."), directive_number))
+
+#define INVALID_INCOMPATIBLE_ARG_TYPES(arg_number) \
+  xasprintf (_("The string refers to argument number %u in incompatible ways."), arg_number)
index f44880405a86400749a886f4bc25cb0ae5c1b2e7..e8ba9047326f8187912b11f73b3db4efc1182db1 100644 (file)
@@ -28,6 +28,8 @@
 #include "format.h"
 #include "c-ctype.h"
 #include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
 #include "error.h"
 #include "progname.h"
 #include "gettext.h"
@@ -129,7 +131,8 @@ struct spec
 /* Forward declaration of local functions.  */
 static bool date_format_parse (const char *format);
 static bool number_format_parse (const char *format);
-static bool choice_format_parse (const char *format, struct spec *spec);
+static bool choice_format_parse (const char *format, struct spec *spec,
+                                char **invalid_reason);
 
 
 /* Quote handling:
@@ -147,7 +150,8 @@ static bool choice_format_parse (const char *format, struct spec *spec);
 /* Return true if a format is a valid messageFormatPattern.
    Extracts argument type information into spec.  */
 static bool
-message_format_parse (const char *format, struct spec *spec)
+message_format_parse (const char *format, struct spec *spec,
+                     char **invalid_reason)
 {
   bool quoting = false;
 
@@ -181,7 +185,11 @@ message_format_parse (const char *format, struct spec *spec)
                }
            }
          if (*format == '\0')
-           return false;
+           {
+             *invalid_reason =
+               xstrdup (_("The string ends in the middle of a directive: found '{' without matching '}'."));
+             return false;
+           }
          element_end = format++;
 
          n = element_end - element_start;
@@ -190,7 +198,11 @@ message_format_parse (const char *format, struct spec *spec)
          element[n] = '\0';
 
          if (!c_isdigit (*element))
-           return false;
+           {
+             *invalid_reason =
+               xasprintf (_("In the directive number %u, '{' is not followed by an argument number."), spec->directives);
+             return false;
+           }
          number = 0;
          do
            {
@@ -209,15 +221,30 @@ message_format_parse (const char *format, struct spec *spec)
              element += 5;
              if (*element == '\0')
                ;
-             else if (*element++ == ','
-                      && (strcmp (element, "short") == 0
-                          || strcmp (element, "medium") == 0
-                          || strcmp (element, "long") == 0
-                          || strcmp (element, "full") == 0
-                          || date_format_parse (element)))
-               ;
+             else if (*element == ',')
+               {
+                 element++;
+                 if (strcmp (element, "short") == 0
+                     || strcmp (element, "medium") == 0
+                     || strcmp (element, "long") == 0
+                     || strcmp (element, "full") == 0
+                     || date_format_parse (element))
+                   ;
+                 else
+                   {
+                     *invalid_reason =
+                       xasprintf (_("In the directive number %u, the substring \"%s\" is not a valid date/time style."), spec->directives, element);
+                     return false;
+                   }
+               }
              else
-               return false;
+               {
+                 *element = '\0';
+                 element -= 4;
+                 *invalid_reason =
+                   xasprintf (_("In the directive number %u, \"%s\" is not followed by a comma."), spec->directives, element);
+                 return false;
+               }
            }
          else if (strncmp (element, ",number", 7) == 0)
            {
@@ -225,14 +252,29 @@ message_format_parse (const char *format, struct spec *spec)
              element += 7;
              if (*element == '\0')
                ;
-             else if (*element++ == ','
-                      && (strcmp (element, "currency") == 0
-                          || strcmp (element, "percent") == 0
-                          || strcmp (element, "integer") == 0
-                          || number_format_parse (element)))
-               ;
+             else if (*element == ',')
+               {
+                 element++;
+                 if (strcmp (element, "currency") == 0
+                     || strcmp (element, "percent") == 0
+                     || strcmp (element, "integer") == 0
+                     || number_format_parse (element))
+                   ;
+                 else
+                   {
+                     *invalid_reason =
+                       xasprintf (_("In the directive number %u, the substring \"%s\" is not a valid number style."), spec->directives, element);
+                     return false;
+                   }
+               }
              else
-               return false;
+               {
+                 *element = '\0';
+                 element -= 6;
+                 *invalid_reason =
+                   xasprintf (_("In the directive number %u, \"%s\" is not followed by a comma."), spec->directives, element);
+                 return false;
+               }
            }
          else if (strncmp (element, ",choice", 7) == 0)
            {
@@ -240,14 +282,29 @@ message_format_parse (const char *format, struct spec *spec)
              element += 7;
              if (*element == '\0')
                ;
-             else if (*element++ == ','
-                      && choice_format_parse (element, spec))
-               ;
+             else if (*element == ',')
+               {
+                 element++;
+                 if (choice_format_parse (element, spec, invalid_reason))
+                   ;
+                 else
+                   return false;
+               }
              else
-               return false;
+               {
+                 *element = '\0';
+                 element -= 6;
+                 *invalid_reason =
+                   xasprintf (_("In the directive number %u, \"%s\" is not followed by a comma."), spec->directives, element);
+                 return false;
+               }
            }
          else
-           return false;
+           {
+             *invalid_reason =
+               xasprintf (_("In the directive number %u, the argument number is not followed by a comma and one of \"%s\", \"%s\", \"%s\", \"%s\"."), spec->directives, "time", "date", "number", "choice");
+             return false;
+           }
 
          if (spec->allocated == spec->numbered_arg_count)
            {
@@ -260,7 +317,11 @@ message_format_parse (const char *format, struct spec *spec)
        }
       /* The doc says "ab}de" is invalid.  Even though JDK accepts it.  */
       else if (!quoting && *format == '}')
-       return false;
+       {
+         *invalid_reason =
+           xstrdup (_("The string starts in the middle of a directive: found '}' without matching '{'."));
+         return false;
+       }
       else if (*format != '\0')
        format++;
       else
@@ -427,7 +488,8 @@ number_format_parse (const char *format)
 /* Return true if a format is a valid choiceFormatPattern.
    Extracts argument type information into spec.  */
 static bool
-choice_format_parse (const char *format, struct spec *spec)
+choice_format_parse (const char *format, struct spec *spec,
+                    char **invalid_reason)
 {
   /* Pattern syntax:
        pattern   := | choice | choice '|' pattern
@@ -446,10 +508,12 @@ choice_format_parse (const char *format, struct spec *spec)
     {
       /* Don't bother looking too precisely into the syntax of the number.
         It can contain various Unicode characters.  */
+      bool number_nonempty;
       char *msgformat;
       char *mp;
 
       /* Parse number.  */
+      number_nonempty = false;
       while (*format != '\0'
             && !(!quoting && (*format == '<' || *format == '#'
                               || strncmp (format, "\\u2264", 6) == 0
@@ -468,6 +532,7 @@ choice_format_parse (const char *format, struct spec *spec)
            }
          else
            format += 1;
+         number_nonempty = true;
          HANDLE_QUOTE;
        }
 
@@ -475,12 +540,23 @@ choice_format_parse (const char *format, struct spec *spec)
       if (*format == '\0')
        break;
 
+      if (!number_nonempty)
+       {
+         *invalid_reason =
+           xasprintf (_("In the directive number %u, a choice contains no number."), spec->directives);
+         return false;
+       }
+
       if (*format == '<' || *format == '#')
        format += 1;
       else if (strncmp (format, "\\u2264", 6) == 0)
        format += 6;
       else
-       return false;
+       {
+         *invalid_reason =
+           xasprintf (_("In the directive number %u, a choice contains a number that is not followed by '<', '#' or '%s'."), spec->directives, "\\u2264");
+         return false;
+       }
       HANDLE_QUOTE;
 
       msgformat = (char *) alloca (strlen (format) + 1);
@@ -493,7 +569,7 @@ choice_format_parse (const char *format, struct spec *spec)
        }
       *mp = '\0';
 
-      if (!message_format_parse (msgformat, spec))
+      if (!message_format_parse (msgformat, spec, invalid_reason))
        return false;
 
       if (*format == '\0')
@@ -516,7 +592,7 @@ numbered_arg_compare (const void *p1, const void *p2)
 }
 
 static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
 {
   struct spec spec;
   struct spec *result;
@@ -526,7 +602,7 @@ format_parse (const char *format)
   spec.allocated = 0;
   spec.numbered = NULL;
 
-  if (!message_format_parse (format, &spec))
+  if (!message_format_parse (format, &spec, invalid_reason))
     goto bad_format;
 
   /* Sort the numbered argument array, and eliminate duplicates.  */
@@ -552,8 +628,14 @@ format_parse (const char *format)
            else if (type1 == FAT_OBJECT)
              type_both = type2;
            else
-             /* Incompatible types.  */
-             type_both = FAT_NONE, err = true;
+             {
+               /* Incompatible types.  */
+               type_both = FAT_NONE;
+               if (!err)
+                 *invalid_reason =
+                   INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
+               err = true;
+             }
 
            spec.numbered[j-1].type = type_both;
          }
@@ -568,6 +650,7 @@ format_parse (const char *format)
          }
       spec.numbered_arg_count = j;
       if (err)
+       /* *invalid_reason has already been set above.  */
        goto bad_format;
     }
 
@@ -755,17 +838,26 @@ main ()
   for (;;)
     {
       char *line = NULL;
-      size_t line_len = 0;
+      size_t line_size = 0;
+      int line_len;
+      char *invalid_reason;
       void *descr;
 
-      if (getline (&line, &line_len, stdin) < 0)
+      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';
 
-      descr = format_parse (line);
+      invalid_reason = NULL;
+      descr = format_parse (line, &invalid_reason);
 
       format_print (descr);
       printf ("\n");
+      if (descr == NULL)
+       printf ("%s\n", invalid_reason);
 
+      free (invalid_reason);
       free (line);
     }
 
index 71cad3fd6366dca23f8ccd9afefca41f9d26a591..2dfcaa403c3d9e907daa9d9ee4ce4dfeefb6fd18 100644 (file)
@@ -1,5 +1,5 @@
 /* librep format strings.
-   Copyright (C) 2001-2002 Free Software Foundation, Inc.
+   Copyright (C) 2001-2003 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
 #include <stdlib.h>
 
 #include "format.h"
+#include "c-ctype.h"
 #include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
 #include "error.h"
 #include "progname.h"
 #include "gettext.h"
@@ -91,7 +94,7 @@ numbered_arg_compare (const void *p1, const void *p2)
 }
 
 static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
 {
   struct spec spec;
   struct spec *result;
@@ -170,6 +173,10 @@ format_parse (const char *format)
            type = FAT_OBJECT;
            break;
          default:
+           *invalid_reason =
+             (*format == '\0'
+              ? INVALID_UNTERMINATED_DIRECTIVE ()
+              : INVALID_CONVERSION_SPECIFIER (spec.directives, *format));
            goto bad_format;
          }
 
@@ -211,8 +218,14 @@ format_parse (const char *format)
            if (type1 == type2)
              type_both = type1;
            else
-             /* Incompatible types.  */
-             type_both = FAT_NONE, err = true;
+             {
+               /* Incompatible types.  */
+               type_both = FAT_NONE;
+               if (!err)
+                 *invalid_reason =
+                   INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
+               err = true;
+             }
 
            spec.numbered[j-1].type = type_both;
          }
@@ -227,6 +240,7 @@ format_parse (const char *format)
          }
       spec.numbered_arg_count = j;
       if (err)
+       /* *invalid_reason has already been set above.  */
        goto bad_format;
     }
 
@@ -417,17 +431,26 @@ main ()
   for (;;)
     {
       char *line = NULL;
-      size_t line_len = 0;
+      size_t line_size = 0;
+      int line_len;
+      char *invalid_reason;
       void *descr;
 
-      if (getline (&line, &line_len, stdin) < 0)
+      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';
 
-      descr = format_parse (line);
+      invalid_reason = NULL;
+      descr = format_parse (line, &invalid_reason);
 
       format_print (descr);
       printf ("\n");
+      if (descr == NULL)
+       printf ("%s\n", invalid_reason);
 
+      free (invalid_reason);
       free (line);
     }
 
index 745c95dc70847f32280c6dad436b65f582c48e17..51cf6ee1e926e65fd0b22f95f6d96cae1d9f6119 100644 (file)
@@ -1,5 +1,5 @@
 /* Lisp format strings.
-   Copyright (C) 2001-2002 Free Software Foundation, Inc.
+   Copyright (C) 2001-2003 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
@@ -27,6 +27,8 @@
 #include "c-ctype.h"
 #include "gcd.h"
 #include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
 #include "minmax.h"
 #include "error.h"
 #include "progname.h"
@@ -2323,12 +2325,17 @@ static const enum format_arg_type THREE [3] = {
 
 
 /* Check the parameters.  For V params, add the constraint to the argument
-   list.  Return false if the format string is invalid.  */
+   list.  Return false and fill in *invalid_reason if the format string is
+   invalid.  */
 static bool
 check_params (struct format_arg_list **listp,
              unsigned int paramcount, struct param *params,
-             unsigned int t_count, const enum format_arg_type *t_types)
+             unsigned int t_count, const enum format_arg_type *t_types,
+             unsigned int directives, char **invalid_reason)
 {
+  unsigned int orig_paramcount = paramcount;
+  unsigned int orig_t_count = t_count;
+
   for (; paramcount > 0 && t_count > 0;
         params++, paramcount--, t_types++, t_count--)
     {
@@ -2342,7 +2349,10 @@ check_params (struct format_arg_list **listp,
            case PT_NIL: case PT_CHARACTER: case PT_V:
              break;
            case PT_INTEGER: case PT_ARGCOUNT:
-             return false; /* wrong param type */
+             /* wrong param type */
+             *invalid_reason =
+               xasprintf (_("In the directive number %u, parameter %u is of type '%s' but a parameter of type '%s' is expected."), directives, orig_paramcount - paramcount + 1, "integer", "character");
+             return false;
            }
          break;
        case FAT_INTEGER_NULL:
@@ -2351,7 +2361,10 @@ check_params (struct format_arg_list **listp,
            case PT_NIL: case PT_INTEGER: case PT_ARGCOUNT: case PT_V:
              break;
            case PT_CHARACTER:
-             return false; /* wrong param type */
+             /* wrong param type */
+             *invalid_reason =
+               xasprintf (_("In the directive number %u, parameter %u is of type '%s' but a parameter of type '%s' is expected."), directives, orig_paramcount - paramcount + 1, "character", "integer");
+             return false;
            }
          break;
        default:
@@ -2371,7 +2384,10 @@ check_params (struct format_arg_list **listp,
       case PT_NIL:
        break;
       case PT_CHARACTER: case PT_INTEGER: case PT_ARGCOUNT:
-       return false; /* too many params for directive */
+       /* too many params for directive */
+       *invalid_reason =
+         xasprintf (_("In the directive number %u, too many parameters are given; expected at most %u parameters."), directives, orig_t_count);
+       return false;
       case PT_V:
        /* Force argument to be NIL.  */
        {
@@ -2393,11 +2409,16 @@ check_params (struct format_arg_list **listp,
 
 /* Handle the parameters, without a priori type information.
    For V params, add the constraint to the argument list.
-   Return false if the format string is invalid.  */
+   Return false and fill in *invalid_reason if the format string is
+   invalid.  */
 static bool
 nocheck_params (struct format_arg_list **listp,
-               unsigned int paramcount, struct param *params)
+               unsigned int paramcount, struct param *params,
+               unsigned int directives, char **invalid_reason)
 {
+  (void) directives;
+  (void) invalid_reason;
+
   for (; paramcount > 0; params++, paramcount--)
     if (params->type == PT_V)
       {
@@ -2426,12 +2447,14 @@ nocheck_params (struct format_arg_list **listp,
    spec is the global struct spec.
    terminator is the directive that terminates this parse.
    separator specifies if ~; separators are allowed.
-   If the format string is invalid, false is returned.  */
+   If the format string is invalid, false is returned and *invalid_reason is
+   set to an error message explaining why.  */
 static bool
 parse_upto (const char **formatp,
            int *positionp, struct format_arg_list **listp,
            struct format_arg_list **escapep, int *separatorp,
-           struct spec *spec, char terminator, bool separator)
+           struct spec *spec, char terminator, bool separator,
+           char **invalid_reason)
 {
   const char *format = *formatp;
   int position = *positionp;
@@ -2471,7 +2494,13 @@ parse_upto (const char **formatp,
                type = PT_INTEGER;
                format++;
                if (!c_isdigit (*format))
-                 return false;
+                 {
+                   *invalid_reason =
+                     (*format == '\0'
+                      ? INVALID_UNTERMINATED_DIRECTIVE ()
+                      : xasprintf (_("In the directive number %u, '%c' is not followed by a digit."), spec->directives, format[-1]));
+                   return false;
+                 }
                do
                  {
                    value = 10 * value + (*format - '0');
@@ -2486,7 +2515,10 @@ parse_upto (const char **formatp,
                type = PT_CHARACTER;
                format++;
                if (*format == '\0')
-                 return false;
+                 {
+                   *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+                   return false;
+                 }
                format++;
              }
            else if (*format == 'V' || *format == 'v')
@@ -2539,14 +2571,16 @@ parse_upto (const char **formatp,
          {
          case 'A': case 'a': /* 22.3.4.1 FORMAT-ASCII */
          case 'S': case 's': /* 22.3.4.2 FORMAT-S-EXPRESSION */
-           if (!check_params (&list, paramcount, params, 4, IIIC))
+           if (!check_params (&list, paramcount, params, 4, IIIC,
+                              spec->directives, invalid_reason))
              return false;
            if (position >= 0)
              add_req_type_constraint (&list, position++, FAT_OBJECT);
            break;
 
          case 'W': case 'w': /* 22.3.4.3 FORMAT-WRITE */
-           if (!check_params (&list, paramcount, params, 0, NULL))
+           if (!check_params (&list, paramcount, params, 0, NULL,
+                              spec->directives, invalid_reason))
              return false;
            if (position >= 0)
              add_req_type_constraint (&list, position++, FAT_OBJECT);
@@ -2556,21 +2590,24 @@ parse_upto (const char **formatp,
          case 'B': case 'b': /* 22.3.2.3 FORMAT-BINARY */
          case 'O': case 'o': /* 22.3.2.4 FORMAT-OCTAL */
          case 'X': case 'x': /* 22.3.2.5 FORMAT-HEXADECIMAL */
-           if (!check_params (&list, paramcount, params, 4, ICCI))
+           if (!check_params (&list, paramcount, params, 4, ICCI,
+                              spec->directives, invalid_reason))
              return false;
            if (position >= 0)
              add_req_type_constraint (&list, position++, FAT_INTEGER);
            break;
 
          case 'R': case 'r': /* 22.3.2.1 FORMAT-RADIX */
-           if (!check_params (&list, paramcount, params, 5, IICCI))
+           if (!check_params (&list, paramcount, params, 5, IICCI,
+                              spec->directives, invalid_reason))
              return false;
            if (position >= 0)
              add_req_type_constraint (&list, position++, FAT_INTEGER);
            break;
 
          case 'P': case 'p': /* 22.3.8.3 FORMAT-PLURAL */
-           if (!check_params (&list, paramcount, params, 0, NULL))
+           if (!check_params (&list, paramcount, params, 0, NULL,
+                              spec->directives, invalid_reason))
              return false;
            if (colon_p)
              {
@@ -2583,14 +2620,16 @@ parse_upto (const char **formatp,
            break;
 
          case 'C': case 'c': /* 22.3.1.1 FORMAT-CHARACTER */
-           if (!check_params (&list, paramcount, params, 0, NULL))
+           if (!check_params (&list, paramcount, params, 0, NULL,
+                              spec->directives, invalid_reason))
              return false;
            if (position >= 0)
              add_req_type_constraint (&list, position++, FAT_CHARACTER);
            break;
 
          case 'F': case 'f': /* 22.3.3.1 FORMAT-FIXED-FLOAT */
-           if (!check_params (&list, paramcount, params, 5, IIICC))
+           if (!check_params (&list, paramcount, params, 5, IIICC,
+                              spec->directives, invalid_reason))
              return false;
            if (position >= 0)
              add_req_type_constraint (&list, position++, FAT_REAL);
@@ -2598,14 +2637,16 @@ parse_upto (const char **formatp,
 
          case 'E': case 'e': /* 22.3.3.2 FORMAT-EXPONENTIAL-FLOAT */
          case 'G': case 'g': /* 22.3.3.3 FORMAT-GENERAL-FLOAT */
-           if (!check_params (&list, paramcount, params, 7, IIIICCC))
+           if (!check_params (&list, paramcount, params, 7, IIIICCC,
+                              spec->directives, invalid_reason))
              return false;
            if (position >= 0)
              add_req_type_constraint (&list, position++, FAT_REAL);
            break;
 
          case '$': /* 22.3.3.4 FORMAT-DOLLARS-FLOAT */
-           if (!check_params (&list, paramcount, params, 4, IIIC))
+           if (!check_params (&list, paramcount, params, 4, IIIC,
+                              spec->directives, invalid_reason))
              return false;
            if (position >= 0)
              add_req_type_constraint (&list, position++, FAT_REAL);
@@ -2616,23 +2657,27 @@ parse_upto (const char **formatp,
          case '|': /* 22.3.1.4 FORMAT-PAGE */
          case '~': /* 22.3.1.5 FORMAT-TILDE */
          case 'I': case 'i': /* 22.3.5.3 */
-           if (!check_params (&list, paramcount, params, 1, I))
+           if (!check_params (&list, paramcount, params, 1, I,
+                              spec->directives, invalid_reason))
              return false;
            break;
 
          case '\n': /* 22.3.9.3 #\Newline */
          case '_': /* 22.3.5.1 */
-           if (!check_params (&list, paramcount, params, 0, NULL))
+           if (!check_params (&list, paramcount, params, 0, NULL,
+                              spec->directives, invalid_reason))
              return false;
            break;
 
          case 'T': case 't': /* 22.3.6.1 FORMAT-TABULATE */
-           if (!check_params (&list, paramcount, params, 2, II))
+           if (!check_params (&list, paramcount, params, 2, II,
+                              spec->directives, invalid_reason))
              return false;
            break;
 
          case '*': /* 22.3.7.1 FORMAT-GOTO */
-           if (!check_params (&list, paramcount, params, 1, I))
+           if (!check_params (&list, paramcount, params, 1, I,
+                              spec->directives, invalid_reason))
              return false;
            {
              int n; /* value of first parameter */
@@ -2648,7 +2693,12 @@ parse_upto (const char **formatp,
                  break;
                }
              if (n < 0)
-               return false; /* invalid argument */
+               {
+                 /* invalid argument */
+                 *invalid_reason =
+                   xasprintf (_("In the directive number %u, the argument %d is negative."), spec->directives, n);
+                 return false;
+               }
              if (atsign_p)
                {
                  /* Absolute goto.  */
@@ -2680,7 +2730,8 @@ parse_upto (const char **formatp,
            break;
 
          case '?': /* 22.3.7.6 FORMAT-INDIRECTION */
-           if (!check_params (&list, paramcount, params, 0, NULL))
+           if (!check_params (&list, paramcount, params, 0, NULL,
+                              spec->directives, invalid_reason))
              return false;
            if (position >= 0)
              add_req_type_constraint (&list, position++, FAT_FORMATSTRING);
@@ -2697,19 +2748,25 @@ parse_upto (const char **formatp,
            break;
 
          case '/': /* 22.3.5.4 FORMAT-CALL-USER-FUNCTION */
-           if (!check_params (&list, paramcount, params, 0, NULL))
+           if (!check_params (&list, paramcount, params, 0, NULL,
+                              spec->directives, invalid_reason))
              return false;
            if (position >= 0)
              add_req_type_constraint (&list, position++, FAT_OBJECT);
            while (*format != '\0' && *format != '/')
              format++;
            if (*format == '\0')
-             return false;
+             {
+               *invalid_reason =
+                 xstrdup (_("The string ends in the middle of a ~/.../ directive."));
+               return false;
+             }
            format++;
            break;
 
          case '(': /* 22.3.8.1 FORMAT-CASE-CONVERSION */
-           if (!check_params (&list, paramcount, params, 0, NULL))
+           if (!check_params (&list, paramcount, params, 0, NULL,
+                              spec->directives, invalid_reason))
              return false;
            *formatp = format;
            *positionp = position;
@@ -2717,7 +2774,8 @@ parse_upto (const char **formatp,
            *escapep = escape;
            {
              if (!parse_upto (formatp, positionp, listp, escapep,
-                              NULL, spec, ')', false))
+                              NULL, spec, ')', false,
+                              invalid_reason))
                return false;
            }
            format = *formatp;
@@ -2728,8 +2786,13 @@ parse_upto (const char **formatp,
 
          case ')': /* 22.3.8.2 FORMAT-CASE-CONVERSION-END */
            if (terminator != ')')
-             return false;
-           if (!check_params (&list, paramcount, params, 0, NULL))
+             {
+               *invalid_reason =
+                 xasprintf (_("Found '~%c' without matching '~%c'."), ')', '(');
+               return false;
+             }
+           if (!check_params (&list, paramcount, params, 0, NULL,
+                              spec->directives, invalid_reason))
              return false;
            *formatp = format;
            *positionp = position;
@@ -2739,13 +2802,18 @@ parse_upto (const char **formatp,
 
          case '[': /* 22.3.7.2 FORMAT-CONDITIONAL */
            if (atsign_p && colon_p)
-             return false;
+             {
+               *invalid_reason =
+                 xasprintf (_("In the directive number %u, both the @ and the : modifiers are given."), spec->directives);
+               return false;
+             }
            else if (atsign_p)
              {
                struct format_arg_list *nil_list;
                struct format_arg_list *union_list;
 
-               if (!check_params (&list, paramcount, params, 0, NULL))
+               if (!check_params (&list, paramcount, params, 0, NULL,
+                                  spec->directives, invalid_reason))
                  return false;
 
                *formatp = format;
@@ -2767,7 +2835,8 @@ parse_upto (const char **formatp,
                  struct format_arg_list *sub_list =
                    (list != NULL ? copy_list (list) : NULL);
                  if (!parse_upto (formatp, &sub_position, &sub_list, escapep,
-                                  NULL, spec, ']', false))
+                                  NULL, spec, ']', false,
+                                  invalid_reason))
                    return false;
                  if (sub_list != NULL)
                    {
@@ -2801,7 +2870,8 @@ parse_upto (const char **formatp,
                int union_position;
                struct format_arg_list *union_list;
 
-               if (!check_params (&list, paramcount, params, 0, NULL))
+               if (!check_params (&list, paramcount, params, 0, NULL,
+                                  spec->directives, invalid_reason))
                  return false;
 
                if (position >= 0)
@@ -2826,10 +2896,15 @@ parse_upto (const char **formatp,
                      free_list (empty_list);
                    }
                  if (!parse_upto (formatp, &sub_position, &sub_list, escapep,
-                                  &sub_separator, spec, ']', true))
+                                  &sub_separator, spec, ']', true,
+                                  invalid_reason))
                    return false;
                  if (!sub_separator)
-                   return false;
+                   {
+                     *invalid_reason =
+                       xasprintf (_("In the directive number %u, '~:[' is not followed by two clauses, separated by '~;'."), spec->directives);
+                     return false;
+                   }
                  if (sub_list != NULL)
                    union_position = sub_position;
                  union_list = union (union_list, sub_list);
@@ -2841,7 +2916,8 @@ parse_upto (const char **formatp,
                  struct format_arg_list *sub_list =
                    (list != NULL ? copy_list (list) : NULL);
                  if (!parse_upto (formatp, &sub_position, &sub_list, escapep,
-                                  NULL, spec, ']', false))
+                                  NULL, spec, ']', false,
+                                  invalid_reason))
                    return false;
                  if (sub_list != NULL)
                    {
@@ -2869,7 +2945,8 @@ parse_upto (const char **formatp,
                struct format_arg_list *union_list;
                bool last_alternative;
 
-               if (!check_params (&list, paramcount, params, 1, I))
+               if (!check_params (&list, paramcount, params, 1, I,
+                                  spec->directives, invalid_reason))
                  return false;
 
                /* If there was no first parameter, an argument is consumed.  */
@@ -2891,7 +2968,8 @@ parse_upto (const char **formatp,
                      (list != NULL ? copy_list (list) : NULL);
                    int sub_separator = 0;
                    if (!parse_upto (formatp, &sub_position, &sub_list, escapep,
-                                    &sub_separator, spec, ']', !last_alternative))
+                                    &sub_separator, spec, ']', !last_alternative,
+                                    invalid_reason))
                      return false;
                    if (sub_list != NULL)
                      {
@@ -2931,8 +3009,13 @@ parse_upto (const char **formatp,
 
          case ']': /* 22.3.7.3 FORMAT-CONDITIONAL-END */
            if (terminator != ']')
-             return false;
-           if (!check_params (&list, paramcount, params, 0, NULL))
+             {
+               *invalid_reason =
+                 xasprintf (_("Found '~%c' without matching '~%c'."), ']', '[');
+               return false;
+             }
+           if (!check_params (&list, paramcount, params, 0, NULL,
+                              spec->directives, invalid_reason))
              return false;
            *formatp = format;
            *positionp = position;
@@ -2941,7 +3024,8 @@ parse_upto (const char **formatp,
            return true;
 
          case '{': /* 22.3.7.4 FORMAT-ITERATION */
-           if (!check_params (&list, paramcount, params, 1, I))
+           if (!check_params (&list, paramcount, params, 1, I,
+                              spec->directives, invalid_reason))
              return false;
            *formatp = format;
            {
@@ -2952,7 +3036,8 @@ parse_upto (const char **formatp,
              sub_spec.directives = 0;
              sub_spec.list = sub_list;
              if (!parse_upto (formatp, &sub_position, &sub_list, &sub_escape,
-                              NULL, &sub_spec, '}', false))
+                              NULL, &sub_spec, '}', false,
+                              invalid_reason))
                return false;
              spec->directives += sub_spec.directives;
 
@@ -3030,8 +3115,13 @@ parse_upto (const char **formatp,
 
          case '}': /* 22.3.7.5 FORMAT-ITERATION-END */
            if (terminator != '}')
-             return false;
-           if (!check_params (&list, paramcount, params, 0, NULL))
+             {
+               *invalid_reason =
+                 xasprintf (_("Found '~%c' without matching '~%c'."), '}', '{');
+               return false;
+             }
+           if (!check_params (&list, paramcount, params, 0, NULL,
+                              spec->directives, invalid_reason))
              return false;
            *formatp = format;
            *positionp = position;
@@ -3040,7 +3130,8 @@ parse_upto (const char **formatp,
            return true;
 
          case '<': /* 22.3.6.2, 22.3.5.2 FORMAT-JUSTIFICATION */
-           if (!check_params (&list, paramcount, params, 4, IIIC))
+           if (!check_params (&list, paramcount, params, 4, IIIC,
+                              spec->directives, invalid_reason))
              return false;
            {
              struct format_arg_list *sub_escape = NULL;
@@ -3053,7 +3144,8 @@ parse_upto (const char **formatp,
                {
                  int sub_separator = 0;
                  if (!parse_upto (formatp, positionp, listp, &sub_escape,
-                                  &sub_separator, spec, '>', true))
+                                  &sub_separator, spec, '>', true,
+                                  invalid_reason))
                    return false;
                  if (!sub_separator)
                    break;
@@ -3072,8 +3164,13 @@ parse_upto (const char **formatp,
 
          case '>': /* 22.3.6.3 FORMAT-JUSTIFICATION-END */
            if (terminator != '>')
-             return false;
-           if (!check_params (&list, paramcount, params, 0, NULL))
+             {
+               *invalid_reason =
+                 xasprintf (_("Found '~%c' without matching '~%c'."), '>', '<');
+               return false;
+             }
+           if (!check_params (&list, paramcount, params, 0, NULL,
+                              spec->directives, invalid_reason))
              return false;
            *formatp = format;
            *positionp = position;
@@ -3082,7 +3179,8 @@ parse_upto (const char **formatp,
            return true;
 
          case '^': /* 22.3.9.2 FORMAT-UP-AND-OUT */
-           if (!check_params (&list, paramcount, params, 3, THREE))
+           if (!check_params (&list, paramcount, params, 3, THREE,
+                              spec->directives, invalid_reason))
              return false;
            if (position >= 0 && list != NULL && is_required (list, position))
              /* This ~^ can never be executed.  Ignore it.  */
@@ -3100,15 +3198,21 @@ parse_upto (const char **formatp,
 
          case ';': /* 22.3.9.1 FORMAT-SEPARATOR */
            if (!separator)
-             return false;
+             {
+               *invalid_reason =
+                 xasprintf (_("In the directive number %u, '~;' is used in an invalid position."), spec->directives);
+               return false;
+             }
            if (terminator == '>')
              {
-               if (!check_params (&list, paramcount, params, 1, I))
+               if (!check_params (&list, paramcount, params, 1, I,
+                                  spec->directives, invalid_reason))
                   return false;
              }
            else
              {
-               if (!check_params (&list, paramcount, params, 0, NULL))
+               if (!check_params (&list, paramcount, params, 0, NULL,
+                                  spec->directives, invalid_reason))
                   return false;
              }
            *formatp = format;
@@ -3119,7 +3223,8 @@ parse_upto (const char **formatp,
            return true;
 
          case '!': /* FORMAT-CALL, a CLISP extension */
-           if (!nocheck_params (&list, paramcount, params))
+           if (!nocheck_params (&list, paramcount, params,
+                                spec->directives, invalid_reason))
              return false;
            if (position >= 0)
              {
@@ -3129,6 +3234,11 @@ parse_upto (const char **formatp,
            break;
 
          default:
+           --format;
+           *invalid_reason =
+             (*format == '\0'
+              ? INVALID_UNTERMINATED_DIRECTIVE ()
+              : INVALID_CONVERSION_SPECIFIER (spec->directives, *format));
            return false;
          }
 
@@ -3139,14 +3249,20 @@ parse_upto (const char **formatp,
   *positionp = position;
   *listp = list;
   *escapep = escape;
-  return (terminator == '\0');
+  if (terminator != '\0')
+    {
+      *invalid_reason =
+       xasprintf (_("Found '~%c' without matching '~%c'."), terminator - 1, terminator);
+      return false;
+    }
+  return true;
 }
 
 
 /* ============== Top level format string handling functions ============== */
 
 static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
 {
   struct spec spec;
   struct spec *result;
@@ -3158,7 +3274,8 @@ format_parse (const char *format)
   escape = NULL;
 
   if (!parse_upto (&format, &position, &spec.list, &escape,
-                  NULL, &spec, '\0', false))
+                  NULL, &spec, '\0', false,
+                  invalid_reason))
     /* Invalid format string.  */
     return NULL;
 
@@ -3166,8 +3283,12 @@ format_parse (const char *format)
   spec.list = union (spec.list, escape);
 
   if (spec.list == NULL)
-    /* Contradictory argument type information.  */
-    return NULL;
+    {
+      /* Contradictory argument type information.  */
+      *invalid_reason =
+       xstrdup (_("The string refers to some argument in incompatible ways."));
+      return NULL;
+    }
 
   /* Normalize the result.  */
   normalize_list (spec.list);
@@ -3253,6 +3374,8 @@ struct formatstring_parser formatstring_lisp =
 
 /* ============================= Testing code ============================= */
 
+#undef union
+
 #ifdef TEST
 
 /* Test program: Print the argument list specification returned by
@@ -3363,16 +3486,26 @@ main ()
   for (;;)
     {
       char *line = NULL;
-      size_t line_len = 0;
+      size_t line_size = 0;
+      int line_len;
+      char *invalid_reason;
       void *descr;
 
-      if (getline (&line, &line_len, stdin) < 0)
+      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, &invalid_reason);
 
-      descr = format_parse (line);
       format_print (descr);
       printf ("\n");
+      if (descr == NULL)
+       printf ("%s\n", invalid_reason);
 
+      free (invalid_reason);
       free (line);
     }
 
index cf2d959a34b358f1bd4fc7190a05d241bdaae7f4..f60f8cd823cfe12ec5f0ed057976b5005f11a40c 100644 (file)
@@ -1,5 +1,5 @@
 /* Object Pascal format strings.
-   Copyright (C) 2001-2002 Free Software Foundation, Inc.
+   Copyright (C) 2001-2003 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
@@ -26,6 +26,8 @@
 #include "format.h"
 #include "c-ctype.h"
 #include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
 #include "error.h"
 #include "progname.h"
 #include "gettext.h"
@@ -103,7 +105,7 @@ numbered_arg_compare (const void *p1, const void *p2)
 }
 
 static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
 {
   unsigned int directives;
   unsigned int numbered_arg_count;
@@ -241,6 +243,10 @@ format_parse (const char *format)
                type = FAT_INTEGER;
                break;
              default:
+               *invalid_reason =
+                 (*format == '\0'
+                  ? INVALID_UNTERMINATED_DIRECTIVE ()
+                  : INVALID_CONVERSION_SPECIFIER (directives, *format));
                goto bad_format;
              }
 
@@ -298,8 +304,14 @@ format_parse (const char *format)
                     || (type1 == FAT_INTEGER64 && type2 == FAT_INTEGER))
              type_both = FAT_INTEGER;
            else
-             /* Incompatible types.  */
-             type_both = type1, err = true;
+             {
+               /* Incompatible types.  */
+               type_both = type1;
+               if (!err)
+                 *invalid_reason =
+                   INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
+               err = true;
+             }
 
            numbered[j-1].type = type_both;
          }
@@ -314,6 +326,7 @@ format_parse (const char *format)
          }
       numbered_arg_count = j;
       if (err)
+       /* *invalid_reason has already been set above.  */
        goto bad_format;
     }
 
@@ -510,17 +523,26 @@ main ()
   for (;;)
     {
       char *line = NULL;
-      size_t line_len = 0;
+      size_t line_size = 0;
+      int line_len;
+      char *invalid_reason;
       void *descr;
 
-      if (getline (&line, &line_len, stdin) < 0)
+      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';
 
-      descr = format_parse (line);
+      invalid_reason = NULL;
+      descr = format_parse (line, &invalid_reason);
 
       format_print (descr);
       printf ("\n");
+      if (descr == NULL)
+       printf ("%s\n", invalid_reason);
 
+      free (invalid_reason);
       free (line);
     }
 
index d91803b1e8c942c382effcd6aa54294469c843a7..d0f718fc6ab5080b35158330fa6c71119c788536 100644 (file)
@@ -1,5 +1,5 @@
 /* PHP format strings.
-   Copyright (C) 2001-2002 Free Software Foundation, Inc.
+   Copyright (C) 2001-2003 Free Software Foundation, Inc.
    Written by Bruno Haible <bruno@clisp.org>, 2002.
 
    This program is free software; you can redistribute it and/or modify
 #include <stdlib.h>
 
 #include "format.h"
+#include "c-ctype.h"
 #include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
 #include "error.h"
 #include "progname.h"
 #include "gettext.h"
@@ -93,7 +96,7 @@ numbered_arg_compare (const void *p1, const void *p2)
 }
 
 static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
 {
   unsigned int directives;
   unsigned int numbered_arg_count;
@@ -136,7 +139,10 @@ format_parse (const char *format)
                if (*f == '$')
                  {
                    if (m == 0)
-                     goto bad_format;
+                     {
+                       *invalid_reason = INVALID_ARGNO_0 (directives);
+                       goto bad_format;
+                     }
                    number = m;
                    format = ++f;
                    --unnumbered_arg_count;
@@ -152,7 +158,10 @@ format_parse (const char *format)
                  {
                    format++;
                    if (*format == '\0')
-                     goto bad_format;
+                     {
+                       *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+                       goto bad_format;
+                     }
                    format++;
                  }
                else
@@ -201,6 +210,10 @@ format_parse (const char *format)
                type = FAT_STRING;
                break;
              default:
+               *invalid_reason =
+                 (*format == '\0'
+                  ? INVALID_UNTERMINATED_DIRECTIVE ()
+                  : INVALID_CONVERSION_SPECIFIER (directives, *format));
                goto bad_format;
              }
 
@@ -238,8 +251,14 @@ format_parse (const char *format)
            if (type1 == type2)
              type_both = type1;
            else
-             /* Incompatible types.  */
-             type_both = type1, err = true;
+             {
+               /* Incompatible types.  */
+               type_both = type1;
+               if (!err)
+                 *invalid_reason =
+                   INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
+               err = true;
+             }
 
            numbered[j-1].type = type_both;
          }
@@ -254,6 +273,7 @@ format_parse (const char *format)
          }
       numbered_arg_count = j;
       if (err)
+       /* *invalid_reason has already been set above.  */
        goto bad_format;
     }
 
@@ -447,17 +467,26 @@ main ()
   for (;;)
     {
       char *line = NULL;
-      size_t line_len = 0;
+      size_t line_size = 0;
+      int line_len;
+      char *invalid_reason;
       void *descr;
 
-      if (getline (&line, &line_len, stdin) < 0)
+      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';
 
-      descr = format_parse (line);
+      invalid_reason = NULL;
+      descr = format_parse (line, &invalid_reason);
 
       format_print (descr);
       printf ("\n");
+      if (descr == NULL)
+       printf ("%s\n", invalid_reason);
 
+      free (invalid_reason);
       free (line);
     }
 
index 1116a70210d824c1195f2a7c5e662532e1353e42..3dbc1827c691d80445d3be8e10ecc1c067496f28 100644 (file)
@@ -1,5 +1,5 @@
 /* Python format strings.
-   Copyright (C) 2001-2002 Free Software Foundation, Inc.
+   Copyright (C) 2001-2003 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
 #include <string.h>
 
 #include "format.h"
+#include "c-ctype.h"
 #include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
 #include "error.h"
 #include "progname.h"
 #include "gettext.h"
@@ -108,8 +111,11 @@ named_arg_compare (const void *p1, const void *p2)
                 ((const struct named_arg *) p2)->name);
 }
 
+#define INVALID_MIXES_NAMED_UNNAMED() \
+  xstrdup (_("The string refers to arguments both through argument names and through unnamed argument specifications."))
+
 static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
 {
   struct spec spec;
   struct spec *result;
@@ -152,7 +158,10 @@ format_parse (const char *format)
                  }
              }
            if (*format == '\0')
-             goto bad_format;
+             {
+               *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+               goto bad_format;
+             }
            name_end = format++;
 
            n = name_end - name_start;
@@ -171,7 +180,10 @@ format_parse (const char *format)
 
            /* Named and unnamed specifications are exclusive.  */
            if (spec.named_arg_count > 0)
-             goto bad_format;
+             {
+               *invalid_reason = INVALID_MIXES_NAMED_UNNAMED ();
+               goto bad_format;
+             }
 
            if (spec.allocated == spec.unnamed_arg_count)
              {
@@ -196,7 +208,10 @@ format_parse (const char *format)
 
                /* Named and unnamed specifications are exclusive.  */
                if (spec.named_arg_count > 0)
-                 goto bad_format;
+                 {
+                   *invalid_reason = INVALID_MIXES_NAMED_UNNAMED ();
+                   goto bad_format;
+                 }
 
                if (spec.allocated == spec.unnamed_arg_count)
                  {
@@ -233,6 +248,10 @@ format_parse (const char *format)
            type = FAT_FLOAT;
            break;
          default:
+           *invalid_reason =
+             (*format == '\0'
+              ? INVALID_UNTERMINATED_DIRECTIVE ()
+              : INVALID_CONVERSION_SPECIFIER (spec.directives, *format));
            goto bad_format;
          }
 
@@ -242,7 +261,10 @@ format_parse (const char *format)
 
            /* Named and unnamed specifications are exclusive.  */
            if (spec.unnamed_arg_count > 0)
-             goto bad_format;
+             {
+               *invalid_reason = INVALID_MIXES_NAMED_UNNAMED ();
+               goto bad_format;
+             }
 
            if (spec.allocated == spec.named_arg_count)
              {
@@ -259,7 +281,10 @@ format_parse (const char *format)
 
            /* Named and unnamed specifications are exclusive.  */
            if (spec.named_arg_count > 0)
-             goto bad_format;
+             {
+               *invalid_reason = INVALID_MIXES_NAMED_UNNAMED ();
+               goto bad_format;
+             }
 
            if (spec.allocated == spec.unnamed_arg_count)
              {
@@ -296,8 +321,14 @@ format_parse (const char *format)
            else if (type1 == FAT_ANY)
              type_both = type2;
            else
-             /* Incompatible types.  */
-             type_both = FAT_NONE, err = true;
+             {
+               /* Incompatible types.  */
+               type_both = FAT_NONE;
+               if (!err)
+                 *invalid_reason =
+                   xasprintf (_("The string refers to the argument named '%s' in incompatible ways."), spec.named[i].name);
+               err = true;
+             }
 
            spec.named[j-1].type = type_both;
            free (spec.named[i].name);
@@ -313,6 +344,7 @@ format_parse (const char *format)
          }
       spec.named_arg_count = j;
       if (err)
+       /* *invalid_reason has already been set above.  */
        goto bad_format;
     }
 
@@ -609,17 +641,26 @@ main ()
   for (;;)
     {
       char *line = NULL;
-      size_t line_len = 0;
+      size_t line_size = 0;
+      int line_len;
+      char *invalid_reason;
       void *descr;
 
-      if (getline (&line, &line_len, stdin) < 0)
+      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';
 
-      descr = format_parse (line);
+      invalid_reason = NULL;
+      descr = format_parse (line, &invalid_reason);
 
       format_print (descr);
       printf ("\n");
+      if (descr == NULL)
+       printf ("%s\n", invalid_reason);
 
+      free (invalid_reason);
       free (line);
     }
 
index e74126d14d9800113ac01d0a4941845fc2ea4640..0ac982d1f3b8d6cdedd9b462cac0268ef8489e7f 100644 (file)
@@ -1,5 +1,5 @@
 /* Tcl format strings.
-   Copyright (C) 2001-2002 Free Software Foundation, Inc.
+   Copyright (C) 2001-2003 Free Software Foundation, Inc.
    Written by Bruno Haible <haible@clisp.cons.org>, 2002.
 
    This program is free software; you can redistribute it and/or modify
 #include <stdlib.h>
 
 #include "format.h"
+#include "c-ctype.h"
 #include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
 #include "error.h"
 #include "progname.h"
 #include "gettext.h"
@@ -97,7 +100,7 @@ numbered_arg_compare (const void *p1, const void *p2)
 }
 
 static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
 {
   struct spec spec;
   struct spec *result;
@@ -141,13 +144,19 @@ format_parse (const char *format)
                if (*f == '$')
                  {
                    if (m == 0)
-                     goto bad_format;
+                     {
+                       *invalid_reason = INVALID_ARGNO_0 (spec.directives);
+                       goto bad_format;
+                     }
                    number = m;
                    format = ++f;
 
                    /* Numbered and unnumbered specifications are exclusive.  */
                    if (seen_unnumbered_arg)
-                     goto bad_format;
+                     {
+                       *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+                       goto bad_format;
+                     }
                    is_numbered_arg = true;
                    seen_numbered_arg = true;
                  }
@@ -157,7 +166,10 @@ format_parse (const char *format)
            if (!is_numbered_arg)
              {
                if (seen_numbered_arg)
-                 goto bad_format;
+                 {
+                   *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+                   goto bad_format;
+                 }
                seen_unnumbered_arg = true;
              }
 
@@ -238,6 +250,10 @@ format_parse (const char *format)
                type = FAT_FLOAT;
                break;
              default:
+               *invalid_reason =
+                 (*format == '\0'
+                  ? INVALID_UNTERMINATED_DIRECTIVE ()
+                  : INVALID_CONVERSION_SPECIFIER (spec.directives, *format));
                goto bad_format;
              }
 
@@ -277,8 +293,14 @@ format_parse (const char *format)
            if (type1 == type2)
              type_both = type1;
            else
-             /* Incompatible types.  */
-             type_both = FAT_NONE, err = true;
+             {
+               /* Incompatible types.  */
+               type_both = FAT_NONE;
+               if (!err)
+                 *invalid_reason =
+                   INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
+               err = true;
+             }
 
            spec.numbered[j-1].type = type_both;
          }
@@ -293,6 +315,7 @@ format_parse (const char *format)
          }
       spec.numbered_arg_count = j;
       if (err)
+       /* *invalid_reason has already been set above.  */
        goto bad_format;
     }
 
@@ -472,7 +495,7 @@ format_print (void *descr)
        case FAT_SHORT_INTEGER:
          printf ("hi");
          break;
-       case FAT_UNSIGNED_SHORT_INTEGER:
+       case FAT_SHORT_UNSIGNED_INTEGER:
          printf ("[unsigned]hi");
          break;
        case FAT_FLOAT:
@@ -492,17 +515,26 @@ main ()
   for (;;)
     {
       char *line = NULL;
-      size_t line_len = 0;
+      size_t line_size = 0;
+      int line_len;
+      char *invalid_reason;
       void *descr;
 
-      if (getline (&line, &line_len, stdin) < 0)
+      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';
 
-      descr = format_parse (line);
+      invalid_reason = NULL;
+      descr = format_parse (line, &invalid_reason);
 
       format_print (descr);
       printf ("\n");
+      if (descr == NULL)
+       printf ("%s\n", invalid_reason);
 
+      free (invalid_reason);
       free (line);
     }
 
index fd41610b0f8fcb98cc4a62f7990dc37f8099faa6..cacab0e1340e11cc5e2454a8000328d57ad4617d 100644 (file)
@@ -1,5 +1,5 @@
 /* YCP and Smalltalk format strings.
-   Copyright (C) 2001-2002 Free Software Foundation, Inc.
+   Copyright (C) 2001-2003 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
 #include <stdlib.h>
 
 #include "format.h"
+#include "c-ctype.h"
 #include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
 #include "error.h"
 #include "progname.h"
 #include "gettext.h"
@@ -47,7 +50,7 @@ struct spec
 
 
 static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
 {
   struct spec spec;
   struct spec *result;
@@ -74,7 +77,15 @@ format_parse (const char *format)
            format++;
          }
        else
-         goto bad_format;
+         {
+           *invalid_reason =
+             (*format == '\0'
+              ? INVALID_UNTERMINATED_DIRECTIVE ()
+              : (c_isprint (*format)
+                 ? xasprintf (_("In the directive number %u, the character '%c' is not a digit between 1 and 9."), spec.directives, *format)
+                 : xasprintf (_("The character that terminates the directive number %u is not a digit between 1 and 9."), spec.directives)));
+           goto bad_format;
+         }
       }
 
   result = (struct spec *) xmalloc (sizeof (struct spec));
@@ -193,17 +204,26 @@ main ()
   for (;;)
     {
       char *line = NULL;
-      size_t line_len = 0;
+      size_t line_size = 0;
+      int line_len;
+      char *invalid_reason;
       void *descr;
 
-      if (getline (&line, &line_len, stdin) < 0)
+      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';
 
-      descr = format_parse (line);
+      invalid_reason = NULL;
+      descr = format_parse (line, &invalid_reason);
 
       format_print (descr);
       printf ("\n");
+      if (descr == NULL)
+       printf ("%s\n", invalid_reason);
 
+      free (invalid_reason);
       free (line);
     }
 
index 9d2105b08fc2600c184cc8b9c74ca4fc858dbfb1..f2857215c5497d75d7757e762903eab287c2a6fe 100644 (file)
@@ -1,5 +1,5 @@
 /* Format strings.
-   Copyright (C) 2001-2002 Free Software Foundation, Inc.
+   Copyright (C) 2001-2003 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
@@ -31,8 +31,9 @@ struct formatstring_parser
      Return a freshly allocated structure describing
        1. the argument types/names needed for the format string,
        2. the total number of format directives.
-     Return NULL if the string is not a valid format string.  */
-  void * (*parse) (const char *string);
+     Return NULL if the string is not a valid format string. In this case,
+     also set *invalid_reason to an error message explaining why.  */
+  void * (*parse) (const char *string, char **invalid_reason);
 
   /* Free a format string descriptor, returned by parse().  */
   void (*free) (void *descr);
index 9e6a4cd3bce584ff1b959c2b422b4825afa19e70..c43a68a7a79d9146f1dc51edda39508b02342622 100644 (file)
@@ -1120,8 +1120,10 @@ check_pair (const char *msgid,
                arguments that are used by other translations.  */
 
          struct formatstring_parser *parser = formatstring_parsers[i];
+         char *invalid_reason = NULL;
          void *msgid_descr =
-           parser->parse (msgid_plural != NULL ? msgid_plural : msgid);
+           parser->parse (msgid_plural != NULL ? msgid_plural : msgid,
+                          &invalid_reason);
 
          if (msgid_descr != NULL)
            {
@@ -1140,7 +1142,7 @@ check_pair (const char *msgid,
                      pretty_msgstr = buf;
                    }
 
-                 msgstr_descr = parser->parse (p);
+                 msgstr_descr = parser->parse (p, &invalid_reason);
 
                  if (msgstr_descr != NULL)
                    {
@@ -1157,15 +1159,19 @@ check_pair (const char *msgid,
                      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]);
+'%s' is not a valid %s format string, unlike 'msgid'. Reason: %s"),
+                                    pretty_msgstr, format_language_pretty[i],
+                                    invalid_reason);
                      error_with_progname = true;
                      exit_status = EXIT_FAILURE;
+                     free (invalid_reason);
                    }
                }
 
              parser->free (msgid_descr);
            }
+         else
+           free (invalid_reason);
        }
 
   if (check_accelerators && msgid_plural == NULL)
index 87bd713d4afc4fc9b6bb95fd7d9a97de025ed6ef..41c51e02f92f7a1062ae2fbe29438d71ef21aeb7 100644 (file)
@@ -513,8 +513,10 @@ msgfmt_check_pair_fails (const lex_pos_ty *pos,
 {
   bool failure;
   struct formatstring_parser *parser = formatstring_parsers[fmt];
+  char *invalid_reason = NULL;
   void *msgid_descr =
-    parser->parse (msgid_plural != NULL ? msgid_plural : msgid);
+    parser->parse (msgid_plural != NULL ? msgid_plural : msgid,
+                  &invalid_reason);
 
   failure = false;
   if (msgid_descr != NULL)
@@ -524,7 +526,7 @@ msgfmt_check_pair_fails (const lex_pos_ty *pos,
 
       for (p = msgstr; p < p_end; p += strlen (p) + 1)
        {
-         void *msgstr_descr = parser->parse (msgstr);
+         void *msgstr_descr = parser->parse (msgstr, &invalid_reason);
 
          if (msgstr_descr != NULL)
            {
@@ -533,7 +535,10 @@ msgfmt_check_pair_fails (const lex_pos_ty *pos,
              parser->free (msgstr_descr);
            }
          else
-           failure = true;
+           {
+             failure = true;
+             free (invalid_reason);
+           }
 
          if (failure)
            break;
@@ -541,6 +546,8 @@ msgfmt_check_pair_fails (const lex_pos_ty *pos,
 
       parser->free (msgid_descr);
     }
+  else
+    free (invalid_reason);
 
   return failure;
 }
index e3a3861bdb79ecd8a296395116a0501c6be48440..e1706a73446c3983c97a19fd26355ce4ee25f692 100644 (file)
@@ -1142,7 +1142,8 @@ meta information, not the empty string.\n")));
          && formatstring_parsers[i] == current_formatstring_parser)
        {
          struct formatstring_parser *parser = formatstring_parsers[i];
-         void *descr = parser->parse (mp->msgid);
+         char *invalid_reason = NULL;
+         void *descr = parser->parse (mp->msgid, &invalid_reason);
 
          if (descr != NULL)
            {
@@ -1160,8 +1161,11 @@ meta information, not the empty string.\n")));
              parser->free (descr);
            }
          else
-           /* msgid is not a valid format string.  */
-           is_format[i] = impossible;
+           {
+             /* msgid is not a valid format string.  */
+             is_format[i] = impossible;
+             free (invalid_reason);
+           }
        }
       mp->is_format[i] = is_format[i];
     }
@@ -1226,7 +1230,8 @@ remember_a_message_plural (message_ty *mp, char *string, lex_pos_ty *pos)
            && (mp->is_format[i] == undecided || mp->is_format[i] == possible))
          {
            struct formatstring_parser *parser = formatstring_parsers[i];
-           void *descr = parser->parse (mp->msgid_plural);
+           char *invalid_reason = NULL;
+           void *descr = parser->parse (mp->msgid_plural, &invalid_reason);
 
            if (descr != NULL)
              {
@@ -1237,8 +1242,11 @@ remember_a_message_plural (message_ty *mp, char *string, lex_pos_ty *pos)
                parser->free (descr);
              }
            else
-             /* msgid_plural is not a valid format string.  */
-             mp->is_format[i] = impossible;
+             {
+               /* msgid_plural is not a valid format string.  */
+               mp->is_format[i] = impossible;
+               free (invalid_reason);
+             }
          }
     }
   else