2 Copyright (C) 2001-2004, 2006-2007, 2009, 2015 Free Software
4 Written by Bruno Haible <haible@clisp.cons.org>, 2002.
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
29 #include "xvasprintf.h"
30 #include "format-invalid.h"
33 #define _(str) gettext (str)
35 /* Tcl format strings are described in the tcl8.3.3/doc/format.n manual
36 page and implemented in the function Tcl_FormatObjCmd in
37 tcl8.3.3/generic/tclCmdAH.c.
39 - starts with '%' or '%m$' where m is a positive integer,
40 - is optionally followed by any of the characters '#', '0', '-', ' ', '+',
41 each of which acts as a flag,
42 - is optionally followed by a width specification: '*' (reads an argument)
43 or a nonempty digit sequence,
44 - is optionally followed by '.' and a precision specification: '*' (reads
45 an argument) or a nonempty digit sequence,
46 - is optionally followed by a size specifier, 'h' or 'l'. 'l' is ignored.
47 - is finished by a specifier
48 - '%', that needs no argument,
49 - 'c', that needs a character argument,
50 - 's', that needs a string argument,
51 - 'i', 'd', that need a signed integer argument,
52 - 'o', 'u', 'x', 'X', that need an unsigned integer argument,
53 - 'e', 'E', 'f', 'g', 'G', that need a floating-point argument.
54 Numbered ('%m$') and unnumbered argument specifications cannot be used
66 FAT_SHORT_UNSIGNED_INTEGER,
73 enum format_arg_type type;
78 unsigned int directives;
79 unsigned int numbered_arg_count;
80 unsigned int allocated;
81 struct numbered_arg *numbered;
84 /* Locale independent test for a decimal digit.
85 Argument can be 'char' or 'unsigned char'. (Whereas the argument of
86 <ctype.h> isdigit must be an 'unsigned char'.) */
88 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
92 numbered_arg_compare (const void *p1, const void *p2)
94 unsigned int n1 = ((const struct numbered_arg *) p1)->number;
95 unsigned int n2 = ((const struct numbered_arg *) p2)->number;
97 return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
101 format_parse (const char *format, bool translated, char *fdi,
102 char **invalid_reason)
104 const char *const format_start = format;
107 bool seen_numbered_arg;
108 bool seen_unnumbered_arg;
112 spec.numbered_arg_count = 0;
114 spec.numbered = NULL;
115 seen_numbered_arg = false;
116 seen_unnumbered_arg = false;
119 for (; *format != '\0';)
120 if (*format++ == '%')
123 FDI_SET (format - 1, FMTDIR_START);
128 bool is_numbered_arg;
130 enum format_arg_type type;
132 is_numbered_arg = false;
133 if (isdigit (*format))
135 const char *f = format;
140 m = 10 * m + (*f - '0');
143 while (isdigit (*f));
149 *invalid_reason = INVALID_ARGNO_0 (spec.directives);
150 FDI_SET (f, FMTDIR_ERROR);
156 /* Numbered and unnumbered specifications are exclusive. */
157 if (seen_unnumbered_arg)
159 *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
160 FDI_SET (format - 1, FMTDIR_ERROR);
163 is_numbered_arg = true;
164 seen_numbered_arg = true;
168 /* Numbered and unnumbered specifications are exclusive. */
169 if (!is_numbered_arg)
171 if (seen_numbered_arg)
173 *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
174 FDI_SET (format - 1, FMTDIR_ERROR);
177 seen_unnumbered_arg = true;
181 while (*format == ' ' || *format == '+' || *format == '-'
182 || *format == '#' || *format == '0')
190 if (spec.allocated == spec.numbered_arg_count)
192 spec.allocated = 2 * spec.allocated + 1;
193 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
195 spec.numbered[spec.numbered_arg_count].number = number;
196 spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
197 spec.numbered_arg_count++;
201 else if (isdigit (*format))
203 do format++; while (isdigit (*format));
206 /* Parse precision. */
215 if (spec.allocated == spec.numbered_arg_count)
217 spec.allocated = 2 * spec.allocated + 1;
218 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
220 spec.numbered[spec.numbered_arg_count].number = number;
221 spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
222 spec.numbered_arg_count++;
226 else if (isdigit (*format))
228 do format++; while (isdigit (*format));
232 /* Parse optional size specification. */
235 short_flag = true, format++;
236 else if (*format == 'l')
242 type = FAT_CHARACTER;
248 type = (short_flag ? FAT_SHORT_INTEGER : FAT_INTEGER);
250 case 'u': case 'o': case 'x': case 'X':
251 type = (short_flag ? FAT_SHORT_UNSIGNED_INTEGER : FAT_UNSIGNED_INTEGER);
253 case 'e': case 'E': case 'f': case 'g': case 'G':
259 *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
260 FDI_SET (format - 1, FMTDIR_ERROR);
265 INVALID_CONVERSION_SPECIFIER (spec.directives, *format);
266 FDI_SET (format, FMTDIR_ERROR);
271 if (spec.allocated == spec.numbered_arg_count)
273 spec.allocated = 2 * spec.allocated + 1;
274 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
276 spec.numbered[spec.numbered_arg_count].number = number;
277 spec.numbered[spec.numbered_arg_count].type = type;
278 spec.numbered_arg_count++;
283 FDI_SET (format, FMTDIR_END);
288 /* Sort the numbered argument array, and eliminate duplicates. */
289 if (spec.numbered_arg_count > 1)
294 qsort (spec.numbered, spec.numbered_arg_count,
295 sizeof (struct numbered_arg), numbered_arg_compare);
297 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
299 for (i = j = 0; i < spec.numbered_arg_count; i++)
300 if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
302 enum format_arg_type type1 = spec.numbered[i].type;
303 enum format_arg_type type2 = spec.numbered[j-1].type;
304 enum format_arg_type type_both;
310 /* Incompatible types. */
311 type_both = FAT_NONE;
314 INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
318 spec.numbered[j-1].type = type_both;
324 spec.numbered[j].number = spec.numbered[i].number;
325 spec.numbered[j].type = spec.numbered[i].type;
329 spec.numbered_arg_count = j;
331 /* *invalid_reason has already been set above. */
335 result = XMALLOC (struct spec);
340 if (spec.numbered != NULL)
341 free (spec.numbered);
346 format_free (void *descr)
348 struct spec *spec = (struct spec *) descr;
350 if (spec->numbered != NULL)
351 free (spec->numbered);
356 format_get_number_of_directives (void *descr)
358 struct spec *spec = (struct spec *) descr;
360 return spec->directives;
364 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
365 formatstring_error_logger_t error_logger,
366 const char *pretty_msgid, const char *pretty_msgstr)
368 struct spec *spec1 = (struct spec *) msgid_descr;
369 struct spec *spec2 = (struct spec *) msgstr_descr;
372 if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
375 unsigned int n1 = spec1->numbered_arg_count;
376 unsigned int n2 = spec2->numbered_arg_count;
378 /* Check the argument names are the same.
379 Both arrays are sorted. We search for the first difference. */
380 for (i = 0, j = 0; i < n1 || j < n2; )
382 int cmp = (i >= n1 ? 1 :
384 spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
385 spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
391 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
392 spec2->numbered[j].number, pretty_msgstr,
402 error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
403 spec1->numbered[i].number, pretty_msgstr);
413 /* Check the argument types are the same. */
415 for (i = 0, j = 0; j < n2; )
417 if (spec1->numbered[i].number == spec2->numbered[j].number)
419 if (spec1->numbered[i].type != spec2->numbered[j].type)
422 error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
423 pretty_msgid, pretty_msgstr,
424 spec2->numbered[j].number);
439 struct formatstring_parser formatstring_tcl =
443 format_get_number_of_directives,
451 /* Test program: Print the argument list specification returned by
452 format_parse for strings read from standard input. */
457 format_print (void *descr)
459 struct spec *spec = (struct spec *) descr;
471 for (i = 0; i < spec->numbered_arg_count; i++)
473 unsigned int number = spec->numbered[i].number;
479 for (; last < number; last++)
481 switch (spec->numbered[i].type)
492 case FAT_UNSIGNED_INTEGER:
493 printf ("[unsigned]i");
495 case FAT_SHORT_INTEGER:
498 case FAT_SHORT_UNSIGNED_INTEGER:
499 printf ("[unsigned]hi");
518 size_t line_size = 0;
520 char *invalid_reason;
523 line_len = getline (&line, &line_size, stdin);
526 if (line_len > 0 && line[line_len - 1] == '\n')
527 line[--line_len] = '\0';
529 invalid_reason = NULL;
530 descr = format_parse (line, false, NULL, &invalid_reason);
532 format_print (descr);
535 printf ("%s\n", invalid_reason);
537 free (invalid_reason);
545 * For Emacs M-x compile
547 * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-tcl.c ../gnulib-lib/libgettextlib.la"