• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* C format strings.
2    Copyright (C) 2001-2004, 2006-2007, 2009-2010, 2019 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 <https://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 "gettext.h"
30 
31 #define _(str) gettext (str)
32 
33 #include "format-invalid.h"
34 
35 #define INVALID_C99_MACRO(directive_number) \
36   xasprintf (_("In the directive number %u, the token after '<' is not the name of a format specifier macro. The valid macro names are listed in ISO C 99 section 7.8.1."), directive_number)
37 
38 #define INVALID_ANGLE_BRACKET(directive_number) \
39   xasprintf (_("In the directive number %u, the token after '<' is not followed by '>'."), directive_number)
40 
41 #define INVALID_IGNORED_ARGUMENT(referenced_arg, ignored_arg) \
42   xasprintf (_("The string refers to argument number %u but ignores argument number %u."), referenced_arg, ignored_arg)
43 
44 /* Execute statement if memory allocation function returned NULL.  */
45 #define IF_OOM(allocated_ptr, statement)  /* nothing, since we use xalloc.h */
46 
47 /* Specifies whether the system dependent segments in msgid and msgstr have
48    been processed.  This means:
49      - If false, ISO C 99 <inttypes.h> directives are denoted with angle
50        brackets.  If true, they have already been expanded, leading in
51        particular to %I64d directives on native Windows platforms.
52      - If false, the 'I' flag may be present in msgstr (also on platforms
53        other than glibc).  If true, the 'I' directive may be present in msgstr
54        only on glibc >= 2.2 platforms.  */
55 #define SYSDEP_SEGMENTS_PROCESSED false
56 
57 /* Include the bulk of the C format string parsing code.  */
58 #include "format-c-parse.h"
59 
60 static void *
format_parse(const char * format,bool translated,bool objc_extensions,char * fdi,char ** invalid_reason)61 format_parse (const char *format, bool translated, bool objc_extensions,
62               char *fdi, char **invalid_reason)
63 {
64   struct spec result_buf;
65   struct spec *result;
66 
67   result = format_parse_entrails (format, translated, objc_extensions, fdi, invalid_reason, &result_buf);
68 
69   if (result != NULL)
70     {
71       /* Copy the result to a heap-allocated object.  */
72       struct spec *safe_result = XMALLOC (struct spec);
73       *safe_result = *result;
74       result = safe_result;
75     }
76   return result;
77 }
78 
79 static void *
format_c_parse(const char * format,bool translated,char * fdi,char ** invalid_reason)80 format_c_parse (const char *format, bool translated, char *fdi,
81                 char **invalid_reason)
82 {
83   return format_parse (format, translated, false, fdi, invalid_reason);
84 }
85 
86 static void *
format_objc_parse(const char * format,bool translated,char * fdi,char ** invalid_reason)87 format_objc_parse (const char *format, bool translated, char *fdi,
88                    char **invalid_reason)
89 {
90   return format_parse (format, translated, true, fdi, invalid_reason);
91 }
92 
93 static void
format_free(void * descr)94 format_free (void *descr)
95 {
96   struct spec *spec = (struct spec *) descr;
97 
98   if (spec->unnumbered != NULL)
99     free (spec->unnumbered);
100   if (spec->sysdep_directives != NULL)
101     free (spec->sysdep_directives);
102   free (spec);
103 }
104 
105 static bool
format_is_unlikely_intentional(void * descr)106 format_is_unlikely_intentional (void *descr)
107 {
108   struct spec *spec = (struct spec *) descr;
109 
110   return spec->unlikely_intentional;
111 }
112 
113 static int
format_get_number_of_directives(void * descr)114 format_get_number_of_directives (void *descr)
115 {
116   struct spec *spec = (struct spec *) descr;
117 
118   return spec->directives;
119 }
120 
121 static bool
format_check(void * msgid_descr,void * msgstr_descr,bool equality,formatstring_error_logger_t error_logger,const char * pretty_msgid,const char * pretty_msgstr)122 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
123               formatstring_error_logger_t error_logger,
124               const char *pretty_msgid, const char *pretty_msgstr)
125 {
126   struct spec *spec1 = (struct spec *) msgid_descr;
127   struct spec *spec2 = (struct spec *) msgstr_descr;
128   bool err = false;
129   unsigned int i;
130 
131   /* Check the argument types are the same.  */
132   if (equality
133       ? spec1->unnumbered_arg_count != spec2->unnumbered_arg_count
134       : spec1->unnumbered_arg_count < spec2->unnumbered_arg_count)
135     {
136       if (error_logger)
137         error_logger (_("number of format specifications in '%s' and '%s' does not match"),
138                       pretty_msgid, pretty_msgstr);
139       err = true;
140     }
141   else
142     for (i = 0; i < spec2->unnumbered_arg_count; i++)
143       if (spec1->unnumbered[i].type != spec2->unnumbered[i].type)
144         {
145           if (error_logger)
146             error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
147                           pretty_msgid, pretty_msgstr, i + 1);
148           err = true;
149         }
150 
151   return err;
152 }
153 
154 
155 struct formatstring_parser formatstring_c =
156 {
157   format_c_parse,
158   format_free,
159   format_get_number_of_directives,
160   format_is_unlikely_intentional,
161   format_check
162 };
163 
164 
165 struct formatstring_parser formatstring_objc =
166 {
167   format_objc_parse,
168   format_free,
169   format_get_number_of_directives,
170   format_is_unlikely_intentional,
171   format_check
172 };
173 
174 
175 void
get_sysdep_c_format_directives(const char * string,bool translated,struct interval ** intervalsp,size_t * lengthp)176 get_sysdep_c_format_directives (const char *string, bool translated,
177                                 struct interval **intervalsp, size_t *lengthp)
178 {
179   /* Parse the format string with all possible extensions turned on.  (The
180      caller has already verified that the format string is valid for the
181      particular language.)  */
182   char *invalid_reason = NULL;
183   struct spec *descr =
184     (struct spec *)
185     format_parse (string, translated, true, NULL, &invalid_reason);
186 
187   if (descr != NULL && descr->sysdep_directives_count > 0)
188     {
189       unsigned int n = descr->sysdep_directives_count;
190       struct interval *intervals = XNMALLOC (n, struct interval);
191       unsigned int i;
192 
193       for (i = 0; i < n; i++)
194         {
195           intervals[i].startpos = descr->sysdep_directives[2 * i] - string;
196           intervals[i].endpos = descr->sysdep_directives[2 * i + 1] - string;
197         }
198       *intervalsp = intervals;
199       *lengthp = n;
200     }
201   else
202     {
203       *intervalsp = NULL;
204       *lengthp = 0;
205     }
206 
207   if (descr != NULL)
208     format_free (descr);
209   else
210     free (invalid_reason);
211 }
212 
213 
214 #ifdef TEST
215 
216 /* Test program: Print the argument list specification returned by
217    format_parse for strings read from standard input.  */
218 
219 #include <stdio.h>
220 
221 static void
format_print(void * descr)222 format_print (void *descr)
223 {
224   struct spec *spec = (struct spec *) descr;
225   unsigned int i;
226 
227   if (spec == NULL)
228     {
229       printf ("INVALID");
230       return;
231     }
232 
233   printf ("(");
234   for (i = 0; i < spec->unnumbered_arg_count; i++)
235     {
236       if (i > 0)
237         printf (" ");
238       if (spec->unnumbered[i].type & FAT_UNSIGNED)
239         printf ("[unsigned]");
240       switch (spec->unnumbered[i].type & FAT_SIZE_MASK)
241         {
242         case 0:
243           break;
244         case FAT_SIZE_SHORT:
245           printf ("[short]");
246           break;
247         case FAT_SIZE_CHAR:
248           printf ("[char]");
249           break;
250         case FAT_SIZE_LONG:
251           printf ("[long]");
252           break;
253         case FAT_SIZE_LONGLONG:
254           printf ("[long long]");
255           break;
256         case FAT_SIZE_8_T:
257           printf ("[int8_t]");
258           break;
259         case FAT_SIZE_16_T:
260           printf ("[int16_t]");
261           break;
262         case FAT_SIZE_32_T:
263           printf ("[int32_t]");
264           break;
265         case FAT_SIZE_64_T:
266           printf ("[int64_t]");
267           break;
268         case FAT_SIZE_LEAST8_T:
269           printf ("[int_least8_t]");
270           break;
271         case FAT_SIZE_LEAST16_T:
272           printf ("[int_least16_t]");
273           break;
274         case FAT_SIZE_LEAST32_T:
275           printf ("[int_least32_t]");
276           break;
277         case FAT_SIZE_LEAST64_T:
278           printf ("[int_least64_t]");
279           break;
280         case FAT_SIZE_FAST8_T:
281           printf ("[int_fast8_t]");
282           break;
283         case FAT_SIZE_FAST16_T:
284           printf ("[int_fast16_t]");
285           break;
286         case FAT_SIZE_FAST32_T:
287           printf ("[int_fast32_t]");
288           break;
289         case FAT_SIZE_FAST64_T:
290           printf ("[int_fast64_t]");
291           break;
292         case FAT_SIZE_INTMAX_T:
293           printf ("[intmax_t]");
294           break;
295         case FAT_SIZE_INTPTR_T:
296           printf ("[intptr_t]");
297           break;
298         case FAT_SIZE_SIZE_T:
299           printf ("[size_t]");
300           break;
301         case FAT_SIZE_PTRDIFF_T:
302           printf ("[ptrdiff_t]");
303           break;
304         default:
305           abort ();
306         }
307       switch (spec->unnumbered[i].type & ~(FAT_UNSIGNED | FAT_SIZE_MASK))
308         {
309         case FAT_INTEGER:
310           printf ("i");
311           break;
312         case FAT_DOUBLE:
313           printf ("f");
314           break;
315         case FAT_CHAR:
316           printf ("c");
317           break;
318         case FAT_STRING:
319           printf ("s");
320           break;
321         case FAT_OBJC_OBJECT:
322           printf ("@");
323           break;
324         case FAT_POINTER:
325           printf ("p");
326           break;
327         case FAT_COUNT_POINTER:
328           printf ("n");
329           break;
330         default:
331           abort ();
332         }
333     }
334   printf (")");
335 }
336 
337 int
main()338 main ()
339 {
340   for (;;)
341     {
342       char *line = NULL;
343       size_t line_size = 0;
344       int line_len;
345       char *invalid_reason;
346       void *descr;
347 
348       line_len = getline (&line, &line_size, stdin);
349       if (line_len < 0)
350         break;
351       if (line_len > 0 && line[line_len - 1] == '\n')
352         line[--line_len] = '\0';
353 
354       invalid_reason = NULL;
355       descr = format_c_parse (line, false, NULL, &invalid_reason);
356 
357       format_print (descr);
358       printf ("\n");
359       if (descr == NULL)
360         printf ("%s\n", invalid_reason);
361 
362       free (invalid_reason);
363       free (line);
364     }
365 
366   return 0;
367 }
368 
369 /*
370  * For Emacs M-x compile
371  * Local Variables:
372  * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../../gettext-runtime/intl -DHAVE_CONFIG_H -DTEST format-c.c ../gnulib-lib/libgettextlib.la"
373  * End:
374  */
375 
376 #endif /* TEST */
377