1/* GCC internal format strings.
2   Copyright (C) 2003-2004 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 "xerror.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-3.3/gcc/diagnostic.c (function
37   output_format), plus some frontend dependent extensions:
38     - for the C/ObjC frontend in gcc-3.3/gcc/c-objc-common.c
39     - for the C++ frontend in gcc-3.3/gcc/cp/error.c
40   Taking these together, GCC internal format strings are specified as follows.
41   A directive
42   - starts with '%',
43   - is optionally followed by a size specifier 'l',
44   - is optionally followed by '+' (only the specifiers of gcc/cp/error.c),
45   - is optionally followed by '#' (only the specifiers of gcc/cp/error.c),
46   - is finished by a specifier
47
48       - '%', that needs no argument,
49       - 'c', that needs a character argument,
50       - 's', that needs a string argument,
51       - 'i', 'd', that need a signed integer argument,
52       - 'o', 'u', 'x', that need an unsigned integer argument,
53       - '.*s', that needs a signed integer argument and a string argument,
54       - 'H', that needs a 'location_t *' argument,
55         [see gcc/diagnostic.c]
56
57       - 'D', that needs a general declaration argument,
58       - 'F', that needs a function declaration argument,
59       - 'T', that needs a type argument,
60         [see gcc/c-objc-common.c and gcc/cp/error.c]
61
62       - 'A', that needs a function argument list argument,
63       - 'C', that needs a tree code argument,
64       - 'E', that needs an expression argument,
65       - 'L', that needs a language argument,
66       - 'O', that needs a binary operator argument,
67       - 'P', that needs a function parameter argument,
68       - 'Q', that needs an assignment operator argument,
69       - 'V', that needs a const/volatile qualifier argument.
70         [see gcc/cp/error.c]
71 */
72
73enum format_arg_type
74{
75  FAT_NONE		= 0,
76  /* Basic types */
77  FAT_INTEGER		= 1,
78  FAT_CHAR		= 2,
79  FAT_STRING		= 3,
80  FAT_LOCATION		= 4,
81  FAT_TREE		= 5,
82  FAT_TREE_CODE		= 6,
83  FAT_LANGUAGES		= 7,
84  /* Flags */
85  FAT_UNSIGNED		= 1 << 3,
86  FAT_SIZE_LONG		= 1 << 4,
87  FAT_TREE_DECL		= 1 << 5,
88  FAT_TREE_FUNCDECL	= 2 << 5,
89  FAT_TREE_TYPE		= 3 << 5,
90  FAT_TREE_ARGUMENT	= 4 << 5,
91  FAT_TREE_EXPRESSION	= 5 << 5,
92  FAT_TREE_CV		= 6 << 5,
93  FAT_TREE_CODE_BINOP	= 1 << 8,
94  FAT_TREE_CODE_ASSOP	= 2 << 8,
95  FAT_FUNCPARAM		= 1 << 10
96};
97
98struct unnumbered_arg
99{
100  enum format_arg_type type;
101};
102
103struct spec
104{
105  unsigned int directives;
106  unsigned int unnumbered_arg_count;
107  unsigned int allocated;
108  struct unnumbered_arg *unnumbered;
109};
110
111
112static void *
113format_parse (const char *format, bool translated, char **invalid_reason)
114{
115  struct spec spec;
116  struct spec *result;
117
118  spec.directives = 0;
119  spec.unnumbered_arg_count = 0;
120  spec.allocated = 0;
121  spec.unnumbered = NULL;
122
123  for (; *format != '\0';)
124    if (*format++ == '%')
125      {
126	/* A directive.  */
127	enum format_arg_type size;
128
129	spec.directives++;
130
131	/* Parse size.  */
132	size = 0;
133	if (*format == 'l')
134	  {
135	    format++;
136	    size = FAT_SIZE_LONG;
137	  }
138
139	if (*format != '%')
140	  {
141	    enum format_arg_type type;
142
143	    if (*format == 'c')
144	      type = FAT_CHAR;
145	    else if (*format == 's')
146	      type = FAT_STRING;
147	    else if (*format == 'i' || *format == 'd')
148	      type = FAT_INTEGER | size;
149	    else if (*format == 'o' || *format == 'u' || *format == 'x')
150	      type = FAT_INTEGER | FAT_UNSIGNED | size;
151	    else if (*format == '.' && format[1] == '*' && format[2] == 's')
152	      {
153		if (spec.allocated == spec.unnumbered_arg_count)
154		  {
155		    spec.allocated = 2 * spec.allocated + 1;
156		    spec.unnumbered = (struct unnumbered_arg *) xrealloc (spec.unnumbered, spec.allocated * sizeof (struct unnumbered_arg));
157		  }
158		spec.unnumbered[spec.unnumbered_arg_count].type = FAT_INTEGER;
159		spec.unnumbered_arg_count++;
160		type = FAT_STRING;
161	      }
162	    else if (*format == 'H')
163	      type = FAT_LOCATION;
164	    else
165	      {
166		if (*format == '+')
167		  format++;
168		if (*format == '#')
169		  format++;
170		if (*format == 'D')
171		  type = FAT_TREE | FAT_TREE_DECL;
172		else if (*format == 'F')
173		  type = FAT_TREE | FAT_TREE_FUNCDECL;
174		else if (*format == 'T')
175		  type = FAT_TREE | FAT_TREE_TYPE;
176		else if (*format == 'A')
177		  type = FAT_TREE | FAT_TREE_ARGUMENT;
178		else if (*format == 'C')
179		  type = FAT_TREE_CODE;
180		else if (*format == 'E')
181		  type = FAT_TREE | FAT_TREE_EXPRESSION;
182		else if (*format == 'L')
183		  type = FAT_LANGUAGES;
184		else if (*format == 'O')
185		  type = FAT_TREE_CODE | FAT_TREE_CODE_BINOP;
186		else if (*format == 'P')
187		  type = FAT_INTEGER | FAT_FUNCPARAM;
188		else if (*format == 'Q')
189		  type = FAT_TREE_CODE | FAT_TREE_CODE_ASSOP;
190		else if (*format == 'V')
191		  type = FAT_TREE | FAT_TREE_CV;
192		else
193		  {
194		    *invalid_reason =
195		      (*format == '\0'
196		       ? INVALID_UNTERMINATED_DIRECTIVE ()
197		       : (*format == 'c'
198			  || *format == 's'
199			  || *format == 'i' || *format == 'd'
200			  || *format == 'o' || *format == 'u' || *format == 'x'
201			  || *format == 'H'
202			  ? xasprintf (_("In the directive number %u, flags are not allowed before '%c'."), spec.directives, *format)
203			  : INVALID_CONVERSION_SPECIFIER (spec.directives,
204							  *format)));
205		    goto bad_format;
206		  }
207	      }
208
209	    if (spec.allocated == spec.unnumbered_arg_count)
210	      {
211		spec.allocated = 2 * spec.allocated + 1;
212		spec.unnumbered = (struct unnumbered_arg *) xrealloc (spec.unnumbered, spec.allocated * sizeof (struct unnumbered_arg));
213	      }
214	    spec.unnumbered[spec.unnumbered_arg_count].type = type;
215	    spec.unnumbered_arg_count++;
216	  }
217
218	format++;
219      }
220
221  result = (struct spec *) xmalloc (sizeof (struct spec));
222  *result = spec;
223  return result;
224
225 bad_format:
226  if (spec.unnumbered != NULL)
227    free (spec.unnumbered);
228  return NULL;
229}
230
231static void
232format_free (void *descr)
233{
234  struct spec *spec = (struct spec *) descr;
235
236  if (spec->unnumbered != NULL)
237    free (spec->unnumbered);
238  free (spec);
239}
240
241static int
242format_get_number_of_directives (void *descr)
243{
244  struct spec *spec = (struct spec *) descr;
245
246  return spec->directives;
247}
248
249static bool
250format_check (void *msgid_descr, void *msgstr_descr, bool equality,
251	      formatstring_error_logger_t error_logger,
252	      const char *pretty_msgstr)
253{
254  struct spec *spec1 = (struct spec *) msgid_descr;
255  struct spec *spec2 = (struct spec *) msgstr_descr;
256  bool err = false;
257  unsigned int i;
258
259  /* Check the argument types are the same.  */
260  if (equality
261      ? spec1->unnumbered_arg_count != spec2->unnumbered_arg_count
262      : spec1->unnumbered_arg_count < spec2->unnumbered_arg_count)
263    {
264      if (error_logger)
265	error_logger (_("number of format specifications in 'msgid' and '%s' does not match"),
266		      pretty_msgstr);
267      err = true;
268    }
269  else
270    for (i = 0; i < spec2->unnumbered_arg_count; i++)
271      if (spec1->unnumbered[i].type != spec2->unnumbered[i].type)
272	{
273	  if (error_logger)
274	    error_logger (_("format specifications in 'msgid' and '%s' for argument %u are not the same"),
275			  pretty_msgstr, i + 1);
276	  err = true;
277	}
278
279  return err;
280}
281
282
283struct formatstring_parser formatstring_gcc_internal =
284{
285  format_parse,
286  format_free,
287  format_get_number_of_directives,
288  format_check
289};
290
291
292#ifdef TEST
293
294/* Test program: Print the argument list specification returned by
295   format_parse for strings read from standard input.  */
296
297#include <stdio.h>
298#include "getline.h"
299
300static void
301format_print (void *descr)
302{
303  struct spec *spec = (struct spec *) descr;
304  unsigned int i;
305
306  if (spec == NULL)
307    {
308      printf ("INVALID");
309      return;
310    }
311
312  printf ("(");
313  for (i = 0; i < spec->unnumbered_arg_count; i++)
314    {
315      if (i > 0)
316	printf (" ");
317      if (spec->unnumbered[i].type & FAT_UNSIGNED)
318	printf ("[unsigned]");
319      if (spec->unnumbered[i].type & FAT_SIZE_LONG)
320	printf ("[long]");
321      switch (spec->unnumbered[i].type & ~(FAT_UNSIGNED | FAT_SIZE_LONG))
322	{
323	case FAT_INTEGER:
324	  printf ("i");
325	  break;
326	case FAT_INTEGER | FAT_FUNCPARAM:
327	  printf ("P");
328	  break;
329	case FAT_CHAR:
330	  printf ("c");
331	  break;
332	case FAT_STRING:
333	  printf ("s");
334	  break;
335	case FAT_LOCATION:
336	  printf ("H");
337	  break;
338	case FAT_TREE | FAT_TREE_DECL:
339	  printf ("D");
340	  break;
341	case FAT_TREE | FAT_TREE_FUNCDECL:
342	  printf ("F");
343	  break;
344	case FAT_TREE | FAT_TREE_TYPE:
345	  printf ("T");
346	  break;
347	case FAT_TREE | FAT_TREE_ARGUMENT:
348	  printf ("A");
349	  break;
350	case FAT_TREE | FAT_TREE_EXPRESSION:
351	  printf ("E");
352	  break;
353	case FAT_TREE | FAT_TREE_CV:
354	  printf ("V");
355	  break;
356	case FAT_TREE_CODE:
357	  printf ("C");
358	  break;
359	case FAT_TREE_CODE | FAT_TREE_CODE_BINOP:
360	  printf ("O");
361	  break;
362	case FAT_TREE_CODE | FAT_TREE_CODE_ASSOP:
363	  printf ("Q");
364	  break;
365	case FAT_LANGUAGES:
366	  printf ("L");
367	  break;
368	default:
369	  abort ();
370	}
371    }
372  printf (")");
373}
374
375int
376main ()
377{
378  for (;;)
379    {
380      char *line = NULL;
381      size_t line_size = 0;
382      int line_len;
383      char *invalid_reason;
384      void *descr;
385
386      line_len = getline (&line, &line_size, stdin);
387      if (line_len < 0)
388	break;
389      if (line_len > 0 && line[line_len - 1] == '\n')
390	line[--line_len] = '\0';
391
392      invalid_reason = NULL;
393      descr = format_parse (line, false, &invalid_reason);
394
395      format_print (descr);
396      printf ("\n");
397      if (descr == NULL)
398	printf ("%s\n", invalid_reason);
399
400      free (invalid_reason);
401      free (line);
402    }
403
404  return 0;
405}
406
407/*
408 * For Emacs M-x compile
409 * Local Variables:
410 * 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"
411 * End:
412 */
413
414#endif /* TEST */
415