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 /* Tcl format strings are described in the tcl8.3.3/doc/format.n manual
36 page and implemented in the function Tcl_FormatObjCmd in
37 tcl8.3.3/generic/tclCmdAH.c.
39 - starts with '%' or '%m$' where m is a positive integer,
40 - is optionally followed by any of the characters '#', '0', '-', ' ', '+',
41 each of which acts as a flag,
42 - is optionally followed by a width specification: '*' (reads an argument)
43 or a nonempty digit sequence,
44 - is optionally followed by '.' and a precision specification: '*' (reads
45 an argument) or a nonempty digit sequence,
46 - is optionally followed by a size specifier, 'h' or 'l'. 'l' is ignored.
47 - is finished by a specifier
48 - '%', that needs no argument,
49 - 'c', that needs a character argument,
50 - 's', that needs a string argument,
51 - 'i', 'd', that need a signed integer argument,
52 - 'o', 'u', 'x', 'X', that need an unsigned integer argument,
53 - 'e', 'E', 'f', 'g', 'G', that need a floating-point argument.
54 Numbered ('%m$') and unnumbered argument specifications cannot be used
66 FAT_SHORT_UNSIGNED_INTEGER
,
73 enum format_arg_type type
;
78 unsigned int directives
;
79 unsigned int numbered_arg_count
;
80 unsigned int allocated
;
81 struct numbered_arg
*numbered
;
84 /* Locale independent test for a decimal digit.
85 Argument can be 'char' or 'unsigned char'. (Whereas the argument of
86 <ctype.h> isdigit must be an 'unsigned char'.) */
88 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
92 numbered_arg_compare (const void *p1
, const void *p2
)
94 unsigned int n1
= ((const struct numbered_arg
*) p1
)->number
;
95 unsigned int n2
= ((const struct numbered_arg
*) p2
)->number
;
97 return (n1
> n2
? 1 : n1
< n2
? -1 : 0);
101 format_parse (const char *format
, bool translated
, char **invalid_reason
)
105 bool seen_numbered_arg
;
106 bool seen_unnumbered_arg
;
110 spec
.numbered_arg_count
= 0;
112 spec
.numbered
= NULL
;
113 seen_numbered_arg
= false;
114 seen_unnumbered_arg
= false;
117 for (; *format
!= '\0';)
118 if (*format
++ == '%')
125 bool is_numbered_arg
;
127 enum format_arg_type type
;
129 is_numbered_arg
= false;
130 if (isdigit (*format
))
132 const char *f
= format
;
137 m
= 10 * m
+ (*f
- '0');
140 while (isdigit (*f
));
146 *invalid_reason
= INVALID_ARGNO_0 (spec
.directives
);
152 /* Numbered and unnumbered specifications are exclusive. */
153 if (seen_unnumbered_arg
)
155 *invalid_reason
= INVALID_MIXES_NUMBERED_UNNUMBERED ();
158 is_numbered_arg
= true;
159 seen_numbered_arg
= true;
163 /* Numbered and unnumbered specifications are exclusive. */
164 if (!is_numbered_arg
)
166 if (seen_numbered_arg
)
168 *invalid_reason
= INVALID_MIXES_NUMBERED_UNNUMBERED ();
171 seen_unnumbered_arg
= true;
175 while (*format
== ' ' || *format
== '+' || *format
== '-'
176 || *format
== '#' || *format
== '0')
184 if (spec
.allocated
== spec
.numbered_arg_count
)
186 spec
.allocated
= 2 * spec
.allocated
+ 1;
187 spec
.numbered
= (struct numbered_arg
*) xrealloc (spec
.numbered
, spec
.allocated
* sizeof (struct numbered_arg
));
189 spec
.numbered
[spec
.numbered_arg_count
].number
= number
;
190 spec
.numbered
[spec
.numbered_arg_count
].type
= FAT_INTEGER
;
191 spec
.numbered_arg_count
++;
195 else if (isdigit (*format
))
197 do format
++; while (isdigit (*format
));
200 /* Parse precision. */
209 if (spec
.allocated
== spec
.numbered_arg_count
)
211 spec
.allocated
= 2 * spec
.allocated
+ 1;
212 spec
.numbered
= (struct numbered_arg
*) xrealloc (spec
.numbered
, spec
.allocated
* sizeof (struct numbered_arg
));
214 spec
.numbered
[spec
.numbered_arg_count
].number
= number
;
215 spec
.numbered
[spec
.numbered_arg_count
].type
= FAT_INTEGER
;
216 spec
.numbered_arg_count
++;
220 else if (isdigit (*format
))
222 do format
++; while (isdigit (*format
));
226 /* Parse optional size specification. */
229 short_flag
= true, format
++;
230 else if (*format
== 'l')
236 type
= FAT_CHARACTER
;
242 type
= (short_flag
? FAT_SHORT_INTEGER
: FAT_INTEGER
);
244 case 'u': case 'o': case 'x': case 'X':
245 type
= (short_flag
? FAT_SHORT_UNSIGNED_INTEGER
: FAT_UNSIGNED_INTEGER
);
247 case 'e': case 'E': case 'f': case 'g': case 'G':
253 ? INVALID_UNTERMINATED_DIRECTIVE ()
254 : INVALID_CONVERSION_SPECIFIER (spec
.directives
, *format
));
258 if (spec
.allocated
== spec
.numbered_arg_count
)
260 spec
.allocated
= 2 * spec
.allocated
+ 1;
261 spec
.numbered
= (struct numbered_arg
*) xrealloc (spec
.numbered
, spec
.allocated
* sizeof (struct numbered_arg
));
263 spec
.numbered
[spec
.numbered_arg_count
].number
= number
;
264 spec
.numbered
[spec
.numbered_arg_count
].type
= type
;
265 spec
.numbered_arg_count
++;
273 /* Sort the numbered argument array, and eliminate duplicates. */
274 if (spec
.numbered_arg_count
> 1)
279 qsort (spec
.numbered
, spec
.numbered_arg_count
,
280 sizeof (struct numbered_arg
), numbered_arg_compare
);
282 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
284 for (i
= j
= 0; i
< spec
.numbered_arg_count
; i
++)
285 if (j
> 0 && spec
.numbered
[i
].number
== spec
.numbered
[j
-1].number
)
287 enum format_arg_type type1
= spec
.numbered
[i
].type
;
288 enum format_arg_type type2
= spec
.numbered
[j
-1].type
;
289 enum format_arg_type type_both
;
295 /* Incompatible types. */
296 type_both
= FAT_NONE
;
299 INVALID_INCOMPATIBLE_ARG_TYPES (spec
.numbered
[i
].number
);
303 spec
.numbered
[j
-1].type
= type_both
;
309 spec
.numbered
[j
].number
= spec
.numbered
[i
].number
;
310 spec
.numbered
[j
].type
= spec
.numbered
[i
].type
;
314 spec
.numbered_arg_count
= j
;
316 /* *invalid_reason has already been set above. */
320 result
= (struct spec
*) xmalloc (sizeof (struct spec
));
325 if (spec
.numbered
!= NULL
)
326 free (spec
.numbered
);
331 format_free (void *descr
)
333 struct spec
*spec
= (struct spec
*) descr
;
335 if (spec
->numbered
!= NULL
)
336 free (spec
->numbered
);
341 format_get_number_of_directives (void *descr
)
343 struct spec
*spec
= (struct spec
*) descr
;
345 return spec
->directives
;
349 format_check (void *msgid_descr
, void *msgstr_descr
, bool equality
,
350 formatstring_error_logger_t error_logger
,
351 const char *pretty_msgstr
)
353 struct spec
*spec1
= (struct spec
*) msgid_descr
;
354 struct spec
*spec2
= (struct spec
*) msgstr_descr
;
357 if (spec1
->numbered_arg_count
+ spec2
->numbered_arg_count
> 0)
360 unsigned int n1
= spec1
->numbered_arg_count
;
361 unsigned int n2
= spec2
->numbered_arg_count
;
363 /* Check the argument names are the same.
364 Both arrays are sorted. We search for the first difference. */
365 for (i
= 0, j
= 0; i
< n1
|| j
< n2
; )
367 int cmp
= (i
>= n1
? 1 :
369 spec1
->numbered
[i
].number
> spec2
->numbered
[j
].number
? 1 :
370 spec1
->numbered
[i
].number
< spec2
->numbered
[j
].number
? -1 :
376 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"),
377 spec2
->numbered
[j
].number
, pretty_msgstr
);
386 error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
387 spec1
->numbered
[i
].number
, pretty_msgstr
);
397 /* Check the argument types are the same. */
399 for (i
= 0, j
= 0; j
< n2
; )
401 if (spec1
->numbered
[i
].number
== spec2
->numbered
[j
].number
)
403 if (spec1
->numbered
[i
].type
!= spec2
->numbered
[j
].type
)
406 error_logger (_("format specifications in 'msgid' and '%s' for argument %u are not the same"),
407 pretty_msgstr
, spec2
->numbered
[j
].number
);
422 struct formatstring_parser formatstring_tcl
=
426 format_get_number_of_directives
,
433 /* Test program: Print the argument list specification returned by
434 format_parse for strings read from standard input. */
440 format_print (void *descr
)
442 struct spec
*spec
= (struct spec
*) descr
;
454 for (i
= 0; i
< spec
->numbered_arg_count
; i
++)
456 unsigned int number
= spec
->numbered
[i
].number
;
462 for (; last
< number
; last
++)
464 switch (spec
->numbered
[i
].type
)
475 case FAT_UNSIGNED_INTEGER
:
476 printf ("[unsigned]i");
478 case FAT_SHORT_INTEGER
:
481 case FAT_SHORT_UNSIGNED_INTEGER
:
482 printf ("[unsigned]hi");
501 size_t line_size
= 0;
503 char *invalid_reason
;
506 line_len
= getline (&line
, &line_size
, stdin
);
509 if (line_len
> 0 && line
[line_len
- 1] == '\n')
510 line
[--line_len
] = '\0';
512 invalid_reason
= NULL
;
513 descr
= format_parse (line
, false, &invalid_reason
);
515 format_print (descr
);
518 printf ("%s\n", invalid_reason
);
520 free (invalid_reason
);
528 * For Emacs M-x compile
530 * 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-tcl.c ../lib/libgettextlib.la"