1 /* Boost format strings.
2 Copyright (C) 2001-2004, 2006-2007, 2009, 2015 Free Software
4 Written by Bruno Haible <haible@clisp.cons.org>, 2006.
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 /* Boost format strings are described in
36 boost_1_33_1/libs/format/doc/format.html
38 boost_1_33_1/boost/format/parsing.hpp.
39 A directive (other than '%%')
40 - starts with '%' or '%|'; in the latter case it must end in '|',
41 - is continued either by
42 - 'm%' where m is a positive integer, starting with a nonzero digit;
43 in this case the directive must not have started with '%|'; or
45 - optional: 'm$' where m is a positive integer, starting with a
47 - optional: any of the characters '#', '0', '-', ' ', '+', "'",
49 - optional: a width specification: '*' (reads an argument) or '*m$'
50 or a nonempty digit sequence,
51 - optional: a '.' and a precision specification: '*' (reads an
52 argument) or '*m$' or a nonempty digit sequence,
53 - optional: any of the characters 'h', 'l', 'L',
54 - if the directive started with '%|':
55 an optional specifier and a final '|',
57 a mandatory specifier.
58 If no specifier is given, it needs an argument of any type.
59 The possible specifiers are:
60 - 'c', 'C', that need a character argument,
61 - 's', 'S', that need an argument of any type,
62 - 'i', 'd', 'o', 'u', 'x', 'X', that need an integer argument,
63 - 'e', 'E', 'f', 'g', 'G', that need a floating-point argument,
64 - 'p', that needs a 'void *' argument,
65 - 't', that doesn't need an argument,
66 - 'TX', where X is any character, that doesn't need an argument,
67 - 'n', that needs a pointer to integer.
68 The Boost format string interpreter doesn't actually care about
69 the argument types, but we do, because it increases the likelihood
70 of detecting translator mistakes.
71 Numbered ('%m%' or '%m$' or '*m$') and unnumbered argument specifications
72 cannot be used in the same string.
89 enum format_arg_type type;
94 unsigned int directives;
95 unsigned int numbered_arg_count;
96 unsigned int allocated;
97 struct numbered_arg *numbered;
100 /* Locale independent test for a decimal digit.
101 Argument can be 'char' or 'unsigned char'. (Whereas the argument of
102 <ctype.h> isdigit must be an 'unsigned char'.) */
104 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
108 numbered_arg_compare (const void *p1, const void *p2)
110 unsigned int n1 = ((const struct numbered_arg *) p1)->number;
111 unsigned int n2 = ((const struct numbered_arg *) p2)->number;
113 return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
117 format_parse (const char *format, bool translated, char *fdi,
118 char **invalid_reason)
120 const char *const format_start = format;
122 unsigned int unnumbered_arg_count;
126 spec.numbered_arg_count = 0;
128 spec.numbered = NULL;
129 unnumbered_arg_count = 0;
131 for (; *format != '\0';)
132 if (*format++ == '%')
135 FDI_SET (format - 1, FMTDIR_START);
142 bool brackets = false;
144 unsigned int number = 0;
145 enum format_arg_type type = FAT_NONE;
153 if (isdigit (*format) && *format != '0')
155 const char *f = format;
160 m = 10 * m + (*f - '0');
163 while (isdigit (*f));
165 if ((!brackets && *f == '%') || *f == '$')
167 if (m == 0) /* can happen if m overflows */
169 *invalid_reason = INVALID_ARGNO_0 (spec.directives);
170 FDI_SET (f, FMTDIR_ERROR);
188 if (*format == ' ' || *format == '+' || *format == '-'
189 || *format == '#' || *format == '0' || *format == '\''
190 || *format == '_' || *format == '=' || *format == 'h'
200 unsigned int width_number = 0;
204 if (isdigit (*format))
206 const char *f = format;
211 m = 10 * m + (*f - '0');
214 while (isdigit (*f));
221 INVALID_WIDTH_ARGNO_0 (spec.directives);
222 FDI_SET (f, FMTDIR_ERROR);
232 /* Numbered argument. */
234 /* Numbered and unnumbered specifications are
236 if (unnumbered_arg_count > 0)
239 INVALID_MIXES_NUMBERED_UNNUMBERED ();
240 FDI_SET (format - 1, FMTDIR_ERROR);
244 if (spec.allocated == spec.numbered_arg_count)
246 spec.allocated = 2 * spec.allocated + 1;
247 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
249 spec.numbered[spec.numbered_arg_count].number = width_number;
250 spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
251 spec.numbered_arg_count++;
255 /* Unnumbered argument. */
257 /* Numbered and unnumbered specifications are
259 if (spec.numbered_arg_count > 0)
262 INVALID_MIXES_NUMBERED_UNNUMBERED ();
263 FDI_SET (format - 1, FMTDIR_ERROR);
267 if (spec.allocated == unnumbered_arg_count)
269 spec.allocated = 2 * spec.allocated + 1;
270 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
272 spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
273 spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
274 unnumbered_arg_count++;
277 else if (isdigit (*format))
279 do format++; while (isdigit (*format));
282 /* Parse precision. */
289 unsigned int precision_number = 0;
293 if (isdigit (*format))
295 const char *f = format;
300 m = 10 * m + (*f - '0');
303 while (isdigit (*f));
310 INVALID_PRECISION_ARGNO_0 (spec.directives);
311 FDI_SET (f, FMTDIR_ERROR);
314 precision_number = m;
319 if (precision_number)
321 /* Numbered argument. */
323 /* Numbered and unnumbered specifications are
325 if (unnumbered_arg_count > 0)
328 INVALID_MIXES_NUMBERED_UNNUMBERED ();
329 FDI_SET (format - 1, FMTDIR_ERROR);
333 if (spec.allocated == spec.numbered_arg_count)
335 spec.allocated = 2 * spec.allocated + 1;
336 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
338 spec.numbered[spec.numbered_arg_count].number = precision_number;
339 spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
340 spec.numbered_arg_count++;
344 /* Unnumbered argument. */
346 /* Numbered and unnumbered specifications are
348 if (spec.numbered_arg_count > 0)
351 INVALID_MIXES_NUMBERED_UNNUMBERED ();
352 FDI_SET (format - 1, FMTDIR_ERROR);
356 if (spec.allocated == unnumbered_arg_count)
358 spec.allocated = 2 * spec.allocated + 1;
359 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
361 spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
362 spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
363 unnumbered_arg_count++;
366 else if (isdigit (*format))
368 do format++; while (isdigit (*format));
375 if (*format == 'h' || *format == 'l' || *format == 'L')
389 case 'i': case 'd': case 'o': case 'u': case 'x': case 'X':
392 case 'e': case 'E': case 'f': case 'g': case 'G':
404 *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
405 FDI_SET (format - 1, FMTDIR_ERROR);
426 *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
427 FDI_SET (format - 1, FMTDIR_ERROR);
432 INVALID_CONVERSION_SPECIFIER (spec.directives,
434 FDI_SET (format, FMTDIR_ERROR);
444 *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
445 FDI_SET (format - 1, FMTDIR_ERROR);
450 xasprintf (_("The directive number %u starts with | but does not end with |."),
452 FDI_SET (format, FMTDIR_ERROR);
460 if (type != FAT_NONE)
464 /* Numbered argument. */
466 /* Numbered and unnumbered specifications are exclusive. */
467 if (unnumbered_arg_count > 0)
469 *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
470 FDI_SET (format - 1, FMTDIR_ERROR);
474 if (spec.allocated == spec.numbered_arg_count)
476 spec.allocated = 2 * spec.allocated + 1;
477 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
479 spec.numbered[spec.numbered_arg_count].number = number;
480 spec.numbered[spec.numbered_arg_count].type = type;
481 spec.numbered_arg_count++;
485 /* Unnumbered argument. */
487 /* Numbered and unnumbered specifications are exclusive. */
488 if (spec.numbered_arg_count > 0)
490 *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
491 FDI_SET (format - 1, FMTDIR_ERROR);
495 if (spec.allocated == unnumbered_arg_count)
497 spec.allocated = 2 * spec.allocated + 1;
498 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
500 spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
501 spec.numbered[unnumbered_arg_count].type = type;
502 unnumbered_arg_count++;
507 FDI_SET (format - 1, FMTDIR_END);
510 /* Convert the unnumbered argument array to numbered arguments. */
511 if (unnumbered_arg_count > 0)
512 spec.numbered_arg_count = unnumbered_arg_count;
513 /* Sort the numbered argument array, and eliminate duplicates. */
514 else if (spec.numbered_arg_count > 1)
519 qsort (spec.numbered, spec.numbered_arg_count,
520 sizeof (struct numbered_arg), numbered_arg_compare);
522 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
524 for (i = j = 0; i < spec.numbered_arg_count; i++)
525 if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
527 enum format_arg_type type1 = spec.numbered[i].type;
528 enum format_arg_type type2 = spec.numbered[j-1].type;
529 enum format_arg_type type_both;
531 if (type1 == type2 || type2 == FAT_ANY)
533 else if (type1 == FAT_ANY)
537 /* Incompatible types. */
538 type_both = FAT_NONE;
541 INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
545 spec.numbered[j-1].type = type_both;
551 spec.numbered[j].number = spec.numbered[i].number;
552 spec.numbered[j].type = spec.numbered[i].type;
556 spec.numbered_arg_count = j;
558 /* *invalid_reason has already been set above. */
562 result = XMALLOC (struct spec);
567 if (spec.numbered != NULL)
568 free (spec.numbered);
573 format_free (void *descr)
575 struct spec *spec = (struct spec *) descr;
577 if (spec->numbered != NULL)
578 free (spec->numbered);
583 format_get_number_of_directives (void *descr)
585 struct spec *spec = (struct spec *) descr;
587 return spec->directives;
591 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
592 formatstring_error_logger_t error_logger,
593 const char *pretty_msgid, const char *pretty_msgstr)
595 struct spec *spec1 = (struct spec *) msgid_descr;
596 struct spec *spec2 = (struct spec *) msgstr_descr;
599 if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
602 unsigned int n1 = spec1->numbered_arg_count;
603 unsigned int n2 = spec2->numbered_arg_count;
605 /* Check the argument names are the same.
606 Both arrays are sorted. We search for the first difference. */
607 for (i = 0, j = 0; i < n1 || j < n2; )
609 int cmp = (i >= n1 ? 1 :
611 spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
612 spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
618 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
619 spec2->numbered[j].number, pretty_msgstr,
629 error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
630 spec1->numbered[i].number, pretty_msgstr);
640 /* Check the argument types are the same. */
642 for (i = 0, j = 0; j < n2; )
644 if (spec1->numbered[i].number == spec2->numbered[j].number)
646 if (spec1->numbered[i].type != spec2->numbered[j].type)
649 error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
650 pretty_msgid, pretty_msgstr,
651 spec2->numbered[j].number);
666 struct formatstring_parser formatstring_boost =
670 format_get_number_of_directives,
678 /* Test program: Print the argument list specification returned by
679 format_parse for strings read from standard input. */
684 format_print (void *descr)
686 struct spec *spec = (struct spec *) descr;
698 for (i = 0; i < spec->numbered_arg_count; i++)
700 unsigned int number = spec->numbered[i].number;
706 for (; last < number; last++)
708 switch (spec->numbered[i].type)
739 size_t line_size = 0;
741 char *invalid_reason;
744 line_len = getline (&line, &line_size, stdin);
747 if (line_len > 0 && line[line_len - 1] == '\n')
748 line[--line_len] = '\0';
750 invalid_reason = NULL;
751 descr = format_parse (line, false, NULL, &invalid_reason);
753 format_print (descr);
756 printf ("%s\n", invalid_reason);
758 free (invalid_reason);
766 * For Emacs M-x compile
768 * 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"