2 Copyright (C) 2001-2004 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2002.
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 2, or (at your option)
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, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
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 **invalid_reason
)
99 unsigned int directives
;
100 unsigned int numbered_arg_count
;
101 unsigned int allocated
;
102 struct numbered_arg
*numbered
;
103 unsigned int unnumbered_arg_count
;
107 numbered_arg_count
= 0;
110 unnumbered_arg_count
= 0;
112 for (; *format
!= '\0';)
113 if (*format
++ == '%')
120 /* A complex directive. */
122 enum format_arg_type type
;
124 number
= ++unnumbered_arg_count
;
125 if (isdigit (*format
))
127 const char *f
= format
;
132 m
= 10 * m
+ (*f
- '0');
135 while (isdigit (*f
));
141 *invalid_reason
= INVALID_ARGNO_0 (directives
);
146 --unnumbered_arg_count
;
153 if (*format
== '0' || *format
== '-' || *format
== ' ')
155 else if (*format
== '\'')
160 *invalid_reason
= INVALID_UNTERMINATED_DIRECTIVE ();
170 if (isdigit (*format
))
174 while (isdigit (*format
));
177 /* Parse precision. */
182 if (isdigit (*format
))
186 while (isdigit (*format
));
189 --format
; /* will jump to bad_format */
198 case 'b': case 'd': case 'u': case 'o': case 'x': case 'X':
205 type
= FAT_CHARACTER
;
213 ? INVALID_UNTERMINATED_DIRECTIVE ()
214 : INVALID_CONVERSION_SPECIFIER (directives
, *format
));
218 if (allocated
== numbered_arg_count
)
220 allocated
= 2 * allocated
+ 1;
221 numbered
= (struct numbered_arg
*) xrealloc (numbered
, allocated
* sizeof (struct numbered_arg
));
223 numbered
[numbered_arg_count
].number
= number
;
224 numbered
[numbered_arg_count
].type
= type
;
225 numbered_arg_count
++;
231 /* Sort the numbered argument array, and eliminate duplicates. */
232 if (numbered_arg_count
> 1)
237 qsort (numbered
, numbered_arg_count
,
238 sizeof (struct numbered_arg
), numbered_arg_compare
);
240 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
242 for (i
= j
= 0; i
< numbered_arg_count
; i
++)
243 if (j
> 0 && numbered
[i
].number
== numbered
[j
-1].number
)
245 enum format_arg_type type1
= numbered
[i
].type
;
246 enum format_arg_type type2
= numbered
[j
-1].type
;
247 enum format_arg_type type_both
;
253 /* Incompatible types. */
257 INVALID_INCOMPATIBLE_ARG_TYPES (numbered
[i
].number
);
261 numbered
[j
-1].type
= type_both
;
267 numbered
[j
].number
= numbered
[i
].number
;
268 numbered
[j
].type
= numbered
[i
].type
;
272 numbered_arg_count
= j
;
274 /* *invalid_reason has already been set above. */
278 result
= (struct spec
*) xmalloc (sizeof (struct spec
));
279 result
->directives
= directives
;
280 result
->numbered_arg_count
= numbered_arg_count
;
281 result
->allocated
= allocated
;
282 result
->numbered
= numbered
;
286 if (numbered
!= NULL
)
292 format_free (void *descr
)
294 struct spec
*spec
= (struct spec
*) descr
;
296 if (spec
->numbered
!= NULL
)
297 free (spec
->numbered
);
302 format_get_number_of_directives (void *descr
)
304 struct spec
*spec
= (struct spec
*) descr
;
306 return spec
->directives
;
310 format_check (void *msgid_descr
, void *msgstr_descr
, bool equality
,
311 formatstring_error_logger_t error_logger
,
312 const char *pretty_msgstr
)
314 struct spec
*spec1
= (struct spec
*) msgid_descr
;
315 struct spec
*spec2
= (struct spec
*) msgstr_descr
;
318 if (spec1
->numbered_arg_count
+ spec2
->numbered_arg_count
> 0)
321 unsigned int n1
= spec1
->numbered_arg_count
;
322 unsigned int n2
= spec2
->numbered_arg_count
;
324 /* Check the argument names are the same.
325 Both arrays are sorted. We search for the first difference. */
326 for (i
= 0, j
= 0; i
< n1
|| j
< n2
; )
328 int cmp
= (i
>= n1
? 1 :
330 spec1
->numbered
[i
].number
> spec2
->numbered
[j
].number
? 1 :
331 spec1
->numbered
[i
].number
< spec2
->numbered
[j
].number
? -1 :
337 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"),
338 spec2
->numbered
[j
].number
, pretty_msgstr
);
347 error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
348 spec1
->numbered
[i
].number
, pretty_msgstr
);
358 /* Check the argument types are the same. */
360 for (i
= 0, j
= 0; j
< n2
; )
362 if (spec1
->numbered
[i
].number
== spec2
->numbered
[j
].number
)
364 if (spec1
->numbered
[i
].type
!= spec2
->numbered
[j
].type
)
367 error_logger (_("format specifications in 'msgid' and '%s' for argument %u are not the same"),
368 pretty_msgstr
, spec2
->numbered
[j
].number
);
383 struct formatstring_parser formatstring_php
=
387 format_get_number_of_directives
,
394 /* Test program: Print the argument list specification returned by
395 format_parse for strings read from standard input. */
401 format_print (void *descr
)
403 struct spec
*spec
= (struct spec
*) descr
;
415 for (i
= 0; i
< spec
->numbered_arg_count
; i
++)
417 unsigned int number
= spec
->numbered
[i
].number
;
423 for (; last
< number
; last
++)
425 switch (spec
->numbered
[i
].type
)
453 size_t line_size
= 0;
455 char *invalid_reason
;
458 line_len
= getline (&line
, &line_size
, stdin
);
461 if (line_len
> 0 && line
[line_len
- 1] == '\n')
462 line
[--line_len
] = '\0';
464 invalid_reason
= NULL
;
465 descr
= format_parse (line
, false, &invalid_reason
);
467 format_print (descr
);
470 printf ("%s\n", invalid_reason
);
472 free (invalid_reason
);
480 * For Emacs M-x compile
482 * compile-command: "/bin/sh ../libtool --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../lib -I../intl -DHAVE_CONFIG_H -DTEST format-php.c ../lib/libgettextlib.la"