summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBruno Haible <bruno@clisp.org>2003-02-24 10:54:10 +0000
committerBruno Haible <bruno@clisp.org>2009-06-23 12:10:14 +0200
commit069c4216f67b5b14c50724d340c4e65b9a559cb3 (patch)
tree3bcf781d3c67220d366b41e9550620e4ec8a45f1
parent1ef59bdf5b24ef74a81e61f4e7f3b82f88dd433b (diff)
downloadexternal_gettext-069c4216f67b5b14c50724d340c4e65b9a559cb3.zip
external_gettext-069c4216f67b5b14c50724d340c4e65b9a559cb3.tar.gz
external_gettext-069c4216f67b5b14c50724d340c4e65b9a559cb3.tar.bz2
Better error message when an msgstr string is an invalid format string.
-rw-r--r--gettext-tools/po/ChangeLog5
-rw-r--r--gettext-tools/po/POTFILES.in3
-rw-r--r--gettext-tools/src/ChangeLog41
-rw-r--r--gettext-tools/src/Makefile.am2
-rw-r--r--gettext-tools/src/format-awk.c84
-rw-r--r--gettext-tools/src/format-c.c133
-rw-r--r--gettext-tools/src/format-elisp.c37
-rw-r--r--gettext-tools/src/format-invalid.h41
-rw-r--r--gettext-tools/src/format-java.c162
-rw-r--r--gettext-tools/src/format-librep.c37
-rw-r--r--gettext-tools/src/format-lisp.c263
-rw-r--r--gettext-tools/src/format-pascal.c36
-rw-r--r--gettext-tools/src/format-php.c47
-rw-r--r--gettext-tools/src/format-python.c65
-rw-r--r--gettext-tools/src/format-tcl.c54
-rw-r--r--gettext-tools/src/format-ycp.c32
-rw-r--r--gettext-tools/src/format.h7
-rw-r--r--gettext-tools/src/msgfmt.c14
-rw-r--r--gettext-tools/src/msgmerge.c13
-rw-r--r--gettext-tools/src/xgettext.c20
20 files changed, 879 insertions, 217 deletions
diff --git a/gettext-tools/po/ChangeLog b/gettext-tools/po/ChangeLog
index 5a929fc..841ddd6 100644
--- a/gettext-tools/po/ChangeLog
+++ b/gettext-tools/po/ChangeLog
@@ -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.
diff --git a/gettext-tools/po/POTFILES.in b/gettext-tools/po/POTFILES.in
index 46ae65a..787a1d1 100644
--- a/gettext-tools/po/POTFILES.in
+++ b/gettext-tools/po/POTFILES.in
@@ -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
diff --git a/gettext-tools/src/ChangeLog b/gettext-tools/src/ChangeLog
index 439703e..2fba9b5 100644
--- a/gettext-tools/src/ChangeLog
+++ b/gettext-tools/src/ChangeLog
@@ -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
diff --git a/gettext-tools/src/Makefile.am b/gettext-tools/src/Makefile.am
index 6dcce15..14c73c0 100644
--- a/gettext-tools/src/Makefile.am
+++ b/gettext-tools/src/Makefile.am
@@ -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
diff --git a/gettext-tools/src/format-awk.c b/gettext-tools/src/format-awk.c
index 0c8bbd5..273cea4 100644
--- a/gettext-tools/src/format-awk.c
+++ b/gettext-tools/src/format-awk.c
@@ -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
@@ -24,7 +24,10 @@
#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);
}
diff --git a/gettext-tools/src/format-c.c b/gettext-tools/src/format-c.c
index 81300ca..4eb3c2c 100644
--- a/gettext-tools/src/format-c.c
+++ b/gettext-tools/src/format-c.c
@@ -24,7 +24,10 @@
#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);
}
diff --git a/gettext-tools/src/format-elisp.c b/gettext-tools/src/format-elisp.c
index b121a4c..f575e3a 100644
--- a/gettext-tools/src/format-elisp.c
+++ b/gettext-tools/src/format-elisp.c
@@ -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
@@ -24,7 +24,10 @@
#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
index 0000000..8e98a62
--- /dev/null
+++ b/gettext-tools/src/format-invalid.h
@@ -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)
diff --git a/gettext-tools/src/format-java.c b/gettext-tools/src/format-java.c
index f448804..e8ba904 100644
--- a/gettext-tools/src/format-java.c
+++ b/gettext-tools/src/format-java.c
@@ -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);
}
diff --git a/gettext-tools/src/format-librep.c b/gettext-tools/src/format-librep.c
index 71cad3f..2dfcaa4 100644
--- a/gettext-tools/src/format-librep.c
+++ b/gettext-tools/src/format-librep.c
@@ -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
@@ -24,7 +24,10 @@
#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);
}
diff --git a/gettext-tools/src/format-lisp.c b/gettext-tools/src/format-lisp.c
index 745c95d..51cf6ee 100644
--- a/gettext-tools/src/format-lisp.c
+++ b/gettext-tools/src/format-lisp.c
@@ -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);
}
diff --git a/gettext-tools/src/format-pascal.c b/gettext-tools/src/format-pascal.c
index cf2d959..f60f8cd 100644
--- a/gettext-tools/src/format-pascal.c
+++ b/gettext-tools/src/format-pascal.c
@@ -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);
}
diff --git a/gettext-tools/src/format-php.c b/gettext-tools/src/format-php.c
index d91803b..d0f718f 100644
--- a/gettext-tools/src/format-php.c
+++ b/gettext-tools/src/format-php.c
@@ -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
@@ -24,7 +24,10 @@
#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);
}
diff --git a/gettext-tools/src/format-python.c b/gettext-tools/src/format-python.c
index 1116a70..3dbc182 100644
--- a/gettext-tools/src/format-python.c
+++ b/gettext-tools/src/format-python.c
@@ -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
@@ -25,7 +25,10 @@
#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);
}
diff --git a/gettext-tools/src/format-tcl.c b/gettext-tools/src/format-tcl.c
index e74126d..0ac982d 100644
--- a/gettext-tools/src/format-tcl.c
+++ b/gettext-tools/src/format-tcl.c
@@ -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
@@ -24,7 +24,10 @@
#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);
}
diff --git a/gettext-tools/src/format-ycp.c b/gettext-tools/src/format-ycp.c
index fd41610..cacab0e 100644
--- a/gettext-tools/src/format-ycp.c
+++ b/gettext-tools/src/format-ycp.c
@@ -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
@@ -24,7 +24,10 @@
#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);
}
diff --git a/gettext-tools/src/format.h b/gettext-tools/src/format.h
index 9d2105b..f285721 100644
--- a/gettext-tools/src/format.h
+++ b/gettext-tools/src/format.h
@@ -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);
diff --git a/gettext-tools/src/msgfmt.c b/gettext-tools/src/msgfmt.c
index 9e6a4cd..c43a68a 100644
--- a/gettext-tools/src/msgfmt.c
+++ b/gettext-tools/src/msgfmt.c
@@ -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)
diff --git a/gettext-tools/src/msgmerge.c b/gettext-tools/src/msgmerge.c
index 87bd713..41c51e0 100644
--- a/gettext-tools/src/msgmerge.c
+++ b/gettext-tools/src/msgmerge.c
@@ -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;
}
diff --git a/gettext-tools/src/xgettext.c b/gettext-tools/src/xgettext.c
index e3a3861..e1706a7 100644
--- a/gettext-tools/src/xgettext.c
+++ b/gettext-tools/src/xgettext.c
@@ -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