Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-tools / src / format-pascal.c
1 /* Object Pascal format strings.
2    Copyright (C) 2001-2004, 2006-2007, 2009-2010, 2015 Free Software
3    Foundation, Inc.
4    Written by Bruno Haible <haible@clisp.cons.org>, 2001.
5
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.
10
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.
15
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/>.  */
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 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.
41
42    A directive
43    - starts with '%',
44    - either
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
54          sequence,
55        - is finished by a case-insensitive specifier. If no index was
56          specified, it reads an argument; otherwise is uses the index-th
57          argument, 0-based.
58          - 'd', 'u', 'x', needs an 'integer' or 'int64' or 'qword' argument,
59          - 'e', 'f', 'g', 'n', 'm', need an 'extended' or 'currency' floating-
60            point argument,
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.
67  */
68
69 enum format_arg_type
70 {
71   FAT_INTEGER,         /* integer, int64, qword */
72   FAT_FLOAT,           /* extended, currency */
73   FAT_STRING,          /* string, char, pchar, widestring, widechar, pwidechar,
74                           ansistring */
75   FAT_POINTER
76 };
77
78 struct numbered_arg
79 {
80   unsigned int number;
81   enum format_arg_type type;
82 };
83
84 struct spec
85 {
86   unsigned int directives;
87   unsigned int numbered_arg_count;
88   unsigned int allocated;
89   struct numbered_arg *numbered;
90 };
91
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'.)  */
95 #undef isdigit
96 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
97
98
99 static int
100 numbered_arg_compare (const void *p1, const void *p2)
101 {
102   unsigned int n1 = ((const struct numbered_arg *) p1)->number;
103   unsigned int n2 = ((const struct numbered_arg *) p2)->number;
104
105   return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
106 }
107
108 static void *
109 format_parse (const char *format, bool translated, char *fdi,
110               char **invalid_reason)
111 {
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;
118   struct spec *result;
119
120   enum arg_index
121   {
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 */
125   };
126
127   directives = 0;
128   numbered_arg_count = 0;
129   allocated = 0;
130   numbered = NULL;
131   unnumbered_arg_count = 0;
132
133   for (; *format != '\0';)
134     if (*format++ == '%')
135       {
136         /* A directive.  */
137         FDI_SET (format - 1, FMTDIR_START);
138         directives++;
139
140         if (*format != '%')
141           {
142             /* A complex directive.  */
143             enum arg_index main_arg = index_unnumbered;
144             unsigned int main_number = 0;
145             enum format_arg_type type;
146
147             if (isdigit (*format) || *format == ':')
148               {
149                 const char *f = format;
150                 unsigned int m = 0;
151
152                 while (isdigit (*f))
153                   {
154                     m = 10 * m + (*f - '0');
155                     f++;
156                   }
157
158                 if (*f == ':')
159                   {
160                     main_number = m;
161                     main_arg = index_numbered;
162                     format = ++f;
163                   }
164               }
165             else if (*format == '*')
166               {
167                 if (format[1] == ':')
168                   {
169                     main_arg = index_unknown;
170                     format += 2;
171                   }
172               }
173
174             /* Parse flags.  */
175             if (*format == '-')
176               format++;
177
178             /* Parse width.  */
179             if (isdigit (*format))
180               {
181                 do
182                   format++;
183                 while (isdigit (*format));
184               }
185             else if (*format == '*')
186               {
187                 /* Unnumbered argument of type FAT_INTEGER.   */
188                 if (allocated == numbered_arg_count)
189                   {
190                     allocated = 2 * allocated + 1;
191                     numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
192                   }
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++;
197
198                 format++;
199               }
200
201             /* Parse precision.  */
202             if (*format == '.')
203               {
204                 format++;
205
206                 if (isdigit (*format))
207                   {
208                     do
209                       format++;
210                     while (isdigit (*format));
211                   }
212                 else if (*format == '*')
213                   {
214                     /* Unnumbered argument of type FAT_INTEGER.   */
215                     if (allocated == unnumbered_arg_count)
216                       {
217                         allocated = 2 * allocated + 1;
218                         numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
219                       }
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++;
224
225                     format++;
226                   }
227                 else
228                   --format;     /* will jump to bad_format */
229               }
230
231             switch (c_tolower (*format))
232               {
233               case 'd': case 'u': case 'x':
234                 type = FAT_INTEGER;
235                 break;
236               case 'e': case 'f': case 'g': case 'n': case 'm':
237                 type = FAT_FLOAT;
238                 break;
239               case 's':
240                 type = FAT_STRING;
241                 break;
242               case 'p':
243                 type = FAT_POINTER;
244                 break;
245               default:
246                 if (*format == '\0')
247                   {
248                     *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
249                     FDI_SET (format - 1, FMTDIR_ERROR);
250                   }
251                 else
252                   {
253                     *invalid_reason =
254                       INVALID_CONVERSION_SPECIFIER (directives, *format);
255                     FDI_SET (format, FMTDIR_ERROR);
256                   }
257                 goto bad_format;
258               }
259
260             if (allocated == numbered_arg_count)
261               {
262                 allocated = 2 * allocated + 1;
263                 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
264               }
265             switch (main_arg)
266               {
267               case index_unnumbered:
268                 numbered[numbered_arg_count].number = unnumbered_arg_count;
269                 numbered[numbered_arg_count].type = type;
270                 unnumbered_arg_count++;
271                 break;
272               case index_numbered:
273                 numbered[numbered_arg_count].number = main_number;
274                 numbered[numbered_arg_count].type = type;
275                 break;
276               case index_unknown:
277                 numbered[numbered_arg_count].number = unnumbered_arg_count;
278                 numbered[numbered_arg_count].type = FAT_INTEGER;
279                 unnumbered_arg_count++;
280                 break;
281               default:
282                 abort ();
283               }
284             numbered_arg_count++;
285           }
286
287         FDI_SET (format, FMTDIR_END);
288
289         format++;
290       }
291
292   /* Sort the numbered argument array, and eliminate duplicates.  */
293   if (numbered_arg_count > 1)
294     {
295       unsigned int i, j;
296       bool err;
297
298       qsort (numbered, numbered_arg_count,
299              sizeof (struct numbered_arg), numbered_arg_compare);
300
301       /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
302       err = false;
303       for (i = j = 0; i < numbered_arg_count; i++)
304         if (j > 0 && numbered[i].number == numbered[j-1].number)
305           {
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;
309
310             if (type1 == type2)
311               type_both = type1;
312             else
313               {
314                 /* Incompatible types.  */
315                 type_both = type1;
316                 if (!err)
317                   *invalid_reason =
318                     INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
319                 err = true;
320               }
321
322             numbered[j-1].type = type_both;
323           }
324         else
325           {
326             if (j < i)
327               {
328                 numbered[j].number = numbered[i].number;
329                 numbered[j].type = numbered[i].type;
330               }
331             j++;
332           }
333       numbered_arg_count = j;
334       if (err)
335         /* *invalid_reason has already been set above.  */
336         goto bad_format;
337     }
338
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;
344   return result;
345
346  bad_format:
347   if (numbered != NULL)
348     free (numbered);
349   return NULL;
350 }
351
352 static void
353 format_free (void *descr)
354 {
355   struct spec *spec = (struct spec *) descr;
356
357   if (spec->numbered != NULL)
358     free (spec->numbered);
359   free (spec);
360 }
361
362 static int
363 format_get_number_of_directives (void *descr)
364 {
365   struct spec *spec = (struct spec *) descr;
366
367   return spec->directives;
368 }
369
370 static bool
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)
374 {
375   struct spec *spec1 = (struct spec *) msgid_descr;
376   struct spec *spec2 = (struct spec *) msgstr_descr;
377   bool err = false;
378
379   if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
380     {
381       unsigned int i, j;
382       unsigned int n1 = spec1->numbered_arg_count;
383       unsigned int n2 = spec2->numbered_arg_count;
384
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; )
388         {
389           int cmp = (i >= n1 ? 1 :
390                      j >= n2 ? -1 :
391                      spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
392                      spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
393                      0);
394
395           if (cmp > 0)
396             {
397               if (error_logger)
398                 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
399                               spec2->numbered[j].number, pretty_msgstr,
400                               pretty_msgid);
401               err = true;
402               break;
403             }
404           else if (cmp < 0)
405             {
406               if (equality)
407                 {
408                   if (error_logger)
409                     error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
410                                   spec1->numbered[i].number, pretty_msgstr);
411                   err = true;
412                   break;
413                 }
414               else
415                 i++;
416             }
417           else
418             j++, i++;
419         }
420       /* Check the argument types are the same.  */
421       if (!err)
422         for (i = 0, j = 0; j < n2; )
423           {
424             if (spec1->numbered[i].number == spec2->numbered[j].number)
425               {
426                 if (spec1->numbered[i].type != spec2->numbered[j].type)
427                   {
428                     if (error_logger)
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);
432                     err = true;
433                     break;
434                   }
435                 j++, i++;
436               }
437             else
438               i++;
439           }
440     }
441
442   return err;
443 }
444
445
446 struct formatstring_parser formatstring_pascal =
447 {
448   format_parse,
449   format_free,
450   format_get_number_of_directives,
451   NULL,
452   format_check
453 };
454
455
456 #ifdef TEST
457
458 /* Test program: Print the argument list specification returned by
459    format_parse for strings read from standard input.  */
460
461 #include <stdio.h>
462
463 static void
464 format_print (void *descr)
465 {
466   struct spec *spec = (struct spec *) descr;
467   unsigned int last;
468   unsigned int i;
469
470   if (spec == NULL)
471     {
472       printf ("INVALID");
473       return;
474     }
475
476   printf ("(");
477   last = 0;
478   for (i = 0; i < spec->numbered_arg_count; i++)
479     {
480       unsigned int number = spec->numbered[i].number;
481
482       if (i > 0)
483         printf (" ");
484       if (number < last)
485         abort ();
486       for (; last < number; last++)
487         printf ("_ ");
488       switch (spec->numbered[i].type)
489         {
490         case FAT_INTEGER:
491           printf ("i");
492           break;
493         case FAT_FLOAT:
494           printf ("f");
495           break;
496         case FAT_STRING:
497           printf ("s");
498           break;
499         case FAT_POINTER:
500           printf ("p");
501           break;
502         default:
503           abort ();
504         }
505       last = number + 1;
506     }
507   printf (")");
508 }
509
510 int
511 main ()
512 {
513   for (;;)
514     {
515       char *line = NULL;
516       size_t line_size = 0;
517       int line_len;
518       char *invalid_reason;
519       void *descr;
520
521       line_len = getline (&line, &line_size, stdin);
522       if (line_len < 0)
523         break;
524       if (line_len > 0 && line[line_len - 1] == '\n')
525         line[--line_len] = '\0';
526
527       invalid_reason = NULL;
528       descr = format_parse (line, false, NULL, &invalid_reason);
529
530       format_print (descr);
531       printf ("\n");
532       if (descr == NULL)
533         printf ("%s\n", invalid_reason);
534
535       free (invalid_reason);
536       free (line);
537     }
538
539   return 0;
540 }
541
542 /*
543  * For Emacs M-x compile
544  * Local Variables:
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"
546  * End:
547  */
548
549 #endif /* TEST */