xref: /netbsd-src/external/gpl2/gettext/dist/gettext-tools/src/format-boost.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1*946379e7Schristos /* Boost format strings.
2*946379e7Schristos    Copyright (C) 2001-2004, 2006 Free Software Foundation, Inc.
3*946379e7Schristos    Written by Bruno Haible <haible@clisp.cons.org>, 2006.
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 /* Boost format strings are described in
36*946379e7Schristos      boost_1_33_1/libs/format/doc/format.html
37*946379e7Schristos    and implemented in
38*946379e7Schristos      boost_1_33_1/boost/format/parsing.hpp.
39*946379e7Schristos    A directive (other than '%%')
40*946379e7Schristos    - starts with '%' or '%|'; in the latter case it must end in '|',
41*946379e7Schristos    - is continued either by
42*946379e7Schristos        - 'm%' where m is a positive integer, starting with a nonzero digit;
43*946379e7Schristos          in this case the directive must not have started with '%|'; or
44*946379e7Schristos        - the following:
45*946379e7Schristos            - optional: 'm$' where m is a positive integer, starting with a
46*946379e7Schristos              nonzero digit,
47*946379e7Schristos            - optional: any of the characters '#', '0', '-', ' ', '+', "'",
48*946379e7Schristos              '_', '=', 'h', 'l',
49*946379e7Schristos            - optional: a width specification: '*' (reads an argument) or '*m$'
50*946379e7Schristos              or a nonempty digit sequence,
51*946379e7Schristos            - optional: a '.' and a precision specification: '*' (reads an
52*946379e7Schristos              argument) or '*m$' or a nonempty digit sequence,
53*946379e7Schristos            - optional: any of the characters 'h', 'l', 'L',
54*946379e7Schristos            - if the directive started with '%|':
55*946379e7Schristos                an optional specifier and a final '|',
56*946379e7Schristos              otherwise
57*946379e7Schristos                a mandatory specifier.
58*946379e7Schristos              If no specifier is given, it needs an argument of any type.
59*946379e7Schristos              The possible specifiers are:
60*946379e7Schristos                - 'c', 'C', that need a character argument,
61*946379e7Schristos                - 's', 'S', that need an argument of any type,
62*946379e7Schristos                - 'i', 'd', 'o', 'u', 'x', 'X', that need an integer argument,
63*946379e7Schristos                - 'e', 'E', 'f', 'g', 'G', that need a floating-point argument,
64*946379e7Schristos                - 'p', that needs a 'void *' argument,
65*946379e7Schristos                - 't', that doesn't need an argument,
66*946379e7Schristos                - 'TX', where X is any character, that doesn't need an argument,
67*946379e7Schristos                - 'n', that needs a pointer to integer.
68*946379e7Schristos              The Boost format string interpreter doesn't actually care about
69*946379e7Schristos              the argument types, but we do, because it increases the likelihood
70*946379e7Schristos              of detecting translator mistakes.
71*946379e7Schristos    Numbered ('%m%' or '%m$' or '*m$') and unnumbered argument specifications
72*946379e7Schristos    cannot be used in the same string.
73*946379e7Schristos  */
74*946379e7Schristos 
75*946379e7Schristos enum format_arg_type
76*946379e7Schristos {
77*946379e7Schristos   FAT_NONE		= 0,
78*946379e7Schristos   /* Basic types */
79*946379e7Schristos   FAT_INTEGER		= 1,
80*946379e7Schristos   FAT_DOUBLE		= 2,
81*946379e7Schristos   FAT_CHAR		= 3,
82*946379e7Schristos   FAT_POINTER		= 4,
83*946379e7Schristos   FAT_ANY		= 5
84*946379e7Schristos };
85*946379e7Schristos 
86*946379e7Schristos struct numbered_arg
87*946379e7Schristos {
88*946379e7Schristos   unsigned int number;
89*946379e7Schristos   enum format_arg_type type;
90*946379e7Schristos };
91*946379e7Schristos 
92*946379e7Schristos struct spec
93*946379e7Schristos {
94*946379e7Schristos   unsigned int directives;
95*946379e7Schristos   unsigned int numbered_arg_count;
96*946379e7Schristos   unsigned int allocated;
97*946379e7Schristos   struct numbered_arg *numbered;
98*946379e7Schristos };
99*946379e7Schristos 
100*946379e7Schristos /* Locale independent test for a decimal digit.
101*946379e7Schristos    Argument can be  'char' or 'unsigned char'.  (Whereas the argument of
102*946379e7Schristos    <ctype.h> isdigit must be an 'unsigned char'.)  */
103*946379e7Schristos #undef isdigit
104*946379e7Schristos #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
105*946379e7Schristos 
106*946379e7Schristos 
107*946379e7Schristos static int
numbered_arg_compare(const void * p1,const void * p2)108*946379e7Schristos numbered_arg_compare (const void *p1, const void *p2)
109*946379e7Schristos {
110*946379e7Schristos   unsigned int n1 = ((const struct numbered_arg *) p1)->number;
111*946379e7Schristos   unsigned int n2 = ((const struct numbered_arg *) p2)->number;
112*946379e7Schristos 
113*946379e7Schristos   return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
114*946379e7Schristos }
115*946379e7Schristos 
116*946379e7Schristos static void *
format_parse(const char * format,bool translated,char ** invalid_reason)117*946379e7Schristos format_parse (const char *format, bool translated, char **invalid_reason)
118*946379e7Schristos {
119*946379e7Schristos   struct spec spec;
120*946379e7Schristos   unsigned int unnumbered_arg_count;
121*946379e7Schristos   struct spec *result;
122*946379e7Schristos 
123*946379e7Schristos   spec.directives = 0;
124*946379e7Schristos   spec.numbered_arg_count = 0;
125*946379e7Schristos   spec.allocated = 0;
126*946379e7Schristos   spec.numbered = NULL;
127*946379e7Schristos   unnumbered_arg_count = 0;
128*946379e7Schristos 
129*946379e7Schristos   for (; *format != '\0';)
130*946379e7Schristos     if (*format++ == '%')
131*946379e7Schristos       {
132*946379e7Schristos 	/* A directive.  */
133*946379e7Schristos 	spec.directives++;
134*946379e7Schristos 
135*946379e7Schristos 	if (*format == '%')
136*946379e7Schristos 	  format++;
137*946379e7Schristos 	else
138*946379e7Schristos 	  {
139*946379e7Schristos 	    bool brackets = false;
140*946379e7Schristos 	    bool done = false;
141*946379e7Schristos 	    unsigned int number = 0;
142*946379e7Schristos 	    enum format_arg_type type = FAT_NONE;
143*946379e7Schristos 
144*946379e7Schristos 	    if (*format == '|')
145*946379e7Schristos 	      {
146*946379e7Schristos 		format++;
147*946379e7Schristos 		brackets = true;
148*946379e7Schristos 	      }
149*946379e7Schristos 
150*946379e7Schristos 	    if (isdigit (*format) && *format != '0')
151*946379e7Schristos 	      {
152*946379e7Schristos 		const char *f = format;
153*946379e7Schristos 		unsigned int m = 0;
154*946379e7Schristos 
155*946379e7Schristos 		do
156*946379e7Schristos 		  {
157*946379e7Schristos 		    m = 10 * m + (*f - '0');
158*946379e7Schristos 		    f++;
159*946379e7Schristos 		  }
160*946379e7Schristos 		while (isdigit (*f));
161*946379e7Schristos 
162*946379e7Schristos 		if ((!brackets && *f == '%') || *f == '$')
163*946379e7Schristos 		  {
164*946379e7Schristos 		    if (m == 0) /* can happen if m overflows */
165*946379e7Schristos 		      {
166*946379e7Schristos 			*invalid_reason = INVALID_ARGNO_0 (spec.directives);
167*946379e7Schristos 			goto bad_format;
168*946379e7Schristos 		      }
169*946379e7Schristos 		    number = m;
170*946379e7Schristos 		    if (*f == '%')
171*946379e7Schristos 		      {
172*946379e7Schristos 			type = FAT_ANY;
173*946379e7Schristos 			done = true;
174*946379e7Schristos 		      }
175*946379e7Schristos 		    format = ++f;
176*946379e7Schristos 		  }
177*946379e7Schristos 	      }
178*946379e7Schristos 
179*946379e7Schristos 	    if (!done)
180*946379e7Schristos 	      {
181*946379e7Schristos 		/* Parse flags.  */
182*946379e7Schristos 		for (;;)
183*946379e7Schristos 		  {
184*946379e7Schristos 		    if (*format == ' ' || *format == '+' || *format == '-'
185*946379e7Schristos 			|| *format == '#' || *format == '0' || *format == '\''
186*946379e7Schristos 			|| *format == '_' || *format == '=' || *format == 'h'
187*946379e7Schristos 			|| *format == 'l')
188*946379e7Schristos 		      format++;
189*946379e7Schristos 		    else
190*946379e7Schristos 		      break;
191*946379e7Schristos 		  }
192*946379e7Schristos 
193*946379e7Schristos 		/* Parse width.  */
194*946379e7Schristos 		if (*format == '*')
195*946379e7Schristos 		  {
196*946379e7Schristos 		    unsigned int width_number = 0;
197*946379e7Schristos 
198*946379e7Schristos 		    format++;
199*946379e7Schristos 
200*946379e7Schristos 		    if (isdigit (*format))
201*946379e7Schristos 		      {
202*946379e7Schristos 			const char *f = format;
203*946379e7Schristos 			unsigned int m = 0;
204*946379e7Schristos 
205*946379e7Schristos 			do
206*946379e7Schristos 			  {
207*946379e7Schristos 			    m = 10 * m + (*f - '0');
208*946379e7Schristos 			    f++;
209*946379e7Schristos 			  }
210*946379e7Schristos 			while (isdigit (*f));
211*946379e7Schristos 
212*946379e7Schristos 			if (*f == '$')
213*946379e7Schristos 			  {
214*946379e7Schristos 			    if (m == 0)
215*946379e7Schristos 			      {
216*946379e7Schristos 				*invalid_reason =
217*946379e7Schristos 				  INVALID_WIDTH_ARGNO_0 (spec.directives);
218*946379e7Schristos 				goto bad_format;
219*946379e7Schristos 			      }
220*946379e7Schristos 			    width_number = m;
221*946379e7Schristos 			    format = ++f;
222*946379e7Schristos 			  }
223*946379e7Schristos 		      }
224*946379e7Schristos 
225*946379e7Schristos 		    if (width_number)
226*946379e7Schristos 		      {
227*946379e7Schristos 			/* Numbered argument.  */
228*946379e7Schristos 
229*946379e7Schristos 			/* Numbered and unnumbered specifications are
230*946379e7Schristos 			   exclusive.  */
231*946379e7Schristos 			if (unnumbered_arg_count > 0)
232*946379e7Schristos 			  {
233*946379e7Schristos 			    *invalid_reason =
234*946379e7Schristos 			      INVALID_MIXES_NUMBERED_UNNUMBERED ();
235*946379e7Schristos 			    goto bad_format;
236*946379e7Schristos 			  }
237*946379e7Schristos 
238*946379e7Schristos 			if (spec.allocated == spec.numbered_arg_count)
239*946379e7Schristos 			  {
240*946379e7Schristos 			    spec.allocated = 2 * spec.allocated + 1;
241*946379e7Schristos 			    spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
242*946379e7Schristos 			  }
243*946379e7Schristos 			spec.numbered[spec.numbered_arg_count].number = width_number;
244*946379e7Schristos 			spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
245*946379e7Schristos 			spec.numbered_arg_count++;
246*946379e7Schristos 		      }
247*946379e7Schristos 		    else
248*946379e7Schristos 		      {
249*946379e7Schristos 			/* Unnumbered argument.  */
250*946379e7Schristos 
251*946379e7Schristos 			/* Numbered and unnumbered specifications are
252*946379e7Schristos 			   exclusive.  */
253*946379e7Schristos 			if (spec.numbered_arg_count > 0)
254*946379e7Schristos 			  {
255*946379e7Schristos 			    *invalid_reason =
256*946379e7Schristos 			      INVALID_MIXES_NUMBERED_UNNUMBERED ();
257*946379e7Schristos 			    goto bad_format;
258*946379e7Schristos 			  }
259*946379e7Schristos 
260*946379e7Schristos 			if (spec.allocated == unnumbered_arg_count)
261*946379e7Schristos 			  {
262*946379e7Schristos 			    spec.allocated = 2 * spec.allocated + 1;
263*946379e7Schristos 			    spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
264*946379e7Schristos 			  }
265*946379e7Schristos 			spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
266*946379e7Schristos 			spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
267*946379e7Schristos 			unnumbered_arg_count++;
268*946379e7Schristos 		      }
269*946379e7Schristos 		  }
270*946379e7Schristos 		else if (isdigit (*format))
271*946379e7Schristos 		  {
272*946379e7Schristos 		    do format++; while (isdigit (*format));
273*946379e7Schristos 		  }
274*946379e7Schristos 
275*946379e7Schristos 		/* Parse precision.  */
276*946379e7Schristos 		if (*format == '.')
277*946379e7Schristos 		  {
278*946379e7Schristos 		    format++;
279*946379e7Schristos 
280*946379e7Schristos 		    if (*format == '*')
281*946379e7Schristos 		      {
282*946379e7Schristos 			unsigned int precision_number = 0;
283*946379e7Schristos 
284*946379e7Schristos 			format++;
285*946379e7Schristos 
286*946379e7Schristos 			if (isdigit (*format))
287*946379e7Schristos 			  {
288*946379e7Schristos 			    const char *f = format;
289*946379e7Schristos 			    unsigned int m = 0;
290*946379e7Schristos 
291*946379e7Schristos 			    do
292*946379e7Schristos 			      {
293*946379e7Schristos 				m = 10 * m + (*f - '0');
294*946379e7Schristos 				f++;
295*946379e7Schristos 			      }
296*946379e7Schristos 			    while (isdigit (*f));
297*946379e7Schristos 
298*946379e7Schristos 			    if (*f == '$')
299*946379e7Schristos 			      {
300*946379e7Schristos 				if (m == 0)
301*946379e7Schristos 				  {
302*946379e7Schristos 				    *invalid_reason =
303*946379e7Schristos 				      INVALID_PRECISION_ARGNO_0 (spec.directives);
304*946379e7Schristos 				    goto bad_format;
305*946379e7Schristos 				  }
306*946379e7Schristos 				precision_number = m;
307*946379e7Schristos 				format = ++f;
308*946379e7Schristos 			      }
309*946379e7Schristos 			  }
310*946379e7Schristos 
311*946379e7Schristos 			if (precision_number)
312*946379e7Schristos 			  {
313*946379e7Schristos 			    /* Numbered argument.  */
314*946379e7Schristos 
315*946379e7Schristos 			    /* Numbered and unnumbered specifications are
316*946379e7Schristos 			       exclusive.  */
317*946379e7Schristos 			    if (unnumbered_arg_count > 0)
318*946379e7Schristos 			      {
319*946379e7Schristos 				*invalid_reason =
320*946379e7Schristos 				  INVALID_MIXES_NUMBERED_UNNUMBERED ();
321*946379e7Schristos 				goto bad_format;
322*946379e7Schristos 			      }
323*946379e7Schristos 
324*946379e7Schristos 			    if (spec.allocated == spec.numbered_arg_count)
325*946379e7Schristos 			      {
326*946379e7Schristos 				spec.allocated = 2 * spec.allocated + 1;
327*946379e7Schristos 				spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
328*946379e7Schristos 			      }
329*946379e7Schristos 			    spec.numbered[spec.numbered_arg_count].number = precision_number;
330*946379e7Schristos 			    spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
331*946379e7Schristos 			    spec.numbered_arg_count++;
332*946379e7Schristos 			  }
333*946379e7Schristos 			else
334*946379e7Schristos 			  {
335*946379e7Schristos 			    /* Unnumbered argument.  */
336*946379e7Schristos 
337*946379e7Schristos 			    /* Numbered and unnumbered specifications are
338*946379e7Schristos 			       exclusive.  */
339*946379e7Schristos 			    if (spec.numbered_arg_count > 0)
340*946379e7Schristos 			      {
341*946379e7Schristos 				*invalid_reason =
342*946379e7Schristos 				  INVALID_MIXES_NUMBERED_UNNUMBERED ();
343*946379e7Schristos 				goto bad_format;
344*946379e7Schristos 			      }
345*946379e7Schristos 
346*946379e7Schristos 			    if (spec.allocated == unnumbered_arg_count)
347*946379e7Schristos 			      {
348*946379e7Schristos 				spec.allocated = 2 * spec.allocated + 1;
349*946379e7Schristos 				spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated  * sizeof (struct numbered_arg));
350*946379e7Schristos 			      }
351*946379e7Schristos 			    spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
352*946379e7Schristos 			    spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
353*946379e7Schristos 			    unnumbered_arg_count++;
354*946379e7Schristos 			  }
355*946379e7Schristos 		      }
356*946379e7Schristos 		    else if (isdigit (*format))
357*946379e7Schristos 		      {
358*946379e7Schristos 			do format++; while (isdigit (*format));
359*946379e7Schristos 		      }
360*946379e7Schristos 		  }
361*946379e7Schristos 
362*946379e7Schristos 		/* Parse size.  */
363*946379e7Schristos 		for (;;)
364*946379e7Schristos 		  {
365*946379e7Schristos 		    if (*format == 'h' || *format == 'l' || *format == 'L')
366*946379e7Schristos 		      format++;
367*946379e7Schristos 		    else
368*946379e7Schristos 		      break;
369*946379e7Schristos 		  }
370*946379e7Schristos 
371*946379e7Schristos 		switch (*format++)
372*946379e7Schristos 		  {
373*946379e7Schristos 		  case 'c': case 'C':
374*946379e7Schristos 		    type = FAT_CHAR;
375*946379e7Schristos 		    break;
376*946379e7Schristos 		  case 's': case 'S':
377*946379e7Schristos 		    type = FAT_ANY;
378*946379e7Schristos 		    break;
379*946379e7Schristos 		  case 'i': case 'd': case 'o': case 'u': case 'x': case 'X':
380*946379e7Schristos 		    type = FAT_INTEGER;
381*946379e7Schristos 		    break;
382*946379e7Schristos 		  case 'e': case 'E': case 'f': case 'g': case 'G':
383*946379e7Schristos 		    type = FAT_DOUBLE;
384*946379e7Schristos 		    break;
385*946379e7Schristos 		  case 'p':
386*946379e7Schristos 		    type = FAT_POINTER;
387*946379e7Schristos 		    break;
388*946379e7Schristos 		  case 't':
389*946379e7Schristos 		    type = FAT_NONE;
390*946379e7Schristos 		    break;
391*946379e7Schristos 		  case 'T':
392*946379e7Schristos 		    if (*format == '\0')
393*946379e7Schristos 		      {
394*946379e7Schristos 			*invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
395*946379e7Schristos 			goto bad_format;
396*946379e7Schristos 		      }
397*946379e7Schristos 		    format++;
398*946379e7Schristos 		    type = FAT_NONE;
399*946379e7Schristos 		    break;
400*946379e7Schristos 		  case 'n':
401*946379e7Schristos 		    type = FAT_NONE;
402*946379e7Schristos 		    break;
403*946379e7Schristos 		  case '|':
404*946379e7Schristos 		    if (brackets)
405*946379e7Schristos 		      {
406*946379e7Schristos 			--format;
407*946379e7Schristos 			type = FAT_ANY;
408*946379e7Schristos 			break;
409*946379e7Schristos 		      }
410*946379e7Schristos 		    /*FALLTHROUGH*/
411*946379e7Schristos 		  default:
412*946379e7Schristos 		    --format;
413*946379e7Schristos 		    *invalid_reason =
414*946379e7Schristos 		      (*format == '\0'
415*946379e7Schristos 		       ? INVALID_UNTERMINATED_DIRECTIVE ()
416*946379e7Schristos 		       : INVALID_CONVERSION_SPECIFIER (spec.directives,
417*946379e7Schristos 						       *format));
418*946379e7Schristos 		    goto bad_format;
419*946379e7Schristos 		  }
420*946379e7Schristos 		if (brackets)
421*946379e7Schristos 		  {
422*946379e7Schristos 		    if (*format != '|')
423*946379e7Schristos 		      {
424*946379e7Schristos 			*invalid_reason =
425*946379e7Schristos 			  (*format == '\0'
426*946379e7Schristos 			   ? INVALID_UNTERMINATED_DIRECTIVE ()
427*946379e7Schristos 			   : xasprintf (_("The directive number %u starts with | but does not end with |."),
428*946379e7Schristos 					spec.directives));
429*946379e7Schristos 			goto bad_format;
430*946379e7Schristos 		      }
431*946379e7Schristos 		    format++;
432*946379e7Schristos 		  }
433*946379e7Schristos 	      }
434*946379e7Schristos 
435*946379e7Schristos 	    if (type != FAT_NONE)
436*946379e7Schristos 	      {
437*946379e7Schristos 		if (number)
438*946379e7Schristos 		  {
439*946379e7Schristos 		    /* Numbered argument.  */
440*946379e7Schristos 
441*946379e7Schristos 		    /* Numbered and unnumbered specifications are exclusive.  */
442*946379e7Schristos 		    if (unnumbered_arg_count > 0)
443*946379e7Schristos 		      {
444*946379e7Schristos 			*invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
445*946379e7Schristos 			goto bad_format;
446*946379e7Schristos 		      }
447*946379e7Schristos 
448*946379e7Schristos 		    if (spec.allocated == spec.numbered_arg_count)
449*946379e7Schristos 		      {
450*946379e7Schristos 			spec.allocated = 2 * spec.allocated + 1;
451*946379e7Schristos 			spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
452*946379e7Schristos 		      }
453*946379e7Schristos 		    spec.numbered[spec.numbered_arg_count].number = number;
454*946379e7Schristos 		    spec.numbered[spec.numbered_arg_count].type = type;
455*946379e7Schristos 		    spec.numbered_arg_count++;
456*946379e7Schristos 		  }
457*946379e7Schristos 		else
458*946379e7Schristos 		  {
459*946379e7Schristos 		    /* Unnumbered argument.  */
460*946379e7Schristos 
461*946379e7Schristos 		    /* Numbered and unnumbered specifications are exclusive.  */
462*946379e7Schristos 		    if (spec.numbered_arg_count > 0)
463*946379e7Schristos 		      {
464*946379e7Schristos 			*invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
465*946379e7Schristos 			goto bad_format;
466*946379e7Schristos 		      }
467*946379e7Schristos 
468*946379e7Schristos 		    if (spec.allocated == unnumbered_arg_count)
469*946379e7Schristos 		      {
470*946379e7Schristos 			spec.allocated = 2 * spec.allocated + 1;
471*946379e7Schristos 			spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
472*946379e7Schristos 		      }
473*946379e7Schristos 		    spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
474*946379e7Schristos 		    spec.numbered[unnumbered_arg_count].type = type;
475*946379e7Schristos 		    unnumbered_arg_count++;
476*946379e7Schristos 		  }
477*946379e7Schristos 	      }
478*946379e7Schristos 	  }
479*946379e7Schristos       }
480*946379e7Schristos 
481*946379e7Schristos   /* Convert the unnumbered argument array to numbered arguments.  */
482*946379e7Schristos   if (unnumbered_arg_count > 0)
483*946379e7Schristos     spec.numbered_arg_count = unnumbered_arg_count;
484*946379e7Schristos   /* Sort the numbered argument array, and eliminate duplicates.  */
485*946379e7Schristos   else if (spec.numbered_arg_count > 1)
486*946379e7Schristos     {
487*946379e7Schristos       unsigned int i, j;
488*946379e7Schristos       bool err;
489*946379e7Schristos 
490*946379e7Schristos       qsort (spec.numbered, spec.numbered_arg_count,
491*946379e7Schristos 	     sizeof (struct numbered_arg), numbered_arg_compare);
492*946379e7Schristos 
493*946379e7Schristos       /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
494*946379e7Schristos       err = false;
495*946379e7Schristos       for (i = j = 0; i < spec.numbered_arg_count; i++)
496*946379e7Schristos 	if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
497*946379e7Schristos 	  {
498*946379e7Schristos 	    enum format_arg_type type1 = spec.numbered[i].type;
499*946379e7Schristos 	    enum format_arg_type type2 = spec.numbered[j-1].type;
500*946379e7Schristos 	    enum format_arg_type type_both;
501*946379e7Schristos 
502*946379e7Schristos 	    if (type1 == type2 || type2 == FAT_ANY)
503*946379e7Schristos 	      type_both = type1;
504*946379e7Schristos 	    else if (type1 == FAT_ANY)
505*946379e7Schristos 	      type_both = type2;
506*946379e7Schristos 	    else
507*946379e7Schristos 	      {
508*946379e7Schristos 		/* Incompatible types.  */
509*946379e7Schristos 		type_both = FAT_NONE;
510*946379e7Schristos 		if (!err)
511*946379e7Schristos 		  *invalid_reason =
512*946379e7Schristos 		    INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
513*946379e7Schristos 		err = true;
514*946379e7Schristos 	      }
515*946379e7Schristos 
516*946379e7Schristos 	    spec.numbered[j-1].type = type_both;
517*946379e7Schristos 	  }
518*946379e7Schristos 	else
519*946379e7Schristos 	  {
520*946379e7Schristos 	    if (j < i)
521*946379e7Schristos 	      {
522*946379e7Schristos 		spec.numbered[j].number = spec.numbered[i].number;
523*946379e7Schristos 		spec.numbered[j].type = spec.numbered[i].type;
524*946379e7Schristos 	      }
525*946379e7Schristos 	    j++;
526*946379e7Schristos 	  }
527*946379e7Schristos       spec.numbered_arg_count = j;
528*946379e7Schristos       if (err)
529*946379e7Schristos 	/* *invalid_reason has already been set above.  */
530*946379e7Schristos 	goto bad_format;
531*946379e7Schristos     }
532*946379e7Schristos 
533*946379e7Schristos   result = (struct spec *) xmalloc (sizeof (struct spec));
534*946379e7Schristos   *result = spec;
535*946379e7Schristos   return result;
536*946379e7Schristos 
537*946379e7Schristos  bad_format:
538*946379e7Schristos   if (spec.numbered != NULL)
539*946379e7Schristos     free (spec.numbered);
540*946379e7Schristos   return NULL;
541*946379e7Schristos }
542*946379e7Schristos 
543*946379e7Schristos static void
format_free(void * descr)544*946379e7Schristos format_free (void *descr)
545*946379e7Schristos {
546*946379e7Schristos   struct spec *spec = (struct spec *) descr;
547*946379e7Schristos 
548*946379e7Schristos   if (spec->numbered != NULL)
549*946379e7Schristos     free (spec->numbered);
550*946379e7Schristos   free (spec);
551*946379e7Schristos }
552*946379e7Schristos 
553*946379e7Schristos static int
format_get_number_of_directives(void * descr)554*946379e7Schristos format_get_number_of_directives (void *descr)
555*946379e7Schristos {
556*946379e7Schristos   struct spec *spec = (struct spec *) descr;
557*946379e7Schristos 
558*946379e7Schristos   return spec->directives;
559*946379e7Schristos }
560*946379e7Schristos 
561*946379e7Schristos static bool
format_check(void * msgid_descr,void * msgstr_descr,bool equality,formatstring_error_logger_t error_logger,const char * pretty_msgstr)562*946379e7Schristos format_check (void *msgid_descr, void *msgstr_descr, bool equality,
563*946379e7Schristos 	      formatstring_error_logger_t error_logger,
564*946379e7Schristos 	      const char *pretty_msgstr)
565*946379e7Schristos {
566*946379e7Schristos   struct spec *spec1 = (struct spec *) msgid_descr;
567*946379e7Schristos   struct spec *spec2 = (struct spec *) msgstr_descr;
568*946379e7Schristos   bool err = false;
569*946379e7Schristos 
570*946379e7Schristos   if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
571*946379e7Schristos     {
572*946379e7Schristos       unsigned int i, j;
573*946379e7Schristos       unsigned int n1 = spec1->numbered_arg_count;
574*946379e7Schristos       unsigned int n2 = spec2->numbered_arg_count;
575*946379e7Schristos 
576*946379e7Schristos       /* Check the argument names are the same.
577*946379e7Schristos 	 Both arrays are sorted.  We search for the first difference.  */
578*946379e7Schristos       for (i = 0, j = 0; i < n1 || j < n2; )
579*946379e7Schristos 	{
580*946379e7Schristos 	  int cmp = (i >= n1 ? 1 :
581*946379e7Schristos 		     j >= n2 ? -1 :
582*946379e7Schristos 		     spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
583*946379e7Schristos 		     spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
584*946379e7Schristos 		     0);
585*946379e7Schristos 
586*946379e7Schristos 	  if (cmp > 0)
587*946379e7Schristos 	    {
588*946379e7Schristos 	      if (error_logger)
589*946379e7Schristos 		error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"),
590*946379e7Schristos 			      spec2->numbered[j].number, pretty_msgstr);
591*946379e7Schristos 	      err = true;
592*946379e7Schristos 	      break;
593*946379e7Schristos 	    }
594*946379e7Schristos 	  else if (cmp < 0)
595*946379e7Schristos 	    {
596*946379e7Schristos 	      if (equality)
597*946379e7Schristos 		{
598*946379e7Schristos 		  if (error_logger)
599*946379e7Schristos 		    error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
600*946379e7Schristos 				  spec1->numbered[i].number, pretty_msgstr);
601*946379e7Schristos 		  err = true;
602*946379e7Schristos 		  break;
603*946379e7Schristos 		}
604*946379e7Schristos 	      else
605*946379e7Schristos 		i++;
606*946379e7Schristos 	    }
607*946379e7Schristos 	  else
608*946379e7Schristos 	    j++, i++;
609*946379e7Schristos 	}
610*946379e7Schristos       /* Check the argument types are the same.  */
611*946379e7Schristos       if (!err)
612*946379e7Schristos 	for (i = 0, j = 0; j < n2; )
613*946379e7Schristos 	  {
614*946379e7Schristos 	    if (spec1->numbered[i].number == spec2->numbered[j].number)
615*946379e7Schristos 	      {
616*946379e7Schristos 		if (spec1->numbered[i].type != spec2->numbered[j].type)
617*946379e7Schristos 		  {
618*946379e7Schristos 		    if (error_logger)
619*946379e7Schristos 		      error_logger (_("format specifications in 'msgid' and '%s' for argument %u are not the same"),
620*946379e7Schristos 				    pretty_msgstr, spec2->numbered[j].number);
621*946379e7Schristos 		    err = true;
622*946379e7Schristos 		    break;
623*946379e7Schristos 		  }
624*946379e7Schristos 		j++, i++;
625*946379e7Schristos 	      }
626*946379e7Schristos 	    else
627*946379e7Schristos 	      i++;
628*946379e7Schristos 	  }
629*946379e7Schristos     }
630*946379e7Schristos 
631*946379e7Schristos   return err;
632*946379e7Schristos }
633*946379e7Schristos 
634*946379e7Schristos 
635*946379e7Schristos struct formatstring_parser formatstring_boost =
636*946379e7Schristos {
637*946379e7Schristos   format_parse,
638*946379e7Schristos   format_free,
639*946379e7Schristos   format_get_number_of_directives,
640*946379e7Schristos   NULL,
641*946379e7Schristos   format_check
642*946379e7Schristos };
643*946379e7Schristos 
644*946379e7Schristos 
645*946379e7Schristos #ifdef TEST
646*946379e7Schristos 
647*946379e7Schristos /* Test program: Print the argument list specification returned by
648*946379e7Schristos    format_parse for strings read from standard input.  */
649*946379e7Schristos 
650*946379e7Schristos #include <stdio.h>
651*946379e7Schristos #include "getline.h"
652*946379e7Schristos 
653*946379e7Schristos static void
format_print(void * descr)654*946379e7Schristos format_print (void *descr)
655*946379e7Schristos {
656*946379e7Schristos   struct spec *spec = (struct spec *) descr;
657*946379e7Schristos   unsigned int last;
658*946379e7Schristos   unsigned int i;
659*946379e7Schristos 
660*946379e7Schristos   if (spec == NULL)
661*946379e7Schristos     {
662*946379e7Schristos       printf ("INVALID");
663*946379e7Schristos       return;
664*946379e7Schristos     }
665*946379e7Schristos 
666*946379e7Schristos   printf ("(");
667*946379e7Schristos   last = 1;
668*946379e7Schristos   for (i = 0; i < spec->numbered_arg_count; i++)
669*946379e7Schristos     {
670*946379e7Schristos       unsigned int number = spec->numbered[i].number;
671*946379e7Schristos 
672*946379e7Schristos       if (i > 0)
673*946379e7Schristos 	printf (" ");
674*946379e7Schristos       if (number < last)
675*946379e7Schristos 	abort ();
676*946379e7Schristos       for (; last < number; last++)
677*946379e7Schristos 	printf ("_ ");
678*946379e7Schristos       switch (spec->numbered[i].type)
679*946379e7Schristos 	{
680*946379e7Schristos 	case FAT_INTEGER:
681*946379e7Schristos 	  printf ("i");
682*946379e7Schristos 	  break;
683*946379e7Schristos 	case FAT_DOUBLE:
684*946379e7Schristos 	  printf ("f");
685*946379e7Schristos 	  break;
686*946379e7Schristos 	case FAT_CHAR:
687*946379e7Schristos 	  printf ("c");
688*946379e7Schristos 	  break;
689*946379e7Schristos 	case FAT_POINTER:
690*946379e7Schristos 	  printf ("p");
691*946379e7Schristos 	  break;
692*946379e7Schristos 	case FAT_ANY:
693*946379e7Schristos 	  printf ("*");
694*946379e7Schristos 	  break;
695*946379e7Schristos 	default:
696*946379e7Schristos 	  abort ();
697*946379e7Schristos 	}
698*946379e7Schristos       last = number + 1;
699*946379e7Schristos     }
700*946379e7Schristos   printf (")");
701*946379e7Schristos }
702*946379e7Schristos 
703*946379e7Schristos int
main()704*946379e7Schristos main ()
705*946379e7Schristos {
706*946379e7Schristos   for (;;)
707*946379e7Schristos     {
708*946379e7Schristos       char *line = NULL;
709*946379e7Schristos       size_t line_size = 0;
710*946379e7Schristos       int line_len;
711*946379e7Schristos       char *invalid_reason;
712*946379e7Schristos       void *descr;
713*946379e7Schristos 
714*946379e7Schristos       line_len = getline (&line, &line_size, stdin);
715*946379e7Schristos       if (line_len < 0)
716*946379e7Schristos 	break;
717*946379e7Schristos       if (line_len > 0 && line[line_len - 1] == '\n')
718*946379e7Schristos 	line[--line_len] = '\0';
719*946379e7Schristos 
720*946379e7Schristos       invalid_reason = NULL;
721*946379e7Schristos       descr = format_parse (line, false, &invalid_reason);
722*946379e7Schristos 
723*946379e7Schristos       format_print (descr);
724*946379e7Schristos       printf ("\n");
725*946379e7Schristos       if (descr == NULL)
726*946379e7Schristos 	printf ("%s\n", invalid_reason);
727*946379e7Schristos 
728*946379e7Schristos       free (invalid_reason);
729*946379e7Schristos       free (line);
730*946379e7Schristos     }
731*946379e7Schristos 
732*946379e7Schristos   return 0;
733*946379e7Schristos }
734*946379e7Schristos 
735*946379e7Schristos /*
736*946379e7Schristos  * For Emacs M-x compile
737*946379e7Schristos  * Local Variables:
738*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-boost.c ../lib/libgettextlib.la"
739*946379e7Schristos  * End:
740*946379e7Schristos  */
741*946379e7Schristos 
742*946379e7Schristos #endif /* TEST */
743*946379e7Schristos 
744