Imported Upstream version 0.18.3.2
[platform/upstream/gettext.git] / gettext-tools / src / format-javascript.c
1 /* JavaScript format strings.
2    Copyright (C) 2001-2004, 2006-2009, 2013 Free Software Foundation, Inc.
3    Written by Andreas Stricker <andy@knitter.ch>, 2010.
4    It's based on python format module from Bruno Haible.
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 /* Although JavaScript specification itself does not define any format
37    strings, many implementations provide printf-like functions.
38    We provide a permissive parser which accepts commonly used format
39    strings, where:
40
41    A directive
42    - starts with '%',
43    - is optionally followed by any of the characters '0', '-', ' ',
44      or, each of which acts as a flag,
45    - is optionally followed by a width specification: a nonempty digit
46      sequence,
47    - is optionally followed by '.' and a precision specification: a nonempty
48      digit sequence,
49    - is finished by a specifier
50        - 's', that needs a string argument,
51        - 'b', 'd', 'u', 'o', 'x', 'X', that need an integer argument,
52        - 'f', that need a floating-point argument,
53        - 'c', that needs a character argument.
54        - 'j', that needs an argument of any type.
55    Additionally there is the directive '%%', which takes no argument.  */
56
57 enum format_arg_type
58 {
59   FAT_NONE,
60   FAT_ANY,
61   FAT_CHARACTER,
62   FAT_STRING,
63   FAT_INTEGER,
64   FAT_FLOAT
65 };
66
67 struct spec
68 {
69   unsigned int directives;
70   unsigned int format_args_count;
71   unsigned int allocated;
72   enum format_arg_type *format_args;
73 };
74
75 /* Locale independent test for a decimal digit.
76    Argument can be  'char' or 'unsigned char'.  (Whereas the argument of
77    <ctype.h> isdigit must be an 'unsigned char'.)  */
78 #undef isdigit
79 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
80
81
82 static void *
83 format_parse (const char *format, bool translated, char *fdi,
84               char **invalid_reason)
85 {
86   const char *const format_start = format;
87   struct spec spec;
88   struct spec *result;
89
90   spec.directives = 0;
91   spec.format_args_count = 0;
92   spec.allocated = 0;
93   spec.format_args = NULL;
94
95   for (; *format != '\0';)
96     if (*format++ == '%')
97       {
98         /* A directive.  */
99         enum format_arg_type type;
100
101         FDI_SET (format - 1, FMTDIR_START);
102         spec.directives++;
103
104         while (*format == '-' || *format == '+' || *format == ' '
105                || *format == '0' || *format == 'I')
106           format++;
107
108         while (isdigit (*format))
109           format++;
110
111         if (*format == '.')
112           {
113             format++;
114
115             while (isdigit (*format))
116               format++;
117           }
118
119         switch (*format)
120           {
121           case '%':
122             type = FAT_NONE;
123             break;
124           case 'c':
125             type = FAT_CHARACTER;
126             break;
127           case 's':
128             type = FAT_STRING;
129             break;
130           case 'b': case 'd': case 'o': case 'x': case 'X':
131             type = FAT_INTEGER;
132             break;
133           case 'f':
134             type = FAT_FLOAT;
135             break;
136           case 'j':
137             type = FAT_ANY;
138             break;
139           default:
140             if (*format == '\0')
141               {
142                 *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
143                 FDI_SET (format - 1, FMTDIR_ERROR);
144               }
145             else
146               {
147                 *invalid_reason =
148                   INVALID_CONVERSION_SPECIFIER (spec.directives, *format);
149                 FDI_SET (format, FMTDIR_ERROR);
150               }
151             goto bad_format;
152           }
153
154         if (*format != '%')
155           {
156             if (spec.allocated == spec.format_args_count)
157               {
158                 spec.allocated = 2 * spec.allocated + 1;
159                 spec.format_args = (enum format_arg_type *) xrealloc (spec.format_args, spec.allocated * sizeof (enum format_arg_type));
160               }
161             spec.format_args[spec.format_args_count] = type;
162             spec.format_args_count++;
163           }
164
165         FDI_SET (format, FMTDIR_END);
166
167         format++;
168       }
169
170   result = XMALLOC (struct spec);
171   *result = spec;
172   return result;
173
174  bad_format:
175   if (spec.format_args != NULL)
176     free (spec.format_args);
177   return NULL;
178 }
179
180 static void
181 format_free (void *descr)
182 {
183   struct spec *spec = (struct spec *) descr;
184
185   if (spec->format_args != NULL)
186     free (spec->format_args);
187   free (spec);
188 }
189
190 static int
191 format_get_number_of_directives (void *descr)
192 {
193   struct spec *spec = (struct spec *) descr;
194
195   return spec->directives;
196 }
197
198 static bool
199 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
200               formatstring_error_logger_t error_logger,
201               const char *pretty_msgid, const char *pretty_msgstr)
202 {
203   struct spec *spec1 = (struct spec *) msgid_descr;
204   struct spec *spec2 = (struct spec *) msgstr_descr;
205   bool err = false;
206
207   if (spec1->format_args_count + spec2->format_args_count > 0)
208     {
209       unsigned int i;
210
211       /* Check the argument types are the same.  */
212       if (spec1->format_args_count != spec2->format_args_count)
213         {
214           if (error_logger)
215             error_logger (_("number of format specifications in '%s' and '%s' does not match"),
216                           pretty_msgid, pretty_msgstr);
217           err = true;
218         }
219       else
220         for (i = 0; i < spec2->format_args_count; i++)
221           if (!(spec1->format_args[i] == spec2->format_args[i]
222                 || (!equality
223                     && (spec1->format_args[i] == FAT_ANY
224                         || spec2->format_args[i] == FAT_ANY))))
225             {
226               if (error_logger)
227                 error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
228                               pretty_msgid, pretty_msgstr, i + 1);
229               err = true;
230             }
231     }
232
233   return err;
234 }
235
236
237 struct formatstring_parser formatstring_javascript =
238 {
239   format_parse,
240   format_free,
241   format_get_number_of_directives,
242   NULL,
243   format_check
244 };
245
246
247 #ifdef TEST
248
249 /* Test program: Print the argument list specification returned by
250    format_parse for strings read from standard input.  */
251
252 #include <stdio.h>
253
254 static void
255 format_print (void *descr)
256 {
257   struct spec *spec = (struct spec *) descr;
258   unsigned int i;
259
260   if (spec == NULL)
261     {
262       printf ("INVALID");
263       return;
264     }
265
266       printf ("(");
267       for (i = 0; i < spec->format_args_count; i++)
268         {
269           if (i > 0)
270             printf (" ");
271           switch (spec->format_args[i])
272             {
273             case FAT_ANY:
274               printf ("*");
275               break;
276             case FAT_CHARACTER:
277               printf ("c");
278               break;
279             case FAT_STRING:
280               printf ("s");
281               break;
282             case FAT_INTEGER:
283               printf ("i");
284               break;
285             case FAT_FLOAT:
286               printf ("f");
287               break;
288             default:
289               abort ();
290             }
291         }
292       printf (")");
293 }
294
295 int
296 main ()
297 {
298   for (;;)
299     {
300       char *line = NULL;
301       size_t line_size = 0;
302       int line_len;
303       char *invalid_reason;
304       void *descr;
305
306       line_len = getline (&line, &line_size, stdin);
307       if (line_len < 0)
308         break;
309       if (line_len > 0 && line[line_len - 1] == '\n')
310         line[--line_len] = '\0';
311
312       invalid_reason = NULL;
313       descr = format_parse (line, false, NULL, &invalid_reason);
314
315       format_print (descr);
316       printf ("\n");
317       if (descr == NULL)
318         printf ("%s\n", invalid_reason);
319
320       free (invalid_reason);
321       free (line);
322     }
323
324   return 0;
325 }
326
327 /*
328  * For Emacs M-x compile
329  * Local Variables:
330  * 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-javascript.c ../gnulib-lib/libgettextlib.la"
331  * End:
332  */
333 #endif /* TEST */