1 /* Emacs Lisp format strings.
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 /* Emacs Lisp format strings are implemented in emacs-21.1/src/editfns.c,
36 xemacs-21.1.14/src/editfns.c and xemacs-21.1.14/src/doprnt.c.
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 a nonempty digit sequence,
43 - is optionally followed by '.' and a precision specification: '*' (reads
44 an argument) or a nonempty digit sequence,
45 - is finished by a specifier
46 - '%', that needs no argument,
47 - 'c', that need a character argument,
48 - 'd', 'i', 'x', 'X', 'o', that need an integer argument,
49 - 'e', 'E', 'f', 'g', 'G', that need a floating-point argument,
50 - 's', that need an argument and prints it using princ,
51 - 'S', that need an argument and prints it using prin1.
52 Numbered ('%m$') and unnumbered argument specifications can be used in the
53 same string. The effect of '%m$' is to set the current argument number to
54 m. The current argument number is incremented after processing a directive.
70 enum format_arg_type type
;
75 unsigned int directives
;
76 unsigned int numbered_arg_count
;
77 unsigned int allocated
;
78 struct numbered_arg
*numbered
;
81 /* Locale independent test for a decimal digit.
82 Argument can be 'char' or 'unsigned char'. (Whereas the argument of
83 <ctype.h> isdigit must be an 'unsigned char'.) */
85 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
89 numbered_arg_compare (const void *p1
, const void *p2
)
91 unsigned int n1
= ((const struct numbered_arg
*) p1
)->number
;
92 unsigned int n2
= ((const struct numbered_arg
*) p2
)->number
;
94 return (n1
> n2
? 1 : n1
< n2
? -1 : 0);
98 format_parse (const char *format
, bool translated
, char **invalid_reason
)
105 spec
.numbered_arg_count
= 0;
107 spec
.numbered
= NULL
;
110 for (; *format
!= '\0';)
111 if (*format
++ == '%')
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
));
130 if (*f
== '$' && m
> 0)
138 while (*format
== ' ' || *format
== '+' || *format
== '-'
139 || *format
== '#' || *format
== '0')
147 if (spec
.allocated
== spec
.numbered_arg_count
)
149 spec
.allocated
= 2 * spec
.allocated
+ 1;
150 spec
.numbered
= (struct numbered_arg
*) xrealloc (spec
.numbered
, spec
.allocated
* sizeof (struct numbered_arg
));
152 spec
.numbered
[spec
.numbered_arg_count
].number
= number
;
153 spec
.numbered
[spec
.numbered_arg_count
].type
= FAT_INTEGER
;
154 spec
.numbered_arg_count
++;
158 else if (isdigit (*format
))
160 do format
++; while (isdigit (*format
));
163 /* Parse precision. */
172 if (spec
.allocated
== spec
.numbered_arg_count
)
174 spec
.allocated
= 2 * spec
.allocated
+ 1;
175 spec
.numbered
= (struct numbered_arg
*) xrealloc (spec
.numbered
, spec
.allocated
* sizeof (struct numbered_arg
));
177 spec
.numbered
[spec
.numbered_arg_count
].number
= number
;
178 spec
.numbered
[spec
.numbered_arg_count
].type
= FAT_INTEGER
;
179 spec
.numbered_arg_count
++;
183 else if (isdigit (*format
))
185 do format
++; while (isdigit (*format
));
195 type
= FAT_CHARACTER
;
197 case 'd': case 'i': case 'x': case 'X': case 'o':
200 case 'e': case 'E': case 'f': case 'g': case 'G':
204 type
= FAT_OBJECT_PRETTY
;
212 ? INVALID_UNTERMINATED_DIRECTIVE ()
213 : INVALID_CONVERSION_SPECIFIER (spec
.directives
, *format
));
217 if (type
!= FAT_NONE
)
219 if (spec
.allocated
== spec
.numbered_arg_count
)
221 spec
.allocated
= 2 * spec
.allocated
+ 1;
222 spec
.numbered
= (struct numbered_arg
*) xrealloc (spec
.numbered
, spec
.allocated
* sizeof (struct numbered_arg
));
224 spec
.numbered
[spec
.numbered_arg_count
].number
= number
;
225 spec
.numbered
[spec
.numbered_arg_count
].type
= type
;
226 spec
.numbered_arg_count
++;
234 /* Sort the numbered argument array, and eliminate duplicates. */
235 if (spec
.numbered_arg_count
> 1)
240 qsort (spec
.numbered
, spec
.numbered_arg_count
,
241 sizeof (struct numbered_arg
), numbered_arg_compare
);
243 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
245 for (i
= j
= 0; i
< spec
.numbered_arg_count
; i
++)
246 if (j
> 0 && spec
.numbered
[i
].number
== spec
.numbered
[j
-1].number
)
248 enum format_arg_type type1
= spec
.numbered
[i
].type
;
249 enum format_arg_type type2
= spec
.numbered
[j
-1].type
;
250 enum format_arg_type type_both
;
256 /* Incompatible types. */
257 type_both
= FAT_NONE
;
260 INVALID_INCOMPATIBLE_ARG_TYPES (spec
.numbered
[i
].number
);
264 spec
.numbered
[j
-1].type
= type_both
;
270 spec
.numbered
[j
].number
= spec
.numbered
[i
].number
;
271 spec
.numbered
[j
].type
= spec
.numbered
[i
].type
;
275 spec
.numbered_arg_count
= j
;
277 /* *invalid_reason has already been set above. */
281 result
= (struct spec
*) xmalloc (sizeof (struct spec
));
286 if (spec
.numbered
!= NULL
)
287 free (spec
.numbered
);
292 format_free (void *descr
)
294 struct spec
*spec
= (struct spec
*) descr
;
296 if (spec
->numbered
!= NULL
)
297 free (spec
->numbered
);
302 format_get_number_of_directives (void *descr
)
304 struct spec
*spec
= (struct spec
*) descr
;
306 return spec
->directives
;
310 format_check (void *msgid_descr
, void *msgstr_descr
, bool equality
,
311 formatstring_error_logger_t error_logger
,
312 const char *pretty_msgstr
)
314 struct spec
*spec1
= (struct spec
*) msgid_descr
;
315 struct spec
*spec2
= (struct spec
*) msgstr_descr
;
318 if (spec1
->numbered_arg_count
+ spec2
->numbered_arg_count
> 0)
321 unsigned int n1
= spec1
->numbered_arg_count
;
322 unsigned int n2
= spec2
->numbered_arg_count
;
324 /* Check the argument names are the same.
325 Both arrays are sorted. We search for the first difference. */
326 for (i
= 0, j
= 0; i
< n1
|| j
< n2
; )
328 int cmp
= (i
>= n1
? 1 :
330 spec1
->numbered
[i
].number
> spec2
->numbered
[j
].number
? 1 :
331 spec1
->numbered
[i
].number
< spec2
->numbered
[j
].number
? -1 :
337 error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"),
338 spec2
->numbered
[j
].number
, pretty_msgstr
);
347 error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
348 spec1
->numbered
[i
].number
, pretty_msgstr
);
358 /* Check the argument types are the same. */
360 for (i
= 0, j
= 0; j
< n2
; )
362 if (spec1
->numbered
[i
].number
== spec2
->numbered
[j
].number
)
364 if (spec1
->numbered
[i
].type
!= spec2
->numbered
[j
].type
)
367 error_logger (_("format specifications in 'msgid' and '%s' for argument %u are not the same"),
368 pretty_msgstr
, spec2
->numbered
[j
].number
);
383 struct formatstring_parser formatstring_elisp
=
387 format_get_number_of_directives
,
394 /* Test program: Print the argument list specification returned by
395 format_parse for strings read from standard input. */
401 format_print (void *descr
)
403 struct spec
*spec
= (struct spec
*) descr
;
415 for (i
= 0; i
< spec
->numbered_arg_count
; i
++)
417 unsigned int number
= spec
->numbered
[i
].number
;
423 for (; last
< number
; last
++)
425 switch (spec
->numbered
[i
].type
)
436 case FAT_OBJECT_PRETTY
:
456 size_t line_size
= 0;
458 char *invalid_reason
;
461 line_len
= getline (&line
, &line_size
, stdin
);
464 if (line_len
> 0 && line
[line_len
- 1] == '\n')
465 line
[--line_len
] = '\0';
467 invalid_reason
= NULL
;
468 descr
= format_parse (line
, false, &invalid_reason
);
470 format_print (descr
);
473 printf ("%s\n", invalid_reason
);
475 free (invalid_reason
);
483 * For Emacs M-x compile
485 * 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-elisp.c ../lib/libgettextlib.la"