1/* Format strings. 2 Copyright (C) 2001-2007 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. */ 35struct 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_lisp */ &formatstring_lisp, 42 /* format_elisp */ &formatstring_elisp, 43 /* format_librep */ &formatstring_librep, 44 /* format_scheme */ &formatstring_scheme, 45 /* format_smalltalk */ &formatstring_smalltalk, 46 /* format_java */ &formatstring_java, 47 /* format_csharp */ &formatstring_csharp, 48 /* format_awk */ &formatstring_awk, 49 /* format_pascal */ &formatstring_pascal, 50 /* format_ycp */ &formatstring_ycp, 51 /* format_tcl */ &formatstring_tcl, 52 /* format_perl */ &formatstring_perl, 53 /* format_perl_brace */ &formatstring_perl_brace, 54 /* format_php */ &formatstring_php, 55 /* format_gcc_internal */ &formatstring_gcc_internal, 56 /* format_qt */ &formatstring_qt, 57 /* format_kde */ &formatstring_kde, 58 /* format_boost */ &formatstring_boost 59}; 60 61/* Check whether both formats strings contain compatible format 62 specifications for format type i (0 <= i < NFORMATS). 63 PLURAL_DISTRIBUTION is either NULL or an array of nplurals elements, 64 PLURAL_DISTRIBUTION[j] being true if the value j appears to be assumed 65 infinitely often by the plural formula. 66 PLURAL_DISTRIBUTION_LENGTH is the length of the PLURAL_DISTRIBUTION array. 67 Return the number of errors that were seen. */ 68int 69check_msgid_msgstr_format_i (const char *msgid, const char *msgid_plural, 70 const char *msgstr, size_t msgstr_len, 71 size_t i, 72 const unsigned char *plural_distribution, 73 unsigned long plural_distribution_length, 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 char buf[18+1]; 98 const char *pretty_msgstr = "msgstr"; 99 bool has_plural_translations = (strlen (msgstr) + 1 < msgstr_len); 100 const char *p_end = msgstr + msgstr_len; 101 const char *p; 102 unsigned int j; 103 104 for (p = msgstr, j = 0; p < p_end; p += strlen (p) + 1, j++) 105 { 106 void *msgstr_descr; 107 108 if (msgid_plural != NULL) 109 { 110 sprintf (buf, "msgstr[%u]", j); 111 pretty_msgstr = buf; 112 } 113 114 msgstr_descr = parser->parse (p, true, NULL, &invalid_reason); 115 116 if (msgstr_descr != NULL) 117 { 118 /* Use strict checking (require same number of format 119 directives on both sides) if the message has no plurals, 120 or if msgid_plural exists but on the msgstr[] side 121 there is only msgstr[0], or if plural_distribution[j] 122 indicates that the variant applies to infinitely many 123 values of N. 124 Use relaxed checking when there are at least two 125 msgstr[] forms and the plural_distribution array does 126 not give more precise information. */ 127 bool strict_checking = 128 (msgid_plural == NULL 129 || !has_plural_translations 130 || (plural_distribution != NULL 131 && j < plural_distribution_length 132 && plural_distribution[j])); 133 134 if (parser->check (msgid_descr, msgstr_descr, 135 strict_checking, 136 error_logger, pretty_msgstr)) 137 seen_errors++; 138 139 parser->free (msgstr_descr); 140 } 141 else 142 { 143 error_logger (_("\ 144'%s' is not a valid %s format string, unlike 'msgid'. Reason: %s"), 145 pretty_msgstr, format_language_pretty[i], 146 invalid_reason); 147 seen_errors++; 148 free (invalid_reason); 149 } 150 } 151 152 parser->free (msgid_descr); 153 } 154 else 155 free (invalid_reason); 156 157 return seen_errors; 158} 159 160/* Check whether both formats strings contain compatible format 161 specifications. 162 PLURAL_DISTRIBUTION is either NULL or an array of nplurals elements, 163 PLURAL_DISTRIBUTION[j] being true if the value j appears to be assumed 164 infinitely often by the plural formula. 165 PLURAL_DISTRIBUTION_LENGTH is the length of the PLURAL_DISTRIBUTION array. 166 Return the number of errors that were seen. */ 167int 168check_msgid_msgstr_format (const char *msgid, const char *msgid_plural, 169 const char *msgstr, size_t msgstr_len, 170 const enum is_format is_format[NFORMATS], 171 const unsigned char *plural_distribution, 172 unsigned long plural_distribution_length, 173 formatstring_error_logger_t error_logger) 174{ 175 int seen_errors = 0; 176 size_t i; 177 178 /* We check only those messages for which the msgid's is_format flag 179 is one of 'yes' or 'possible'. We don't check msgids with is_format 180 'no' or 'impossible', to obey the programmer's order. We don't check 181 msgids with is_format 'undecided' because that would introduce too 182 many checks, thus forcing the programmer to add "xgettext: no-c-format" 183 anywhere where a translator wishes to use a percent sign. */ 184 for (i = 0; i < NFORMATS; i++) 185 if (possible_format_p (is_format[i])) 186 seen_errors += check_msgid_msgstr_format_i (msgid, msgid_plural, 187 msgstr, msgstr_len, i, 188 plural_distribution, 189 plural_distribution_length, 190 error_logger); 191 192 return seen_errors; 193} 194