No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / gettext / gettext-tools / src / format-pascal.c
blobd2afd017361ad4a4e035f23385695be96f3eed3b
1 /* Object Pascal format strings.
2 Copyright (C) 2001-2004 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
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 /* 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.
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.
66 enum format_arg_type
68 FAT_INTEGER, /* integer */
69 FAT_INTEGER64, /* integer, int64 */
70 FAT_FLOAT, /* extended */
71 FAT_STRING, /* string, char, pchar, ansistring */
72 FAT_POINTER
75 struct numbered_arg
77 unsigned int number;
78 enum format_arg_type type;
81 struct spec
83 unsigned int directives;
84 unsigned int numbered_arg_count;
85 unsigned int allocated;
86 struct numbered_arg *numbered;
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)
96 static int
97 numbered_arg_compare (const void *p1, const void *p2)
99 unsigned int n1 = ((const struct numbered_arg *) p1)->number;
100 unsigned int n2 = ((const struct numbered_arg *) p2)->number;
102 return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
105 static void *
106 format_parse (const char *format, bool translated, char **invalid_reason)
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;
115 enum arg_index
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 */
122 directives = 0;
123 numbered_arg_count = 0;
124 allocated = 0;
125 numbered = NULL;
126 unnumbered_arg_count = 0;
128 for (; *format != '\0';)
129 if (*format++ == '%')
131 /* A directive. */
132 directives++;
134 if (*format != '%')
136 /* A complex directive. */
137 enum arg_index main_arg = index_unnumbered;
138 unsigned int main_number = 0;
139 enum format_arg_type type;
141 if (isdigit (*format))
143 const char *f = format;
144 unsigned int m = 0;
148 m = 10 * m + (*f - '0');
149 f++;
151 while (isdigit (*f));
153 if (*f == ':')
155 main_number = m;
156 main_arg = index_numbered;
157 format = ++f;
160 else if (*format == '*')
162 if (format[1] == ':')
164 main_arg = index_unknown;
165 format += 2;
169 /* Parse flags. */
170 if (*format == '-')
171 format++;
173 /* Parse width. */
174 if (isdigit (*format))
177 format++;
178 while (isdigit (*format));
180 else if (*format == '*')
182 /* Unnumbered argument of type FAT_INTEGER. */
183 if (allocated == numbered_arg_count)
185 allocated = 2 * allocated + 1;
186 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
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++;
193 format++;
196 /* Parse precision. */
197 if (*format == '.')
199 format++;
201 if (isdigit (*format))
204 format++;
205 while (isdigit (*format));
207 else if (*format == '*')
209 /* Unnumbered argument of type FAT_INTEGER. */
210 if (allocated == unnumbered_arg_count)
212 allocated = 2 * allocated + 1;
213 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
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++;
220 format++;
222 else
223 --format; /* will jump to bad_format */
226 switch (c_tolower (*format))
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;
251 if (allocated == numbered_arg_count)
253 allocated = 2 * allocated + 1;
254 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
256 switch (main_arg)
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 ();
275 numbered_arg_count++;
278 format++;
281 /* Sort the numbered argument array, and eliminate duplicates. */
282 if (numbered_arg_count > 1)
284 unsigned int i, j;
285 bool err;
287 qsort (numbered, numbered_arg_count,
288 sizeof (struct numbered_arg), numbered_arg_compare);
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)
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;
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
306 /* Incompatible types. */
307 type_both = type1;
308 if (!err)
309 *invalid_reason =
310 INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
311 err = true;
314 numbered[j-1].type = type_both;
316 else
318 if (j < i)
320 numbered[j].number = numbered[i].number;
321 numbered[j].type = numbered[i].type;
323 j++;
325 numbered_arg_count = j;
326 if (err)
327 /* *invalid_reason has already been set above. */
328 goto bad_format;
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;
338 bad_format:
339 if (numbered != NULL)
340 free (numbered);
341 return NULL;
344 static void
345 format_free (void *descr)
347 struct spec *spec = (struct spec *) descr;
349 if (spec->numbered != NULL)
350 free (spec->numbered);
351 free (spec);
354 static int
355 format_get_number_of_directives (void *descr)
357 struct spec *spec = (struct spec *) descr;
359 return spec->directives;
362 static bool
363 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
364 formatstring_error_logger_t error_logger,
365 const char *pretty_msgstr)
367 struct spec *spec1 = (struct spec *) msgid_descr;
368 struct spec *spec2 = (struct spec *) msgstr_descr;
369 bool err = false;
371 if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
373 unsigned int i, j;
374 unsigned int n1 = spec1->numbered_arg_count;
375 unsigned int n2 = spec2->numbered_arg_count;
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; )
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 :
387 if (cmp > 0)
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;
395 else if (cmp < 0)
397 if (equality)
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;
405 else
406 i++;
408 else
409 j++, i++;
411 /* Check the argument types are the same. */
412 if (!err)
413 for (i = 0, j = 0; j < n2; )
415 if (spec1->numbered[i].number == spec2->numbered[j].number)
417 if (spec1->numbered[i].type != spec2->numbered[j].type)
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;
425 j++, i++;
427 else
428 i++;
432 return err;
436 struct formatstring_parser formatstring_pascal =
438 format_parse,
439 format_free,
440 format_get_number_of_directives,
441 format_check
445 #ifdef TEST
447 /* Test program: Print the argument list specification returned by
448 format_parse for strings read from standard input. */
450 #include <stdio.h>
451 #include "getline.h"
453 static void
454 format_print (void *descr)
456 struct spec *spec = (struct spec *) descr;
457 unsigned int last;
458 unsigned int i;
460 if (spec == NULL)
462 printf ("INVALID");
463 return;
466 printf ("(");
467 last = 0;
468 for (i = 0; i < spec->numbered_arg_count; i++)
470 unsigned int number = spec->numbered[i].number;
472 if (i > 0)
473 printf (" ");
474 if (number < last)
475 abort ();
476 for (; last < number; last++)
477 printf ("_ ");
478 switch (spec->numbered[i].type)
480 case FAT_INTEGER:
481 printf ("i");
482 break;
483 case FAT_INTEGER64:
484 printf ("I");
485 break;
486 case FAT_FLOAT:
487 printf ("f");
488 break;
489 case FAT_STRING:
490 printf ("s");
491 break;
492 case FAT_POINTER:
493 printf ("p");
494 break;
495 default:
496 abort ();
498 last = number + 1;
500 printf (")");
504 main ()
506 for (;;)
508 char *line = NULL;
509 size_t line_size = 0;
510 int line_len;
511 char *invalid_reason;
512 void *descr;
514 line_len = getline (&line, &line_size, stdin);
515 if (line_len < 0)
516 break;
517 if (line_len > 0 && line[line_len - 1] == '\n')
518 line[--line_len] = '\0';
520 invalid_reason = NULL;
521 descr = format_parse (line, false, &invalid_reason);
523 format_print (descr);
524 printf ("\n");
525 if (descr == NULL)
526 printf ("%s\n", invalid_reason);
528 free (invalid_reason);
529 free (line);
532 return 0;
536 * For Emacs M-x compile
537 * Local Variables:
538 * 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"
539 * End:
542 #endif /* TEST */