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