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