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)
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. */
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 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.
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,
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
53 - is finished by a case-insensitive specifier. If no index was
54 specified, it reads an argument; otherwise is uses the index-th
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.
68 FAT_INTEGER
, /* integer */
69 FAT_INTEGER64
, /* integer, int64 */
70 FAT_FLOAT
, /* extended */
71 FAT_STRING
, /* string, char, pchar, ansistring */
78 enum format_arg_type type
;
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'.) */
93 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
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);
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
;
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 */
123 numbered_arg_count
= 0;
126 unnumbered_arg_count
= 0;
128 for (; *format
!= '\0';)
129 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
;
148 m
= 10 * m
+ (*f
- '0');
151 while (isdigit (*f
));
156 main_arg
= index_numbered
;
160 else if (*format
== '*')
162 if (format
[1] == ':')
164 main_arg
= index_unknown
;
174 if (isdigit (*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
++;
196 /* Parse precision. */
201 if (isdigit (*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
++;
223 --format
; /* will jump to bad_format */
226 switch (c_tolower (*format
))
229 type
= FAT_INTEGER64
;
231 case 'e': case 'f': case 'g': case 'n': case 'm':
246 ? INVALID_UNTERMINATED_DIRECTIVE ()
247 : INVALID_CONVERSION_SPECIFIER (directives
, *format
));
251 if (allocated
== numbered_arg_count
)
253 allocated
= 2 * allocated
+ 1;
254 numbered
= (struct numbered_arg
*) xrealloc (numbered
, allocated
* sizeof (struct numbered_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
++;
264 numbered
[numbered_arg_count
].number
= main_number
;
265 numbered
[numbered_arg_count
].type
= type
;
268 numbered
[numbered_arg_count
].number
= unnumbered_arg_count
;
269 numbered
[numbered_arg_count
].type
= FAT_INTEGER
;
270 unnumbered_arg_count
++;
275 numbered_arg_count
++;
281 /* Sort the numbered argument array, and eliminate duplicates. */
282 if (numbered_arg_count
> 1)
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. */
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
;
301 else if ((type1
== FAT_INTEGER
&& type2
== FAT_INTEGER64
)
302 || (type1
== FAT_INTEGER64
&& type2
== FAT_INTEGER
))
303 type_both
= FAT_INTEGER
;
306 /* Incompatible types. */
310 INVALID_INCOMPATIBLE_ARG_TYPES (numbered
[i
].number
);
314 numbered
[j
-1].type
= type_both
;
320 numbered
[j
].number
= numbered
[i
].number
;
321 numbered
[j
].type
= numbered
[i
].type
;
325 numbered_arg_count
= j
;
327 /* *invalid_reason has already been set above. */
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
;
339 if (numbered
!= NULL
)
345 format_free (void *descr
)
347 struct spec
*spec
= (struct spec
*) descr
;
349 if (spec
->numbered
!= NULL
)
350 free (spec
->numbered
);
355 format_get_number_of_directives (void *descr
)
357 struct spec
*spec
= (struct spec
*) descr
;
359 return spec
->directives
;
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
;
371 if (spec1
->numbered_arg_count
+ spec2
->numbered_arg_count
> 0)
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 :
383 spec1
->numbered
[i
].number
> spec2
->numbered
[j
].number
? 1 :
384 spec1
->numbered
[i
].number
< spec2
->numbered
[j
].number
? -1 :
390 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"),
391 spec2
->numbered
[j
].number
, pretty_msgstr
);
400 error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
401 spec1
->numbered
[i
].number
, pretty_msgstr
);
411 /* Check the argument types are the same. */
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
)
420 error_logger (_("format specifications in 'msgid' and '%s' for argument %u are not the same"),
421 pretty_msgstr
, spec2
->numbered
[j
].number
);
436 struct formatstring_parser formatstring_pascal
=
440 format_get_number_of_directives
,
447 /* Test program: Print the argument list specification returned by
448 format_parse for strings read from standard input. */
454 format_print (void *descr
)
456 struct spec
*spec
= (struct spec
*) descr
;
468 for (i
= 0; i
< spec
->numbered_arg_count
; i
++)
470 unsigned int number
= spec
->numbered
[i
].number
;
476 for (; last
< number
; last
++)
478 switch (spec
->numbered
[i
].type
)
509 size_t line_size
= 0;
511 char *invalid_reason
;
514 line_len
= getline (&line
, &line_size
, stdin
);
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
);
526 printf ("%s\n", invalid_reason
);
528 free (invalid_reason
);
536 * For Emacs M-x compile
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"