Imported Upstream version 0.18.3.2
[platform/upstream/gettext.git] / gettext-tools / src / format.c
1 /* Format strings.
2    Copyright (C) 2001-2009 Free Software Foundation, Inc.
3    Written by Bruno Haible <haible@clisp.cons.org>, 2001.
4
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 3 of the License, or
8    (at your option) any later version.
9
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.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 /* Specification.  */
23 #include "format.h"
24
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28
29 #include "message.h"
30 #include "gettext.h"
31
32 #define _(str) gettext (str)
33
34 /* Table of all format string parsers.  */
35 struct formatstring_parser *formatstring_parsers[NFORMATS] =
36 {
37   /* format_c */                &formatstring_c,
38   /* format_objc */             &formatstring_objc,
39   /* format_sh */               &formatstring_sh,
40   /* format_python */           &formatstring_python,
41   /* format_python_brace */     &formatstring_python_brace,
42   /* format_lisp */             &formatstring_lisp,
43   /* format_elisp */            &formatstring_elisp,
44   /* format_librep */           &formatstring_librep,
45   /* format_scheme */           &formatstring_scheme,
46   /* format_smalltalk */        &formatstring_smalltalk,
47   /* format_java */             &formatstring_java,
48   /* format_csharp */           &formatstring_csharp,
49   /* format_awk */              &formatstring_awk,
50   /* format_pascal */           &formatstring_pascal,
51   /* format_ycp */              &formatstring_ycp,
52   /* format_tcl */              &formatstring_tcl,
53   /* format_perl */             &formatstring_perl,
54   /* format_perl_brace */       &formatstring_perl_brace,
55   /* format_php */              &formatstring_php,
56   /* format_gcc_internal */     &formatstring_gcc_internal,
57   /* format_gfc_internal */     &formatstring_gfc_internal,
58   /* format_qt */               &formatstring_qt,
59   /* format_qt_plural */        &formatstring_qt_plural,
60   /* format_kde */              &formatstring_kde,
61   /* format_boost */            &formatstring_boost,
62   /* format_lua */              &formatstring_lua,
63   /* format_javascript */       &formatstring_javascript
64 };
65
66 /* Check whether both formats strings contain compatible format
67    specifications for format type i (0 <= i < NFORMATS).  */
68 int
69 check_msgid_msgstr_format_i (const char *msgid, const char *msgid_plural,
70                              const char *msgstr, size_t msgstr_len,
71                              size_t i,
72                              struct argument_range range,
73                              const struct plural_distribution *distribution,
74                              formatstring_error_logger_t error_logger)
75 {
76   int seen_errors = 0;
77
78   /* At runtime, we can assume the program passes arguments that fit well for
79      msgid.  We must signal an error if msgstr wants more arguments that msgid
80      accepts.
81      If msgstr wants fewer arguments than msgid, it wouldn't lead to a crash
82      at runtime, but we nevertheless give an error because
83      1) this situation occurs typically after the programmer has added some
84         arguments to msgid, so we must make the translator specially aware
85         of it (more than just "fuzzy"),
86      2) it is generally wrong if a translation wants to ignore arguments that
87         are used by other translations.  */
88
89   struct formatstring_parser *parser = formatstring_parsers[i];
90   char *invalid_reason = NULL;
91   void *msgid_descr =
92     parser->parse (msgid_plural != NULL ? msgid_plural : msgid, false, NULL,
93                    &invalid_reason);
94
95   if (msgid_descr != NULL)
96     {
97       const char *pretty_msgid =
98         (msgid_plural != NULL ? "msgid_plural" : "msgid");
99       char buf[18+1];
100       const char *pretty_msgstr = "msgstr";
101       bool has_plural_translations = (strlen (msgstr) + 1 < msgstr_len);
102       const char *p_end = msgstr + msgstr_len;
103       const char *p;
104       unsigned int j;
105
106       for (p = msgstr, j = 0; p < p_end; p += strlen (p) + 1, j++)
107         {
108           void *msgstr_descr;
109
110           if (msgid_plural != NULL)
111             {
112               sprintf (buf, "msgstr[%u]", j);
113               pretty_msgstr = buf;
114             }
115
116           msgstr_descr = parser->parse (p, true, NULL, &invalid_reason);
117
118           if (msgstr_descr != NULL)
119             {
120               /* Use strict checking (require same number of format
121                  directives on both sides) if the message has no plurals,
122                  or if msgid_plural exists but on the msgstr[] side
123                  there is only msgstr[0], or if distribution->often[j]
124                  indicates that the variant applies to infinitely many
125                  values of N and the N range is not restricted in a way
126                  that the variant applies to only one N.
127                  Use relaxed checking when there are at least two
128                  msgstr[] forms and the distribution does not give more
129                  precise information.  */
130               bool strict_checking =
131                 (msgid_plural == NULL
132                  || !has_plural_translations
133                  || (distribution != NULL
134                      && distribution->often != NULL
135                      && j < distribution->often_length
136                      && distribution->often[j]
137                      && !(has_range_p (range)
138                           && distribution->histogram (distribution,
139                                                       range.min, range.max, j)
140                              <= 1)));
141
142               if (parser->check (msgid_descr, msgstr_descr,
143                                  strict_checking,
144                                  error_logger, pretty_msgid, pretty_msgstr))
145                 seen_errors++;
146
147               parser->free (msgstr_descr);
148             }
149           else
150             {
151               error_logger (_("\
152 '%s' is not a valid %s format string, unlike '%s'. Reason: %s"),
153                             pretty_msgstr, format_language_pretty[i],
154                             pretty_msgid, invalid_reason);
155               seen_errors++;
156               free (invalid_reason);
157             }
158         }
159
160       parser->free (msgid_descr);
161     }
162   else
163     free (invalid_reason);
164
165   return seen_errors;
166 }
167
168 /* Check whether both formats strings contain compatible format
169    specifications.
170    Return the number of errors that were seen.  */
171 int
172 check_msgid_msgstr_format (const char *msgid, const char *msgid_plural,
173                            const char *msgstr, size_t msgstr_len,
174                            const enum is_format is_format[NFORMATS],
175                            struct argument_range range,
176                            const struct plural_distribution *distribution,
177                            formatstring_error_logger_t error_logger)
178 {
179   int seen_errors = 0;
180   size_t i;
181
182   /* We check only those messages for which the msgid's is_format flag
183      is one of 'yes' or 'possible'.  We don't check msgids with is_format
184      'no' or 'impossible', to obey the programmer's order.  We don't check
185      msgids with is_format 'undecided' because that would introduce too
186      many checks, thus forcing the programmer to add "xgettext: no-c-format"
187      anywhere where a translator wishes to use a percent sign.  */
188   for (i = 0; i < NFORMATS; i++)
189     if (possible_format_p (is_format[i]))
190       seen_errors += check_msgid_msgstr_format_i (msgid, msgid_plural,
191                                                   msgstr, msgstr_len, i,
192                                                   range,
193                                                   distribution,
194                                                   error_logger);
195
196   return seen_errors;
197 }