Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-tools / src / format-python-brace.c
1 /* Python brace format strings.
2    Copyright (C) 2004, 2006-2007, 2013, 2015 Free Software Foundation,
3    Inc.
4    Written by Daiki Ueno <ueno@gnu.org>, 2013.
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 #include <string.h>
26
27 #include "format.h"
28 #include "c-ctype.h"
29 #include "xalloc.h"
30 #include "xvasprintf.h"
31 #include "format-invalid.h"
32 #include "gettext.h"
33
34 #define _(str) gettext (str)
35
36 /* Python brace format strings are defined by PEP3101 together with
37    'format' method of string class.
38    A format string directive here consists of
39      - an opening brace '{',
40      - an identifier [_A-Za-z][_0-9A-Za-z]*|[0-9]+,
41      - an optional getattr ('.') or getitem ('['..']') operator with
42        an identifier as argument,
43      - an optional format specifier starting with ':', with a
44        (unnested) format string as argument,
45      - a closing brace '}'.
46    Brace characters '{' and '}' can be escaped by doubles '{{' and '}}'.
47 */
48
49 struct named_arg
50 {
51   char *name;
52 };
53
54 struct spec
55 {
56   unsigned int directives;
57   unsigned int named_arg_count;
58   unsigned int allocated;
59   struct named_arg *named;
60 };
61
62
63 static bool parse_upto (struct spec *spec, const char **formatp,
64                         bool is_toplevel, char terminator,
65                         bool translated, char *fdi, char **invalid_reason);
66 static void free_named_args (struct spec *spec);
67
68
69 /* All the parse_* functions (except parse_upto) follow the same
70    calling convention.  FORMATP shall point to the beginning of a token.
71    If parsing succeeds, FORMATP will point to the next character after
72    the token, and true is returned.  Otherwise, FORMATP will be
73    unchanged and false is returned.  */
74
75 static bool
76 parse_named_field (struct spec *spec,
77                    const char **formatp, bool translated, char *fdi,
78                    char **invalid_reason)
79 {
80   const char *format = *formatp;
81   char c;
82
83   c = *format;
84   if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
85     {
86       do
87         c = *++format;
88       while ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_'
89              || (c >= '0' && c <= '9'));
90       *formatp = format;
91       return true;
92     }
93   return false;
94 }
95
96 static bool
97 parse_numeric_field (struct spec *spec,
98                      const char **formatp, bool translated, char *fdi,
99                      char **invalid_reason)
100 {
101   const char *format = *formatp;
102   char c;
103
104   c = *format;
105   if (c >= '0' && c <= '9')
106     {
107       do
108         c = *++format;
109       while (c >= '0' && c <= '9');
110       *formatp = format;
111       return true;
112     }
113   return false;
114 }
115
116 static bool
117 parse_directive (struct spec *spec,
118                  const char **formatp, bool is_toplevel,
119                  bool translated, char *fdi, char **invalid_reason)
120 {
121   const char *format = *formatp;
122   const char *const format_start = format;
123   const char *name_start;
124   char c;
125
126   c = *++format;
127   if (c == '{')
128     {
129       *formatp = ++format;
130       return true;
131     }
132
133   name_start = format;
134   if (!parse_named_field (spec, &format, translated, fdi, invalid_reason)
135       && !parse_numeric_field (spec, &format, translated, fdi, invalid_reason))
136     {
137       *invalid_reason =
138         xasprintf (_("In the directive number %u, '%c' cannot start a field name."), spec->directives, *format);
139       FDI_SET (format, FMTDIR_ERROR);
140       return false;
141     }
142
143   c = *format;
144   if (c == '.')
145     {
146       format++;
147       if (!parse_named_field (spec, &format, translated, fdi,
148                               invalid_reason))
149         {
150           *invalid_reason =
151             xasprintf (_("In the directive number %u, '%c' cannot start a getattr argument."), spec->directives, *format);
152           FDI_SET (format, FMTDIR_ERROR);
153           return false;
154         }
155       c = *format;
156     }
157   else if (c == '[')
158     {
159       format++;
160       if (!parse_named_field (spec, &format, translated, fdi,
161                               invalid_reason)
162           && !parse_numeric_field (spec, &format, translated, fdi,
163                                    invalid_reason))
164         {
165           *invalid_reason =
166             xasprintf (_("In the directive number %u, '%c' cannot start a getitem argument."), spec->directives, *format);
167           FDI_SET (format, FMTDIR_ERROR);
168           return false;
169         }
170
171       c = *format++;
172       if (c != ']')
173         {
174           *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
175           FDI_SET (format, FMTDIR_ERROR);
176           return false;
177         }
178       c = *format;
179     }
180
181   if (c == ':')
182     {
183       if (!is_toplevel)
184         {
185           *invalid_reason =
186             xasprintf (_("In the directive number %u, no more nesting is allowed in a format specifier."), spec->directives);
187           FDI_SET (format, FMTDIR_ERROR);
188           return false;
189         }
190
191       /* Format specifiers.  Although a format specifier can be any
192          string in theory, we can only recognize two types of format
193          specifiers below, because otherwise we would need to evaluate
194          Python expressions by ourselves:
195
196            - A nested format directive expanding to the whole string
197            - The Standard Format Specifiers, as described in PEP3101,
198              not including a nested format directive  */
199       format++;
200       if (*format == '{')
201         {
202           /* Nested format directive.  */
203           if (!parse_directive (spec, &format, false, translated, fdi,
204                                 invalid_reason))
205             {
206               /* FDI and INVALID_REASON will be set by a recursive call of
207                  parse_directive.  */
208               return false;
209             }
210
211           if (*format != '}')
212             {
213               *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
214               FDI_SET (format, FMTDIR_ERROR);
215               return false;
216             }
217         }
218       else
219         {
220           /* Standard format specifiers is in the form:
221              [[fill]align][sign][#][0][minimumwidth][.precision][type]  */
222
223           /* Look ahead two characters to skip [[fill]align].  */
224           int c1, c2;
225
226           c1 = format[0];
227           c2 = format[1];
228
229           if (c2 == '<' || c2 == '>' || c2 == '=' || c2 == '^')
230             format += 2;
231           else if (c1 == '<' || c1 == '>' || c1 == '=' || c1 == '^')
232             format++;
233           if (*format == '+' || *format == '-' || *format == ' ')
234             format++;
235           if (*format == '#')
236             format++;
237           if (*format == '0')
238             format++;
239           while (c_isdigit (*format))
240             format++;
241           if (*format == '.')
242             {
243               format++;
244               while (c_isdigit (*format))
245                 format++;
246             }
247           switch (*format)
248             {
249             case 'b': case 'c': case 'd': case 'o': case 'x': case 'X':
250             case 'n':
251             case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
252             case '%':
253               format++;
254               break;
255             default:
256               break;
257             }
258           if (*format != '}')
259             {
260               *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
261               FDI_SET (format, FMTDIR_ERROR);
262               return false;
263             }
264         }
265       c = *format;
266     }
267
268   if (c != '}')
269     {
270       *invalid_reason =
271         xasprintf (_("In the directive number %u, there is an unterminated format directive."), spec->directives);
272       FDI_SET (format, FMTDIR_ERROR);
273       return false;
274     }
275
276   if (is_toplevel)
277     {
278       char *name;
279       size_t n = format - name_start;
280
281       FDI_SET (name_start - 1, FMTDIR_START);
282
283       name = XNMALLOC (n + 1, char);
284       memcpy (name, name_start, n);
285       name[n] = '\0';
286
287       spec->directives++;
288
289       if (spec->allocated == spec->named_arg_count)
290         {
291           spec->allocated = 2 * spec->allocated + 1;
292           spec->named = (struct named_arg *) xrealloc (spec->named, spec->allocated * sizeof (struct named_arg));
293         }
294       spec->named[spec->named_arg_count].name = name;
295       spec->named_arg_count++;
296
297       FDI_SET (format, FMTDIR_END);
298     }
299
300   *formatp = ++format;
301   return true;
302 }
303
304 static bool
305 parse_upto (struct spec *spec,
306             const char **formatp, bool is_toplevel, char terminator,
307             bool translated, char *fdi, char **invalid_reason)
308 {
309   const char *format = *formatp;
310
311   for (; *format != terminator && *format != '\0';)
312     {
313       if (*format == '{')
314         {
315           if (!parse_directive (spec, &format, is_toplevel, translated, fdi,
316                                 invalid_reason))
317             return false;
318         }
319       else
320         format++;
321     }
322
323   *formatp = format;
324   return true;
325 }
326
327 static int
328 named_arg_compare (const void *p1, const void *p2)
329 {
330   return strcmp (((const struct named_arg *) p1)->name,
331                  ((const struct named_arg *) p2)->name);
332 }
333
334 static void *
335 format_parse (const char *format, bool translated, char *fdi,
336               char **invalid_reason)
337 {
338   struct spec spec;
339   struct spec *result;
340
341   spec.directives = 0;
342   spec.named_arg_count = 0;
343   spec.allocated = 0;
344   spec.named = NULL;
345
346   if (!parse_upto (&spec, &format, true, '\0', translated, fdi, invalid_reason))
347     {
348       free_named_args (&spec);
349       return NULL;
350     }
351
352   /* Sort the named argument array, and eliminate duplicates.  */
353   if (spec.named_arg_count > 1)
354     {
355       unsigned int i, j;
356
357       qsort (spec.named, spec.named_arg_count, sizeof (struct named_arg),
358              named_arg_compare);
359
360       /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
361       for (i = j = 0; i < spec.named_arg_count; i++)
362         if (j > 0 && strcmp (spec.named[i].name, spec.named[j-1].name) == 0)
363           free (spec.named[i].name);
364         else
365           {
366             if (j < i)
367               spec.named[j].name = spec.named[i].name;
368             j++;
369           }
370       spec.named_arg_count = j;
371     }
372
373   result = XMALLOC (struct spec);
374   *result = spec;
375   return result;
376 }
377
378 static void
379 free_named_args (struct spec *spec)
380 {
381   if (spec->named != NULL)
382     {
383       unsigned int i;
384       for (i = 0; i < spec->named_arg_count; i++)
385         free (spec->named[i].name);
386       free (spec->named);
387     }
388 }
389
390 static void
391 format_free (void *descr)
392 {
393   struct spec *spec = (struct spec *) descr;
394
395   free_named_args (spec);
396   free (spec);
397 }
398
399 static int
400 format_get_number_of_directives (void *descr)
401 {
402   struct spec *spec = (struct spec *) descr;
403
404   return spec->directives;
405 }
406
407 static bool
408 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
409               formatstring_error_logger_t error_logger,
410               const char *pretty_msgid, const char *pretty_msgstr)
411 {
412   struct spec *spec1 = (struct spec *) msgid_descr;
413   struct spec *spec2 = (struct spec *) msgstr_descr;
414   bool err = false;
415
416   if (spec1->named_arg_count + spec2->named_arg_count > 0)
417     {
418       unsigned int i, j;
419       unsigned int n1 = spec1->named_arg_count;
420       unsigned int n2 = spec2->named_arg_count;
421
422       /* Check the argument names in spec1 are contained in those of spec2.
423          Both arrays are sorted.  We search for the differences.  */
424       for (i = 0, j = 0; i < n1 || j < n2; )
425         {
426           int cmp = (i >= n1 ? 1 :
427                      j >= n2 ? -1 :
428                      strcmp (spec1->named[i].name, spec2->named[j].name));
429
430           if (cmp > 0)
431             {
432               if (equality)
433                 {
434                   if (error_logger)
435                     error_logger (_("a format specification for argument '%s' doesn't exist in '%s'"),
436                                   spec2->named[i].name, pretty_msgid);
437                   err = true;
438                   break;
439                 }
440               else
441                 j++;
442             }
443           else if (cmp < 0)
444             {
445               if (equality)
446                 {
447                   if (error_logger)
448                     error_logger (_("a format specification for argument '%s' doesn't exist in '%s'"),
449                                   spec1->named[i].name, pretty_msgstr);
450                   err = true;
451                   break;
452                 }
453               else
454                 i++;
455             }
456           else
457             j++, i++;
458         }
459     }
460
461   return err;
462 }
463
464
465 struct formatstring_parser formatstring_python_brace =
466 {
467   format_parse,
468   format_free,
469   format_get_number_of_directives,
470   NULL,
471   format_check
472 };
473
474
475 #ifdef TEST
476
477 /* Test program: Print the argument list specification returned by
478    format_parse for strings read from standard input.  */
479
480 #include <stdio.h>
481
482 static void
483 format_print (void *descr)
484 {
485   struct spec *spec = (struct spec *) descr;
486   unsigned int i;
487
488   if (spec == NULL)
489     {
490       printf ("INVALID");
491       return;
492     }
493
494   printf ("{");
495   for (i = 0; i < spec->named_arg_count; i++)
496     {
497       if (i > 0)
498         printf (", ");
499       printf ("'%s'", spec->named[i].name);
500     }
501   printf ("}");
502 }
503
504 int
505 main ()
506 {
507   for (;;)
508     {
509       char *line = NULL;
510       size_t line_size = 0;
511       int line_len;
512       char *invalid_reason;
513       void *descr;
514
515       line_len = getline (&line, &line_size, stdin);
516       if (line_len < 0)
517         break;
518       if (line_len > 0 && line[line_len - 1] == '\n')
519         line[--line_len] = '\0';
520
521       invalid_reason = NULL;
522       descr = format_parse (line, false, NULL, &invalid_reason);
523
524       format_print (descr);
525       printf ("\n");
526       if (descr == NULL)
527         printf ("%s\n", invalid_reason);
528
529       free (invalid_reason);
530       free (line);
531     }
532
533   return 0;
534 }
535
536 /*
537  * For Emacs M-x compile
538  * Local Variables:
539  * 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-python-brace.c ../gnulib-lib/libgettextlib.la"
540  * End:
541  */
542
543 #endif /* TEST */