1 /* Boost format strings.
2 Copyright (C) 2001-2004, 2006-2007, 2009 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2006.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
28 #include "xvasprintf.h"
29 #include "format-invalid.h"
32 #define _(str) gettext (str)
34 /* Boost format strings are described in
35 boost_1_33_1/libs/format/doc/format.html
37 boost_1_33_1/boost/format/parsing.hpp.
38 A directive (other than '%%')
39 - starts with '%' or '%|'; in the latter case it must end in '|',
40 - is continued either by
41 - 'm%' where m is a positive integer, starting with a nonzero digit;
42 in this case the directive must not have started with '%|'; or
44 - optional: 'm$' where m is a positive integer, starting with a
46 - optional: any of the characters '#', '0', '-', ' ', '+', "'",
48 - optional: a width specification: '*' (reads an argument) or '*m$'
49 or a nonempty digit sequence,
50 - optional: a '.' and a precision specification: '*' (reads an
51 argument) or '*m$' or a nonempty digit sequence,
52 - optional: any of the characters 'h', 'l', 'L',
53 - if the directive started with '%|':
54 an optional specifier and a final '|',
56 a mandatory specifier.
57 If no specifier is given, it needs an argument of any type.
58 The possible specifiers are:
59 - 'c', 'C', that need a character argument,
60 - 's', 'S', that need an argument of any type,
61 - 'i', 'd', 'o', 'u', 'x', 'X', that need an integer argument,
62 - 'e', 'E', 'f', 'g', 'G', that need a floating-point argument,
63 - 'p', that needs a 'void *' argument,
64 - 't', that doesn't need an argument,
65 - 'TX', where X is any character, that doesn't need an argument,
66 - 'n', that needs a pointer to integer.
67 The Boost format string interpreter doesn't actually care about
68 the argument types, but we do, because it increases the likelihood
69 of detecting translator mistakes.
70 Numbered ('%m%' or '%m$' or '*m$') and unnumbered argument specifications
71 cannot be used in the same string.
88 enum format_arg_type type;
93 unsigned int directives;
94 unsigned int numbered_arg_count;
95 unsigned int allocated;
96 struct numbered_arg *numbered;
99 /* Locale independent test for a decimal digit.
100 Argument can be 'char' or 'unsigned char'. (Whereas the argument of
101 <ctype.h> isdigit must be an 'unsigned char'.) */
103 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
107 numbered_arg_compare (const void *p1, const void *p2)
109 unsigned int n1 = ((const struct numbered_arg *) p1)->number;
110 unsigned int n2 = ((const struct numbered_arg *) p2)->number;
112 return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
116 format_parse (const char *format, bool translated, char *fdi,
117 char **invalid_reason)
119 const char *const format_start = format;
121 unsigned int unnumbered_arg_count;
125 spec.numbered_arg_count = 0;
127 spec.numbered = NULL;
128 unnumbered_arg_count = 0;
130 for (; *format != '\0';)
131 if (*format++ == '%')
134 FDI_SET (format - 1, FMTDIR_START);
141 bool brackets = false;
143 unsigned int number = 0;
144 enum format_arg_type type = FAT_NONE;
152 if (isdigit (*format) && *format != '0')
154 const char *f = format;
159 m = 10 * m + (*f - '0');
162 while (isdigit (*f));
164 if ((!brackets && *f == '%') || *f == '$')
166 if (m == 0) /* can happen if m overflows */
168 *invalid_reason = INVALID_ARGNO_0 (spec.directives);
169 FDI_SET (f, FMTDIR_ERROR);
187 if (*format == ' ' || *format == '+' || *format == '-'
188 || *format == '#' || *format == '0' || *format == '\''
189 || *format == '_' || *format == '=' || *format == 'h'
199 unsigned int width_number = 0;
203 if (isdigit (*format))
205 const char *f = format;
210 m = 10 * m + (*f - '0');
213 while (isdigit (*f));
220 INVALID_WIDTH_ARGNO_0 (spec.directives);
221 FDI_SET (f, FMTDIR_ERROR);
231 /* Numbered argument. */
233 /* Numbered and unnumbered specifications are
235 if (unnumbered_arg_count > 0)
238 INVALID_MIXES_NUMBERED_UNNUMBERED ();
239 FDI_SET (format - 1, FMTDIR_ERROR);
243 if (spec.allocated == spec.numbered_arg_count)
245 spec.allocated = 2 * spec.allocated + 1;
246 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
248 spec.numbered[spec.numbered_arg_count].number = width_number;
249 spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
250 spec.numbered_arg_count++;
254 /* Unnumbered argument. */
256 /* Numbered and unnumbered specifications are
258 if (spec.numbered_arg_count > 0)
261 INVALID_MIXES_NUMBERED_UNNUMBERED ();
262 FDI_SET (format - 1, FMTDIR_ERROR);
266 if (spec.allocated == unnumbered_arg_count)
268 spec.allocated = 2 * spec.allocated + 1;
269 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
271 spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
272 spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
273 unnumbered_arg_count++;
276 else if (isdigit (*format))
278 do format++; while (isdigit (*format));
281 /* Parse precision. */
288 unsigned int precision_number = 0;
292 if (isdigit (*format))
294 const char *f = format;
299 m = 10 * m + (*f - '0');
302 while (isdigit (*f));
309 INVALID_PRECISION_ARGNO_0 (spec.directives);
310 FDI_SET (f, FMTDIR_ERROR);
313 precision_number = m;
318 if (precision_number)
320 /* Numbered argument. */
322 /* Numbered and unnumbered specifications are
324 if (unnumbered_arg_count > 0)
327 INVALID_MIXES_NUMBERED_UNNUMBERED ();
328 FDI_SET (format - 1, FMTDIR_ERROR);
332 if (spec.allocated == spec.numbered_arg_count)
334 spec.allocated = 2 * spec.allocated + 1;
335 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
337 spec.numbered[spec.numbered_arg_count].number = precision_number;
338 spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
339 spec.numbered_arg_count++;
343 /* Unnumbered argument. */
345 /* Numbered and unnumbered specifications are
347 if (spec.numbered_arg_count > 0)
350 INVALID_MIXES_NUMBERED_UNNUMBERED ();
351 FDI_SET (format - 1, FMTDIR_ERROR);
355 if (spec.allocated == unnumbered_arg_count)
357 spec.allocated = 2 * spec.allocated + 1;
358 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
360 spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
361 spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
362 unnumbered_arg_count++;
365 else if (isdigit (*format))
367 do format++; while (isdigit (*format));
374 if (*format == 'h' || *format == 'l' || *format == 'L')
388 case 'i': case 'd': case 'o': case 'u': case 'x': case 'X':
391 case 'e': case 'E': case 'f': case 'g': case 'G':
403 *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
404 FDI_SET (format - 1, FMTDIR_ERROR);
425 *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
426 FDI_SET (format - 1, FMTDIR_ERROR);
431 INVALID_CONVERSION_SPECIFIER (spec.directives,
433 FDI_SET (format, FMTDIR_ERROR);
443 *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
444 FDI_SET (format - 1, FMTDIR_ERROR);
449 xasprintf (_("The directive number %u starts with | but does not end with |."),
451 FDI_SET (format, FMTDIR_ERROR);
459 if (type != FAT_NONE)
463 /* Numbered argument. */
465 /* Numbered and unnumbered specifications are exclusive. */
466 if (unnumbered_arg_count > 0)
468 *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
469 FDI_SET (format - 1, FMTDIR_ERROR);
473 if (spec.allocated == spec.numbered_arg_count)
475 spec.allocated = 2 * spec.allocated + 1;
476 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
478 spec.numbered[spec.numbered_arg_count].number = number;
479 spec.numbered[spec.numbered_arg_count].type = type;
480 spec.numbered_arg_count++;
484 /* Unnumbered argument. */
486 /* Numbered and unnumbered specifications are exclusive. */
487 if (spec.numbered_arg_count > 0)
489 *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
490 FDI_SET (format - 1, FMTDIR_ERROR);
494 if (spec.allocated == unnumbered_arg_count)
496 spec.allocated = 2 * spec.allocated + 1;
497 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
499 spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
500 spec.numbered[unnumbered_arg_count].type = type;
501 unnumbered_arg_count++;
506 FDI_SET (format - 1, FMTDIR_END);
509 /* Convert the unnumbered argument array to numbered arguments. */
510 if (unnumbered_arg_count > 0)
511 spec.numbered_arg_count = unnumbered_arg_count;
512 /* Sort the numbered argument array, and eliminate duplicates. */
513 else if (spec.numbered_arg_count > 1)
518 qsort (spec.numbered, spec.numbered_arg_count,
519 sizeof (struct numbered_arg), numbered_arg_compare);
521 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
523 for (i = j = 0; i < spec.numbered_arg_count; i++)
524 if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
526 enum format_arg_type type1 = spec.numbered[i].type;
527 enum format_arg_type type2 = spec.numbered[j-1].type;
528 enum format_arg_type type_both;
530 if (type1 == type2 || type2 == FAT_ANY)
532 else if (type1 == FAT_ANY)
536 /* Incompatible types. */
537 type_both = FAT_NONE;
540 INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
544 spec.numbered[j-1].type = type_both;
550 spec.numbered[j].number = spec.numbered[i].number;
551 spec.numbered[j].type = spec.numbered[i].type;
555 spec.numbered_arg_count = j;
557 /* *invalid_reason has already been set above. */
561 result = XMALLOC (struct spec);
566 if (spec.numbered != NULL)
567 free (spec.numbered);
572 format_free (void *descr)
574 struct spec *spec = (struct spec *) descr;
576 if (spec->numbered != NULL)
577 free (spec->numbered);
582 format_get_number_of_directives (void *descr)
584 struct spec *spec = (struct spec *) descr;
586 return spec->directives;
590 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
591 formatstring_error_logger_t error_logger,
592 const char *pretty_msgid, const char *pretty_msgstr)
594 struct spec *spec1 = (struct spec *) msgid_descr;
595 struct spec *spec2 = (struct spec *) msgstr_descr;
598 if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
601 unsigned int n1 = spec1->numbered_arg_count;
602 unsigned int n2 = spec2->numbered_arg_count;
604 /* Check the argument names are the same.
605 Both arrays are sorted. We search for the first difference. */
606 for (i = 0, j = 0; i < n1 || j < n2; )
608 int cmp = (i >= n1 ? 1 :
610 spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
611 spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
617 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
618 spec2->numbered[j].number, pretty_msgstr,
628 error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
629 spec1->numbered[i].number, pretty_msgstr);
639 /* Check the argument types are the same. */
641 for (i = 0, j = 0; j < n2; )
643 if (spec1->numbered[i].number == spec2->numbered[j].number)
645 if (spec1->numbered[i].type != spec2->numbered[j].type)
648 error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
649 pretty_msgid, pretty_msgstr,
650 spec2->numbered[j].number);
665 struct formatstring_parser formatstring_boost =
669 format_get_number_of_directives,
677 /* Test program: Print the argument list specification returned by
678 format_parse for strings read from standard input. */
683 format_print (void *descr)
685 struct spec *spec = (struct spec *) descr;
697 for (i = 0; i < spec->numbered_arg_count; i++)
699 unsigned int number = spec->numbered[i].number;
705 for (; last < number; last++)
707 switch (spec->numbered[i].type)
738 size_t line_size = 0;
740 char *invalid_reason;
743 line_len = getline (&line, &line_size, stdin);
746 if (line_len > 0 && line[line_len - 1] == '\n')
747 line[--line_len] = '\0';
749 invalid_reason = NULL;
750 descr = format_parse (line, false, NULL, &invalid_reason);
752 format_print (descr);
755 printf ("%s\n", invalid_reason);
757 free (invalid_reason);
765 * For Emacs M-x compile
767 * 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-boost.c ../gnulib-lib/libgettextlib.la"