1 /* Object Pascal format strings.
2 Copyright (C) 2001-2004, 2006-2007, 2009-2010, 2015 Free Software
4 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
29 #include "xvasprintf.h"
30 #include "format-invalid.h"
33 #define _(str) gettext (str)
35 /* Object Pascal format strings are usable with the "format" function in the
36 "sysutils" unit. They are described in
37 <http://www.freepascal.org/docs-html/rtl/sysutils/format.html>
38 and are implemented in fpc-2.4.0/rtl/objpas/sysutils/sysformt.inc.
39 Another implementation exists in Borland Delphi. The GNU Pascal's
40 "sysutils" doesn't (yet?) have the "format" function.
45 - is finished with '%', or
46 - - is optionally followed by an index specification: '*' (reads an
47 argument, must be of type integer) or a nonempty digit sequence
48 or nothing (equivalent to 0), followed by ':',
49 - is optionally followed by '-', which acts as a flag,
50 - is optionally followed by a width specification: '*' (reads an
51 argument, must be of type integer) or a nonempty digit sequence,
52 - is optionally followed by '.' and a precision specification: '*'
53 (reads an argument, must be of type integer) or a nonempty digit
55 - is finished by a case-insensitive specifier. If no index was
56 specified, it reads an argument; otherwise is uses the index-th
58 - 'd', 'u', 'x', needs an 'integer' or 'int64' or 'qword' argument,
59 - 'e', 'f', 'g', 'n', 'm', need an 'extended' or 'currency' floating-
61 - 's', needs a 'string', 'char', 'pchar', 'widestring', 'widechar',
62 'pwidechar' or 'ansistring' argument,
63 - 'p', needs a 'pointer' argument.
64 Numbered and unnumbered argument specifications can be used in the same
65 string. Numbered argument specifications have no influence on the
66 "current argument index", that is incremented each time an argument is read.
71 FAT_INTEGER, /* integer, int64, qword */
72 FAT_FLOAT, /* extended, currency */
73 FAT_STRING, /* string, char, pchar, widestring, widechar, pwidechar,
81 enum format_arg_type type;
86 unsigned int directives;
87 unsigned int numbered_arg_count;
88 unsigned int allocated;
89 struct numbered_arg *numbered;
92 /* Locale independent test for a decimal digit.
93 Argument can be 'char' or 'unsigned char'. (Whereas the argument of
94 <ctype.h> isdigit must be an 'unsigned char'.) */
96 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
100 numbered_arg_compare (const void *p1, const void *p2)
102 unsigned int n1 = ((const struct numbered_arg *) p1)->number;
103 unsigned int n2 = ((const struct numbered_arg *) p2)->number;
105 return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
109 format_parse (const char *format, bool translated, char *fdi,
110 char **invalid_reason)
112 const char *const format_start = format;
113 unsigned int directives;
114 unsigned int numbered_arg_count;
115 unsigned int allocated;
116 struct numbered_arg *numbered;
117 unsigned int unnumbered_arg_count;
122 index_numbered, /* index given by a fixed integer */
123 index_unnumbered, /* index given by unnumbered_arg_count++ */
124 index_unknown /* index is only known at run time */
128 numbered_arg_count = 0;
131 unnumbered_arg_count = 0;
133 for (; *format != '\0';)
134 if (*format++ == '%')
137 FDI_SET (format - 1, FMTDIR_START);
142 /* A complex directive. */
143 enum arg_index main_arg = index_unnumbered;
144 unsigned int main_number = 0;
145 enum format_arg_type type;
147 if (isdigit (*format) || *format == ':')
149 const char *f = format;
154 m = 10 * m + (*f - '0');
161 main_arg = index_numbered;
165 else if (*format == '*')
167 if (format[1] == ':')
169 main_arg = index_unknown;
179 if (isdigit (*format))
183 while (isdigit (*format));
185 else if (*format == '*')
187 /* Unnumbered argument of type FAT_INTEGER. */
188 if (allocated == numbered_arg_count)
190 allocated = 2 * allocated + 1;
191 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
193 numbered[numbered_arg_count].number = unnumbered_arg_count;
194 numbered[numbered_arg_count].type = FAT_INTEGER;
195 numbered_arg_count++;
196 unnumbered_arg_count++;
201 /* Parse precision. */
206 if (isdigit (*format))
210 while (isdigit (*format));
212 else if (*format == '*')
214 /* Unnumbered argument of type FAT_INTEGER. */
215 if (allocated == unnumbered_arg_count)
217 allocated = 2 * allocated + 1;
218 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
220 numbered[numbered_arg_count].number = unnumbered_arg_count;
221 numbered[numbered_arg_count].type = FAT_INTEGER;
222 numbered_arg_count++;
223 unnumbered_arg_count++;
228 --format; /* will jump to bad_format */
231 switch (c_tolower (*format))
233 case 'd': case 'u': case 'x':
236 case 'e': case 'f': case 'g': case 'n': case 'm':
248 *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
249 FDI_SET (format - 1, FMTDIR_ERROR);
254 INVALID_CONVERSION_SPECIFIER (directives, *format);
255 FDI_SET (format, FMTDIR_ERROR);
260 if (allocated == numbered_arg_count)
262 allocated = 2 * allocated + 1;
263 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
267 case index_unnumbered:
268 numbered[numbered_arg_count].number = unnumbered_arg_count;
269 numbered[numbered_arg_count].type = type;
270 unnumbered_arg_count++;
273 numbered[numbered_arg_count].number = main_number;
274 numbered[numbered_arg_count].type = type;
277 numbered[numbered_arg_count].number = unnumbered_arg_count;
278 numbered[numbered_arg_count].type = FAT_INTEGER;
279 unnumbered_arg_count++;
284 numbered_arg_count++;
287 FDI_SET (format, FMTDIR_END);
292 /* Sort the numbered argument array, and eliminate duplicates. */
293 if (numbered_arg_count > 1)
298 qsort (numbered, numbered_arg_count,
299 sizeof (struct numbered_arg), numbered_arg_compare);
301 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
303 for (i = j = 0; i < numbered_arg_count; i++)
304 if (j > 0 && numbered[i].number == numbered[j-1].number)
306 enum format_arg_type type1 = numbered[i].type;
307 enum format_arg_type type2 = numbered[j-1].type;
308 enum format_arg_type type_both;
314 /* Incompatible types. */
318 INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
322 numbered[j-1].type = type_both;
328 numbered[j].number = numbered[i].number;
329 numbered[j].type = numbered[i].type;
333 numbered_arg_count = j;
335 /* *invalid_reason has already been set above. */
339 result = XMALLOC (struct spec);
340 result->directives = directives;
341 result->numbered_arg_count = numbered_arg_count;
342 result->allocated = allocated;
343 result->numbered = numbered;
347 if (numbered != NULL)
353 format_free (void *descr)
355 struct spec *spec = (struct spec *) descr;
357 if (spec->numbered != NULL)
358 free (spec->numbered);
363 format_get_number_of_directives (void *descr)
365 struct spec *spec = (struct spec *) descr;
367 return spec->directives;
371 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
372 formatstring_error_logger_t error_logger,
373 const char *pretty_msgid, const char *pretty_msgstr)
375 struct spec *spec1 = (struct spec *) msgid_descr;
376 struct spec *spec2 = (struct spec *) msgstr_descr;
379 if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
382 unsigned int n1 = spec1->numbered_arg_count;
383 unsigned int n2 = spec2->numbered_arg_count;
385 /* Check the argument names are the same.
386 Both arrays are sorted. We search for the first difference. */
387 for (i = 0, j = 0; i < n1 || j < n2; )
389 int cmp = (i >= n1 ? 1 :
391 spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
392 spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
398 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
399 spec2->numbered[j].number, pretty_msgstr,
409 error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
410 spec1->numbered[i].number, pretty_msgstr);
420 /* Check the argument types are the same. */
422 for (i = 0, j = 0; j < n2; )
424 if (spec1->numbered[i].number == spec2->numbered[j].number)
426 if (spec1->numbered[i].type != spec2->numbered[j].type)
429 error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
430 pretty_msgid, pretty_msgstr,
431 spec2->numbered[j].number);
446 struct formatstring_parser formatstring_pascal =
450 format_get_number_of_directives,
458 /* Test program: Print the argument list specification returned by
459 format_parse for strings read from standard input. */
464 format_print (void *descr)
466 struct spec *spec = (struct spec *) descr;
478 for (i = 0; i < spec->numbered_arg_count; i++)
480 unsigned int number = spec->numbered[i].number;
486 for (; last < number; last++)
488 switch (spec->numbered[i].type)
516 size_t line_size = 0;
518 char *invalid_reason;
521 line_len = getline (&line, &line_size, stdin);
524 if (line_len > 0 && line[line_len - 1] == '\n')
525 line[--line_len] = '\0';
527 invalid_reason = NULL;
528 descr = format_parse (line, false, NULL, &invalid_reason);
530 format_print (descr);
533 printf ("%s\n", invalid_reason);
535 free (invalid_reason);
543 * For Emacs M-x compile
545 * 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"