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