1 /* Object Pascal format strings.
2 Copyright (C) 2001-2004, 2006-2007, 2009-2010 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 3 of the License, or
8 (at your option) 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, see <http://www.gnu.org/licenses/>. */
28 #include "xvasprintf.h"
29 #include "format-invalid.h"
32 #define _(str) gettext (str)
34 /* Object Pascal format strings are usable with the "format" function in the
35 "sysutils" unit. They are described in
36 <http://www.freepascal.org/docs-html/rtl/sysutils/format.html>
37 and are implemented in fpc-2.4.0/rtl/objpas/sysutils/sysformt.inc.
38 Another implementation exists in Borland Delphi. The GNU Pascal's
39 "sysutils" doesn't (yet?) have the "format" function.
44 - is finished with '%', or
45 - - is optionally followed by an index specification: '*' (reads an
46 argument, must be of type integer) or a nonempty digit sequence
47 or nothing (equivalent to 0), followed by ':',
48 - is optionally followed by '-', which acts as a flag,
49 - is optionally followed by a width specification: '*' (reads an
50 argument, must be of type integer) or a nonempty digit sequence,
51 - is optionally followed by '.' and a precision specification: '*'
52 (reads an argument, must be of type integer) or a nonempty digit
54 - is finished by a case-insensitive specifier. If no index was
55 specified, it reads an argument; otherwise is uses the index-th
57 - 'd', 'u', 'x', needs an 'integer' or 'int64' or 'qword' argument,
58 - 'e', 'f', 'g', 'n', 'm', need an 'extended' or 'currency' floating-
60 - 's', needs a 'string', 'char', 'pchar', 'widestring', 'widechar',
61 'pwidechar' or 'ansistring' argument,
62 - 'p', needs a 'pointer' argument.
63 Numbered and unnumbered argument specifications can be used in the same
64 string. Numbered argument specifications have no influence on the
65 "current argument index", that is incremented each time an argument is read.
70 FAT_INTEGER, /* integer, int64, qword */
71 FAT_FLOAT, /* extended, currency */
72 FAT_STRING, /* string, char, pchar, widestring, widechar, pwidechar,
80 enum format_arg_type type;
85 unsigned int directives;
86 unsigned int numbered_arg_count;
87 unsigned int allocated;
88 struct numbered_arg *numbered;
91 /* Locale independent test for a decimal digit.
92 Argument can be 'char' or 'unsigned char'. (Whereas the argument of
93 <ctype.h> isdigit must be an 'unsigned char'.) */
95 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
99 numbered_arg_compare (const void *p1, const void *p2)
101 unsigned int n1 = ((const struct numbered_arg *) p1)->number;
102 unsigned int n2 = ((const struct numbered_arg *) p2)->number;
104 return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
108 format_parse (const char *format, bool translated, char *fdi,
109 char **invalid_reason)
111 const char *const format_start = format;
112 unsigned int directives;
113 unsigned int numbered_arg_count;
114 unsigned int allocated;
115 struct numbered_arg *numbered;
116 unsigned int unnumbered_arg_count;
121 index_numbered, /* index given by a fixed integer */
122 index_unnumbered, /* index given by unnumbered_arg_count++ */
123 index_unknown /* index is only known at run time */
127 numbered_arg_count = 0;
130 unnumbered_arg_count = 0;
132 for (; *format != '\0';)
133 if (*format++ == '%')
136 FDI_SET (format - 1, FMTDIR_START);
141 /* A complex directive. */
142 enum arg_index main_arg = index_unnumbered;
143 unsigned int main_number = 0;
144 enum format_arg_type type;
146 if (isdigit (*format) || *format == ':')
148 const char *f = format;
153 m = 10 * m + (*f - '0');
160 main_arg = index_numbered;
164 else if (*format == '*')
166 if (format[1] == ':')
168 main_arg = index_unknown;
178 if (isdigit (*format))
182 while (isdigit (*format));
184 else if (*format == '*')
186 /* Unnumbered argument of type FAT_INTEGER. */
187 if (allocated == numbered_arg_count)
189 allocated = 2 * allocated + 1;
190 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
192 numbered[numbered_arg_count].number = unnumbered_arg_count;
193 numbered[numbered_arg_count].type = FAT_INTEGER;
194 numbered_arg_count++;
195 unnumbered_arg_count++;
200 /* Parse precision. */
205 if (isdigit (*format))
209 while (isdigit (*format));
211 else if (*format == '*')
213 /* Unnumbered argument of type FAT_INTEGER. */
214 if (allocated == unnumbered_arg_count)
216 allocated = 2 * allocated + 1;
217 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
219 numbered[numbered_arg_count].number = unnumbered_arg_count;
220 numbered[numbered_arg_count].type = FAT_INTEGER;
221 numbered_arg_count++;
222 unnumbered_arg_count++;
227 --format; /* will jump to bad_format */
230 switch (c_tolower (*format))
232 case 'd': case 'u': case 'x':
235 case 'e': case 'f': case 'g': case 'n': case 'm':
247 *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
248 FDI_SET (format - 1, FMTDIR_ERROR);
253 INVALID_CONVERSION_SPECIFIER (directives, *format);
254 FDI_SET (format, FMTDIR_ERROR);
259 if (allocated == numbered_arg_count)
261 allocated = 2 * allocated + 1;
262 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
266 case index_unnumbered:
267 numbered[numbered_arg_count].number = unnumbered_arg_count;
268 numbered[numbered_arg_count].type = type;
269 unnumbered_arg_count++;
272 numbered[numbered_arg_count].number = main_number;
273 numbered[numbered_arg_count].type = type;
276 numbered[numbered_arg_count].number = unnumbered_arg_count;
277 numbered[numbered_arg_count].type = FAT_INTEGER;
278 unnumbered_arg_count++;
283 numbered_arg_count++;
286 FDI_SET (format, FMTDIR_END);
291 /* Sort the numbered argument array, and eliminate duplicates. */
292 if (numbered_arg_count > 1)
297 qsort (numbered, numbered_arg_count,
298 sizeof (struct numbered_arg), numbered_arg_compare);
300 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
302 for (i = j = 0; i < numbered_arg_count; i++)
303 if (j > 0 && numbered[i].number == numbered[j-1].number)
305 enum format_arg_type type1 = numbered[i].type;
306 enum format_arg_type type2 = numbered[j-1].type;
307 enum format_arg_type type_both;
313 /* Incompatible types. */
317 INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
321 numbered[j-1].type = type_both;
327 numbered[j].number = numbered[i].number;
328 numbered[j].type = numbered[i].type;
332 numbered_arg_count = j;
334 /* *invalid_reason has already been set above. */
338 result = XMALLOC (struct spec);
339 result->directives = directives;
340 result->numbered_arg_count = numbered_arg_count;
341 result->allocated = allocated;
342 result->numbered = numbered;
346 if (numbered != NULL)
352 format_free (void *descr)
354 struct spec *spec = (struct spec *) descr;
356 if (spec->numbered != NULL)
357 free (spec->numbered);
362 format_get_number_of_directives (void *descr)
364 struct spec *spec = (struct spec *) descr;
366 return spec->directives;
370 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
371 formatstring_error_logger_t error_logger,
372 const char *pretty_msgid, const char *pretty_msgstr)
374 struct spec *spec1 = (struct spec *) msgid_descr;
375 struct spec *spec2 = (struct spec *) msgstr_descr;
378 if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
381 unsigned int n1 = spec1->numbered_arg_count;
382 unsigned int n2 = spec2->numbered_arg_count;
384 /* Check the argument names are the same.
385 Both arrays are sorted. We search for the first difference. */
386 for (i = 0, j = 0; i < n1 || j < n2; )
388 int cmp = (i >= n1 ? 1 :
390 spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
391 spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
397 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
398 spec2->numbered[j].number, pretty_msgstr,
408 error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
409 spec1->numbered[i].number, pretty_msgstr);
419 /* Check the argument types are the same. */
421 for (i = 0, j = 0; j < n2; )
423 if (spec1->numbered[i].number == spec2->numbered[j].number)
425 if (spec1->numbered[i].type != spec2->numbered[j].type)
428 error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
429 pretty_msgid, pretty_msgstr,
430 spec2->numbered[j].number);
445 struct formatstring_parser formatstring_pascal =
449 format_get_number_of_directives,
457 /* Test program: Print the argument list specification returned by
458 format_parse for strings read from standard input. */
463 format_print (void *descr)
465 struct spec *spec = (struct spec *) descr;
477 for (i = 0; i < spec->numbered_arg_count; i++)
479 unsigned int number = spec->numbered[i].number;
485 for (; last < number; last++)
487 switch (spec->numbered[i].type)
515 size_t line_size = 0;
517 char *invalid_reason;
520 line_len = getline (&line, &line_size, stdin);
523 if (line_len > 0 && line[line_len - 1] == '\n')
524 line[--line_len] = '\0';
526 invalid_reason = NULL;
527 descr = format_parse (line, false, NULL, &invalid_reason);
529 format_print (descr);
532 printf ("%s\n", invalid_reason);
534 free (invalid_reason);
542 * For Emacs M-x compile
544 * 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-pascal.c ../gnulib-lib/libgettextlib.la"