2 Copyright (C) 2001-2004 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2002.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
30 #include "format-invalid.h"
33 #define _(str) gettext (str)
35 /* awk format strings are described in the gawk-3.1 documentation and
36 implemented in gawk-3.1.0/builtin.c: format_tree().
38 - starts with '%' or '%m$' where m is a positive integer,
39 - is optionally followed by any of the characters '#', '0', '-', ' ', '+',
40 each of which acts as a flag,
41 - is optionally followed by a width specification: '*' (reads an argument)
42 or '*m$' or a nonempty digit sequence,
43 - is optionally followed by '.' and a precision specification: '*' (reads
44 an argument) or '*m$' or a nonempty digit sequence,
45 - is finished by a specifier
46 - '%', that needs no argument,
47 - 'c', that need a character argument,
48 - 's', that need a string argument,
49 - 'i', 'd', that need a signed integer argument,
50 - 'o', 'u', 'x', 'X', that need an unsigned integer argument,
51 - 'e', 'E', 'f', 'g', 'G', that need a floating-point argument.
52 Numbered ('%m$' or '*m$') and unnumbered argument specifications cannot
53 be used in the same string.
69 enum format_arg_type type
;
74 unsigned int directives
;
75 unsigned int numbered_arg_count
;
76 unsigned int allocated
;
77 struct numbered_arg
*numbered
;
80 /* Locale independent test for a decimal digit.
81 Argument can be 'char' or 'unsigned char'. (Whereas the argument of
82 <ctype.h> isdigit must be an 'unsigned char'.) */
84 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
88 numbered_arg_compare (const void *p1
, const void *p2
)
90 unsigned int n1
= ((const struct numbered_arg
*) p1
)->number
;
91 unsigned int n2
= ((const struct numbered_arg
*) p2
)->number
;
93 return (n1
> n2
? 1 : n1
< n2
? -1 : 0);
97 format_parse (const char *format
, bool translated
, char **invalid_reason
)
100 unsigned int unnumbered_arg_count
;
104 spec
.numbered_arg_count
= 0;
106 spec
.numbered
= NULL
;
107 unnumbered_arg_count
= 0;
109 for (; *format
!= '\0';)
110 if (*format
++ == '%')
113 unsigned int number
= 0;
114 enum format_arg_type type
;
118 if (isdigit (*format
))
120 const char *f
= format
;
125 m
= 10 * m
+ (*f
- '0');
128 while (isdigit (*f
));
134 *invalid_reason
= INVALID_ARGNO_0 (spec
.directives
);
143 while (*format
== ' ' || *format
== '+' || *format
== '-'
144 || *format
== '#' || *format
== '0')
150 unsigned int width_number
= 0;
154 if (isdigit (*format
))
156 const char *f
= format
;
161 m
= 10 * m
+ (*f
- '0');
164 while (isdigit (*f
));
171 INVALID_WIDTH_ARGNO_0 (spec
.directives
);
181 /* Numbered argument. */
183 /* Numbered and unnumbered specifications are exclusive. */
184 if (unnumbered_arg_count
> 0)
186 *invalid_reason
= INVALID_MIXES_NUMBERED_UNNUMBERED ();
190 if (spec
.allocated
== spec
.numbered_arg_count
)
192 spec
.allocated
= 2 * spec
.allocated
+ 1;
193 spec
.numbered
= (struct numbered_arg
*) xrealloc (spec
.numbered
, spec
.allocated
* sizeof (struct numbered_arg
));
195 spec
.numbered
[spec
.numbered_arg_count
].number
= width_number
;
196 spec
.numbered
[spec
.numbered_arg_count
].type
= FAT_INTEGER
;
197 spec
.numbered_arg_count
++;
201 /* Unnumbered argument. */
203 /* Numbered and unnumbered specifications are exclusive. */
204 if (spec
.numbered_arg_count
> 0)
206 *invalid_reason
= INVALID_MIXES_NUMBERED_UNNUMBERED ();
210 if (spec
.allocated
== unnumbered_arg_count
)
212 spec
.allocated
= 2 * spec
.allocated
+ 1;
213 spec
.numbered
= (struct numbered_arg
*) xrealloc (spec
.numbered
, spec
.allocated
* sizeof (struct numbered_arg
));
215 spec
.numbered
[unnumbered_arg_count
].number
= unnumbered_arg_count
+ 1;
216 spec
.numbered
[unnumbered_arg_count
].type
= FAT_INTEGER
;
217 unnumbered_arg_count
++;
220 else if (isdigit (*format
))
222 do format
++; while (isdigit (*format
));
225 /* Parse precision. */
232 unsigned int precision_number
= 0;
236 if (isdigit (*format
))
238 const char *f
= format
;
243 m
= 10 * m
+ (*f
- '0');
246 while (isdigit (*f
));
253 INVALID_PRECISION_ARGNO_0 (spec
.directives
);
256 precision_number
= m
;
261 if (precision_number
)
263 /* Numbered argument. */
265 /* Numbered and unnumbered specifications are exclusive. */
266 if (unnumbered_arg_count
> 0)
268 *invalid_reason
= INVALID_MIXES_NUMBERED_UNNUMBERED ();
272 if (spec
.allocated
== spec
.numbered_arg_count
)
274 spec
.allocated
= 2 * spec
.allocated
+ 1;
275 spec
.numbered
= (struct numbered_arg
*) xrealloc (spec
.numbered
, spec
.allocated
* sizeof (struct numbered_arg
));
277 spec
.numbered
[spec
.numbered_arg_count
].number
= precision_number
;
278 spec
.numbered
[spec
.numbered_arg_count
].type
= FAT_INTEGER
;
279 spec
.numbered_arg_count
++;
283 /* Unnumbered argument. */
285 /* Numbered and unnumbered specifications are exclusive. */
286 if (spec
.numbered_arg_count
> 0)
288 *invalid_reason
= INVALID_MIXES_NUMBERED_UNNUMBERED ();
292 if (spec
.allocated
== unnumbered_arg_count
)
294 spec
.allocated
= 2 * spec
.allocated
+ 1;
295 spec
.numbered
= (struct numbered_arg
*) xrealloc (spec
.numbered
, spec
.allocated
* sizeof (struct numbered_arg
));
297 spec
.numbered
[unnumbered_arg_count
].type
= unnumbered_arg_count
+ 1;
298 spec
.numbered
[unnumbered_arg_count
].type
= FAT_INTEGER
;
299 unnumbered_arg_count
++;
302 else if (isdigit (*format
))
304 do format
++; while (isdigit (*format
));
314 type
= FAT_CHARACTER
;
322 case 'u': case 'o': case 'x': case 'X':
323 type
= FAT_UNSIGNED_INTEGER
;
325 case 'e': case 'E': case 'f': case 'g': case 'G':
331 ? INVALID_UNTERMINATED_DIRECTIVE ()
332 : INVALID_CONVERSION_SPECIFIER (spec
.directives
, *format
));
336 if (type
!= FAT_NONE
)
340 /* Numbered argument. */
342 /* Numbered and unnumbered specifications are exclusive. */
343 if (unnumbered_arg_count
> 0)
345 *invalid_reason
= INVALID_MIXES_NUMBERED_UNNUMBERED ();
349 if (spec
.allocated
== spec
.numbered_arg_count
)
351 spec
.allocated
= 2 * spec
.allocated
+ 1;
352 spec
.numbered
= (struct numbered_arg
*) xrealloc (spec
.numbered
, spec
.allocated
* sizeof (struct numbered_arg
));
354 spec
.numbered
[spec
.numbered_arg_count
].number
= number
;
355 spec
.numbered
[spec
.numbered_arg_count
].type
= type
;
356 spec
.numbered_arg_count
++;
360 /* Unnumbered argument. */
362 /* Numbered and unnumbered specifications are exclusive. */
363 if (spec
.numbered_arg_count
> 0)
365 *invalid_reason
= INVALID_MIXES_NUMBERED_UNNUMBERED ();
369 if (spec
.allocated
== unnumbered_arg_count
)
371 spec
.allocated
= 2 * spec
.allocated
+ 1;
372 spec
.numbered
= (struct numbered_arg
*) xrealloc (spec
.numbered
, spec
.allocated
* sizeof (struct numbered_arg
));
374 spec
.numbered
[unnumbered_arg_count
].number
= unnumbered_arg_count
+ 1;
375 spec
.numbered
[unnumbered_arg_count
].type
= type
;
376 unnumbered_arg_count
++;
383 /* Convert the unnumbered argument array to numbered arguments. */
384 if (unnumbered_arg_count
> 0)
385 spec
.numbered_arg_count
= unnumbered_arg_count
;
386 /* Sort the numbered argument array, and eliminate duplicates. */
387 else if (spec
.numbered_arg_count
> 1)
392 qsort (spec
.numbered
, spec
.numbered_arg_count
,
393 sizeof (struct numbered_arg
), numbered_arg_compare
);
395 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
397 for (i
= j
= 0; i
< spec
.numbered_arg_count
; i
++)
398 if (j
> 0 && spec
.numbered
[i
].number
== spec
.numbered
[j
-1].number
)
400 enum format_arg_type type1
= spec
.numbered
[i
].type
;
401 enum format_arg_type type2
= spec
.numbered
[j
-1].type
;
402 enum format_arg_type type_both
;
408 /* Incompatible types. */
409 type_both
= FAT_NONE
;
412 INVALID_INCOMPATIBLE_ARG_TYPES (spec
.numbered
[i
].number
);
416 spec
.numbered
[j
-1].type
= type_both
;
422 spec
.numbered
[j
].number
= spec
.numbered
[i
].number
;
423 spec
.numbered
[j
].type
= spec
.numbered
[i
].type
;
427 spec
.numbered_arg_count
= j
;
429 /* *invalid_reason has already been set above. */
433 result
= (struct spec
*) xmalloc (sizeof (struct spec
));
438 if (spec
.numbered
!= NULL
)
439 free (spec
.numbered
);
444 format_free (void *descr
)
446 struct spec
*spec
= (struct spec
*) descr
;
448 if (spec
->numbered
!= NULL
)
449 free (spec
->numbered
);
454 format_get_number_of_directives (void *descr
)
456 struct spec
*spec
= (struct spec
*) descr
;
458 return spec
->directives
;
462 format_check (void *msgid_descr
, void *msgstr_descr
, bool equality
,
463 formatstring_error_logger_t error_logger
,
464 const char *pretty_msgstr
)
466 struct spec
*spec1
= (struct spec
*) msgid_descr
;
467 struct spec
*spec2
= (struct spec
*) msgstr_descr
;
470 if (spec1
->numbered_arg_count
+ spec2
->numbered_arg_count
> 0)
473 unsigned int n1
= spec1
->numbered_arg_count
;
474 unsigned int n2
= spec2
->numbered_arg_count
;
476 /* Check the argument names are the same.
477 Both arrays are sorted. We search for the first difference. */
478 for (i
= 0, j
= 0; i
< n1
|| j
< n2
; )
480 int cmp
= (i
>= n1
? 1 :
482 spec1
->numbered
[i
].number
> spec2
->numbered
[j
].number
? 1 :
483 spec1
->numbered
[i
].number
< spec2
->numbered
[j
].number
? -1 :
489 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"),
490 spec2
->numbered
[j
].number
, pretty_msgstr
);
499 error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
500 spec1
->numbered
[i
].number
, pretty_msgstr
);
510 /* Check the argument types are the same. */
512 for (i
= 0, j
= 0; j
< n2
; )
514 if (spec1
->numbered
[i
].number
== spec2
->numbered
[j
].number
)
516 if (spec1
->numbered
[i
].type
!= spec2
->numbered
[j
].type
)
519 error_logger (_("format specifications in 'msgid' and '%s' for argument %u are not the same"),
520 pretty_msgstr
, spec2
->numbered
[j
].number
);
535 struct formatstring_parser formatstring_awk
=
539 format_get_number_of_directives
,
546 /* Test program: Print the argument list specification returned by
547 format_parse for strings read from standard input. */
553 format_print (void *descr
)
555 struct spec
*spec
= (struct spec
*) descr
;
567 for (i
= 0; i
< spec
->numbered_arg_count
; i
++)
569 unsigned int number
= spec
->numbered
[i
].number
;
575 for (; last
< number
; last
++)
577 switch (spec
->numbered
[i
].type
)
588 case FAT_UNSIGNED_INTEGER
:
589 printf ("[unsigned]i");
608 size_t line_size
= 0;
610 char *invalid_reason
;
613 line_len
= getline (&line
, &line_size
, stdin
);
616 if (line_len
> 0 && line
[line_len
- 1] == '\n')
617 line
[--line_len
] = '\0';
619 invalid_reason
= NULL
;
620 descr
= format_parse (line
, false, &invalid_reason
);
622 format_print (descr
);
625 printf ("%s\n", invalid_reason
);
627 free (invalid_reason
);
635 * For Emacs M-x compile
637 * 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-awk.c ../lib/libgettextlib.la"