1 /* Object Pascal format strings.
2 Copyright (C) 2001-2004, 2006 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
4
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.
9
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.
14
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include <stdbool.h>
24 #include <stdlib.h>
25
26 #include "format.h"
27 #include "c-ctype.h"
28 #include "xalloc.h"
29 #include "xvasprintf.h"
30 #include "format-invalid.h"
31 #include "gettext.h"
32
33 #define _(str) gettext (str)
34
35 /* Object Pascal format strings are usable with the "format" function in the
36 "sysutils" unit. They are implemented in fpc-1.0.4/rtl/objpas/sysstr.inc.
37 Another implementation exists in Borland Delphi. The GNU Pascal's
38 "sysutils" doesn't (yet?) have the "format" function.
39
40 A directive
41 - starts with '%',
42 - either
43 - is finished with '%', or
44 - - is optionally followed by an index specification: '*' (reads an
45 argument, must be of type integer) or a nonempty digit sequence,
46 followed by ':',
47 - is optionally followed by '-', which acts as a flag,
48 - is optionally followed by a width specification: '*' (reads an
49 argument, must be of type integer) or a nonempty digit sequence,
50 - is optionally followed by '.' and a precision specification: '*'
51 (reads an argument, must be of type integer) or a nonempty digit
52 sequence,
53 - is finished by a case-insensitive specifier. If no index was
54 specified, it reads an argument; otherwise is uses the index-th
55 argument, 0-based.
56 - 'd', needs an 'integer' or 'int64' argument,
57 - 'e', 'f', 'g', 'n', 'm', need an 'extended' floating-point argument,
58 - 's', needs a 'string', 'char', 'pchar' or 'ansistring' argument,
59 - 'p', needs a 'pointer' argument,
60 - 'x', needs an integer argument.
61 Numbered and unnumbered argument specifications can be used in the same
62 string. Numbered argument specifications have no influence on the
63 "current argument index", that is incremented each time an argument is read.
64 */
65
66 enum format_arg_type
67 {
68 FAT_INTEGER, /* integer */
69 FAT_INTEGER64, /* integer, int64 */
70 FAT_FLOAT, /* extended */
71 FAT_STRING, /* string, char, pchar, ansistring */
72 FAT_POINTER
73 };
74
75 struct numbered_arg
76 {
77 unsigned int number;
78 enum format_arg_type type;
79 };
80
81 struct spec
82 {
83 unsigned int directives;
84 unsigned int numbered_arg_count;
85 unsigned int allocated;
86 struct numbered_arg *numbered;
87 };
88
89 /* Locale independent test for a decimal digit.
90 Argument can be 'char' or 'unsigned char'. (Whereas the argument of
91 <ctype.h> isdigit must be an 'unsigned char'.) */
92 #undef isdigit
93 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
94
95
96 static int
numbered_arg_compare(const void * p1,const void * p2)97 numbered_arg_compare (const void *p1, const void *p2)
98 {
99 unsigned int n1 = ((const struct numbered_arg *) p1)->number;
100 unsigned int n2 = ((const struct numbered_arg *) p2)->number;
101
102 return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
103 }
104
105 static void *
format_parse(const char * format,bool translated,char ** invalid_reason)106 format_parse (const char *format, bool translated, char **invalid_reason)
107 {
108 unsigned int directives;
109 unsigned int numbered_arg_count;
110 unsigned int allocated;
111 struct numbered_arg *numbered;
112 unsigned int unnumbered_arg_count;
113 struct spec *result;
114
115 enum arg_index
116 {
117 index_numbered, /* index given by a fixed integer */
118 index_unnumbered, /* index given by unnumbered_arg_count++ */
119 index_unknown /* index is only known at run time */
120 };
121
122 directives = 0;
123 numbered_arg_count = 0;
124 allocated = 0;
125 numbered = NULL;
126 unnumbered_arg_count = 0;
127
128 for (; *format != '\0';)
129 if (*format++ == '%')
130 {
131 /* A directive. */
132 directives++;
133
134 if (*format != '%')
135 {
136 /* A complex directive. */
137 enum arg_index main_arg = index_unnumbered;
138 unsigned int main_number = 0;
139 enum format_arg_type type;
140
141 if (isdigit (*format))
142 {
143 const char *f = format;
144 unsigned int m = 0;
145
146 do
147 {
148 m = 10 * m + (*f - '0');
149 f++;
150 }
151 while (isdigit (*f));
152
153 if (*f == ':')
154 {
155 main_number = m;
156 main_arg = index_numbered;
157 format = ++f;
158 }
159 }
160 else if (*format == '*')
161 {
162 if (format[1] == ':')
163 {
164 main_arg = index_unknown;
165 format += 2;
166 }
167 }
168
169 /* Parse flags. */
170 if (*format == '-')
171 format++;
172
173 /* Parse width. */
174 if (isdigit (*format))
175 {
176 do
177 format++;
178 while (isdigit (*format));
179 }
180 else if (*format == '*')
181 {
182 /* Unnumbered argument of type FAT_INTEGER. */
183 if (allocated == numbered_arg_count)
184 {
185 allocated = 2 * allocated + 1;
186 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
187 }
188 numbered[numbered_arg_count].number = unnumbered_arg_count;
189 numbered[numbered_arg_count].type = FAT_INTEGER;
190 numbered_arg_count++;
191 unnumbered_arg_count++;
192
193 format++;
194 }
195
196 /* Parse precision. */
197 if (*format == '.')
198 {
199 format++;
200
201 if (isdigit (*format))
202 {
203 do
204 format++;
205 while (isdigit (*format));
206 }
207 else if (*format == '*')
208 {
209 /* Unnumbered argument of type FAT_INTEGER. */
210 if (allocated == unnumbered_arg_count)
211 {
212 allocated = 2 * allocated + 1;
213 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
214 }
215 numbered[numbered_arg_count].number = unnumbered_arg_count;
216 numbered[numbered_arg_count].type = FAT_INTEGER;
217 numbered_arg_count++;
218 unnumbered_arg_count++;
219
220 format++;
221 }
222 else
223 --format; /* will jump to bad_format */
224 }
225
226 switch (c_tolower (*format))
227 {
228 case 'd':
229 type = FAT_INTEGER64;
230 break;
231 case 'e': case 'f': case 'g': case 'n': case 'm':
232 type = FAT_FLOAT;
233 break;
234 case 's':
235 type = FAT_STRING;
236 break;
237 case 'p':
238 type = FAT_POINTER;
239 break;
240 case 'x':
241 type = FAT_INTEGER;
242 break;
243 default:
244 *invalid_reason =
245 (*format == '\0'
246 ? INVALID_UNTERMINATED_DIRECTIVE ()
247 : INVALID_CONVERSION_SPECIFIER (directives, *format));
248 goto bad_format;
249 }
250
251 if (allocated == numbered_arg_count)
252 {
253 allocated = 2 * allocated + 1;
254 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
255 }
256 switch (main_arg)
257 {
258 case index_unnumbered:
259 numbered[numbered_arg_count].number = unnumbered_arg_count;
260 numbered[numbered_arg_count].type = type;
261 unnumbered_arg_count++;
262 break;
263 case index_numbered:
264 numbered[numbered_arg_count].number = main_number;
265 numbered[numbered_arg_count].type = type;
266 break;
267 case index_unknown:
268 numbered[numbered_arg_count].number = unnumbered_arg_count;
269 numbered[numbered_arg_count].type = FAT_INTEGER;
270 unnumbered_arg_count++;
271 break;
272 default:
273 abort ();
274 }
275 numbered_arg_count++;
276 }
277
278 format++;
279 }
280
281 /* Sort the numbered argument array, and eliminate duplicates. */
282 if (numbered_arg_count > 1)
283 {
284 unsigned int i, j;
285 bool err;
286
287 qsort (numbered, numbered_arg_count,
288 sizeof (struct numbered_arg), numbered_arg_compare);
289
290 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
291 err = false;
292 for (i = j = 0; i < numbered_arg_count; i++)
293 if (j > 0 && numbered[i].number == numbered[j-1].number)
294 {
295 enum format_arg_type type1 = numbered[i].type;
296 enum format_arg_type type2 = numbered[j-1].type;
297 enum format_arg_type type_both;
298
299 if (type1 == type2)
300 type_both = type1;
301 else if ((type1 == FAT_INTEGER && type2 == FAT_INTEGER64)
302 || (type1 == FAT_INTEGER64 && type2 == FAT_INTEGER))
303 type_both = FAT_INTEGER;
304 else
305 {
306 /* Incompatible types. */
307 type_both = type1;
308 if (!err)
309 *invalid_reason =
310 INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
311 err = true;
312 }
313
314 numbered[j-1].type = type_both;
315 }
316 else
317 {
318 if (j < i)
319 {
320 numbered[j].number = numbered[i].number;
321 numbered[j].type = numbered[i].type;
322 }
323 j++;
324 }
325 numbered_arg_count = j;
326 if (err)
327 /* *invalid_reason has already been set above. */
328 goto bad_format;
329 }
330
331 result = (struct spec *) xmalloc (sizeof (struct spec));
332 result->directives = directives;
333 result->numbered_arg_count = numbered_arg_count;
334 result->allocated = allocated;
335 result->numbered = numbered;
336 return result;
337
338 bad_format:
339 if (numbered != NULL)
340 free (numbered);
341 return NULL;
342 }
343
344 static void
format_free(void * descr)345 format_free (void *descr)
346 {
347 struct spec *spec = (struct spec *) descr;
348
349 if (spec->numbered != NULL)
350 free (spec->numbered);
351 free (spec);
352 }
353
354 static int
format_get_number_of_directives(void * descr)355 format_get_number_of_directives (void *descr)
356 {
357 struct spec *spec = (struct spec *) descr;
358
359 return spec->directives;
360 }
361
362 static bool
format_check(void * msgid_descr,void * msgstr_descr,bool equality,formatstring_error_logger_t error_logger,const char * pretty_msgstr)363 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
364 formatstring_error_logger_t error_logger,
365 const char *pretty_msgstr)
366 {
367 struct spec *spec1 = (struct spec *) msgid_descr;
368 struct spec *spec2 = (struct spec *) msgstr_descr;
369 bool err = false;
370
371 if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
372 {
373 unsigned int i, j;
374 unsigned int n1 = spec1->numbered_arg_count;
375 unsigned int n2 = spec2->numbered_arg_count;
376
377 /* Check the argument names are the same.
378 Both arrays are sorted. We search for the first difference. */
379 for (i = 0, j = 0; i < n1 || j < n2; )
380 {
381 int cmp = (i >= n1 ? 1 :
382 j >= n2 ? -1 :
383 spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
384 spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
385 0);
386
387 if (cmp > 0)
388 {
389 if (error_logger)
390 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"),
391 spec2->numbered[j].number, pretty_msgstr);
392 err = true;
393 break;
394 }
395 else if (cmp < 0)
396 {
397 if (equality)
398 {
399 if (error_logger)
400 error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
401 spec1->numbered[i].number, pretty_msgstr);
402 err = true;
403 break;
404 }
405 else
406 i++;
407 }
408 else
409 j++, i++;
410 }
411 /* Check the argument types are the same. */
412 if (!err)
413 for (i = 0, j = 0; j < n2; )
414 {
415 if (spec1->numbered[i].number == spec2->numbered[j].number)
416 {
417 if (spec1->numbered[i].type != spec2->numbered[j].type)
418 {
419 if (error_logger)
420 error_logger (_("format specifications in 'msgid' and '%s' for argument %u are not the same"),
421 pretty_msgstr, spec2->numbered[j].number);
422 err = true;
423 break;
424 }
425 j++, i++;
426 }
427 else
428 i++;
429 }
430 }
431
432 return err;
433 }
434
435
436 struct formatstring_parser formatstring_pascal =
437 {
438 format_parse,
439 format_free,
440 format_get_number_of_directives,
441 NULL,
442 format_check
443 };
444
445
446 #ifdef TEST
447
448 /* Test program: Print the argument list specification returned by
449 format_parse for strings read from standard input. */
450
451 #include <stdio.h>
452 #include "getline.h"
453
454 static void
format_print(void * descr)455 format_print (void *descr)
456 {
457 struct spec *spec = (struct spec *) descr;
458 unsigned int last;
459 unsigned int i;
460
461 if (spec == NULL)
462 {
463 printf ("INVALID");
464 return;
465 }
466
467 printf ("(");
468 last = 0;
469 for (i = 0; i < spec->numbered_arg_count; i++)
470 {
471 unsigned int number = spec->numbered[i].number;
472
473 if (i > 0)
474 printf (" ");
475 if (number < last)
476 abort ();
477 for (; last < number; last++)
478 printf ("_ ");
479 switch (spec->numbered[i].type)
480 {
481 case FAT_INTEGER:
482 printf ("i");
483 break;
484 case FAT_INTEGER64:
485 printf ("I");
486 break;
487 case FAT_FLOAT:
488 printf ("f");
489 break;
490 case FAT_STRING:
491 printf ("s");
492 break;
493 case FAT_POINTER:
494 printf ("p");
495 break;
496 default:
497 abort ();
498 }
499 last = number + 1;
500 }
501 printf (")");
502 }
503
504 int
main()505 main ()
506 {
507 for (;;)
508 {
509 char *line = NULL;
510 size_t line_size = 0;
511 int line_len;
512 char *invalid_reason;
513 void *descr;
514
515 line_len = getline (&line, &line_size, stdin);
516 if (line_len < 0)
517 break;
518 if (line_len > 0 && line[line_len - 1] == '\n')
519 line[--line_len] = '\0';
520
521 invalid_reason = NULL;
522 descr = format_parse (line, false, &invalid_reason);
523
524 format_print (descr);
525 printf ("\n");
526 if (descr == NULL)
527 printf ("%s\n", invalid_reason);
528
529 free (invalid_reason);
530 free (line);
531 }
532
533 return 0;
534 }
535
536 /*
537 * For Emacs M-x compile
538 * Local Variables:
539 * 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-pascal.c ../lib/libgettextlib.la"
540 * End:
541 */
542
543 #endif /* TEST */
544