2 Copyright (C) 2001-2004, 2006-2007, 2009, 2015 Free Software
4 Written by Bruno Haible <bruno@clisp.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 /* PHP format strings are described in phpdoc-4.0.6, file
36 phpdoc/manual/function.sprintf.html, and are implemented in
37 php-4.1.0/ext/standard/formatted_print.c.
39 - starts with '%' or '%m$' where m is a positive integer,
40 - is optionally followed by any of the characters '0', '-', ' ', or
41 "'<anychar>", each of which acts as a flag,
42 - is optionally followed by a width specification: a nonempty digit
44 - is optionally followed by '.' and a precision specification: a nonempty
46 - is optionally followed by a size specifier 'l', which is ignored,
47 - is finished by a specifier
48 - 's', that needs a string argument,
49 - 'b', 'd', 'u', 'o', 'x', 'X', that need an integer argument,
50 - 'e', 'f', that need a floating-point argument,
51 - 'c', that needs a character argument.
52 Additionally there is the directive '%%', which takes no argument.
53 Numbered and unnumbered argument specifications can be used in the same
54 string. Numbered argument specifications have no influence on the
55 "current argument index", that is incremented each time an argument is read.
69 enum format_arg_type type;
74 unsigned int directives;
75 unsigned int numbered_arg_count;
76 unsigned int allocated;
77 struct numbered_arg *numbered;
80 /* Locale independent test for a decimal digit.
81 Argument can be 'char' or 'unsigned char'. (Whereas the argument of
82 <ctype.h> isdigit must be an 'unsigned char'.) */
84 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
88 numbered_arg_compare (const void *p1, const void *p2)
90 unsigned int n1 = ((const struct numbered_arg *) p1)->number;
91 unsigned int n2 = ((const struct numbered_arg *) p2)->number;
93 return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
97 format_parse (const char *format, bool translated, char *fdi,
98 char **invalid_reason)
100 const char *const format_start = format;
101 unsigned int directives;
102 unsigned int numbered_arg_count;
103 unsigned int allocated;
104 struct numbered_arg *numbered;
105 unsigned int unnumbered_arg_count;
109 numbered_arg_count = 0;
112 unnumbered_arg_count = 0;
114 for (; *format != '\0';)
115 if (*format++ == '%')
118 FDI_SET (format - 1, FMTDIR_START);
123 /* A complex directive. */
125 enum format_arg_type type;
127 number = ++unnumbered_arg_count;
128 if (isdigit (*format))
130 const char *f = format;
135 m = 10 * m + (*f - '0');
138 while (isdigit (*f));
144 *invalid_reason = INVALID_ARGNO_0 (directives);
145 FDI_SET (f, FMTDIR_ERROR);
150 --unnumbered_arg_count;
157 if (*format == '0' || *format == '-' || *format == ' ')
159 else if (*format == '\'')
164 *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
165 FDI_SET (format - 1, FMTDIR_ERROR);
175 if (isdigit (*format))
179 while (isdigit (*format));
182 /* Parse precision. */
187 if (isdigit (*format))
191 while (isdigit (*format));
194 --format; /* will jump to bad_format */
203 case 'b': case 'd': case 'u': case 'o': case 'x': case 'X':
210 type = FAT_CHARACTER;
218 *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
219 FDI_SET (format - 1, FMTDIR_ERROR);
224 INVALID_CONVERSION_SPECIFIER (directives, *format);
225 FDI_SET (format, FMTDIR_ERROR);
230 if (allocated == numbered_arg_count)
232 allocated = 2 * allocated + 1;
233 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
235 numbered[numbered_arg_count].number = number;
236 numbered[numbered_arg_count].type = type;
237 numbered_arg_count++;
240 FDI_SET (format, FMTDIR_END);
245 /* Sort the numbered argument array, and eliminate duplicates. */
246 if (numbered_arg_count > 1)
251 qsort (numbered, numbered_arg_count,
252 sizeof (struct numbered_arg), numbered_arg_compare);
254 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
256 for (i = j = 0; i < numbered_arg_count; i++)
257 if (j > 0 && numbered[i].number == numbered[j-1].number)
259 enum format_arg_type type1 = numbered[i].type;
260 enum format_arg_type type2 = numbered[j-1].type;
261 enum format_arg_type type_both;
267 /* Incompatible types. */
271 INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
275 numbered[j-1].type = type_both;
281 numbered[j].number = numbered[i].number;
282 numbered[j].type = numbered[i].type;
286 numbered_arg_count = j;
288 /* *invalid_reason has already been set above. */
292 result = XMALLOC (struct spec);
293 result->directives = directives;
294 result->numbered_arg_count = numbered_arg_count;
295 result->allocated = allocated;
296 result->numbered = numbered;
300 if (numbered != NULL)
306 format_free (void *descr)
308 struct spec *spec = (struct spec *) descr;
310 if (spec->numbered != NULL)
311 free (spec->numbered);
316 format_get_number_of_directives (void *descr)
318 struct spec *spec = (struct spec *) descr;
320 return spec->directives;
324 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
325 formatstring_error_logger_t error_logger,
326 const char *pretty_msgid, const char *pretty_msgstr)
328 struct spec *spec1 = (struct spec *) msgid_descr;
329 struct spec *spec2 = (struct spec *) msgstr_descr;
332 if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
335 unsigned int n1 = spec1->numbered_arg_count;
336 unsigned int n2 = spec2->numbered_arg_count;
338 /* Check the argument names are the same.
339 Both arrays are sorted. We search for the first difference. */
340 for (i = 0, j = 0; i < n1 || j < n2; )
342 int cmp = (i >= n1 ? 1 :
344 spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
345 spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
351 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
352 spec2->numbered[j].number, pretty_msgstr,
362 error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
363 spec1->numbered[i].number, pretty_msgstr);
373 /* Check the argument types are the same. */
375 for (i = 0, j = 0; j < n2; )
377 if (spec1->numbered[i].number == spec2->numbered[j].number)
379 if (spec1->numbered[i].type != spec2->numbered[j].type)
382 error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
383 pretty_msgid, pretty_msgstr,
384 spec2->numbered[j].number);
399 struct formatstring_parser formatstring_php =
403 format_get_number_of_directives,
411 /* Test program: Print the argument list specification returned by
412 format_parse for strings read from standard input. */
417 format_print (void *descr)
419 struct spec *spec = (struct spec *) descr;
431 for (i = 0; i < spec->numbered_arg_count; i++)
433 unsigned int number = spec->numbered[i].number;
439 for (; last < number; last++)
441 switch (spec->numbered[i].type)
469 size_t line_size = 0;
471 char *invalid_reason;
474 line_len = getline (&line, &line_size, stdin);
477 if (line_len > 0 && line[line_len - 1] == '\n')
478 line[--line_len] = '\0';
480 invalid_reason = NULL;
481 descr = format_parse (line, false, NULL, &invalid_reason);
483 format_print (descr);
486 printf ("%s\n", invalid_reason);
488 free (invalid_reason);
496 * For Emacs M-x compile
498 * 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-php.c ../gnulib-lib/libgettextlib.la"