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