+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.
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
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
+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
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
/* awk format strings.
- Copyright (C) 2001-2002 Free Software Foundation, Inc.
+ Copyright (C) 2001-2003 Free Software Foundation, Inc.
Written by Bruno Haible <haible@clisp.cons.org>, 2002.
This program is free software; you can redistribute it and/or modify
#include <stdlib.h>
#include "format.h"
+#include "c-ctype.h"
#include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
#include "error.h"
#include "progname.h"
#include "gettext.h"
}
static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
{
struct spec spec;
unsigned int unnumbered_arg_count;
if (*f == '$')
{
if (m == 0)
- goto bad_format;
+ {
+ *invalid_reason = INVALID_ARGNO_0 (spec.directives);
+ goto bad_format;
+ }
number = m;
format = ++f;
}
if (*f == '$')
{
if (m == 0)
- goto bad_format;
+ {
+ *invalid_reason =
+ INVALID_WIDTH_ARGNO_0 (spec.directives);
+ goto bad_format;
+ }
width_number = m;
format = ++f;
}
/* 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)
{
/* 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)
{
if (*f == '$')
{
if (m == 0)
- goto bad_format;
+ {
+ *invalid_reason =
+ INVALID_PRECISION_ARGNO_0 (spec.directives);
+ goto bad_format;
+ }
precision_number = m;
format = ++f;
}
/* 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)
{
/* 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)
{
type = FAT_FLOAT;
break;
default:
+ *invalid_reason =
+ (*format == '\0'
+ ? INVALID_UNTERMINATED_DIRECTIVE ()
+ : INVALID_CONVERSION_SPECIFIER (spec.directives, *format));
goto bad_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)
{
/* 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)
{
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;
}
}
spec.numbered_arg_count = j;
if (err)
+ /* *invalid_reason has already been set above. */
goto bad_format;
}
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);
}
#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"
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;
if (*f == '$')
{
if (m == 0)
- goto bad_format;
+ {
+ *invalid_reason = INVALID_ARGNO_0 (spec.directives);
+ goto bad_format;
+ }
number = m;
format = ++f;
}
if (*f == '$')
{
if (m == 0)
- goto bad_format;
+ {
+ *invalid_reason =
+ INVALID_WIDTH_ARGNO_0 (spec.directives);
+ goto bad_format;
+ }
width_number = m;
format = ++f;
}
/* 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)
{
/* 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)
{
if (*f == '$')
{
if (m == 0)
- goto bad_format;
+ {
+ *invalid_reason =
+ INVALID_PRECISION_ARGNO_0 (spec.directives);
+ goto bad_format;
+ }
precision_number = m;
format = ++f;
}
/* 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)
{
/* 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)
{
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)
type = FAT_INTEGER | FAT_UNSIGNED;
break;
default:
+ *invalid_reason = INVALID_C99_MACRO (spec.directives);
goto bad_format;
}
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')
format += 2;
}
else
- goto bad_format;
+ {
+ *invalid_reason = INVALID_C99_MACRO (spec.directives);
+ goto bad_format;
+ }
}
else
{
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++;
type |= (size & FAT_SIZE_MASK);
break;
default:
+ *invalid_reason =
+ (*format == '\0'
+ ? INVALID_UNTERMINATED_DIRECTIVE ()
+ : INVALID_CONVERSION_SPECIFIER (spec.directives, *format));
goto bad_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)
{
/* 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)
{
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;
}
}
numbered_arg_count = j;
if (err)
+ /* *invalid_reason has already been set above. */
goto bad_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. */
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)
{
if (descr != NULL)
format_free (descr);
+ else
+ free (invalid_reason);
}
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);
}
/* Emacs Lisp format strings.
- Copyright (C) 2001-2002 Free Software Foundation, Inc.
+ Copyright (C) 2001-2003 Free Software Foundation, Inc.
Written by Bruno Haible <haible@clisp.cons.org>, 2002.
This program is free software; you can redistribute it and/or modify
#include <stdlib.h>
#include "format.h"
+#include "c-ctype.h"
#include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
#include "error.h"
#include "progname.h"
#include "gettext.h"
}
static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
{
struct spec spec;
struct spec *result;
type = FAT_OBJECT;
break;
default:
+ *invalid_reason =
+ (*format == '\0'
+ ? INVALID_UNTERMINATED_DIRECTIVE ()
+ : INVALID_CONVERSION_SPECIFIER (spec.directives, *format));
goto bad_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;
}
}
spec.numbered_arg_count = j;
if (err)
+ /* *invalid_reason has already been set above. */
goto bad_format;
}
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);
}
--- /dev/null
+/* 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)
#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"
/* 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:
/* 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;
}
}
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;
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
{
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)
{
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)
{
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)
{
}
/* 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
/* 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
{
/* 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
}
else
format += 1;
+ number_nonempty = true;
HANDLE_QUOTE;
}
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);
}
*mp = '\0';
- if (!message_format_parse (msgformat, spec))
+ if (!message_format_parse (msgformat, spec, invalid_reason))
return false;
if (*format == '\0')
}
static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
{
struct spec spec;
struct spec *result;
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. */
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;
}
}
spec.numbered_arg_count = j;
if (err)
+ /* *invalid_reason has already been set above. */
goto bad_format;
}
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);
}
/* librep format strings.
- Copyright (C) 2001-2002 Free Software Foundation, Inc.
+ Copyright (C) 2001-2003 Free Software Foundation, Inc.
Written by Bruno Haible <haible@clisp.cons.org>, 2001.
This program is free software; you can redistribute it and/or modify
#include <stdlib.h>
#include "format.h"
+#include "c-ctype.h"
#include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
#include "error.h"
#include "progname.h"
#include "gettext.h"
}
static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
{
struct spec spec;
struct spec *result;
type = FAT_OBJECT;
break;
default:
+ *invalid_reason =
+ (*format == '\0'
+ ? INVALID_UNTERMINATED_DIRECTIVE ()
+ : INVALID_CONVERSION_SPECIFIER (spec.directives, *format));
goto bad_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;
}
}
spec.numbered_arg_count = j;
if (err)
+ /* *invalid_reason has already been set above. */
goto bad_format;
}
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);
}
/* 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
#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"
/* 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--)
{
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:
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:
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. */
{
/* 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)
{
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;
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');
type = PT_CHARACTER;
format++;
if (*format == '\0')
- return false;
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ return false;
+ }
format++;
}
else if (*format == 'V' || *format == 'v')
{
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);
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)
{
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);
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);
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 */
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. */
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);
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;
*escapep = escape;
{
if (!parse_upto (formatp, positionp, listp, escapep,
- NULL, spec, ')', false))
+ NULL, spec, ')', false,
+ invalid_reason))
return false;
}
format = *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;
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;
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)
{
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)
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);
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)
{
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. */
(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)
{
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;
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;
{
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;
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;
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;
{
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;
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;
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. */
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;
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)
{
break;
default:
+ --format;
+ *invalid_reason =
+ (*format == '\0'
+ ? INVALID_UNTERMINATED_DIRECTIVE ()
+ : INVALID_CONVERSION_SPECIFIER (spec->directives, *format));
return false;
}
*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;
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;
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);
/* ============================= Testing code ============================= */
+#undef union
+
#ifdef TEST
/* Test program: Print the argument list specification returned by
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);
}
/* 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
#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"
}
static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
{
unsigned int directives;
unsigned int numbered_arg_count;
type = FAT_INTEGER;
break;
default:
+ *invalid_reason =
+ (*format == '\0'
+ ? INVALID_UNTERMINATED_DIRECTIVE ()
+ : INVALID_CONVERSION_SPECIFIER (directives, *format));
goto bad_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;
}
}
numbered_arg_count = j;
if (err)
+ /* *invalid_reason has already been set above. */
goto bad_format;
}
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);
}
/* PHP format strings.
- Copyright (C) 2001-2002 Free Software Foundation, Inc.
+ Copyright (C) 2001-2003 Free Software Foundation, Inc.
Written by Bruno Haible <bruno@clisp.org>, 2002.
This program is free software; you can redistribute it and/or modify
#include <stdlib.h>
#include "format.h"
+#include "c-ctype.h"
#include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
#include "error.h"
#include "progname.h"
#include "gettext.h"
}
static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
{
unsigned int directives;
unsigned int numbered_arg_count;
if (*f == '$')
{
if (m == 0)
- goto bad_format;
+ {
+ *invalid_reason = INVALID_ARGNO_0 (directives);
+ goto bad_format;
+ }
number = m;
format = ++f;
--unnumbered_arg_count;
{
format++;
if (*format == '\0')
- goto bad_format;
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ goto bad_format;
+ }
format++;
}
else
type = FAT_STRING;
break;
default:
+ *invalid_reason =
+ (*format == '\0'
+ ? INVALID_UNTERMINATED_DIRECTIVE ()
+ : INVALID_CONVERSION_SPECIFIER (directives, *format));
goto bad_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;
}
}
numbered_arg_count = j;
if (err)
+ /* *invalid_reason has already been set above. */
goto bad_format;
}
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);
}
/* Python format strings.
- Copyright (C) 2001-2002 Free Software Foundation, Inc.
+ Copyright (C) 2001-2003 Free Software Foundation, Inc.
Written by Bruno Haible <haible@clisp.cons.org>, 2001.
This program is free software; you can redistribute it and/or modify
#include <string.h>
#include "format.h"
+#include "c-ctype.h"
#include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
#include "error.h"
#include "progname.h"
#include "gettext.h"
((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;
}
}
if (*format == '\0')
- goto bad_format;
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ goto bad_format;
+ }
name_end = format++;
n = name_end - name_start;
/* 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)
{
/* 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)
{
type = FAT_FLOAT;
break;
default:
+ *invalid_reason =
+ (*format == '\0'
+ ? INVALID_UNTERMINATED_DIRECTIVE ()
+ : INVALID_CONVERSION_SPECIFIER (spec.directives, *format));
goto bad_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)
{
/* 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)
{
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);
}
spec.named_arg_count = j;
if (err)
+ /* *invalid_reason has already been set above. */
goto bad_format;
}
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);
}
/* Tcl format strings.
- Copyright (C) 2001-2002 Free Software Foundation, Inc.
+ Copyright (C) 2001-2003 Free Software Foundation, Inc.
Written by Bruno Haible <haible@clisp.cons.org>, 2002.
This program is free software; you can redistribute it and/or modify
#include <stdlib.h>
#include "format.h"
+#include "c-ctype.h"
#include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
#include "error.h"
#include "progname.h"
#include "gettext.h"
}
static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
{
struct spec spec;
struct spec *result;
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;
}
if (!is_numbered_arg)
{
if (seen_numbered_arg)
- goto bad_format;
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ goto bad_format;
+ }
seen_unnumbered_arg = true;
}
type = FAT_FLOAT;
break;
default:
+ *invalid_reason =
+ (*format == '\0'
+ ? INVALID_UNTERMINATED_DIRECTIVE ()
+ : INVALID_CONVERSION_SPECIFIER (spec.directives, *format));
goto bad_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;
}
}
spec.numbered_arg_count = j;
if (err)
+ /* *invalid_reason has already been set above. */
goto bad_format;
}
case FAT_SHORT_INTEGER:
printf ("hi");
break;
- case FAT_UNSIGNED_SHORT_INTEGER:
+ case FAT_SHORT_UNSIGNED_INTEGER:
printf ("[unsigned]hi");
break;
case FAT_FLOAT:
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);
}
/* YCP and Smalltalk format strings.
- Copyright (C) 2001-2002 Free Software Foundation, Inc.
+ Copyright (C) 2001-2003 Free Software Foundation, Inc.
Written by Bruno Haible <haible@clisp.cons.org>, 2001.
This program is free software; you can redistribute it and/or modify
#include <stdlib.h>
#include "format.h"
+#include "c-ctype.h"
#include "xmalloc.h"
+#include "xerror.h"
+#include "format-invalid.h"
#include "error.h"
#include "progname.h"
#include "gettext.h"
static void *
-format_parse (const char *format)
+format_parse (const char *format, char **invalid_reason)
{
struct spec spec;
struct spec *result;
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));
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);
}
/* 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
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);
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)
{
pretty_msgstr = buf;
}
- msgstr_descr = parser->parse (p);
+ msgstr_descr = parser->parse (p, &invalid_reason);
if (msgstr_descr != NULL)
{
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)
{
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)
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)
{
parser->free (msgstr_descr);
}
else
- failure = true;
+ {
+ failure = true;
+ free (invalid_reason);
+ }
if (failure)
break;
parser->free (msgid_descr);
}
+ else
+ free (invalid_reason);
return failure;
}
&& 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)
{
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];
}
&& (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)
{
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