No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / gettext / gettext-tools / src / format-php.c
blob72a707e2acef6487ce7a15e63f514206a310bbe5
1 /* PHP format strings.
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)
8 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, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
23 #include <stdbool.h>
24 #include <stdlib.h>
26 #include "format.h"
27 #include "c-ctype.h"
28 #include "xalloc.h"
29 #include "xerror.h"
30 #include "format-invalid.h"
31 #include "gettext.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.
38 A directive
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
43 sequence,
44 - is optionally followed by '.' and a precision specification: a nonempty
45 digit sequence,
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.
58 enum format_arg_type
60 FAT_INTEGER,
61 FAT_FLOAT,
62 FAT_CHARACTER,
63 FAT_STRING
66 struct numbered_arg
68 unsigned int number;
69 enum format_arg_type type;
72 struct spec
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'.) */
83 #undef isdigit
84 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
87 static int
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);
96 static void *
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;
104 struct spec *result;
106 directives = 0;
107 numbered_arg_count = 0;
108 allocated = 0;
109 numbered = NULL;
110 unnumbered_arg_count = 0;
112 for (; *format != '\0';)
113 if (*format++ == '%')
115 /* A directive. */
116 directives++;
118 if (*format != '%')
120 /* A complex directive. */
121 unsigned int number;
122 enum format_arg_type type;
124 number = ++unnumbered_arg_count;
125 if (isdigit (*format))
127 const char *f = format;
128 unsigned int m = 0;
132 m = 10 * m + (*f - '0');
133 f++;
135 while (isdigit (*f));
137 if (*f == '$')
139 if (m == 0)
141 *invalid_reason = INVALID_ARGNO_0 (directives);
142 goto bad_format;
144 number = m;
145 format = ++f;
146 --unnumbered_arg_count;
150 /* Parse flags. */
151 for (;;)
153 if (*format == '0' || *format == '-' || *format == ' ')
154 format++;
155 else if (*format == '\'')
157 format++;
158 if (*format == '\0')
160 *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
161 goto bad_format;
163 format++;
165 else
166 break;
169 /* Parse width. */
170 if (isdigit (*format))
173 format++;
174 while (isdigit (*format));
177 /* Parse precision. */
178 if (*format == '.')
180 format++;
182 if (isdigit (*format))
185 format++;
186 while (isdigit (*format));
188 else
189 --format; /* will jump to bad_format */
192 /* Parse size. */
193 if (*format == 'l')
194 format++;
196 switch (*format)
198 case 'b': case 'd': case 'u': case 'o': case 'x': case 'X':
199 type = FAT_INTEGER;
200 break;
201 case 'e': case 'f':
202 type = FAT_FLOAT;
203 break;
204 case 'c':
205 type = FAT_CHARACTER;
206 break;
207 case 's':
208 type = FAT_STRING;
209 break;
210 default:
211 *invalid_reason =
212 (*format == '\0'
213 ? INVALID_UNTERMINATED_DIRECTIVE ()
214 : INVALID_CONVERSION_SPECIFIER (directives, *format));
215 goto bad_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++;
228 format++;
231 /* Sort the numbered argument array, and eliminate duplicates. */
232 if (numbered_arg_count > 1)
234 unsigned int i, j;
235 bool err;
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. */
241 err = false;
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;
249 if (type1 == type2)
250 type_both = type1;
251 else
253 /* Incompatible types. */
254 type_both = type1;
255 if (!err)
256 *invalid_reason =
257 INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
258 err = true;
261 numbered[j-1].type = type_both;
263 else
265 if (j < i)
267 numbered[j].number = numbered[i].number;
268 numbered[j].type = numbered[i].type;
270 j++;
272 numbered_arg_count = j;
273 if (err)
274 /* *invalid_reason has already been set above. */
275 goto bad_format;
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;
283 return result;
285 bad_format:
286 if (numbered != NULL)
287 free (numbered);
288 return NULL;
291 static void
292 format_free (void *descr)
294 struct spec *spec = (struct spec *) descr;
296 if (spec->numbered != NULL)
297 free (spec->numbered);
298 free (spec);
301 static int
302 format_get_number_of_directives (void *descr)
304 struct spec *spec = (struct spec *) descr;
306 return spec->directives;
309 static bool
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;
316 bool err = false;
318 if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
320 unsigned int i, j;
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 :
329 j >= n2 ? -1 :
330 spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
331 spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
334 if (cmp > 0)
336 if (error_logger)
337 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"),
338 spec2->numbered[j].number, pretty_msgstr);
339 err = true;
340 break;
342 else if (cmp < 0)
344 if (equality)
346 if (error_logger)
347 error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
348 spec1->numbered[i].number, pretty_msgstr);
349 err = true;
350 break;
352 else
353 i++;
355 else
356 j++, i++;
358 /* Check the argument types are the same. */
359 if (!err)
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)
366 if (error_logger)
367 error_logger (_("format specifications in 'msgid' and '%s' for argument %u are not the same"),
368 pretty_msgstr, spec2->numbered[j].number);
369 err = true;
370 break;
372 j++, i++;
374 else
375 i++;
379 return err;
383 struct formatstring_parser formatstring_php =
385 format_parse,
386 format_free,
387 format_get_number_of_directives,
388 format_check
392 #ifdef TEST
394 /* Test program: Print the argument list specification returned by
395 format_parse for strings read from standard input. */
397 #include <stdio.h>
398 #include "getline.h"
400 static void
401 format_print (void *descr)
403 struct spec *spec = (struct spec *) descr;
404 unsigned int last;
405 unsigned int i;
407 if (spec == NULL)
409 printf ("INVALID");
410 return;
413 printf ("(");
414 last = 1;
415 for (i = 0; i < spec->numbered_arg_count; i++)
417 unsigned int number = spec->numbered[i].number;
419 if (i > 0)
420 printf (" ");
421 if (number < last)
422 abort ();
423 for (; last < number; last++)
424 printf ("_ ");
425 switch (spec->numbered[i].type)
427 case FAT_INTEGER:
428 printf ("i");
429 break;
430 case FAT_FLOAT:
431 printf ("f");
432 break;
433 case FAT_CHARACTER:
434 printf ("c");
435 break;
436 case FAT_STRING:
437 printf ("s");
438 break;
439 default:
440 abort ();
442 last = number + 1;
444 printf (")");
448 main ()
450 for (;;)
452 char *line = NULL;
453 size_t line_size = 0;
454 int line_len;
455 char *invalid_reason;
456 void *descr;
458 line_len = getline (&line, &line_size, stdin);
459 if (line_len < 0)
460 break;
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);
468 printf ("\n");
469 if (descr == NULL)
470 printf ("%s\n", invalid_reason);
472 free (invalid_reason);
473 free (line);
476 return 0;
480 * For Emacs M-x compile
481 * Local Variables:
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"
483 * End:
486 #endif /* TEST */