xref: /netbsd-src/external/gpl2/gettext/dist/gettext-tools/src/format-c.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1*946379e7Schristos /* C format strings.
2*946379e7Schristos    Copyright (C) 2001-2004, 2006 Free Software Foundation, Inc.
3*946379e7Schristos    Written by Bruno Haible <haible@clisp.cons.org>, 2001.
4*946379e7Schristos 
5*946379e7Schristos    This program is free software; you can redistribute it and/or modify
6*946379e7Schristos    it under the terms of the GNU General Public License as published by
7*946379e7Schristos    the Free Software Foundation; either version 2, or (at your option)
8*946379e7Schristos    any later version.
9*946379e7Schristos 
10*946379e7Schristos    This program is distributed in the hope that it will be useful,
11*946379e7Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
12*946379e7Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13*946379e7Schristos    GNU General Public License for more details.
14*946379e7Schristos 
15*946379e7Schristos    You should have received a copy of the GNU General Public License
16*946379e7Schristos    along with this program; if not, write to the Free Software Foundation,
17*946379e7Schristos    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18*946379e7Schristos 
19*946379e7Schristos #ifdef HAVE_CONFIG_H
20*946379e7Schristos # include <config.h>
21*946379e7Schristos #endif
22*946379e7Schristos 
23*946379e7Schristos #include <stdbool.h>
24*946379e7Schristos #include <stdlib.h>
25*946379e7Schristos 
26*946379e7Schristos #include "format.h"
27*946379e7Schristos #include "c-ctype.h"
28*946379e7Schristos #include "xalloc.h"
29*946379e7Schristos #include "xvasprintf.h"
30*946379e7Schristos #include "format-invalid.h"
31*946379e7Schristos #include "gettext.h"
32*946379e7Schristos 
33*946379e7Schristos #define _(str) gettext (str)
34*946379e7Schristos 
35*946379e7Schristos /* C format strings are described in POSIX (IEEE P1003.1 2001), section
36*946379e7Schristos    XSH 3 fprintf().  See also Linux fprintf(3) manual page.
37*946379e7Schristos    A directive
38*946379e7Schristos    - starts with '%' or '%m$' where m is a positive integer,
39*946379e7Schristos    - is optionally followed by any of the characters '#', '0', '-', ' ', '+',
40*946379e7Schristos      "'", or - only in msgstr strings - the string "I", each of which acts as
41*946379e7Schristos      a flag,
42*946379e7Schristos    - is optionally followed by a width specification: '*' (reads an argument)
43*946379e7Schristos      or '*m$' or a nonempty digit sequence,
44*946379e7Schristos    - is optionally followed by '.' and a precision specification: '*' (reads
45*946379e7Schristos      an argument) or '*m$' or a nonempty digit sequence,
46*946379e7Schristos    - is either continued like this:
47*946379e7Schristos        - is optionally followed by a size specifier, one of 'hh' 'h' 'l' 'll'
48*946379e7Schristos          'L' 'q' 'j' 'z' 't',
49*946379e7Schristos        - is finished by a specifier
50*946379e7Schristos            - '%', that needs no argument,
51*946379e7Schristos            - 'c', 'C', that need a character argument,
52*946379e7Schristos            - 's', 'S', that need a string argument,
53*946379e7Schristos            - 'i', 'd', that need a signed integer argument,
54*946379e7Schristos            - 'o', 'u', 'x', 'X', that need an unsigned integer argument,
55*946379e7Schristos            - 'e', 'E', 'f', 'F', 'g', 'G', 'a', 'A', that need a floating-point
56*946379e7Schristos              argument,
57*946379e7Schristos            - 'p', that needs a 'void *' argument,
58*946379e7Schristos            - 'n', that needs a pointer to integer.
59*946379e7Schristos      or is finished by a specifier '<' inttypes-macro '>' where inttypes-macro
60*946379e7Schristos      is an ISO C 99 section 7.8.1 format directive.
61*946379e7Schristos    Numbered ('%m$' or '*m$') and unnumbered argument specifications cannot
62*946379e7Schristos    be used in the same string.  When numbered argument specifications are
63*946379e7Schristos    used, specifying the Nth argument requires that all the leading arguments,
64*946379e7Schristos    from the first to the (N-1)th, are specified in the format string.
65*946379e7Schristos  */
66*946379e7Schristos 
67*946379e7Schristos enum format_arg_type
68*946379e7Schristos {
69*946379e7Schristos   FAT_NONE		= 0,
70*946379e7Schristos   /* Basic types */
71*946379e7Schristos   FAT_INTEGER		= 1,
72*946379e7Schristos   FAT_DOUBLE		= 2,
73*946379e7Schristos   FAT_CHAR		= 3,
74*946379e7Schristos   FAT_STRING		= 4,
75*946379e7Schristos   FAT_OBJC_OBJECT	= 5,
76*946379e7Schristos   FAT_POINTER		= 6,
77*946379e7Schristos   FAT_COUNT_POINTER	= 7,
78*946379e7Schristos   /* Flags */
79*946379e7Schristos   FAT_UNSIGNED		= 1 << 3,
80*946379e7Schristos   FAT_SIZE_SHORT	= 1 << 4,
81*946379e7Schristos   FAT_SIZE_CHAR		= 2 << 4,
82*946379e7Schristos   FAT_SIZE_LONG		= 1 << 6,
83*946379e7Schristos   FAT_SIZE_LONGLONG	= 2 << 6,
84*946379e7Schristos   FAT_SIZE_8_T		= 1 << 8,
85*946379e7Schristos   FAT_SIZE_16_T		= 1 << 9,
86*946379e7Schristos   FAT_SIZE_32_T		= 1 << 10,
87*946379e7Schristos   FAT_SIZE_64_T		= 1 << 11,
88*946379e7Schristos   FAT_SIZE_LEAST8_T	= 1 << 12,
89*946379e7Schristos   FAT_SIZE_LEAST16_T	= 1 << 13,
90*946379e7Schristos   FAT_SIZE_LEAST32_T	= 1 << 14,
91*946379e7Schristos   FAT_SIZE_LEAST64_T	= 1 << 15,
92*946379e7Schristos   FAT_SIZE_FAST8_T	= 1 << 16,
93*946379e7Schristos   FAT_SIZE_FAST16_T	= 1 << 17,
94*946379e7Schristos   FAT_SIZE_FAST32_T	= 1 << 18,
95*946379e7Schristos   FAT_SIZE_FAST64_T	= 1 << 19,
96*946379e7Schristos   FAT_SIZE_INTMAX_T	= 1 << 20,
97*946379e7Schristos   FAT_SIZE_INTPTR_T	= 1 << 21,
98*946379e7Schristos   FAT_SIZE_SIZE_T	= 1 << 22,
99*946379e7Schristos   FAT_SIZE_PTRDIFF_T	= 1 << 23,
100*946379e7Schristos   FAT_WIDE		= FAT_SIZE_LONG,
101*946379e7Schristos   /* Meaningful combinations of basic types and flags:
102*946379e7Schristos   'signed char'			= FAT_INTEGER | FAT_SIZE_CHAR,
103*946379e7Schristos   'unsigned char'		= FAT_INTEGER | FAT_SIZE_CHAR | FAT_UNSIGNED,
104*946379e7Schristos   'short'			= FAT_INTEGER | FAT_SIZE_SHORT,
105*946379e7Schristos   'unsigned short'		= FAT_INTEGER | FAT_SIZE_SHORT | FAT_UNSIGNED,
106*946379e7Schristos   'int'				= FAT_INTEGER,
107*946379e7Schristos   'unsigned int'		= FAT_INTEGER | FAT_UNSIGNED,
108*946379e7Schristos   'long int'			= FAT_INTEGER | FAT_SIZE_LONG,
109*946379e7Schristos   'unsigned long int'		= FAT_INTEGER | FAT_SIZE_LONG | FAT_UNSIGNED,
110*946379e7Schristos   'long long int'		= FAT_INTEGER | FAT_SIZE_LONGLONG,
111*946379e7Schristos   'unsigned long long int'	= FAT_INTEGER | FAT_SIZE_LONGLONG | FAT_UNSIGNED,
112*946379e7Schristos   'double'			= FAT_DOUBLE,
113*946379e7Schristos   'long double'			= FAT_DOUBLE | FAT_SIZE_LONGLONG,
114*946379e7Schristos   'char'/'int'			= FAT_CHAR,
115*946379e7Schristos   'wchar_t'/'wint_t'		= FAT_CHAR | FAT_SIZE_LONG,
116*946379e7Schristos   'const char *'		= FAT_STRING,
117*946379e7Schristos   'const wchar_t *'		= FAT_STRING | FAT_SIZE_LONG,
118*946379e7Schristos   'void *'			= FAT_POINTER,
119*946379e7Schristos   FAT_COUNT_SCHAR_POINTER	= FAT_COUNT_POINTER | FAT_SIZE_CHAR,
120*946379e7Schristos   FAT_COUNT_SHORT_POINTER	= FAT_COUNT_POINTER | FAT_SIZE_SHORT,
121*946379e7Schristos   FAT_COUNT_INT_POINTER		= FAT_COUNT_POINTER,
122*946379e7Schristos   FAT_COUNT_LONGINT_POINTER	= FAT_COUNT_POINTER | FAT_SIZE_LONG,
123*946379e7Schristos   FAT_COUNT_LONGLONGINT_POINTER	= FAT_COUNT_POINTER | FAT_SIZE_LONGLONG,
124*946379e7Schristos   */
125*946379e7Schristos   /* Bitmasks */
126*946379e7Schristos   FAT_SIZE_MASK		= (FAT_SIZE_SHORT | FAT_SIZE_CHAR
127*946379e7Schristos 			   | FAT_SIZE_LONG | FAT_SIZE_LONGLONG
128*946379e7Schristos 			   | FAT_SIZE_8_T | FAT_SIZE_16_T
129*946379e7Schristos 			   | FAT_SIZE_32_T | FAT_SIZE_64_T
130*946379e7Schristos 			   | FAT_SIZE_LEAST8_T | FAT_SIZE_LEAST16_T
131*946379e7Schristos 			   | FAT_SIZE_LEAST32_T | FAT_SIZE_LEAST64_T
132*946379e7Schristos 			   | FAT_SIZE_FAST8_T | FAT_SIZE_FAST16_T
133*946379e7Schristos 			   | FAT_SIZE_FAST32_T | FAT_SIZE_FAST64_T
134*946379e7Schristos 			   | FAT_SIZE_INTMAX_T | FAT_SIZE_INTPTR_T
135*946379e7Schristos 			   | FAT_SIZE_SIZE_T | FAT_SIZE_PTRDIFF_T)
136*946379e7Schristos };
137*946379e7Schristos 
138*946379e7Schristos struct numbered_arg
139*946379e7Schristos {
140*946379e7Schristos   unsigned int number;
141*946379e7Schristos   enum format_arg_type type;
142*946379e7Schristos };
143*946379e7Schristos 
144*946379e7Schristos struct unnumbered_arg
145*946379e7Schristos {
146*946379e7Schristos   enum format_arg_type type;
147*946379e7Schristos };
148*946379e7Schristos 
149*946379e7Schristos struct spec
150*946379e7Schristos {
151*946379e7Schristos   unsigned int directives;
152*946379e7Schristos   unsigned int unnumbered_arg_count;
153*946379e7Schristos   unsigned int allocated;
154*946379e7Schristos   struct unnumbered_arg *unnumbered;
155*946379e7Schristos   bool unlikely_intentional;
156*946379e7Schristos   unsigned int sysdep_directives_count;
157*946379e7Schristos   const char **sysdep_directives;
158*946379e7Schristos };
159*946379e7Schristos 
160*946379e7Schristos /* Locale independent test for a decimal digit.
161*946379e7Schristos    Argument can be  'char' or 'unsigned char'.  (Whereas the argument of
162*946379e7Schristos    <ctype.h> isdigit must be an 'unsigned char'.)  */
163*946379e7Schristos #undef isdigit
164*946379e7Schristos #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
165*946379e7Schristos 
166*946379e7Schristos 
167*946379e7Schristos static int
numbered_arg_compare(const void * p1,const void * p2)168*946379e7Schristos numbered_arg_compare (const void *p1, const void *p2)
169*946379e7Schristos {
170*946379e7Schristos   unsigned int n1 = ((const struct numbered_arg *) p1)->number;
171*946379e7Schristos   unsigned int n2 = ((const struct numbered_arg *) p2)->number;
172*946379e7Schristos 
173*946379e7Schristos   return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
174*946379e7Schristos }
175*946379e7Schristos 
176*946379e7Schristos #define INVALID_C99_MACRO(directive_number) \
177*946379e7Schristos   xasprintf (_("In the directive number %u, the token after '<' is not the name of a format specifier macro. The valid macro names are listed in ISO C 99 section 7.8.1."), directive_number)
178*946379e7Schristos 
179*946379e7Schristos static void *
format_parse(const char * format,bool translated,bool objc_extensions,char ** invalid_reason)180*946379e7Schristos format_parse (const char *format, bool translated, bool objc_extensions,
181*946379e7Schristos 	      char **invalid_reason)
182*946379e7Schristos {
183*946379e7Schristos   struct spec spec;
184*946379e7Schristos   unsigned int numbered_arg_count;
185*946379e7Schristos   struct numbered_arg *numbered;
186*946379e7Schristos   struct spec *result;
187*946379e7Schristos 
188*946379e7Schristos   spec.directives = 0;
189*946379e7Schristos   numbered_arg_count = 0;
190*946379e7Schristos   spec.unnumbered_arg_count = 0;
191*946379e7Schristos   spec.allocated = 0;
192*946379e7Schristos   numbered = NULL;
193*946379e7Schristos   spec.unnumbered = NULL;
194*946379e7Schristos   spec.unlikely_intentional = false;
195*946379e7Schristos   spec.sysdep_directives_count = 0;
196*946379e7Schristos   spec.sysdep_directives = NULL;
197*946379e7Schristos 
198*946379e7Schristos   for (; *format != '\0';)
199*946379e7Schristos     if (*format++ == '%')
200*946379e7Schristos       {
201*946379e7Schristos 	/* A directive.  */
202*946379e7Schristos 	unsigned int number = 0;
203*946379e7Schristos 	enum format_arg_type type;
204*946379e7Schristos 	enum format_arg_type size;
205*946379e7Schristos 
206*946379e7Schristos 	spec.directives++;
207*946379e7Schristos 
208*946379e7Schristos 	if (isdigit (*format))
209*946379e7Schristos 	  {
210*946379e7Schristos 	    const char *f = format;
211*946379e7Schristos 	    unsigned int m = 0;
212*946379e7Schristos 
213*946379e7Schristos 	    do
214*946379e7Schristos 	      {
215*946379e7Schristos 		m = 10 * m + (*f - '0');
216*946379e7Schristos 		f++;
217*946379e7Schristos 	      }
218*946379e7Schristos 	    while (isdigit (*f));
219*946379e7Schristos 
220*946379e7Schristos 	    if (*f == '$')
221*946379e7Schristos 	      {
222*946379e7Schristos 		if (m == 0)
223*946379e7Schristos 		  {
224*946379e7Schristos 		    *invalid_reason = INVALID_ARGNO_0 (spec.directives);
225*946379e7Schristos 		    goto bad_format;
226*946379e7Schristos 		  }
227*946379e7Schristos 		number = m;
228*946379e7Schristos 		format = ++f;
229*946379e7Schristos 	      }
230*946379e7Schristos 	  }
231*946379e7Schristos 
232*946379e7Schristos 	/* Parse flags.  */
233*946379e7Schristos 	for (;;)
234*946379e7Schristos 	  {
235*946379e7Schristos 	    if (*format == ' ' || *format == '+' || *format == '-'
236*946379e7Schristos 		|| *format == '#' || *format == '0' || *format == '\'')
237*946379e7Schristos 	      format++;
238*946379e7Schristos 	    else if (translated && *format == 'I')
239*946379e7Schristos 	      {
240*946379e7Schristos 		spec.sysdep_directives =
241*946379e7Schristos 		  (const char **)
242*946379e7Schristos 		  xrealloc (spec.sysdep_directives,
243*946379e7Schristos 			    2 * (spec.sysdep_directives_count + 1)
244*946379e7Schristos 			    * sizeof (const char *));
245*946379e7Schristos 		spec.sysdep_directives[2 * spec.sysdep_directives_count] = format;
246*946379e7Schristos 		spec.sysdep_directives[2 * spec.sysdep_directives_count + 1] = format + 1;
247*946379e7Schristos 		spec.sysdep_directives_count++;
248*946379e7Schristos 		format++;
249*946379e7Schristos 	      }
250*946379e7Schristos 	    else
251*946379e7Schristos 	      break;
252*946379e7Schristos 	  }
253*946379e7Schristos 
254*946379e7Schristos 	/* Parse width.  */
255*946379e7Schristos 	if (*format == '*')
256*946379e7Schristos 	  {
257*946379e7Schristos 	    unsigned int width_number = 0;
258*946379e7Schristos 
259*946379e7Schristos 	    format++;
260*946379e7Schristos 
261*946379e7Schristos 	    if (isdigit (*format))
262*946379e7Schristos 	      {
263*946379e7Schristos 		const char *f = format;
264*946379e7Schristos 		unsigned int m = 0;
265*946379e7Schristos 
266*946379e7Schristos 		do
267*946379e7Schristos 		  {
268*946379e7Schristos 		    m = 10 * m + (*f - '0');
269*946379e7Schristos 		    f++;
270*946379e7Schristos 		  }
271*946379e7Schristos 		while (isdigit (*f));
272*946379e7Schristos 
273*946379e7Schristos 		if (*f == '$')
274*946379e7Schristos 		  {
275*946379e7Schristos 		    if (m == 0)
276*946379e7Schristos 		      {
277*946379e7Schristos 			*invalid_reason =
278*946379e7Schristos 			  INVALID_WIDTH_ARGNO_0 (spec.directives);
279*946379e7Schristos 			goto bad_format;
280*946379e7Schristos 		      }
281*946379e7Schristos 		    width_number = m;
282*946379e7Schristos 		    format = ++f;
283*946379e7Schristos 		  }
284*946379e7Schristos 	      }
285*946379e7Schristos 
286*946379e7Schristos 	    if (width_number)
287*946379e7Schristos 	      {
288*946379e7Schristos 		/* Numbered argument.  */
289*946379e7Schristos 
290*946379e7Schristos 		/* Numbered and unnumbered specifications are exclusive.  */
291*946379e7Schristos 		if (spec.unnumbered_arg_count > 0)
292*946379e7Schristos 		  {
293*946379e7Schristos 		    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
294*946379e7Schristos 		    goto bad_format;
295*946379e7Schristos 		  }
296*946379e7Schristos 
297*946379e7Schristos 		if (spec.allocated == numbered_arg_count)
298*946379e7Schristos 		  {
299*946379e7Schristos 		    spec.allocated = 2 * spec.allocated + 1;
300*946379e7Schristos 		    numbered = (struct numbered_arg *) xrealloc (numbered, spec.allocated * sizeof (struct numbered_arg));
301*946379e7Schristos 		  }
302*946379e7Schristos 		numbered[numbered_arg_count].number = width_number;
303*946379e7Schristos 		numbered[numbered_arg_count].type = FAT_INTEGER;
304*946379e7Schristos 		numbered_arg_count++;
305*946379e7Schristos 	      }
306*946379e7Schristos 	    else
307*946379e7Schristos 	      {
308*946379e7Schristos 		/* Unnumbered argument.  */
309*946379e7Schristos 
310*946379e7Schristos 		/* Numbered and unnumbered specifications are exclusive.  */
311*946379e7Schristos 		if (numbered_arg_count > 0)
312*946379e7Schristos 		  {
313*946379e7Schristos 		    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
314*946379e7Schristos 		    goto bad_format;
315*946379e7Schristos 		  }
316*946379e7Schristos 
317*946379e7Schristos 		if (spec.allocated == spec.unnumbered_arg_count)
318*946379e7Schristos 		  {
319*946379e7Schristos 		    spec.allocated = 2 * spec.allocated + 1;
320*946379e7Schristos 		    spec.unnumbered = (struct unnumbered_arg *) xrealloc (spec.unnumbered, spec.allocated * sizeof (struct unnumbered_arg));
321*946379e7Schristos 		  }
322*946379e7Schristos 		spec.unnumbered[spec.unnumbered_arg_count].type = FAT_INTEGER;
323*946379e7Schristos 		spec.unnumbered_arg_count++;
324*946379e7Schristos 	      }
325*946379e7Schristos 	  }
326*946379e7Schristos 	else if (isdigit (*format))
327*946379e7Schristos 	  {
328*946379e7Schristos 	    do format++; while (isdigit (*format));
329*946379e7Schristos 	  }
330*946379e7Schristos 
331*946379e7Schristos 	/* Parse precision.  */
332*946379e7Schristos 	if (*format == '.')
333*946379e7Schristos 	  {
334*946379e7Schristos 	    format++;
335*946379e7Schristos 
336*946379e7Schristos 	    if (*format == '*')
337*946379e7Schristos 	      {
338*946379e7Schristos 		unsigned int precision_number = 0;
339*946379e7Schristos 
340*946379e7Schristos 		format++;
341*946379e7Schristos 
342*946379e7Schristos 		if (isdigit (*format))
343*946379e7Schristos 		  {
344*946379e7Schristos 		    const char *f = format;
345*946379e7Schristos 		    unsigned int m = 0;
346*946379e7Schristos 
347*946379e7Schristos 		    do
348*946379e7Schristos 		      {
349*946379e7Schristos 			m = 10 * m + (*f - '0');
350*946379e7Schristos 			f++;
351*946379e7Schristos 		      }
352*946379e7Schristos 		    while (isdigit (*f));
353*946379e7Schristos 
354*946379e7Schristos 		    if (*f == '$')
355*946379e7Schristos 		      {
356*946379e7Schristos 			if (m == 0)
357*946379e7Schristos 			  {
358*946379e7Schristos 			    *invalid_reason =
359*946379e7Schristos 			      INVALID_PRECISION_ARGNO_0 (spec.directives);
360*946379e7Schristos 			    goto bad_format;
361*946379e7Schristos 			  }
362*946379e7Schristos 			precision_number = m;
363*946379e7Schristos 			format = ++f;
364*946379e7Schristos 		      }
365*946379e7Schristos 		  }
366*946379e7Schristos 
367*946379e7Schristos 		if (precision_number)
368*946379e7Schristos 		  {
369*946379e7Schristos 		    /* Numbered argument.  */
370*946379e7Schristos 
371*946379e7Schristos 		    /* Numbered and unnumbered specifications are exclusive.  */
372*946379e7Schristos 		    if (spec.unnumbered_arg_count > 0)
373*946379e7Schristos 		      {
374*946379e7Schristos 			*invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
375*946379e7Schristos 			goto bad_format;
376*946379e7Schristos 		      }
377*946379e7Schristos 
378*946379e7Schristos 		    if (spec.allocated == numbered_arg_count)
379*946379e7Schristos 		      {
380*946379e7Schristos 			spec.allocated = 2 * spec.allocated + 1;
381*946379e7Schristos 			numbered = (struct numbered_arg *) xrealloc (numbered, spec.allocated * sizeof (struct numbered_arg));
382*946379e7Schristos 		      }
383*946379e7Schristos 		    numbered[numbered_arg_count].number = precision_number;
384*946379e7Schristos 		    numbered[numbered_arg_count].type = FAT_INTEGER;
385*946379e7Schristos 		    numbered_arg_count++;
386*946379e7Schristos 		  }
387*946379e7Schristos 		else
388*946379e7Schristos 		  {
389*946379e7Schristos 		    /* Unnumbered argument.  */
390*946379e7Schristos 
391*946379e7Schristos 		    /* Numbered and unnumbered specifications are exclusive.  */
392*946379e7Schristos 		    if (numbered_arg_count > 0)
393*946379e7Schristos 		      {
394*946379e7Schristos 			*invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
395*946379e7Schristos 			goto bad_format;
396*946379e7Schristos 		      }
397*946379e7Schristos 
398*946379e7Schristos 		    if (spec.allocated == spec.unnumbered_arg_count)
399*946379e7Schristos 		      {
400*946379e7Schristos 			spec.allocated = 2 * spec.allocated + 1;
401*946379e7Schristos 			spec.unnumbered = (struct unnumbered_arg *) xrealloc (spec.unnumbered, spec.allocated * sizeof (struct unnumbered_arg));
402*946379e7Schristos 		      }
403*946379e7Schristos 		    spec.unnumbered[spec.unnumbered_arg_count].type = FAT_INTEGER;
404*946379e7Schristos 		    spec.unnumbered_arg_count++;
405*946379e7Schristos 		  }
406*946379e7Schristos 	      }
407*946379e7Schristos 	    else if (isdigit (*format))
408*946379e7Schristos 	      {
409*946379e7Schristos 		do format++; while (isdigit (*format));
410*946379e7Schristos 	      }
411*946379e7Schristos 	  }
412*946379e7Schristos 
413*946379e7Schristos 	if (*format == '<')
414*946379e7Schristos 	  {
415*946379e7Schristos 	    spec.sysdep_directives =
416*946379e7Schristos 	      (const char **)
417*946379e7Schristos 	      xrealloc (spec.sysdep_directives,
418*946379e7Schristos 			2 * (spec.sysdep_directives_count + 1)
419*946379e7Schristos 			* sizeof (const char *));
420*946379e7Schristos 	    spec.sysdep_directives[2 * spec.sysdep_directives_count] = format;
421*946379e7Schristos 
422*946379e7Schristos 	    format++;
423*946379e7Schristos 	    /* Parse ISO C 99 section 7.8.1 format string directive.
424*946379e7Schristos 	       Syntax:
425*946379e7Schristos 	       P R I { d | i | o | u | x | X }
426*946379e7Schristos 	       { { | LEAST | FAST } { 8 | 16 | 32 | 64 } | MAX | PTR }  */
427*946379e7Schristos 	    if (*format != 'P')
428*946379e7Schristos 	      {
429*946379e7Schristos 		*invalid_reason = INVALID_C99_MACRO (spec.directives);
430*946379e7Schristos 		goto bad_format;
431*946379e7Schristos 	      }
432*946379e7Schristos 	    format++;
433*946379e7Schristos 	    if (*format != 'R')
434*946379e7Schristos 	      {
435*946379e7Schristos 		*invalid_reason = INVALID_C99_MACRO (spec.directives);
436*946379e7Schristos 		goto bad_format;
437*946379e7Schristos 	      }
438*946379e7Schristos 	    format++;
439*946379e7Schristos 	    if (*format != 'I')
440*946379e7Schristos 	      {
441*946379e7Schristos 		*invalid_reason = INVALID_C99_MACRO (spec.directives);
442*946379e7Schristos 		goto bad_format;
443*946379e7Schristos 	      }
444*946379e7Schristos 	    format++;
445*946379e7Schristos 
446*946379e7Schristos 	    switch (*format)
447*946379e7Schristos 	      {
448*946379e7Schristos 	      case 'i': case 'd':
449*946379e7Schristos 		type = FAT_INTEGER;
450*946379e7Schristos 		break;
451*946379e7Schristos 	      case 'u': case 'o': case 'x': case 'X':
452*946379e7Schristos 		type = FAT_INTEGER | FAT_UNSIGNED;
453*946379e7Schristos 		break;
454*946379e7Schristos 	      default:
455*946379e7Schristos 		*invalid_reason = INVALID_C99_MACRO (spec.directives);
456*946379e7Schristos 		goto bad_format;
457*946379e7Schristos 	      }
458*946379e7Schristos 	    format++;
459*946379e7Schristos 
460*946379e7Schristos 	    if (format[0] == 'M' && format[1] == 'A' && format[2] == 'X')
461*946379e7Schristos 	      {
462*946379e7Schristos 		type |= FAT_SIZE_INTMAX_T;
463*946379e7Schristos 		format += 3;
464*946379e7Schristos 	      }
465*946379e7Schristos 	    else if (format[0] == 'P' && format[1] == 'T' && format[2] == 'R')
466*946379e7Schristos 	      {
467*946379e7Schristos 		type |= FAT_SIZE_INTPTR_T;
468*946379e7Schristos 		format += 3;
469*946379e7Schristos 	      }
470*946379e7Schristos 	    else
471*946379e7Schristos 	      {
472*946379e7Schristos 		if (format[0] == 'L' && format[1] == 'E' && format[2] == 'A'
473*946379e7Schristos 		    && format[3] == 'S' && format[4] == 'T')
474*946379e7Schristos 		  {
475*946379e7Schristos 		    format += 5;
476*946379e7Schristos 		    if (format[0] == '8')
477*946379e7Schristos 		      {
478*946379e7Schristos 			type |= FAT_SIZE_LEAST8_T;
479*946379e7Schristos 			format++;
480*946379e7Schristos 		      }
481*946379e7Schristos 		    else if (format[0] == '1' && format[1] == '6')
482*946379e7Schristos 		      {
483*946379e7Schristos 			type |= FAT_SIZE_LEAST16_T;
484*946379e7Schristos 			format += 2;
485*946379e7Schristos 		      }
486*946379e7Schristos 		    else if (format[0] == '3' && format[1] == '2')
487*946379e7Schristos 		      {
488*946379e7Schristos 			type |= FAT_SIZE_LEAST32_T;
489*946379e7Schristos 			format += 2;
490*946379e7Schristos 		      }
491*946379e7Schristos 		    else if (format[0] == '6' && format[1] == '4')
492*946379e7Schristos 		      {
493*946379e7Schristos 			type |= FAT_SIZE_LEAST64_T;
494*946379e7Schristos 			format += 2;
495*946379e7Schristos 		      }
496*946379e7Schristos 		    else
497*946379e7Schristos 		      {
498*946379e7Schristos 			*invalid_reason = INVALID_C99_MACRO (spec.directives);
499*946379e7Schristos 			goto bad_format;
500*946379e7Schristos 		      }
501*946379e7Schristos 		  }
502*946379e7Schristos 		else if (format[0] == 'F' && format[1] == 'A'
503*946379e7Schristos 			 && format[2] == 'S' && format[3] == 'T')
504*946379e7Schristos 		  {
505*946379e7Schristos 		    format += 4;
506*946379e7Schristos 		    if (format[0] == '8')
507*946379e7Schristos 		      {
508*946379e7Schristos 			type |= FAT_SIZE_FAST8_T;
509*946379e7Schristos 			format++;
510*946379e7Schristos 		      }
511*946379e7Schristos 		    else if (format[0] == '1' && format[1] == '6')
512*946379e7Schristos 		      {
513*946379e7Schristos 			type |= FAT_SIZE_FAST16_T;
514*946379e7Schristos 			format += 2;
515*946379e7Schristos 		      }
516*946379e7Schristos 		    else if (format[0] == '3' && format[1] == '2')
517*946379e7Schristos 		      {
518*946379e7Schristos 			type |= FAT_SIZE_FAST32_T;
519*946379e7Schristos 			format += 2;
520*946379e7Schristos 		      }
521*946379e7Schristos 		    else if (format[0] == '6' && format[1] == '4')
522*946379e7Schristos 		      {
523*946379e7Schristos 			type |= FAT_SIZE_FAST64_T;
524*946379e7Schristos 			format += 2;
525*946379e7Schristos 		      }
526*946379e7Schristos 		    else
527*946379e7Schristos 		      {
528*946379e7Schristos 			*invalid_reason = INVALID_C99_MACRO (spec.directives);
529*946379e7Schristos 			goto bad_format;
530*946379e7Schristos 		      }
531*946379e7Schristos 		  }
532*946379e7Schristos 		else
533*946379e7Schristos 		  {
534*946379e7Schristos 		    if (format[0] == '8')
535*946379e7Schristos 		      {
536*946379e7Schristos 			type |= FAT_SIZE_8_T;
537*946379e7Schristos 			format++;
538*946379e7Schristos 		      }
539*946379e7Schristos 		    else if (format[0] == '1' && format[1] == '6')
540*946379e7Schristos 		      {
541*946379e7Schristos 			type |= FAT_SIZE_16_T;
542*946379e7Schristos 			format += 2;
543*946379e7Schristos 		      }
544*946379e7Schristos 		    else if (format[0] == '3' && format[1] == '2')
545*946379e7Schristos 		      {
546*946379e7Schristos 			type |= FAT_SIZE_32_T;
547*946379e7Schristos 			format += 2;
548*946379e7Schristos 		      }
549*946379e7Schristos 		    else if (format[0] == '6' && format[1] == '4')
550*946379e7Schristos 		      {
551*946379e7Schristos 			type |= FAT_SIZE_64_T;
552*946379e7Schristos 			format += 2;
553*946379e7Schristos 		      }
554*946379e7Schristos 		    else
555*946379e7Schristos 		      {
556*946379e7Schristos 			*invalid_reason = INVALID_C99_MACRO (spec.directives);
557*946379e7Schristos 			goto bad_format;
558*946379e7Schristos 		      }
559*946379e7Schristos 		  }
560*946379e7Schristos 	      }
561*946379e7Schristos 
562*946379e7Schristos 	    if (*format != '>')
563*946379e7Schristos 	      {
564*946379e7Schristos 		*invalid_reason =
565*946379e7Schristos 		  xasprintf (_("In the directive number %u, the token after '<' is not followed by '>'."), spec.directives);
566*946379e7Schristos 		goto bad_format;
567*946379e7Schristos 	      }
568*946379e7Schristos 
569*946379e7Schristos 	    spec.sysdep_directives[2 * spec.sysdep_directives_count + 1] = format + 1;
570*946379e7Schristos 	    spec.sysdep_directives_count++;
571*946379e7Schristos 	  }
572*946379e7Schristos 	else
573*946379e7Schristos 	  {
574*946379e7Schristos 	    /* Parse size.  */
575*946379e7Schristos 	    size = 0;
576*946379e7Schristos 	    for (;; format++)
577*946379e7Schristos 	      {
578*946379e7Schristos 		if (*format == 'h')
579*946379e7Schristos 		  {
580*946379e7Schristos 		    if (size & (FAT_SIZE_SHORT | FAT_SIZE_CHAR))
581*946379e7Schristos 		      size = FAT_SIZE_CHAR;
582*946379e7Schristos 		    else
583*946379e7Schristos 		      size = FAT_SIZE_SHORT;
584*946379e7Schristos 		  }
585*946379e7Schristos 		else if (*format == 'l')
586*946379e7Schristos 		  {
587*946379e7Schristos 		    if (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG))
588*946379e7Schristos 		      size = FAT_SIZE_LONGLONG;
589*946379e7Schristos 		    else
590*946379e7Schristos 		      size = FAT_SIZE_LONG;
591*946379e7Schristos 		  }
592*946379e7Schristos 		else if (*format == 'L')
593*946379e7Schristos 		  size = FAT_SIZE_LONGLONG;
594*946379e7Schristos 		else if (*format == 'q')
595*946379e7Schristos 		  /* Old BSD 4.4 convention.  */
596*946379e7Schristos 		  size = FAT_SIZE_LONGLONG;
597*946379e7Schristos 		else if (*format == 'j')
598*946379e7Schristos 		  size = FAT_SIZE_INTMAX_T;
599*946379e7Schristos 		else if (*format == 'z' || *format == 'Z')
600*946379e7Schristos 		  /* 'z' is standardized in ISO C 99, but glibc uses 'Z'
601*946379e7Schristos 		     because the warning facility in gcc-2.95.2 understands
602*946379e7Schristos 		     only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784).  */
603*946379e7Schristos 		  size = FAT_SIZE_SIZE_T;
604*946379e7Schristos 		else if (*format == 't')
605*946379e7Schristos 		  size = FAT_SIZE_PTRDIFF_T;
606*946379e7Schristos 		else
607*946379e7Schristos 		  break;
608*946379e7Schristos 	      }
609*946379e7Schristos 
610*946379e7Schristos 	    switch (*format)
611*946379e7Schristos 	      {
612*946379e7Schristos 	      case '%':
613*946379e7Schristos 		/* Programmers writing _("%2%") most often will not want to
614*946379e7Schristos 		   use this string as a c-format string, but rather as a
615*946379e7Schristos 		   literal or as a different kind of format string.  */
616*946379e7Schristos 		if (format[-1] != '%')
617*946379e7Schristos 		  spec.unlikely_intentional = true;
618*946379e7Schristos 		type = FAT_NONE;
619*946379e7Schristos 		break;
620*946379e7Schristos 	      case 'm': /* glibc extension */
621*946379e7Schristos 		type = FAT_NONE;
622*946379e7Schristos 		break;
623*946379e7Schristos 	      case 'c':
624*946379e7Schristos 		type = FAT_CHAR;
625*946379e7Schristos 		type |= (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG)
626*946379e7Schristos 			 ? FAT_WIDE : 0);
627*946379e7Schristos 		break;
628*946379e7Schristos 	      case 'C': /* obsolete */
629*946379e7Schristos 		type = FAT_CHAR | FAT_WIDE;
630*946379e7Schristos 		break;
631*946379e7Schristos 	      case 's':
632*946379e7Schristos 		type = FAT_STRING;
633*946379e7Schristos 		type |= (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG)
634*946379e7Schristos 			 ? FAT_WIDE : 0);
635*946379e7Schristos 		break;
636*946379e7Schristos 	      case 'S': /* obsolete */
637*946379e7Schristos 		type = FAT_STRING | FAT_WIDE;
638*946379e7Schristos 		break;
639*946379e7Schristos 	      case 'i': case 'd':
640*946379e7Schristos 		type = FAT_INTEGER;
641*946379e7Schristos 		type |= (size & FAT_SIZE_MASK);
642*946379e7Schristos 		break;
643*946379e7Schristos 	      case 'u': case 'o': case 'x': case 'X':
644*946379e7Schristos 		type = FAT_INTEGER | FAT_UNSIGNED;
645*946379e7Schristos 		type |= (size & FAT_SIZE_MASK);
646*946379e7Schristos 		break;
647*946379e7Schristos 	      case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
648*946379e7Schristos 	      case 'a': case 'A':
649*946379e7Schristos 		type = FAT_DOUBLE;
650*946379e7Schristos 		type |= (size & FAT_SIZE_LONGLONG);
651*946379e7Schristos 		break;
652*946379e7Schristos 	      case '@':
653*946379e7Schristos 		if (objc_extensions)
654*946379e7Schristos 		  {
655*946379e7Schristos 		    type = FAT_OBJC_OBJECT;
656*946379e7Schristos 		    break;
657*946379e7Schristos 		  }
658*946379e7Schristos 		goto other;
659*946379e7Schristos 	      case 'p':
660*946379e7Schristos 		type = FAT_POINTER;
661*946379e7Schristos 		break;
662*946379e7Schristos 	      case 'n':
663*946379e7Schristos 		type = FAT_COUNT_POINTER;
664*946379e7Schristos 		type |= (size & FAT_SIZE_MASK);
665*946379e7Schristos 		break;
666*946379e7Schristos 	      other:
667*946379e7Schristos 	      default:
668*946379e7Schristos 		*invalid_reason =
669*946379e7Schristos 		  (*format == '\0'
670*946379e7Schristos 		   ? INVALID_UNTERMINATED_DIRECTIVE ()
671*946379e7Schristos 		   : INVALID_CONVERSION_SPECIFIER (spec.directives, *format));
672*946379e7Schristos 		goto bad_format;
673*946379e7Schristos 	      }
674*946379e7Schristos 	  }
675*946379e7Schristos 
676*946379e7Schristos 	if (type != FAT_NONE)
677*946379e7Schristos 	  {
678*946379e7Schristos 	    if (number)
679*946379e7Schristos 	      {
680*946379e7Schristos 		/* Numbered argument.  */
681*946379e7Schristos 
682*946379e7Schristos 		/* Numbered and unnumbered specifications are exclusive.  */
683*946379e7Schristos 		if (spec.unnumbered_arg_count > 0)
684*946379e7Schristos 		  {
685*946379e7Schristos 		    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
686*946379e7Schristos 		    goto bad_format;
687*946379e7Schristos 		  }
688*946379e7Schristos 
689*946379e7Schristos 		if (spec.allocated == numbered_arg_count)
690*946379e7Schristos 		  {
691*946379e7Schristos 		    spec.allocated = 2 * spec.allocated + 1;
692*946379e7Schristos 		    numbered = (struct numbered_arg *) xrealloc (numbered, spec.allocated * sizeof (struct numbered_arg));
693*946379e7Schristos 		  }
694*946379e7Schristos 		numbered[numbered_arg_count].number = number;
695*946379e7Schristos 		numbered[numbered_arg_count].type = type;
696*946379e7Schristos 		numbered_arg_count++;
697*946379e7Schristos 	      }
698*946379e7Schristos 	    else
699*946379e7Schristos 	      {
700*946379e7Schristos 		/* Unnumbered argument.  */
701*946379e7Schristos 
702*946379e7Schristos 		/* Numbered and unnumbered specifications are exclusive.  */
703*946379e7Schristos 		if (numbered_arg_count > 0)
704*946379e7Schristos 		  {
705*946379e7Schristos 		    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
706*946379e7Schristos 		    goto bad_format;
707*946379e7Schristos 		  }
708*946379e7Schristos 
709*946379e7Schristos 		if (spec.allocated == spec.unnumbered_arg_count)
710*946379e7Schristos 		  {
711*946379e7Schristos 		    spec.allocated = 2 * spec.allocated + 1;
712*946379e7Schristos 		    spec.unnumbered = (struct unnumbered_arg *) xrealloc (spec.unnumbered, spec.allocated * sizeof (struct unnumbered_arg));
713*946379e7Schristos 		  }
714*946379e7Schristos 		spec.unnumbered[spec.unnumbered_arg_count].type = type;
715*946379e7Schristos 		spec.unnumbered_arg_count++;
716*946379e7Schristos 	      }
717*946379e7Schristos 	  }
718*946379e7Schristos 
719*946379e7Schristos 	format++;
720*946379e7Schristos       }
721*946379e7Schristos 
722*946379e7Schristos   /* Sort the numbered argument array, and eliminate duplicates.  */
723*946379e7Schristos   if (numbered_arg_count > 1)
724*946379e7Schristos     {
725*946379e7Schristos       unsigned int i, j;
726*946379e7Schristos       bool err;
727*946379e7Schristos 
728*946379e7Schristos       qsort (numbered, numbered_arg_count,
729*946379e7Schristos 	     sizeof (struct numbered_arg), numbered_arg_compare);
730*946379e7Schristos 
731*946379e7Schristos       /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
732*946379e7Schristos       err = false;
733*946379e7Schristos       for (i = j = 0; i < numbered_arg_count; i++)
734*946379e7Schristos 	if (j > 0 && numbered[i].number == numbered[j-1].number)
735*946379e7Schristos 	  {
736*946379e7Schristos 	    enum format_arg_type type1 = numbered[i].type;
737*946379e7Schristos 	    enum format_arg_type type2 = numbered[j-1].type;
738*946379e7Schristos 	    enum format_arg_type type_both;
739*946379e7Schristos 
740*946379e7Schristos 	    if (type1 == type2)
741*946379e7Schristos 	      type_both = type1;
742*946379e7Schristos 	    else
743*946379e7Schristos 	      {
744*946379e7Schristos 		/* Incompatible types.  */
745*946379e7Schristos 		type_both = FAT_NONE;
746*946379e7Schristos 		if (!err)
747*946379e7Schristos 		  *invalid_reason =
748*946379e7Schristos 		    INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
749*946379e7Schristos 		err = true;
750*946379e7Schristos 	      }
751*946379e7Schristos 
752*946379e7Schristos 	    numbered[j-1].type = type_both;
753*946379e7Schristos 	  }
754*946379e7Schristos 	else
755*946379e7Schristos 	  {
756*946379e7Schristos 	    if (j < i)
757*946379e7Schristos 	      {
758*946379e7Schristos 		numbered[j].number = numbered[i].number;
759*946379e7Schristos 		numbered[j].type = numbered[i].type;
760*946379e7Schristos 	      }
761*946379e7Schristos 	    j++;
762*946379e7Schristos 	  }
763*946379e7Schristos       numbered_arg_count = j;
764*946379e7Schristos       if (err)
765*946379e7Schristos 	/* *invalid_reason has already been set above.  */
766*946379e7Schristos 	goto bad_format;
767*946379e7Schristos     }
768*946379e7Schristos 
769*946379e7Schristos   /* Verify that the format strings uses all arguments up to the highest
770*946379e7Schristos      numbered one.  */
771*946379e7Schristos   if (numbered_arg_count > 0)
772*946379e7Schristos     {
773*946379e7Schristos       unsigned int i;
774*946379e7Schristos 
775*946379e7Schristos       for (i = 0; i < numbered_arg_count; i++)
776*946379e7Schristos 	if (numbered[i].number != i + 1)
777*946379e7Schristos 	  {
778*946379e7Schristos 	    *invalid_reason =
779*946379e7Schristos 	      xasprintf (_("The string refers to argument number %u but ignores argument number %u."), numbered[i].number, i + 1);
780*946379e7Schristos 	    goto bad_format;
781*946379e7Schristos 	  }
782*946379e7Schristos 
783*946379e7Schristos       /* So now the numbered arguments array is equivalent to a sequence
784*946379e7Schristos 	 of unnumbered arguments.  */
785*946379e7Schristos       spec.unnumbered_arg_count = numbered_arg_count;
786*946379e7Schristos       spec.allocated = spec.unnumbered_arg_count;
787*946379e7Schristos       spec.unnumbered = (struct unnumbered_arg *) xmalloc (spec.allocated * sizeof (struct unnumbered_arg));
788*946379e7Schristos       for (i = 0; i < spec.unnumbered_arg_count; i++)
789*946379e7Schristos 	spec.unnumbered[i].type = numbered[i].type;
790*946379e7Schristos       free (numbered);
791*946379e7Schristos       numbered_arg_count = 0;
792*946379e7Schristos     }
793*946379e7Schristos 
794*946379e7Schristos   result = (struct spec *) xmalloc (sizeof (struct spec));
795*946379e7Schristos   *result = spec;
796*946379e7Schristos   return result;
797*946379e7Schristos 
798*946379e7Schristos  bad_format:
799*946379e7Schristos   if (numbered != NULL)
800*946379e7Schristos     free (numbered);
801*946379e7Schristos   if (spec.unnumbered != NULL)
802*946379e7Schristos     free (spec.unnumbered);
803*946379e7Schristos   if (spec.sysdep_directives != NULL)
804*946379e7Schristos     free (spec.sysdep_directives);
805*946379e7Schristos   return NULL;
806*946379e7Schristos }
807*946379e7Schristos 
808*946379e7Schristos static void *
format_c_parse(const char * format,bool translated,char ** invalid_reason)809*946379e7Schristos format_c_parse (const char *format, bool translated, char **invalid_reason)
810*946379e7Schristos {
811*946379e7Schristos   return format_parse (format, translated, false, invalid_reason);
812*946379e7Schristos }
813*946379e7Schristos 
814*946379e7Schristos static void *
format_objc_parse(const char * format,bool translated,char ** invalid_reason)815*946379e7Schristos format_objc_parse (const char *format, bool translated, char **invalid_reason)
816*946379e7Schristos {
817*946379e7Schristos   return format_parse (format, translated, true, invalid_reason);
818*946379e7Schristos }
819*946379e7Schristos 
820*946379e7Schristos static void
format_free(void * descr)821*946379e7Schristos format_free (void *descr)
822*946379e7Schristos {
823*946379e7Schristos   struct spec *spec = (struct spec *) descr;
824*946379e7Schristos 
825*946379e7Schristos   if (spec->unnumbered != NULL)
826*946379e7Schristos     free (spec->unnumbered);
827*946379e7Schristos   if (spec->sysdep_directives != NULL)
828*946379e7Schristos     free (spec->sysdep_directives);
829*946379e7Schristos   free (spec);
830*946379e7Schristos }
831*946379e7Schristos 
832*946379e7Schristos static bool
format_is_unlikely_intentional(void * descr)833*946379e7Schristos format_is_unlikely_intentional (void *descr)
834*946379e7Schristos {
835*946379e7Schristos   struct spec *spec = (struct spec *) descr;
836*946379e7Schristos 
837*946379e7Schristos   return spec->unlikely_intentional;
838*946379e7Schristos }
839*946379e7Schristos 
840*946379e7Schristos static int
format_get_number_of_directives(void * descr)841*946379e7Schristos format_get_number_of_directives (void *descr)
842*946379e7Schristos {
843*946379e7Schristos   struct spec *spec = (struct spec *) descr;
844*946379e7Schristos 
845*946379e7Schristos   return spec->directives;
846*946379e7Schristos }
847*946379e7Schristos 
848*946379e7Schristos static bool
format_check(void * msgid_descr,void * msgstr_descr,bool equality,formatstring_error_logger_t error_logger,const char * pretty_msgstr)849*946379e7Schristos format_check (void *msgid_descr, void *msgstr_descr, bool equality,
850*946379e7Schristos 	      formatstring_error_logger_t error_logger,
851*946379e7Schristos 	      const char *pretty_msgstr)
852*946379e7Schristos {
853*946379e7Schristos   struct spec *spec1 = (struct spec *) msgid_descr;
854*946379e7Schristos   struct spec *spec2 = (struct spec *) msgstr_descr;
855*946379e7Schristos   bool err = false;
856*946379e7Schristos   unsigned int i;
857*946379e7Schristos 
858*946379e7Schristos   /* Check the argument types are the same.  */
859*946379e7Schristos   if (equality
860*946379e7Schristos       ? spec1->unnumbered_arg_count != spec2->unnumbered_arg_count
861*946379e7Schristos       : spec1->unnumbered_arg_count < spec2->unnumbered_arg_count)
862*946379e7Schristos     {
863*946379e7Schristos       if (error_logger)
864*946379e7Schristos 	error_logger (_("number of format specifications in 'msgid' and '%s' does not match"),
865*946379e7Schristos 		      pretty_msgstr);
866*946379e7Schristos       err = true;
867*946379e7Schristos     }
868*946379e7Schristos   else
869*946379e7Schristos     for (i = 0; i < spec2->unnumbered_arg_count; i++)
870*946379e7Schristos       if (spec1->unnumbered[i].type != spec2->unnumbered[i].type)
871*946379e7Schristos 	{
872*946379e7Schristos 	  if (error_logger)
873*946379e7Schristos 	    error_logger (_("format specifications in 'msgid' and '%s' for argument %u are not the same"),
874*946379e7Schristos 			  pretty_msgstr, i + 1);
875*946379e7Schristos 	  err = true;
876*946379e7Schristos 	}
877*946379e7Schristos 
878*946379e7Schristos   return err;
879*946379e7Schristos }
880*946379e7Schristos 
881*946379e7Schristos 
882*946379e7Schristos struct formatstring_parser formatstring_c =
883*946379e7Schristos {
884*946379e7Schristos   format_c_parse,
885*946379e7Schristos   format_free,
886*946379e7Schristos   format_get_number_of_directives,
887*946379e7Schristos   format_is_unlikely_intentional,
888*946379e7Schristos   format_check
889*946379e7Schristos };
890*946379e7Schristos 
891*946379e7Schristos 
892*946379e7Schristos struct formatstring_parser formatstring_objc =
893*946379e7Schristos {
894*946379e7Schristos   format_objc_parse,
895*946379e7Schristos   format_free,
896*946379e7Schristos   format_get_number_of_directives,
897*946379e7Schristos   format_is_unlikely_intentional,
898*946379e7Schristos   format_check
899*946379e7Schristos };
900*946379e7Schristos 
901*946379e7Schristos 
902*946379e7Schristos void
get_sysdep_c_format_directives(const char * string,bool translated,struct interval ** intervalsp,size_t * lengthp)903*946379e7Schristos get_sysdep_c_format_directives (const char *string, bool translated,
904*946379e7Schristos 				struct interval **intervalsp, size_t *lengthp)
905*946379e7Schristos {
906*946379e7Schristos   /* Parse the format string with all possible extensions turned on.  (The
907*946379e7Schristos      caller has already verified that the format string is valid for the
908*946379e7Schristos      particular language.)  */
909*946379e7Schristos   char *invalid_reason = NULL;
910*946379e7Schristos   struct spec *descr =
911*946379e7Schristos     (struct spec *) format_parse (string, translated, true, &invalid_reason);
912*946379e7Schristos 
913*946379e7Schristos   if (descr != NULL && descr->sysdep_directives_count > 0)
914*946379e7Schristos     {
915*946379e7Schristos       unsigned int n = descr->sysdep_directives_count;
916*946379e7Schristos       struct interval *intervals =
917*946379e7Schristos 	(struct interval *) xmalloc (n * sizeof (struct interval));
918*946379e7Schristos       unsigned int i;
919*946379e7Schristos 
920*946379e7Schristos       for (i = 0; i < n; i++)
921*946379e7Schristos 	{
922*946379e7Schristos 	  intervals[i].startpos = descr->sysdep_directives[2 * i] - string;
923*946379e7Schristos 	  intervals[i].endpos = descr->sysdep_directives[2 * i + 1] - string;
924*946379e7Schristos 	}
925*946379e7Schristos       *intervalsp = intervals;
926*946379e7Schristos       *lengthp = n;
927*946379e7Schristos     }
928*946379e7Schristos   else
929*946379e7Schristos     {
930*946379e7Schristos       *intervalsp = NULL;
931*946379e7Schristos       *lengthp = 0;
932*946379e7Schristos     }
933*946379e7Schristos 
934*946379e7Schristos   if (descr != NULL)
935*946379e7Schristos     format_free (descr);
936*946379e7Schristos   else
937*946379e7Schristos     free (invalid_reason);
938*946379e7Schristos }
939*946379e7Schristos 
940*946379e7Schristos 
941*946379e7Schristos #ifdef TEST
942*946379e7Schristos 
943*946379e7Schristos /* Test program: Print the argument list specification returned by
944*946379e7Schristos    format_parse for strings read from standard input.  */
945*946379e7Schristos 
946*946379e7Schristos #include <stdio.h>
947*946379e7Schristos #include "getline.h"
948*946379e7Schristos 
949*946379e7Schristos static void
format_print(void * descr)950*946379e7Schristos format_print (void *descr)
951*946379e7Schristos {
952*946379e7Schristos   struct spec *spec = (struct spec *) descr;
953*946379e7Schristos   unsigned int i;
954*946379e7Schristos 
955*946379e7Schristos   if (spec == NULL)
956*946379e7Schristos     {
957*946379e7Schristos       printf ("INVALID");
958*946379e7Schristos       return;
959*946379e7Schristos     }
960*946379e7Schristos 
961*946379e7Schristos   printf ("(");
962*946379e7Schristos   for (i = 0; i < spec->unnumbered_arg_count; i++)
963*946379e7Schristos     {
964*946379e7Schristos       if (i > 0)
965*946379e7Schristos 	printf (" ");
966*946379e7Schristos       if (spec->unnumbered[i].type & FAT_UNSIGNED)
967*946379e7Schristos 	printf ("[unsigned]");
968*946379e7Schristos       switch (spec->unnumbered[i].type & FAT_SIZE_MASK)
969*946379e7Schristos 	{
970*946379e7Schristos 	case 0:
971*946379e7Schristos 	  break;
972*946379e7Schristos 	case FAT_SIZE_SHORT:
973*946379e7Schristos 	  printf ("[short]");
974*946379e7Schristos 	  break;
975*946379e7Schristos 	case FAT_SIZE_CHAR:
976*946379e7Schristos 	  printf ("[char]");
977*946379e7Schristos 	  break;
978*946379e7Schristos 	case FAT_SIZE_LONG:
979*946379e7Schristos 	  printf ("[long]");
980*946379e7Schristos 	  break;
981*946379e7Schristos 	case FAT_SIZE_LONGLONG:
982*946379e7Schristos 	  printf ("[long long]");
983*946379e7Schristos 	  break;
984*946379e7Schristos 	case FAT_SIZE_8_T:
985*946379e7Schristos 	  printf ("[int8_t]");
986*946379e7Schristos 	  break;
987*946379e7Schristos 	case FAT_SIZE_16_T:
988*946379e7Schristos 	  printf ("[int16_t]");
989*946379e7Schristos 	  break;
990*946379e7Schristos 	case FAT_SIZE_32_T:
991*946379e7Schristos 	  printf ("[int32_t]");
992*946379e7Schristos 	  break;
993*946379e7Schristos 	case FAT_SIZE_64_T:
994*946379e7Schristos 	  printf ("[int64_t]");
995*946379e7Schristos 	  break;
996*946379e7Schristos 	case FAT_SIZE_LEAST8_T:
997*946379e7Schristos 	  printf ("[int_least8_t]");
998*946379e7Schristos 	  break;
999*946379e7Schristos 	case FAT_SIZE_LEAST16_T:
1000*946379e7Schristos 	  printf ("[int_least16_t]");
1001*946379e7Schristos 	  break;
1002*946379e7Schristos 	case FAT_SIZE_LEAST32_T:
1003*946379e7Schristos 	  printf ("[int_least32_t]");
1004*946379e7Schristos 	  break;
1005*946379e7Schristos 	case FAT_SIZE_LEAST64_T:
1006*946379e7Schristos 	  printf ("[int_least64_t]");
1007*946379e7Schristos 	  break;
1008*946379e7Schristos 	case FAT_SIZE_FAST8_T:
1009*946379e7Schristos 	  printf ("[int_fast8_t]");
1010*946379e7Schristos 	  break;
1011*946379e7Schristos 	case FAT_SIZE_FAST16_T:
1012*946379e7Schristos 	  printf ("[int_fast16_t]");
1013*946379e7Schristos 	  break;
1014*946379e7Schristos 	case FAT_SIZE_FAST32_T:
1015*946379e7Schristos 	  printf ("[int_fast32_t]");
1016*946379e7Schristos 	  break;
1017*946379e7Schristos 	case FAT_SIZE_FAST64_T:
1018*946379e7Schristos 	  printf ("[int_fast64_t]");
1019*946379e7Schristos 	  break;
1020*946379e7Schristos 	case FAT_SIZE_INTMAX_T:
1021*946379e7Schristos 	  printf ("[intmax_t]");
1022*946379e7Schristos 	  break;
1023*946379e7Schristos 	case FAT_SIZE_INTPTR_T:
1024*946379e7Schristos 	  printf ("[intptr_t]");
1025*946379e7Schristos 	  break;
1026*946379e7Schristos 	case FAT_SIZE_SIZE_T:
1027*946379e7Schristos 	  printf ("[size_t]");
1028*946379e7Schristos 	  break;
1029*946379e7Schristos 	case FAT_SIZE_PTRDIFF_T:
1030*946379e7Schristos 	  printf ("[ptrdiff_t]");
1031*946379e7Schristos 	  break;
1032*946379e7Schristos 	default:
1033*946379e7Schristos 	  abort ();
1034*946379e7Schristos 	}
1035*946379e7Schristos       switch (spec->unnumbered[i].type & ~(FAT_UNSIGNED | FAT_SIZE_MASK))
1036*946379e7Schristos 	{
1037*946379e7Schristos 	case FAT_INTEGER:
1038*946379e7Schristos 	  printf ("i");
1039*946379e7Schristos 	  break;
1040*946379e7Schristos 	case FAT_DOUBLE:
1041*946379e7Schristos 	  printf ("f");
1042*946379e7Schristos 	  break;
1043*946379e7Schristos 	case FAT_CHAR:
1044*946379e7Schristos 	  printf ("c");
1045*946379e7Schristos 	  break;
1046*946379e7Schristos 	case FAT_STRING:
1047*946379e7Schristos 	  printf ("s");
1048*946379e7Schristos 	  break;
1049*946379e7Schristos 	case FAT_OBJC_OBJECT:
1050*946379e7Schristos 	  printf ("@");
1051*946379e7Schristos 	  break;
1052*946379e7Schristos 	case FAT_POINTER:
1053*946379e7Schristos 	  printf ("p");
1054*946379e7Schristos 	  break;
1055*946379e7Schristos 	case FAT_COUNT_POINTER:
1056*946379e7Schristos 	  printf ("n");
1057*946379e7Schristos 	  break;
1058*946379e7Schristos 	default:
1059*946379e7Schristos 	  abort ();
1060*946379e7Schristos 	}
1061*946379e7Schristos     }
1062*946379e7Schristos   printf (")");
1063*946379e7Schristos }
1064*946379e7Schristos 
1065*946379e7Schristos int
main()1066*946379e7Schristos main ()
1067*946379e7Schristos {
1068*946379e7Schristos   for (;;)
1069*946379e7Schristos     {
1070*946379e7Schristos       char *line = NULL;
1071*946379e7Schristos       size_t line_size = 0;
1072*946379e7Schristos       int line_len;
1073*946379e7Schristos       char *invalid_reason;
1074*946379e7Schristos       void *descr;
1075*946379e7Schristos 
1076*946379e7Schristos       line_len = getline (&line, &line_size, stdin);
1077*946379e7Schristos       if (line_len < 0)
1078*946379e7Schristos 	break;
1079*946379e7Schristos       if (line_len > 0 && line[line_len - 1] == '\n')
1080*946379e7Schristos 	line[--line_len] = '\0';
1081*946379e7Schristos 
1082*946379e7Schristos       invalid_reason = NULL;
1083*946379e7Schristos       descr = format_c_parse (line, false, &invalid_reason);
1084*946379e7Schristos 
1085*946379e7Schristos       format_print (descr);
1086*946379e7Schristos       printf ("\n");
1087*946379e7Schristos       if (descr == NULL)
1088*946379e7Schristos 	printf ("%s\n", invalid_reason);
1089*946379e7Schristos 
1090*946379e7Schristos       free (invalid_reason);
1091*946379e7Schristos       free (line);
1092*946379e7Schristos     }
1093*946379e7Schristos 
1094*946379e7Schristos   return 0;
1095*946379e7Schristos }
1096*946379e7Schristos 
1097*946379e7Schristos /*
1098*946379e7Schristos  * For Emacs M-x compile
1099*946379e7Schristos  * Local Variables:
1100*946379e7Schristos  * 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-c.c ../lib/libgettextlib.la"
1101*946379e7Schristos  * End:
1102*946379e7Schristos  */
1103*946379e7Schristos 
1104*946379e7Schristos #endif /* TEST */
1105