• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GFC (GNU Fortran Compiler) internal format strings.
2    Copyright (C) 2003-2009, 2019-2020 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2009.
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 "format-invalid.h"
30 #include "gettext.h"
31 
32 #define _(str) gettext (str)
33 
34 /* GFC internal format strings consist of format directives that are specific
35    to the GNU Fortran Compiler frontend of GCC, implemented in
36    gcc-4.3.3/gcc/fortran/error.c (function error_print).
37 
38    A directive
39    - starts with '%',
40    - either is finished by '%', that needs no argument,
41    - or is continued like this:
42        - optionally 'm$' where m is a positive integer,
43        - finished by a specifier
44            - 'C', that needs no argument but uses a particular variable
45                   (but for the purposes of 'm$' numbering it consumes an
46                   argument nevertheless, of 'void' type),
47            - 'L', that needs a 'locus *' argument,
48            - 'i', 'd', that need a signed integer argument,
49            - 'u', that needs an unsigned integer argument,
50            - 'li', 'ld', that need a signed long integer argument,
51            - 'lu', that needs an unsigned long integer argument,
52            - 'c', that needs a character argument,
53            - 's', that needs a string argument.
54 
55    Numbered ('%m$') and unnumbered argument specifications can be used in the
56    same string. The effect of '%m$' is to set the current argument number to
57    m. The current argument number is incremented after processing a directive.
58 
59    When numbered argument specifications are used, specifying the Nth argument
60    requires that all the leading arguments, from the first to the (N-1)th, are
61    specified in the format string.  */
62 
63 enum format_arg_type
64 {
65   FAT_NONE              = 0,
66   /* Basic types */
67   FAT_VOID              = 1,
68   FAT_INTEGER           = 2,
69   FAT_CHAR              = 3,
70   FAT_STRING            = 4,
71   FAT_LOCUS             = 5,
72   /* Flags */
73   FAT_UNSIGNED          = 1 << 3,
74   FAT_SIZE_LONG         = 1 << 4,
75   /* Bitmasks */
76   FAT_SIZE_MASK         = FAT_SIZE_LONG
77 };
78 #ifdef __cplusplus
79 typedef int format_arg_type_t;
80 #else
81 typedef enum format_arg_type format_arg_type_t;
82 #endif
83 
84 struct numbered_arg
85 {
86   unsigned int number;
87   format_arg_type_t type;
88 };
89 
90 struct unnumbered_arg
91 {
92   format_arg_type_t type;
93 };
94 
95 struct spec
96 {
97   unsigned int directives;
98   unsigned int unnumbered_arg_count;
99   struct unnumbered_arg *unnumbered;
100   bool uses_currentloc;
101 };
102 
103 /* Locale independent test for a decimal digit.
104    Argument can be  'char' or 'unsigned char'.  (Whereas the argument of
105    <ctype.h> isdigit must be an 'unsigned char'.)  */
106 #undef isdigit
107 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
108 
109 
110 static int
numbered_arg_compare(const void * p1,const void * p2)111 numbered_arg_compare (const void *p1, const void *p2)
112 {
113   unsigned int n1 = ((const struct numbered_arg *) p1)->number;
114   unsigned int n2 = ((const struct numbered_arg *) p2)->number;
115 
116   return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
117 }
118 
119 static void *
format_parse(const char * format,bool translated,char * fdi,char ** invalid_reason)120 format_parse (const char *format, bool translated, char *fdi,
121               char **invalid_reason)
122 {
123   const char *const format_start = format;
124   struct spec spec;
125   unsigned int numbered_arg_count;
126   unsigned int numbered_allocated;
127   struct numbered_arg *numbered;
128   struct spec *result;
129   unsigned int number;
130 
131   spec.directives = 0;
132   numbered_arg_count = 0;
133   numbered_allocated = 0;
134   numbered = NULL;
135   spec.uses_currentloc = false;
136   number = 1;
137 
138   for (; *format != '\0';)
139     if (*format++ == '%')
140       {
141         /* A directive.  */
142         FDI_SET (format - 1, FMTDIR_START);
143         spec.directives++;
144 
145         if (*format != '%')
146           {
147             format_arg_type_t type;
148 
149             if (isdigit (*format))
150               {
151                 const char *f = format;
152                 unsigned int m = 0;
153 
154                 do
155                   {
156                     m = 10 * m + (*f - '0');
157                     f++;
158                   }
159                 while (isdigit (*f));
160 
161                 if (*f == '$')
162                   {
163                     if (m == 0)
164                       {
165                         *invalid_reason = INVALID_ARGNO_0 (spec.directives);
166                         FDI_SET (f, FMTDIR_ERROR);
167                         goto bad_format;
168                       }
169                     number = m;
170                     format = ++f;
171                   }
172               }
173 
174             if (*format == 'C')
175               {
176                 type = FAT_VOID;
177                 spec.uses_currentloc = true;
178               }
179             else if (*format == 'L')
180               type = FAT_LOCUS;
181             else if (*format == 'c')
182               type = FAT_CHAR;
183             else if (*format == 's')
184               type = FAT_STRING;
185             else
186               {
187                 format_arg_type_t size = 0;
188 
189                 if (*format == 'l')
190                   {
191                     ++format;
192                     size = FAT_SIZE_LONG;
193                   }
194 
195                 if (*format == 'i' || *format == 'd')
196                   type = FAT_INTEGER | size;
197                 else if (*format == 'u')
198                   type = FAT_INTEGER | FAT_UNSIGNED | size;
199                 else
200                   {
201                     if (*format == '\0')
202                       {
203                         *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
204                         FDI_SET (format - 1, FMTDIR_ERROR);
205                       }
206                     else
207                       {
208                         *invalid_reason =
209                           INVALID_CONVERSION_SPECIFIER (spec.directives, *format);
210                         FDI_SET (format, FMTDIR_ERROR);
211                       }
212                     goto bad_format;
213                   }
214               }
215 
216             if (numbered_allocated == numbered_arg_count)
217               {
218                 numbered_allocated = 2 * numbered_allocated + 1;
219                 numbered = (struct numbered_arg *) xrealloc (numbered, numbered_allocated * sizeof (struct numbered_arg));
220               }
221             numbered[numbered_arg_count].number = number;
222             numbered[numbered_arg_count].type = type;
223             numbered_arg_count++;
224 
225             number++;
226           }
227 
228         FDI_SET (format, FMTDIR_END);
229 
230         format++;
231       }
232 
233   /* Sort the numbered argument array, and eliminate duplicates.  */
234   if (numbered_arg_count > 1)
235     {
236       unsigned int i, j;
237       bool err;
238 
239       qsort (numbered, numbered_arg_count,
240              sizeof (struct numbered_arg), numbered_arg_compare);
241 
242       /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
243       err = false;
244       for (i = j = 0; i < numbered_arg_count; i++)
245         if (j > 0 && numbered[i].number == numbered[j-1].number)
246           {
247             format_arg_type_t type1 = numbered[i].type;
248             format_arg_type_t type2 = numbered[j-1].type;
249             format_arg_type_t type_both;
250 
251             if (type1 == type2)
252               type_both = type1;
253             else
254               {
255                 /* Incompatible types.  */
256                 type_both = FAT_NONE;
257                 if (!err)
258                   *invalid_reason =
259                     INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
260                 err = true;
261               }
262 
263             numbered[j-1].type = type_both;
264           }
265         else
266           {
267             if (j < i)
268               {
269                 numbered[j].number = numbered[i].number;
270                 numbered[j].type = numbered[i].type;
271               }
272             j++;
273           }
274       numbered_arg_count = j;
275       if (err)
276         /* *invalid_reason has already been set above.  */
277         goto bad_format;
278     }
279 
280   /* Verify that the format strings uses all arguments up to the highest
281      numbered one.  */
282   {
283     unsigned int i;
284 
285     for (i = 0; i < numbered_arg_count; i++)
286       if (numbered[i].number != i + 1)
287         {
288           *invalid_reason =
289             xasprintf (_("The string refers to argument number %u but ignores argument number %u."), numbered[i].number, i + 1);
290           goto bad_format;
291         }
292   }
293 
294   /* So now the numbered arguments array is equivalent to a sequence
295      of unnumbered arguments.  Eliminate the FAT_VOID placeholders.  */
296   {
297     unsigned int i;
298 
299     spec.unnumbered_arg_count = 0;
300     for (i = 0; i < numbered_arg_count; i++)
301       if (numbered[i].type != FAT_VOID)
302         spec.unnumbered_arg_count++;
303 
304     if (spec.unnumbered_arg_count > 0)
305       {
306         unsigned int j;
307 
308         spec.unnumbered = XNMALLOC (spec.unnumbered_arg_count, struct unnumbered_arg);
309         j = 0;
310         for (i = 0; i < numbered_arg_count; i++)
311           if (numbered[i].type != FAT_VOID)
312             spec.unnumbered[j++].type = numbered[i].type;
313       }
314     else
315       spec.unnumbered = NULL;
316   }
317   free (numbered);
318 
319   result = XMALLOC (struct spec);
320   *result = spec;
321   return result;
322 
323  bad_format:
324   if (numbered != NULL)
325     free (numbered);
326   return NULL;
327 }
328 
329 static void
format_free(void * descr)330 format_free (void *descr)
331 {
332   struct spec *spec = (struct spec *) descr;
333 
334   if (spec->unnumbered != NULL)
335     free (spec->unnumbered);
336   free (spec);
337 }
338 
339 static int
format_get_number_of_directives(void * descr)340 format_get_number_of_directives (void *descr)
341 {
342   struct spec *spec = (struct spec *) descr;
343 
344   return spec->directives;
345 }
346 
347 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)348 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
349               formatstring_error_logger_t error_logger,
350               const char *pretty_msgid, const char *pretty_msgstr)
351 {
352   struct spec *spec1 = (struct spec *) msgid_descr;
353   struct spec *spec2 = (struct spec *) msgstr_descr;
354   bool err = false;
355   unsigned int i;
356 
357   /* Check the argument types are the same.  */
358   if (equality
359       ? spec1->unnumbered_arg_count != spec2->unnumbered_arg_count
360       : spec1->unnumbered_arg_count < spec2->unnumbered_arg_count)
361     {
362       if (error_logger)
363         error_logger (_("number of format specifications in '%s' and '%s' does not match"),
364                       pretty_msgid, pretty_msgstr);
365       err = true;
366     }
367   else
368     for (i = 0; i < spec2->unnumbered_arg_count; i++)
369       if (spec1->unnumbered[i].type != spec2->unnumbered[i].type)
370         {
371           if (error_logger)
372             error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
373                           pretty_msgid, pretty_msgstr, i + 1);
374           err = true;
375         }
376 
377   /* Check that the use of currentloc is the same.  */
378   if (spec1->uses_currentloc != spec2->uses_currentloc)
379     {
380       if (error_logger)
381         {
382           if (spec1->uses_currentloc)
383             error_logger (_("'%s' uses %%C but '%s' doesn't"),
384                           pretty_msgid, pretty_msgstr);
385           else
386             error_logger (_("'%s' does not use %%C but '%s' uses %%C"),
387                           pretty_msgid, pretty_msgstr);
388         }
389       err = true;
390     }
391 
392   return err;
393 }
394 
395 
396 struct formatstring_parser formatstring_gfc_internal =
397 {
398   format_parse,
399   format_free,
400   format_get_number_of_directives,
401   NULL,
402   format_check
403 };
404 
405 
406 #ifdef TEST
407 
408 /* Test program: Print the argument list specification returned by
409    format_parse for strings read from standard input.  */
410 
411 #include <stdio.h>
412 
413 static void
format_print(void * descr)414 format_print (void *descr)
415 {
416   struct spec *spec = (struct spec *) descr;
417   unsigned int i;
418 
419   if (spec == NULL)
420     {
421       printf ("INVALID");
422       return;
423     }
424 
425   printf ("(");
426   for (i = 0; i < spec->unnumbered_arg_count; i++)
427     {
428       if (i > 0)
429         printf (" ");
430       if (spec->unnumbered[i].type & FAT_UNSIGNED)
431         printf ("[unsigned]");
432       switch (spec->unnumbered[i].type & FAT_SIZE_MASK)
433         {
434         case 0:
435           break;
436         case FAT_SIZE_LONG:
437           printf ("[long]");
438           break;
439         default:
440           abort ();
441         }
442       switch (spec->unnumbered[i].type & ~(FAT_UNSIGNED | FAT_SIZE_MASK))
443         {
444         case FAT_INTEGER:
445           printf ("i");
446           break;
447         case FAT_CHAR:
448           printf ("c");
449           break;
450         case FAT_STRING:
451           printf ("s");
452           break;
453         case FAT_LOCUS:
454           printf ("L");
455           break;
456         default:
457           abort ();
458         }
459     }
460   printf (")");
461   if (spec->uses_currentloc)
462     printf (" C");
463 }
464 
465 int
main()466 main ()
467 {
468   for (;;)
469     {
470       char *line = NULL;
471       size_t line_size = 0;
472       int line_len;
473       char *invalid_reason;
474       void *descr;
475 
476       line_len = getline (&line, &line_size, stdin);
477       if (line_len < 0)
478         break;
479       if (line_len > 0 && line[line_len - 1] == '\n')
480         line[--line_len] = '\0';
481 
482       invalid_reason = NULL;
483       descr = format_parse (line, false, NULL, &invalid_reason);
484 
485       format_print (descr);
486       printf ("\n");
487       if (descr == NULL)
488         printf ("%s\n", invalid_reason);
489 
490       free (invalid_reason);
491       free (line);
492     }
493 
494   return 0;
495 }
496 
497 /*
498  * For Emacs M-x compile
499  * Local Variables:
500  * 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-gfc-internal.c ../gnulib-lib/libgettextlib.la"
501  * End:
502  */
503 
504 #endif /* TEST */
505