1*946379e7Schristos /* Format strings.
2*946379e7Schristos Copyright (C) 2001-2006 Free Software Foundation, Inc.
3*946379e7Schristos Written by Bruno Haible <haible@clisp.cons.org>, 2001.
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 /* Specification. */
24*946379e7Schristos #include "format.h"
25*946379e7Schristos
26*946379e7Schristos #include <stdbool.h>
27*946379e7Schristos #include <stdio.h>
28*946379e7Schristos #include <stdlib.h>
29*946379e7Schristos
30*946379e7Schristos #include "message.h"
31*946379e7Schristos #include "gettext.h"
32*946379e7Schristos
33*946379e7Schristos #define _(str) gettext (str)
34*946379e7Schristos
35*946379e7Schristos /* Table of all format string parsers. */
36*946379e7Schristos struct formatstring_parser *formatstring_parsers[NFORMATS] =
37*946379e7Schristos {
38*946379e7Schristos /* format_c */ &formatstring_c,
39*946379e7Schristos /* format_objc */ &formatstring_objc,
40*946379e7Schristos /* format_sh */ &formatstring_sh,
41*946379e7Schristos /* format_python */ &formatstring_python,
42*946379e7Schristos /* format_lisp */ &formatstring_lisp,
43*946379e7Schristos /* format_elisp */ &formatstring_elisp,
44*946379e7Schristos /* format_librep */ &formatstring_librep,
45*946379e7Schristos /* format_scheme */ &formatstring_scheme,
46*946379e7Schristos /* format_smalltalk */ &formatstring_smalltalk,
47*946379e7Schristos /* format_java */ &formatstring_java,
48*946379e7Schristos /* format_csharp */ &formatstring_csharp,
49*946379e7Schristos /* format_awk */ &formatstring_awk,
50*946379e7Schristos /* format_pascal */ &formatstring_pascal,
51*946379e7Schristos /* format_ycp */ &formatstring_ycp,
52*946379e7Schristos /* format_tcl */ &formatstring_tcl,
53*946379e7Schristos /* format_perl */ &formatstring_perl,
54*946379e7Schristos /* format_perl_brace */ &formatstring_perl_brace,
55*946379e7Schristos /* format_php */ &formatstring_php,
56*946379e7Schristos /* format_gcc_internal */ &formatstring_gcc_internal,
57*946379e7Schristos /* format_qt */ &formatstring_qt,
58*946379e7Schristos /* format_boost */ &formatstring_boost
59*946379e7Schristos };
60*946379e7Schristos
61*946379e7Schristos /* Check whether both formats strings contain compatible format
62*946379e7Schristos specifications.
63*946379e7Schristos PLURAL_DISTRIBUTION is either NULL or an array of nplurals elements,
64*946379e7Schristos PLURAL_DISTRIBUTION[j] being true if the value j appears to be assumed
65*946379e7Schristos infinitely often by the plural formula.
66*946379e7Schristos Return the number of errors that were seen. */
67*946379e7Schristos int
check_msgid_msgstr_format(const char * msgid,const char * msgid_plural,const char * msgstr,size_t msgstr_len,const enum is_format is_format[NFORMATS],const unsigned char * plural_distribution,formatstring_error_logger_t error_logger)68*946379e7Schristos check_msgid_msgstr_format (const char *msgid, const char *msgid_plural,
69*946379e7Schristos const char *msgstr, size_t msgstr_len,
70*946379e7Schristos const enum is_format is_format[NFORMATS],
71*946379e7Schristos const unsigned char *plural_distribution,
72*946379e7Schristos formatstring_error_logger_t error_logger)
73*946379e7Schristos {
74*946379e7Schristos int seen_errors = 0;
75*946379e7Schristos size_t i;
76*946379e7Schristos unsigned int j;
77*946379e7Schristos
78*946379e7Schristos /* We check only those messages for which the msgid's is_format flag
79*946379e7Schristos is one of 'yes' or 'possible'. We don't check msgids with is_format
80*946379e7Schristos 'no' or 'impossible', to obey the programmer's order. We don't check
81*946379e7Schristos msgids with is_format 'undecided' because that would introduce too
82*946379e7Schristos many checks, thus forcing the programmer to add "xgettext: no-c-format"
83*946379e7Schristos anywhere where a translator wishes to use a percent sign. */
84*946379e7Schristos for (i = 0; i < NFORMATS; i++)
85*946379e7Schristos if (possible_format_p (is_format[i]))
86*946379e7Schristos {
87*946379e7Schristos /* At runtime, we can assume the program passes arguments that
88*946379e7Schristos fit well for msgid. We must signal an error if msgstr wants
89*946379e7Schristos more arguments that msgid accepts.
90*946379e7Schristos If msgstr wants fewer arguments than msgid, it wouldn't lead
91*946379e7Schristos to a crash at runtime, but we nevertheless give an error because
92*946379e7Schristos 1) this situation occurs typically after the programmer has
93*946379e7Schristos added some arguments to msgid, so we must make the translator
94*946379e7Schristos specially aware of it (more than just "fuzzy"),
95*946379e7Schristos 2) it is generally wrong if a translation wants to ignore
96*946379e7Schristos arguments that are used by other translations. */
97*946379e7Schristos
98*946379e7Schristos struct formatstring_parser *parser = formatstring_parsers[i];
99*946379e7Schristos char *invalid_reason = NULL;
100*946379e7Schristos void *msgid_descr =
101*946379e7Schristos parser->parse (msgid_plural != NULL ? msgid_plural : msgid,
102*946379e7Schristos false, &invalid_reason);
103*946379e7Schristos
104*946379e7Schristos if (msgid_descr != NULL)
105*946379e7Schristos {
106*946379e7Schristos char buf[18+1];
107*946379e7Schristos const char *pretty_msgstr = "msgstr";
108*946379e7Schristos bool has_plural_translations = (strlen (msgstr) + 1 < msgstr_len);
109*946379e7Schristos const char *p_end = msgstr + msgstr_len;
110*946379e7Schristos const char *p;
111*946379e7Schristos
112*946379e7Schristos for (p = msgstr, j = 0; p < p_end; p += strlen (p) + 1, j++)
113*946379e7Schristos {
114*946379e7Schristos void *msgstr_descr;
115*946379e7Schristos
116*946379e7Schristos if (msgid_plural != NULL)
117*946379e7Schristos {
118*946379e7Schristos sprintf (buf, "msgstr[%u]", j);
119*946379e7Schristos pretty_msgstr = buf;
120*946379e7Schristos }
121*946379e7Schristos
122*946379e7Schristos msgstr_descr = parser->parse (p, true, &invalid_reason);
123*946379e7Schristos
124*946379e7Schristos if (msgstr_descr != NULL)
125*946379e7Schristos {
126*946379e7Schristos /* Use strict checking (require same number of format
127*946379e7Schristos directives on both sides) if the message has no plurals,
128*946379e7Schristos or if msgid_plural exists but on the msgstr[] side
129*946379e7Schristos there is only msgstr[0], or if plural_distribution[j]
130*946379e7Schristos indicates that the variant applies to infinitely many
131*946379e7Schristos values of N.
132*946379e7Schristos Use relaxed checking when there are at least two
133*946379e7Schristos msgstr[] forms and the plural_distribution array does
134*946379e7Schristos not give more precise information. */
135*946379e7Schristos bool strict_checking =
136*946379e7Schristos (msgid_plural == NULL
137*946379e7Schristos || !has_plural_translations
138*946379e7Schristos || (plural_distribution != NULL && plural_distribution[j]));
139*946379e7Schristos
140*946379e7Schristos if (parser->check (msgid_descr, msgstr_descr,
141*946379e7Schristos strict_checking,
142*946379e7Schristos error_logger, pretty_msgstr))
143*946379e7Schristos seen_errors++;
144*946379e7Schristos
145*946379e7Schristos parser->free (msgstr_descr);
146*946379e7Schristos }
147*946379e7Schristos else
148*946379e7Schristos {
149*946379e7Schristos error_logger (_("\
150*946379e7Schristos '%s' is not a valid %s format string, unlike 'msgid'. Reason: %s"),
151*946379e7Schristos pretty_msgstr, format_language_pretty[i],
152*946379e7Schristos invalid_reason);
153*946379e7Schristos seen_errors++;
154*946379e7Schristos free (invalid_reason);
155*946379e7Schristos }
156*946379e7Schristos }
157*946379e7Schristos
158*946379e7Schristos parser->free (msgid_descr);
159*946379e7Schristos }
160*946379e7Schristos else
161*946379e7Schristos free (invalid_reason);
162*946379e7Schristos }
163*946379e7Schristos
164*946379e7Schristos return seen_errors;
165*946379e7Schristos }
166