1 /* GCC internal format strings.
2 Copyright (C) 2003-2006 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
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 2, or (at your option)
8 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, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include <stdbool.h>
24 #include <stdlib.h>
25
26 #include "format.h"
27 #include "c-ctype.h"
28 #include "xalloc.h"
29 #include "xvasprintf.h"
30 #include "format-invalid.h"
31 #include "gettext.h"
32
33 #define _(str) gettext (str)
34
35 /* GCC internal format strings consist of language frontend independent
36 format directives, implemented in gcc-4.1.0/gcc/pretty-print.c (function
37 pp_base_format), plus some frontend dependent extensions:
38 - for the C/ObjC frontend
39 in gcc-4.1.0/gcc/c-objc-common.c (function c_tree_printer)
40 - for the C++ frontend
41 in gcc-4.1.0/gcc/cp/error.c (function cp_printer)
42 Taking these together, GCC internal format strings are specified as follows.
43
44 A directive
45 - starts with '%',
46 - either is finished by one of these:
47 - '%', '<', '>', "'", that need no argument,
48 - 'm', that needs no argument but looks at an err_no variable,
49 - or is continued like this:
50 - optionally 'm$' where m is a positive integer,
51 - optionally any number of flags:
52 'q' (once only),
53 'l' (up to twice) or 'w' (once only) (exclusive),
54 '+' (once only),
55 '#' (once only),
56 - finished by a specifier
57
58 - 'c', that needs a character argument,
59 - 's', that needs a string argument,
60 - '.NNNs', where NNN is a nonempty digit sequence, that needs a
61 string argument,
62 - '.*NNN$s' where NNN is a positive integer and NNN = m - 1, that
63 needs a signed integer argument at position NNN and a string
64 argument,
65 - '.*s', that needs a signed integer argument and a string argument,
66 - 'i', 'd', that need a signed integer argument of the specified
67 size,
68 - 'o', 'u', 'x', that need an unsigned integer argument of the
69 specified size,
70 - 'p', that needs a 'void *' argument,
71 - 'H', that needs a 'location_t *' argument,
72 - 'J', that needs a general declaration argument,
73 [see gcc/pretty-print.c]
74
75 - 'D', that needs a general declaration argument,
76 - 'F', that needs a function declaration argument,
77 - 'T', that needs a type argument,
78 - 'E', that needs an expression argument,
79 [see gcc/c-objc-common.c and gcc/cp/error.c]
80
81 - 'A', that needs a function argument list argument,
82 - 'C', that needs a tree code argument,
83 - 'L', that needs a language argument,
84 - 'O', that needs a binary operator argument,
85 - 'P', that needs a function parameter argument,
86 - 'Q', that needs an assignment operator argument,
87 - 'V', that needs a const/volatile qualifier argument.
88 [see gcc/cp/error.c]
89
90 Numbered ('%m$' or '*m$') and unnumbered argument specifications cannot
91 be used in the same string. */
92
93 enum format_arg_type
94 {
95 FAT_NONE = 0,
96 /* Basic types */
97 FAT_INTEGER = 1,
98 FAT_CHAR = 2,
99 FAT_STRING = 3,
100 FAT_POINTER = 4,
101 FAT_LOCATION = 5,
102 FAT_TREE = 6,
103 FAT_TREE_CODE = 7,
104 FAT_LANGUAGES = 8,
105 /* Flags */
106 FAT_UNSIGNED = 1 << 4,
107 FAT_SIZE_LONG = 1 << 5,
108 FAT_SIZE_LONGLONG = 2 << 5,
109 FAT_SIZE_WIDE = 3 << 5,
110 FAT_TREE_DECL = 1 << 7,
111 FAT_TREE_FUNCDECL = 2 << 7,
112 FAT_TREE_TYPE = 3 << 7,
113 FAT_TREE_ARGUMENT = 4 << 7,
114 FAT_TREE_EXPRESSION = 5 << 7,
115 FAT_TREE_CV = 6 << 7,
116 FAT_TREE_CODE_BINOP = 1 << 10,
117 FAT_TREE_CODE_ASSOP = 2 << 10,
118 FAT_FUNCPARAM = 1 << 12,
119 /* Bitmasks */
120 FAT_SIZE_MASK = (FAT_SIZE_LONG | FAT_SIZE_LONGLONG | FAT_SIZE_WIDE)
121 };
122
123 struct numbered_arg
124 {
125 unsigned int number;
126 enum format_arg_type type;
127 };
128
129 struct spec
130 {
131 unsigned int directives;
132 unsigned int numbered_arg_count;
133 unsigned int allocated;
134 struct numbered_arg *numbered;
135 bool uses_err_no;
136 };
137
138 /* Locale independent test for a decimal digit.
139 Argument can be 'char' or 'unsigned char'. (Whereas the argument of
140 <ctype.h> isdigit must be an 'unsigned char'.) */
141 #undef isdigit
142 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
143
144
145 static int
numbered_arg_compare(const void * p1,const void * p2)146 numbered_arg_compare (const void *p1, const void *p2)
147 {
148 unsigned int n1 = ((const struct numbered_arg *) p1)->number;
149 unsigned int n2 = ((const struct numbered_arg *) p2)->number;
150
151 return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
152 }
153
154 static void *
format_parse(const char * format,bool translated,char ** invalid_reason)155 format_parse (const char *format, bool translated, char **invalid_reason)
156 {
157 struct spec spec;
158 unsigned int unnumbered_arg_count;
159 struct spec *result;
160
161 spec.directives = 0;
162 spec.numbered_arg_count = 0;
163 spec.allocated = 0;
164 spec.numbered = NULL;
165 spec.uses_err_no = false;
166 unnumbered_arg_count = 0;
167
168 for (; *format != '\0';)
169 if (*format++ == '%')
170 {
171 /* A directive. */
172 spec.directives++;
173
174 if (*format == '%' || *format == '<' || *format == '>'
175 || *format == '\'')
176 ;
177 else if (*format == 'm')
178 spec.uses_err_no = true;
179 else
180 {
181 unsigned int number = 0;
182 unsigned int flag_q = 0;
183 unsigned int flag_l = 0;
184 unsigned int flag_w = 0;
185 unsigned int flag_plus = 0;
186 unsigned int flag_sharp = 0;
187 enum format_arg_type size;
188 enum format_arg_type type;
189
190 if (isdigit (*format))
191 {
192 const char *f = format;
193 unsigned int m = 0;
194
195 do
196 {
197 m = 10 * m + (*f - '0');
198 f++;
199 }
200 while (isdigit (*f));
201
202 if (*f == '$')
203 {
204 if (m == 0)
205 {
206 *invalid_reason = INVALID_ARGNO_0 (spec.directives);
207 goto bad_format;
208 }
209 number = m;
210 format = ++f;
211 }
212 }
213
214 /* Parse flags and size. */
215 for (;; format++)
216 {
217 switch (*format)
218 {
219 case 'q':
220 if (flag_q > 0)
221 goto invalid_flags;
222 flag_q = 1;
223 continue;
224 case 'l':
225 if (flag_l > 1 || flag_w)
226 goto invalid_flags;
227 flag_l++;
228 continue;
229 case 'w':
230 if (flag_w > 0 || flag_l)
231 goto invalid_flags;
232 flag_w = 1;
233 continue;
234 case '+':
235 if (flag_plus > 0)
236 goto invalid_flags;
237 flag_plus = 1;
238 continue;
239 case '#':
240 if (flag_sharp > 0)
241 goto invalid_flags;
242 flag_sharp = 1;
243 continue;
244 invalid_flags:
245 *invalid_reason = xasprintf (_("In the directive number %u, the flags combination is invalid."), spec.directives);
246 goto bad_format;
247 default:
248 break;
249 }
250 break;
251 }
252 size = (flag_l == 2 ? FAT_SIZE_LONGLONG :
253 flag_l == 1 ? FAT_SIZE_LONG :
254 flag_w ? FAT_SIZE_WIDE :
255 0);
256
257 if (*format == 'c')
258 type = FAT_CHAR;
259 else if (*format == 's')
260 type = FAT_STRING;
261 else if (*format == '.')
262 {
263 format++;
264
265 if (isdigit (*format))
266 {
267 do
268 format++;
269 while (isdigit (*format));
270
271 if (*format != 's')
272 {
273 *invalid_reason =
274 (*format == '\0'
275 ? INVALID_UNTERMINATED_DIRECTIVE ()
276 : xasprintf (_("In the directive number %u, a precision is not allowed before '%c'."), spec.directives, *format));
277 goto bad_format;
278 }
279
280 type = FAT_STRING;
281 }
282 else if (*format == '*')
283 {
284 unsigned int precision_number = 0;
285
286 format++;
287
288 if (isdigit (*format))
289 {
290 const char *f = format;
291 unsigned int m = 0;
292
293 do
294 {
295 m = 10 * m + (*f - '0');
296 f++;
297 }
298 while (isdigit (*f));
299
300 if (*f == '$')
301 {
302 if (m == 0)
303 {
304 *invalid_reason = INVALID_WIDTH_ARGNO_0 (spec.directives);
305 goto bad_format;
306 }
307 if (unnumbered_arg_count > 0 || number == 0)
308 {
309 *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
310 goto bad_format;
311 }
312 if (m != number - 1)
313 {
314 *invalid_reason = xasprintf (_("In the directive number %u, the argument number for the precision must be equal to %u."), spec.directives, number - 1);
315 goto bad_format;
316 }
317 precision_number = m;
318 format = ++f;
319 }
320 }
321
322 if (precision_number)
323 {
324 /* Numbered argument. */
325
326 /* Numbered and unnumbered specifications are exclusive. */
327 if (unnumbered_arg_count > 0)
328 {
329 *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
330 goto bad_format;
331 }
332
333 if (spec.allocated == spec.numbered_arg_count)
334 {
335 spec.allocated = 2 * spec.allocated + 1;
336 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
337 }
338 spec.numbered[spec.numbered_arg_count].number = precision_number;
339 spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
340 spec.numbered_arg_count++;
341 }
342 else
343 {
344 /* Unnumbered argument. */
345
346 /* Numbered and unnumbered specifications are exclusive. */
347 if (spec.numbered_arg_count > 0)
348 {
349 *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
350 goto bad_format;
351 }
352
353 if (spec.allocated == unnumbered_arg_count)
354 {
355 spec.allocated = 2 * spec.allocated + 1;
356 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
357 }
358 spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
359 spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
360 unnumbered_arg_count++;
361 }
362
363 if (*format == 's')
364 type = FAT_STRING;
365 else
366 {
367 *invalid_reason =
368 (*format == '\0'
369 ? INVALID_UNTERMINATED_DIRECTIVE ()
370 : xasprintf (_("In the directive number %u, a precision is not allowed before '%c'."), spec.directives, *format));
371 goto bad_format;
372 }
373 }
374 else
375 {
376 *invalid_reason = xasprintf (_("In the directive number %u, the precision specification is invalid."), spec.directives);
377 goto bad_format;
378 }
379 }
380 else if (*format == 'i' || *format == 'd')
381 type = FAT_INTEGER | size;
382 else if (*format == 'o' || *format == 'u' || *format == 'x')
383 type = FAT_INTEGER | FAT_UNSIGNED | size;
384 else if (*format == 'p')
385 type = FAT_POINTER;
386 else if (*format == 'H')
387 type = FAT_LOCATION;
388 else if (*format == 'J')
389 type = FAT_TREE | FAT_TREE_DECL;
390 else
391 {
392 if (*format == 'D')
393 type = FAT_TREE | FAT_TREE_DECL;
394 else if (*format == 'F')
395 type = FAT_TREE | FAT_TREE_FUNCDECL;
396 else if (*format == 'T')
397 type = FAT_TREE | FAT_TREE_TYPE;
398 else if (*format == 'E')
399 type = FAT_TREE | FAT_TREE_EXPRESSION;
400 else if (*format == 'A')
401 type = FAT_TREE | FAT_TREE_ARGUMENT;
402 else if (*format == 'C')
403 type = FAT_TREE_CODE;
404 else if (*format == 'L')
405 type = FAT_LANGUAGES;
406 else if (*format == 'O')
407 type = FAT_TREE_CODE | FAT_TREE_CODE_BINOP;
408 else if (*format == 'P')
409 type = FAT_INTEGER | FAT_FUNCPARAM;
410 else if (*format == 'Q')
411 type = FAT_TREE_CODE | FAT_TREE_CODE_ASSOP;
412 else if (*format == 'V')
413 type = FAT_TREE | FAT_TREE_CV;
414 else
415 {
416 *invalid_reason =
417 (*format == '\0'
418 ? INVALID_UNTERMINATED_DIRECTIVE ()
419 : (*format == 'c'
420 || *format == 's'
421 || *format == 'i' || *format == 'd'
422 || *format == 'o' || *format == 'u' || *format == 'x'
423 || *format == 'H'
424 ? xasprintf (_("In the directive number %u, flags are not allowed before '%c'."), spec.directives, *format)
425 : INVALID_CONVERSION_SPECIFIER (spec.directives,
426 *format)));
427 goto bad_format;
428 }
429 }
430
431 if (number)
432 {
433 /* Numbered argument. */
434
435 /* Numbered and unnumbered specifications are exclusive. */
436 if (unnumbered_arg_count > 0)
437 {
438 *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
439 goto bad_format;
440 }
441
442 if (spec.allocated == spec.numbered_arg_count)
443 {
444 spec.allocated = 2 * spec.allocated + 1;
445 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
446 }
447 spec.numbered[spec.numbered_arg_count].number = number;
448 spec.numbered[spec.numbered_arg_count].type = type;
449 spec.numbered_arg_count++;
450 }
451 else
452 {
453 /* Unnumbered argument. */
454
455 /* Numbered and unnumbered specifications are exclusive. */
456 if (spec.numbered_arg_count > 0)
457 {
458 *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
459 goto bad_format;
460 }
461
462 if (spec.allocated == unnumbered_arg_count)
463 {
464 spec.allocated = 2 * spec.allocated + 1;
465 spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
466 }
467 spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
468 spec.numbered[unnumbered_arg_count].type = type;
469 unnumbered_arg_count++;
470 }
471 }
472
473 format++;
474 }
475
476 /* Convert the unnumbered argument array to numbered arguments. */
477 if (unnumbered_arg_count > 0)
478 spec.numbered_arg_count = unnumbered_arg_count;
479 /* Sort the numbered argument array, and eliminate duplicates. */
480 else if (spec.numbered_arg_count > 1)
481 {
482 unsigned int i, j;
483 bool err;
484
485 qsort (spec.numbered, spec.numbered_arg_count,
486 sizeof (struct numbered_arg), numbered_arg_compare);
487
488 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
489 err = false;
490 for (i = j = 0; i < spec.numbered_arg_count; i++)
491 if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
492 {
493 enum format_arg_type type1 = spec.numbered[i].type;
494 enum format_arg_type type2 = spec.numbered[j-1].type;
495 enum format_arg_type type_both;
496
497 if (type1 == type2)
498 type_both = type1;
499 else
500 {
501 /* Incompatible types. */
502 type_both = FAT_NONE;
503 if (!err)
504 *invalid_reason =
505 INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
506 err = true;
507 }
508
509 spec.numbered[j-1].type = type_both;
510 }
511 else
512 {
513 if (j < i)
514 {
515 spec.numbered[j].number = spec.numbered[i].number;
516 spec.numbered[j].type = spec.numbered[i].type;
517 }
518 j++;
519 }
520 spec.numbered_arg_count = j;
521 if (err)
522 /* *invalid_reason has already been set above. */
523 goto bad_format;
524 }
525
526 result = (struct spec *) xmalloc (sizeof (struct spec));
527 *result = spec;
528 return result;
529
530 bad_format:
531 if (spec.numbered != NULL)
532 free (spec.numbered);
533 return NULL;
534 }
535
536 static void
format_free(void * descr)537 format_free (void *descr)
538 {
539 struct spec *spec = (struct spec *) descr;
540
541 if (spec->numbered != NULL)
542 free (spec->numbered);
543 free (spec);
544 }
545
546 static int
format_get_number_of_directives(void * descr)547 format_get_number_of_directives (void *descr)
548 {
549 struct spec *spec = (struct spec *) descr;
550
551 return spec->directives;
552 }
553
554 static bool
format_check(void * msgid_descr,void * msgstr_descr,bool equality,formatstring_error_logger_t error_logger,const char * pretty_msgstr)555 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
556 formatstring_error_logger_t error_logger,
557 const char *pretty_msgstr)
558 {
559 struct spec *spec1 = (struct spec *) msgid_descr;
560 struct spec *spec2 = (struct spec *) msgstr_descr;
561 bool err = false;
562
563 if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
564 {
565 unsigned int i, j;
566 unsigned int n1 = spec1->numbered_arg_count;
567 unsigned int n2 = spec2->numbered_arg_count;
568
569 /* Check the argument names are the same.
570 Both arrays are sorted. We search for the first difference. */
571 for (i = 0, j = 0; i < n1 || j < n2; )
572 {
573 int cmp = (i >= n1 ? 1 :
574 j >= n2 ? -1 :
575 spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
576 spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
577 0);
578
579 if (cmp > 0)
580 {
581 if (error_logger)
582 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"),
583 spec2->numbered[j].number, pretty_msgstr);
584 err = true;
585 break;
586 }
587 else if (cmp < 0)
588 {
589 if (equality)
590 {
591 if (error_logger)
592 error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
593 spec1->numbered[i].number, pretty_msgstr);
594 err = true;
595 break;
596 }
597 else
598 i++;
599 }
600 else
601 j++, i++;
602 }
603 /* Check the argument types are the same. */
604 if (!err)
605 for (i = 0, j = 0; j < n2; )
606 {
607 if (spec1->numbered[i].number == spec2->numbered[j].number)
608 {
609 if (spec1->numbered[i].type != spec2->numbered[j].type)
610 {
611 if (error_logger)
612 error_logger (_("format specifications in 'msgid' and '%s' for argument %u are not the same"),
613 pretty_msgstr, spec2->numbered[j].number);
614 err = true;
615 break;
616 }
617 j++, i++;
618 }
619 else
620 i++;
621 }
622 }
623
624 /* Check that the use of err_no is the same. */
625 if (spec1->uses_err_no != spec2->uses_err_no)
626 {
627 if (error_logger)
628 {
629 if (spec1->uses_err_no)
630 error_logger (_("'msgid' uses %%m but '%s' doesn't"),
631 pretty_msgstr);
632 else
633 error_logger (_("'msgid' does not use %%m but '%s' uses %%m"),
634 pretty_msgstr);
635 }
636 err = true;
637 }
638
639 return err;
640 }
641
642
643 struct formatstring_parser formatstring_gcc_internal =
644 {
645 format_parse,
646 format_free,
647 format_get_number_of_directives,
648 NULL,
649 format_check
650 };
651
652
653 #ifdef TEST
654
655 /* Test program: Print the argument list specification returned by
656 format_parse for strings read from standard input. */
657
658 #include <stdio.h>
659 #include "getline.h"
660
661 static void
format_print(void * descr)662 format_print (void *descr)
663 {
664 struct spec *spec = (struct spec *) descr;
665 unsigned int last;
666 unsigned int i;
667
668 if (spec == NULL)
669 {
670 printf ("INVALID");
671 return;
672 }
673
674 printf ("(");
675 last = 1;
676 for (i = 0; i < spec->numbered_arg_count; i++)
677 {
678 unsigned int number = spec->numbered[i].number;
679
680 if (i > 0)
681 printf (" ");
682 if (number < last)
683 abort ();
684 for (; last < number; last++)
685 printf ("_ ");
686 if (spec->numbered[i].type & FAT_UNSIGNED)
687 printf ("[unsigned]");
688 switch (spec->numbered[i].type & FAT_SIZE_MASK)
689 {
690 case 0:
691 break;
692 case FAT_SIZE_LONG:
693 printf ("[long]");
694 break;
695 case FAT_SIZE_LONGLONG:
696 printf ("[long long]");
697 break;
698 case FAT_SIZE_WIDE:
699 printf ("[host-wide]");
700 break;
701 default:
702 abort ();
703 }
704 switch (spec->numbered[i].type & ~(FAT_UNSIGNED | FAT_SIZE_MASK))
705 {
706 case FAT_INTEGER:
707 printf ("i");
708 break;
709 case FAT_INTEGER | FAT_FUNCPARAM:
710 printf ("P");
711 break;
712 case FAT_CHAR:
713 printf ("c");
714 break;
715 case FAT_STRING:
716 printf ("s");
717 break;
718 case FAT_POINTER:
719 printf ("p");
720 break;
721 case FAT_LOCATION:
722 printf ("H");
723 break;
724 case FAT_TREE | FAT_TREE_DECL:
725 printf ("D");
726 break;
727 case FAT_TREE | FAT_TREE_FUNCDECL:
728 printf ("F");
729 break;
730 case FAT_TREE | FAT_TREE_TYPE:
731 printf ("T");
732 break;
733 case FAT_TREE | FAT_TREE_ARGUMENT:
734 printf ("A");
735 break;
736 case FAT_TREE | FAT_TREE_EXPRESSION:
737 printf ("E");
738 break;
739 case FAT_TREE | FAT_TREE_CV:
740 printf ("V");
741 break;
742 case FAT_TREE_CODE:
743 printf ("C");
744 break;
745 case FAT_TREE_CODE | FAT_TREE_CODE_BINOP:
746 printf ("O");
747 break;
748 case FAT_TREE_CODE | FAT_TREE_CODE_ASSOP:
749 printf ("Q");
750 break;
751 case FAT_LANGUAGES:
752 printf ("L");
753 break;
754 default:
755 abort ();
756 }
757 last = number + 1;
758 }
759 printf (")");
760 if (spec->uses_err_no)
761 printf (" ERR_NO");
762 }
763
764 int
main()765 main ()
766 {
767 for (;;)
768 {
769 char *line = NULL;
770 size_t line_size = 0;
771 int line_len;
772 char *invalid_reason;
773 void *descr;
774
775 line_len = getline (&line, &line_size, stdin);
776 if (line_len < 0)
777 break;
778 if (line_len > 0 && line[line_len - 1] == '\n')
779 line[--line_len] = '\0';
780
781 invalid_reason = NULL;
782 descr = format_parse (line, false, &invalid_reason);
783
784 format_print (descr);
785 printf ("\n");
786 if (descr == NULL)
787 printf ("%s\n", invalid_reason);
788
789 free (invalid_reason);
790 free (line);
791 }
792
793 return 0;
794 }
795
796 /*
797 * For Emacs M-x compile
798 * Local Variables:
799 * compile-command: "/bin/sh ../libtool --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../lib -I../intl -DHAVE_CONFIG_H -DTEST format-gcc-internal.c ../lib/libgettextlib.la"
800 * End:
801 */
802
803 #endif /* TEST */
804