Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-tools / src / format-perl.c
1 /* Perl format strings.
2    Copyright (C) 2004, 2006-2007, 2009, 2015 Free Software Foundation,
3    Inc.
4    Written by Bruno Haible <bruno@clisp.org>, 2003.
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 /* Perl format strings are implemented in function Perl_sv_vcatpvfn in
36    perl-5.8.0/sv.c.
37    A directive
38    - starts with '%' or '%m$' where m is a positive integer starting with a
39      nonzero digit,
40    - is optionally followed by any of the characters '#', '0', '-', ' ', '+',
41      each of which acts as a flag,
42    - is optionally followed by a vector specification: 'v' or '*v' (reads an
43      argument) or '*m$v' where m is a positive integer starting with a nonzero
44      digit,
45    - is optionally followed by a width specification: '*' (reads an argument)
46      or '*m$' where m is a positive integer starting with a nonzero digit or
47      a nonempty digit sequence starting with a nonzero digit,
48    - is optionally followed by '.' and a precision specification: '*' (reads
49      an argument) or '*m$' where m is a positive integer starting with a
50      nonzero digit or a digit sequence,
51    - is optionally followed by a size specifier, one of 'h' 'l' 'll' 'L' 'q'
52      'V' 'I32' 'I64' 'I',
53    - is finished by a specifier
54        - '%', that needs no argument,
55        - 'c', that needs a small integer argument,
56        - 's', that needs a string argument,
57        - '_', that needs a scalar vector argument,
58        - 'p', that needs a pointer argument,
59        - 'i', 'd', 'D', that need an integer argument,
60        - 'u', 'U', 'b', 'o', 'O', 'x', 'X', that need an unsigned integer
61          argument,
62        - 'e', 'E', 'f', 'F', 'g', 'G', that need a floating-point argument,
63        - 'n', that needs a pointer to integer.
64    So there can be numbered argument specifications:
65    - '%m$' for the format string,
66    - '*m$v' for the vector,
67    - '*m$' for the width,
68    - '.*m$' for the precision.
69    Numbered and unnumbered argument specifications can be used in the same
70    string. The effect of '%m$' is to take argument number m, without affecting
71    the current argument number. The current argument number is incremented
72    after processing a directive with an unnumbered argument specification.
73  */
74
75 enum format_arg_type
76 {
77   FAT_NONE              = 0,
78   /* Basic types */
79   FAT_INTEGER           = 1,
80   FAT_DOUBLE            = 2,
81   FAT_CHAR              = 3,
82   FAT_STRING            = 4,
83   FAT_SCALAR_VECTOR     = 5,
84   FAT_POINTER           = 6,
85   FAT_COUNT_POINTER     = 7,
86   /* Flags */
87   FAT_UNSIGNED          = 1 << 3,
88   FAT_SIZE_SHORT        = 1 << 4,
89   FAT_SIZE_V            = 2 << 4,
90   FAT_SIZE_PTR          = 3 << 4,
91   FAT_SIZE_LONG         = 4 << 4,
92   FAT_SIZE_LONGLONG     = 5 << 4,
93   /* Bitmasks */
94   FAT_SIZE_MASK         = (FAT_SIZE_SHORT | FAT_SIZE_V | FAT_SIZE_PTR
95                            | FAT_SIZE_LONG | FAT_SIZE_LONGLONG)
96 };
97 #ifdef __cplusplus
98 typedef int format_arg_type_t;
99 #else
100 typedef enum format_arg_type format_arg_type_t;
101 #endif
102
103 struct numbered_arg
104 {
105   unsigned int number;
106   format_arg_type_t type;
107 };
108
109 struct spec
110 {
111   unsigned int directives;
112   unsigned int numbered_arg_count;
113   unsigned int allocated;
114   struct numbered_arg *numbered;
115 };
116
117 /* Locale independent test for a decimal digit.
118    Argument can be  'char' or 'unsigned char'.  (Whereas the argument of
119    <ctype.h> isdigit must be an 'unsigned char'.)  */
120 #undef isdigit
121 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
122
123 /* Locale independent test for a nonzero decimal digit.  */
124 #define isnonzerodigit(c) ((unsigned int) ((c) - '1') < 9)
125
126
127 static int
128 numbered_arg_compare (const void *p1, const void *p2)
129 {
130   unsigned int n1 = ((const struct numbered_arg *) p1)->number;
131   unsigned int n2 = ((const struct numbered_arg *) p2)->number;
132
133   return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
134 }
135
136 static void *
137 format_parse (const char *format, bool translated, char *fdi,
138               char **invalid_reason)
139 {
140   const char *const format_start = format;
141   unsigned int directives;
142   unsigned int numbered_arg_count;
143   unsigned int allocated;
144   struct numbered_arg *numbered;
145   unsigned int unnumbered_arg_count;
146   struct spec *result;
147
148   directives = 0;
149   numbered_arg_count = 0;
150   unnumbered_arg_count = 0;
151   allocated = 0;
152   numbered = NULL;
153
154   for (; *format != '\0';)
155     if (*format++ == '%')
156       {
157         /* A directive.  */
158         unsigned int number = 0;
159         bool vectorize = false;
160         format_arg_type_t type;
161         format_arg_type_t size;
162
163         FDI_SET (format - 1, FMTDIR_START);
164         directives++;
165
166         if (isnonzerodigit (*format))
167           {
168             const char *f = format;
169             unsigned int m = 0;
170
171             do
172               {
173                 m = 10 * m + (*f - '0');
174                 f++;
175               }
176             while (isdigit (*f));
177
178             if (*f == '$')
179               {
180                 number = m;
181                 format = ++f;
182               }
183           }
184
185         /* Parse flags.  */
186         while (*format == ' ' || *format == '+' || *format == '-'
187                || *format == '#' || *format == '0')
188           format++;
189
190         /* Parse vector.  */
191         if (*format == 'v')
192           {
193             format++;
194             vectorize = true;
195           }
196         else if (*format == '*')
197           {
198             const char *f = format;
199
200             f++;
201             if (*f == 'v')
202               {
203                 format = ++f;
204                 vectorize = true;
205
206                 /* Unnumbered argument.  */
207                 if (allocated == numbered_arg_count)
208                   {
209                     allocated = 2 * allocated + 1;
210                     numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
211                   }
212                 numbered[numbered_arg_count].number = ++unnumbered_arg_count;
213                 numbered[numbered_arg_count].type = FAT_SCALAR_VECTOR; /* or FAT_STRING? */
214                 numbered_arg_count++;
215               }
216             else if (isnonzerodigit (*f))
217               {
218                 unsigned int m = 0;
219
220                 do
221                   {
222                     m = 10 * m + (*f - '0');
223                     f++;
224                   }
225                 while (isdigit (*f));
226
227                 if (*f == '$')
228                   {
229                     f++;
230                     if (*f == 'v')
231                       {
232                         unsigned int vector_number = m;
233
234                         format = ++f;
235                         vectorize = true;
236
237                         /* Numbered argument.  */
238                         /* Note: As of perl-5.8.0, this is not correctly
239                            implemented in perl's sv.c.  */
240                         if (allocated == numbered_arg_count)
241                           {
242                             allocated = 2 * allocated + 1;
243                             numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
244                           }
245                         numbered[numbered_arg_count].number = vector_number;
246                         numbered[numbered_arg_count].type = FAT_SCALAR_VECTOR; /* or FAT_STRING? */
247                         numbered_arg_count++;
248                       }
249                   }
250               }
251           }
252
253         if (vectorize)
254           {
255             /* Numbered or unnumbered argument.  */
256             if (allocated == numbered_arg_count)
257               {
258                 allocated = 2 * allocated + 1;
259                 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
260               }
261             numbered[numbered_arg_count].number = (number ? number : ++unnumbered_arg_count);
262             numbered[numbered_arg_count].type = FAT_SCALAR_VECTOR;
263             numbered_arg_count++;
264           }
265
266         /* Parse width.  */
267         if (*format == '*')
268           {
269             unsigned int width_number = 0;
270
271             format++;
272
273             if (isnonzerodigit (*format))
274               {
275                 const char *f = format;
276                 unsigned int m = 0;
277
278                 do
279                   {
280                     m = 10 * m + (*f - '0');
281                     f++;
282                   }
283                 while (isdigit (*f));
284
285                 if (*f == '$')
286                   {
287                     width_number = m;
288                     format = ++f;
289                   }
290               }
291
292             /* Numbered or unnumbered argument.  */
293             /* Note: As of perl-5.8.0, this is not correctly
294                implemented in perl's sv.c.  */
295             if (allocated == numbered_arg_count)
296               {
297                 allocated = 2 * allocated + 1;
298                 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
299               }
300             numbered[numbered_arg_count].number = (width_number ? width_number : ++unnumbered_arg_count);
301             numbered[numbered_arg_count].type = FAT_INTEGER;
302             numbered_arg_count++;
303           }
304         else if (isnonzerodigit (*format))
305           {
306             do format++; while (isdigit (*format));
307           }
308
309         /* Parse precision.  */
310         if (*format == '.')
311           {
312             format++;
313
314             if (*format == '*')
315               {
316                 unsigned int precision_number = 0;
317
318                 format++;
319
320                 if (isnonzerodigit (*format))
321                   {
322                     const char *f = format;
323                     unsigned int m = 0;
324
325                     do
326                       {
327                         m = 10 * m + (*f - '0');
328                         f++;
329                       }
330                     while (isdigit (*f));
331
332                     if (*f == '$')
333                       {
334                         precision_number = m;
335                         format = ++f;
336                       }
337                   }
338
339                 /* Numbered or unnumbered argument.  */
340                 if (allocated == numbered_arg_count)
341                   {
342                     allocated = 2 * allocated + 1;
343                     numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
344                   }
345                 numbered[numbered_arg_count].number = (precision_number ? precision_number : ++unnumbered_arg_count);
346                 numbered[numbered_arg_count].type = FAT_INTEGER;
347                 numbered_arg_count++;
348               }
349             else
350               {
351                 while (isdigit (*format)) format++;
352               }
353           }
354
355         /* Parse size.  */
356         size = 0;
357         if (*format == 'h')
358           {
359             size = FAT_SIZE_SHORT;
360             format++;
361           }
362         else if (*format == 'l')
363           {
364             if (format[1] == 'l')
365               {
366                 size = FAT_SIZE_LONGLONG;
367                 format += 2;
368               }
369             else
370               {
371                 size = FAT_SIZE_LONG;
372                 format++;
373               }
374           }
375         else if (*format == 'L' || *format == 'q')
376           {
377             size = FAT_SIZE_LONGLONG;
378             format++;
379           }
380         else if (*format == 'V')
381           {
382             size = FAT_SIZE_V;
383             format++;
384           }
385         else if (*format == 'I')
386           {
387             if (format[1] == '6' && format[2] == '4')
388               {
389                 size = FAT_SIZE_LONGLONG;
390                 format += 3;
391               }
392             else if (format[1] == '3' && format[2] == '2')
393               {
394                 size = 0; /* FAT_SIZE_INT */
395                 format += 3;
396               }
397             else
398               {
399                 size = FAT_SIZE_PTR;
400                 format++;
401               }
402           }
403
404         switch (*format)
405           {
406           case '%':
407             type = FAT_NONE;
408             break;
409           case 'c':
410             type = FAT_CHAR;
411             break;
412           case 's':
413             type = FAT_STRING;
414             break;
415           case '_':
416             type = FAT_SCALAR_VECTOR;
417             break;
418           case 'D':
419             type = FAT_INTEGER | FAT_SIZE_V;
420             break;
421           case 'i': case 'd':
422             type = FAT_INTEGER | size;
423             break;
424           case 'U': case 'O':
425             type = FAT_INTEGER | FAT_UNSIGNED | FAT_SIZE_V;
426             break;
427           case 'u': case 'b': case 'o': case 'x': case 'X':
428             type = FAT_INTEGER | FAT_UNSIGNED | size;
429             break;
430           case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
431             if (size == FAT_SIZE_SHORT || size == FAT_SIZE_LONG)
432               {
433                 *invalid_reason =
434                   xasprintf (_("In the directive number %u, the size specifier is incompatible with the conversion specifier '%c'."), directives, *format);
435                 FDI_SET (format, FMTDIR_ERROR);
436                 goto bad_format;
437               }
438             type = FAT_DOUBLE | size;
439             break;
440           case 'p':
441             type = FAT_POINTER;
442             break;
443           case 'n':
444             type = FAT_COUNT_POINTER | size;
445             break;
446           default:
447             if (*format == '\0')
448               {
449                 *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
450                 FDI_SET (format - 1, FMTDIR_ERROR);
451               }
452             else
453               {
454                 *invalid_reason =
455                   INVALID_CONVERSION_SPECIFIER (directives, *format);
456                 FDI_SET (format, FMTDIR_ERROR);
457               }
458             goto bad_format;
459           }
460
461         if (type != FAT_NONE && !vectorize)
462           {
463             /* Numbered or unnumbered argument.  */
464             if (allocated == numbered_arg_count)
465               {
466                 allocated = 2 * allocated + 1;
467                 numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
468               }
469             numbered[numbered_arg_count].number = (number ? number : ++unnumbered_arg_count);
470             numbered[numbered_arg_count].type = type;
471             numbered_arg_count++;
472           }
473
474         FDI_SET (format, FMTDIR_END);
475
476         format++;
477       }
478
479   /* Sort the numbered argument array, and eliminate duplicates.  */
480   if (numbered_arg_count > 1)
481     {
482       unsigned int i, j;
483       bool err;
484
485       qsort (numbered, numbered_arg_count,
486              sizeof (struct numbered_arg), numbered_arg_compare);
487
488       /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
489       err = false;
490       for (i = j = 0; i < numbered_arg_count; i++)
491         if (j > 0 && numbered[i].number == numbered[j-1].number)
492           {
493             format_arg_type_t type1 = numbered[i].type;
494             format_arg_type_t type2 = numbered[j-1].type;
495             format_arg_type_t type_both;
496
497             if (type1 == type2)
498               type_both = type1;
499             else
500               {
501                 /* Incompatible types.  */
502                 type_both = FAT_NONE;
503                 if (!err)
504                   *invalid_reason =
505                     INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
506                 err = true;
507               }
508
509             numbered[j-1].type = type_both;
510           }
511         else
512           {
513             if (j < i)
514               {
515                 numbered[j].number = numbered[i].number;
516                 numbered[j].type = numbered[i].type;
517               }
518             j++;
519           }
520       numbered_arg_count = j;
521       if (err)
522         /* *invalid_reason has already been set above.  */
523         goto bad_format;
524     }
525
526   result = XMALLOC (struct spec);
527   result->directives = directives;
528   result->numbered_arg_count = numbered_arg_count;
529   result->allocated = allocated;
530   result->numbered = numbered;
531   return result;
532
533  bad_format:
534   if (numbered != NULL)
535     free (numbered);
536   return NULL;
537 }
538
539 static void
540 format_free (void *descr)
541 {
542   struct spec *spec = (struct spec *) descr;
543
544   if (spec->numbered != NULL)
545     free (spec->numbered);
546   free (spec);
547 }
548
549 static int
550 format_get_number_of_directives (void *descr)
551 {
552   struct spec *spec = (struct spec *) descr;
553
554   return spec->directives;
555 }
556
557 static bool
558 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
559               formatstring_error_logger_t error_logger,
560               const char *pretty_msgid, const char *pretty_msgstr)
561 {
562   struct spec *spec1 = (struct spec *) msgid_descr;
563   struct spec *spec2 = (struct spec *) msgstr_descr;
564   bool err = false;
565
566   if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
567     {
568       unsigned int i, j;
569       unsigned int n1 = spec1->numbered_arg_count;
570       unsigned int n2 = spec2->numbered_arg_count;
571
572       /* Check the argument names are the same.
573          Both arrays are sorted.  We search for the first difference.  */
574       for (i = 0, j = 0; i < n1 || j < n2; )
575         {
576           int cmp = (i >= n1 ? 1 :
577                      j >= n2 ? -1 :
578                      spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
579                      spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
580                      0);
581
582           if (cmp > 0)
583             {
584               if (error_logger)
585                 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
586                               spec2->numbered[j].number, pretty_msgstr,
587                               pretty_msgid);
588               err = true;
589               break;
590             }
591           else if (cmp < 0)
592             {
593               if (equality)
594                 {
595                   if (error_logger)
596                     error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
597                                   spec1->numbered[i].number, pretty_msgstr);
598                   err = true;
599                   break;
600                 }
601               else
602                 i++;
603             }
604           else
605             j++, i++;
606         }
607       /* Check the argument types are the same.  */
608       if (!err)
609         for (i = 0, j = 0; j < n2; )
610           {
611             if (spec1->numbered[i].number == spec2->numbered[j].number)
612               {
613                 if (spec1->numbered[i].type != spec2->numbered[j].type)
614                   {
615                     if (error_logger)
616                       error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
617                                     pretty_msgid, pretty_msgstr,
618                                     spec2->numbered[j].number);
619                     err = true;
620                     break;
621                   }
622                 j++, i++;
623               }
624             else
625               i++;
626           }
627     }
628
629   return err;
630 }
631
632
633 struct formatstring_parser formatstring_perl =
634 {
635   format_parse,
636   format_free,
637   format_get_number_of_directives,
638   NULL,
639   format_check
640 };
641
642
643 #ifdef TEST
644
645 /* Test program: Print the argument list specification returned by
646    format_parse for strings read from standard input.  */
647
648 #include <stdio.h>
649
650 static void
651 format_print (void *descr)
652 {
653   struct spec *spec = (struct spec *) descr;
654   unsigned int last;
655   unsigned int i;
656
657   if (spec == NULL)
658     {
659       printf ("INVALID");
660       return;
661     }
662
663   printf ("(");
664   last = 1;
665   for (i = 0; i < spec->numbered_arg_count; i++)
666     {
667       unsigned int number = spec->numbered[i].number;
668
669       if (i > 0)
670         printf (" ");
671       if (number < last)
672         abort ();
673       for (; last < number; last++)
674         printf ("_ ");
675       if (spec->numbered[i].type & FAT_UNSIGNED)
676         printf ("[unsigned]");
677       switch (spec->numbered[i].type & FAT_SIZE_MASK)
678         {
679         case 0:
680           break;
681         case FAT_SIZE_SHORT:
682           printf ("[short]");
683           break;
684         case FAT_SIZE_V:
685           printf ("[IV]");
686           break;
687         case FAT_SIZE_PTR:
688           printf ("[PTR]");
689           break;
690         case FAT_SIZE_LONG:
691           printf ("[long]");
692           break;
693         case FAT_SIZE_LONGLONG:
694           printf ("[long long]");
695           break;
696         default:
697           abort ();
698         }
699       switch (spec->numbered[i].type & ~(FAT_UNSIGNED | FAT_SIZE_MASK))
700         {
701         case FAT_INTEGER:
702           printf ("i");
703           break;
704         case FAT_DOUBLE:
705           printf ("f");
706           break;
707         case FAT_CHAR:
708           printf ("c");
709           break;
710         case FAT_STRING:
711           printf ("s");
712           break;
713         case FAT_SCALAR_VECTOR:
714           printf ("sv");
715           break;
716         case FAT_POINTER:
717           printf ("p");
718           break;
719         case FAT_COUNT_POINTER:
720           printf ("n");
721           break;
722         default:
723           abort ();
724         }
725       last = number + 1;
726     }
727   printf (")");
728 }
729
730 int
731 main ()
732 {
733   for (;;)
734     {
735       char *line = NULL;
736       size_t line_size = 0;
737       int line_len;
738       char *invalid_reason;
739       void *descr;
740
741       line_len = getline (&line, &line_size, stdin);
742       if (line_len < 0)
743         break;
744       if (line_len > 0 && line[line_len - 1] == '\n')
745         line[--line_len] = '\0';
746
747       invalid_reason = NULL;
748       descr = format_parse (line, false, NULL, &invalid_reason);
749
750       format_print (descr);
751       printf ("\n");
752       if (descr == NULL)
753         printf ("%s\n", invalid_reason);
754
755       free (invalid_reason);
756       free (line);
757     }
758
759   return 0;
760 }
761
762 /*
763  * For Emacs M-x compile
764  * Local Variables:
765  * 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-perl.c ../gnulib-lib/libgettextlib.la"
766  * End:
767  */
768
769 #endif /* TEST */