diff options
author | Bruno Haible <bruno@clisp.org> | 2003-02-24 10:54:10 +0000 |
---|---|---|
committer | Bruno Haible <bruno@clisp.org> | 2009-06-23 12:10:14 +0200 |
commit | 069c4216f67b5b14c50724d340c4e65b9a559cb3 (patch) | |
tree | 3bcf781d3c67220d366b41e9550620e4ec8a45f1 | |
parent | 1ef59bdf5b24ef74a81e61f4e7f3b82f88dd433b (diff) | |
download | external_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/ChangeLog | 5 | ||||
-rw-r--r-- | gettext-tools/po/POTFILES.in | 3 | ||||
-rw-r--r-- | gettext-tools/src/ChangeLog | 41 | ||||
-rw-r--r-- | gettext-tools/src/Makefile.am | 2 | ||||
-rw-r--r-- | gettext-tools/src/format-awk.c | 84 | ||||
-rw-r--r-- | gettext-tools/src/format-c.c | 133 | ||||
-rw-r--r-- | gettext-tools/src/format-elisp.c | 37 | ||||
-rw-r--r-- | gettext-tools/src/format-invalid.h | 41 | ||||
-rw-r--r-- | gettext-tools/src/format-java.c | 162 | ||||
-rw-r--r-- | gettext-tools/src/format-librep.c | 37 | ||||
-rw-r--r-- | gettext-tools/src/format-lisp.c | 263 | ||||
-rw-r--r-- | gettext-tools/src/format-pascal.c | 36 | ||||
-rw-r--r-- | gettext-tools/src/format-php.c | 47 | ||||
-rw-r--r-- | gettext-tools/src/format-python.c | 65 | ||||
-rw-r--r-- | gettext-tools/src/format-tcl.c | 54 | ||||
-rw-r--r-- | gettext-tools/src/format-ycp.c | 32 | ||||
-rw-r--r-- | gettext-tools/src/format.h | 7 | ||||
-rw-r--r-- | gettext-tools/src/msgfmt.c | 14 | ||||
-rw-r--r-- | gettext-tools/src/msgmerge.c | 13 | ||||
-rw-r--r-- | gettext-tools/src/xgettext.c | 20 |
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 |