1 /* librep format strings.
2 Copyright (C) 2001-2004 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
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 /* librep format strings are implemented in librep-0.14/src/streams.c.
37 - starts with '%' or '%m$' where m is a positive integer,
38 - is optionally followed by any of the characters '-', '^', '0', '+', ' ',
39 each of which acts as a flag,
40 - is optionally followed by a width specification: a nonempty digit
42 - is optionally followed by '.' and a precision specification: a nonempty
44 - is finished by a specifier
45 - '%', that needs no argument,
46 - 'c', that need a character argument,
47 - 'd', 'x', 'X', 'o', that need an integer argument,
48 - 's', that need an argument and prints it using princ,
49 - 'S', that need an argument and prints it using prin1.
50 Numbered ('%m$') and unnumbered argument specifications can be used in the
51 same string. The effect of '%m$' is to set the current argument number to
52 m. The current argument number is incremented after processing a directive.
67 enum format_arg_type type
;
72 unsigned int directives
;
73 unsigned int numbered_arg_count
;
74 unsigned int allocated
;
75 struct numbered_arg
*numbered
;
78 /* Locale independent test for a decimal digit.
79 Argument can be 'char' or 'unsigned char'. (Whereas the argument of
80 <ctype.h> isdigit must be an 'unsigned char'.) */
82 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
86 numbered_arg_compare (const void *p1
, const void *p2
)
88 unsigned int n1
= ((const struct numbered_arg
*) p1
)->number
;
89 unsigned int n2
= ((const struct numbered_arg
*) p2
)->number
;
91 return (n1
> n2
? 1 : n1
< n2
? -1 : 0);
95 format_parse (const char *format
, bool translated
, char **invalid_reason
)
102 spec
.numbered_arg_count
= 0;
104 spec
.numbered
= NULL
;
107 for (; *format
!= '\0';)
108 if (*format
++ == '%')
111 enum format_arg_type type
;
115 if (isdigit (*format
))
117 const char *f
= format
;
122 m
= 10 * m
+ (*f
- '0');
125 while (isdigit (*f
));
127 if (*f
== '$' && m
> 0)
135 while (*format
== '-' || *format
== '^' || *format
== '0'
136 || *format
== '+' || *format
== ' ')
140 if (isdigit (*format
))
142 do format
++; while (isdigit (*format
));
145 /* Parse precision. */
150 if (isdigit (*format
))
152 do format
++; while (isdigit (*format
));
162 type
= FAT_CHARACTER
;
164 case 'd': case 'x': case 'X': case 'o':
168 type
= FAT_OBJECT_PRETTY
;
176 ? INVALID_UNTERMINATED_DIRECTIVE ()
177 : INVALID_CONVERSION_SPECIFIER (spec
.directives
, *format
));
181 if (type
!= FAT_NONE
)
183 if (spec
.allocated
== spec
.numbered_arg_count
)
185 spec
.allocated
= 2 * spec
.allocated
+ 1;
186 spec
.numbered
= (struct numbered_arg
*) xrealloc (spec
.numbered
, spec
.allocated
* sizeof (struct numbered_arg
));
188 spec
.numbered
[spec
.numbered_arg_count
].number
= number
;
189 spec
.numbered
[spec
.numbered_arg_count
].type
= type
;
190 spec
.numbered_arg_count
++;
198 /* Sort the numbered argument array, and eliminate duplicates. */
199 if (spec
.numbered_arg_count
> 1)
204 qsort (spec
.numbered
, spec
.numbered_arg_count
,
205 sizeof (struct numbered_arg
), numbered_arg_compare
);
207 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
209 for (i
= j
= 0; i
< spec
.numbered_arg_count
; i
++)
210 if (j
> 0 && spec
.numbered
[i
].number
== spec
.numbered
[j
-1].number
)
212 enum format_arg_type type1
= spec
.numbered
[i
].type
;
213 enum format_arg_type type2
= spec
.numbered
[j
-1].type
;
214 enum format_arg_type type_both
;
220 /* Incompatible types. */
221 type_both
= FAT_NONE
;
224 INVALID_INCOMPATIBLE_ARG_TYPES (spec
.numbered
[i
].number
);
228 spec
.numbered
[j
-1].type
= type_both
;
234 spec
.numbered
[j
].number
= spec
.numbered
[i
].number
;
235 spec
.numbered
[j
].type
= spec
.numbered
[i
].type
;
239 spec
.numbered_arg_count
= j
;
241 /* *invalid_reason has already been set above. */
245 result
= (struct spec
*) xmalloc (sizeof (struct spec
));
250 if (spec
.numbered
!= NULL
)
251 free (spec
.numbered
);
256 format_free (void *descr
)
258 struct spec
*spec
= (struct spec
*) descr
;
260 if (spec
->numbered
!= NULL
)
261 free (spec
->numbered
);
266 format_get_number_of_directives (void *descr
)
268 struct spec
*spec
= (struct spec
*) descr
;
270 return spec
->directives
;
274 format_check (void *msgid_descr
, void *msgstr_descr
, bool equality
,
275 formatstring_error_logger_t error_logger
,
276 const char *pretty_msgstr
)
278 struct spec
*spec1
= (struct spec
*) msgid_descr
;
279 struct spec
*spec2
= (struct spec
*) msgstr_descr
;
282 if (spec1
->numbered_arg_count
+ spec2
->numbered_arg_count
> 0)
285 unsigned int n1
= spec1
->numbered_arg_count
;
286 unsigned int n2
= spec2
->numbered_arg_count
;
288 /* Check the argument names are the same.
289 Both arrays are sorted. We search for the first difference. */
290 for (i
= 0, j
= 0; i
< n1
|| j
< n2
; )
292 int cmp
= (i
>= n1
? 1 :
294 spec1
->numbered
[i
].number
> spec2
->numbered
[j
].number
? 1 :
295 spec1
->numbered
[i
].number
< spec2
->numbered
[j
].number
? -1 :
301 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"),
302 spec2
->numbered
[j
].number
, pretty_msgstr
);
311 error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
312 spec1
->numbered
[i
].number
, pretty_msgstr
);
322 /* Check the argument types are the same. */
324 for (i
= 0, j
= 0; j
< n2
; )
326 if (spec1
->numbered
[i
].number
== spec2
->numbered
[j
].number
)
328 if (spec1
->numbered
[i
].type
!= spec2
->numbered
[j
].type
)
331 error_logger (_("format specifications in 'msgid' and '%s' for argument %u are not the same"),
332 pretty_msgstr
, spec2
->numbered
[j
].number
);
347 struct formatstring_parser formatstring_librep
=
351 format_get_number_of_directives
,
358 /* Test program: Print the argument list specification returned by
359 format_parse for strings read from standard input. */
365 format_print (void *descr
)
367 struct spec
*spec
= (struct spec
*) descr
;
379 for (i
= 0; i
< spec
->numbered_arg_count
; i
++)
381 unsigned int number
= spec
->numbered
[i
].number
;
387 for (; last
< number
; last
++)
389 switch (spec
->numbered
[i
].type
)
397 case FAT_OBJECT_PRETTY
:
417 size_t line_size
= 0;
419 char *invalid_reason
;
422 line_len
= getline (&line
, &line_size
, stdin
);
425 if (line_len
> 0 && line
[line_len
- 1] == '\n')
426 line
[--line_len
] = '\0';
428 invalid_reason
= NULL
;
429 descr
= format_parse (line
, false, &invalid_reason
);
431 format_print (descr
);
434 printf ("%s\n", invalid_reason
);
436 free (invalid_reason
);
444 * For Emacs M-x compile
446 * 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-librep.c ../lib/libgettextlib.la"