--check-format.
+2007-10-20 Bruno Haible <bruno@clisp.org>
+
+ Make msgmerge's introduction of fuzzy markers more consistent with
+ msgfmt's --check-format.
+ * format.h (check_msgid_msgstr_format_i): New declaration.
+ * format.c (check_msgid_msgstr_format_i): New function, extracted from
+ check_msgid_msgstr_format.
+ (check_msgid_msgstr_format): Use it.
+ * msgl-check.h: Include plural-eval.h.
+ (check_plural_eval): New declaration.
+ * msgl-check.c (check_plural_eval): Add const to first parameter. Make
+ non-static.
+ (check_plural): Update.
+ * msgmerge.c: Include plural-exp.h, msgl-check.h, po-xerror.h.
+ (msgfmt_check_pair_fails): Remove function.
+ (silent_error_logger, silent_xerror): New functions.
+ (message_merge): Add plural_distribution, plural_distribution_length
+ arguments. Call check_msgid_msgstr_format_i instead of
+ msgfmt_check_pair_fails.
+ (match_domain): Extract not only the plural count, but also the
+ plural expression from the header entry. Determine the plural
+ distribution from it. Pass it to message_merge.
+ Reported by Chusslove Illich (Часлав Илић) <caslav.ilic@gmx.net>.
+
2007-10-20 Bruno Haible <bruno@clisp.org>
* msgl-check.c (check_plural): If there is no header entry, or if the
/* format_boost */ &formatstring_boost
};
+/* Check whether both formats strings contain compatible format
+ specifications for format type i (0 <= i < NFORMATS).
+ PLURAL_DISTRIBUTION is either NULL or an array of nplurals elements,
+ PLURAL_DISTRIBUTION[j] being true if the value j appears to be assumed
+ infinitely often by the plural formula.
+ PLURAL_DISTRIBUTION_LENGTH is the length of the PLURAL_DISTRIBUTION array.
+ Return the number of errors that were seen. */
+int
+check_msgid_msgstr_format_i (const char *msgid, const char *msgid_plural,
+ const char *msgstr, size_t msgstr_len,
+ size_t i,
+ const unsigned char *plural_distribution,
+ unsigned long plural_distribution_length,
+ formatstring_error_logger_t error_logger)
+{
+ int seen_errors = 0;
+
+ /* At runtime, we can assume the program passes arguments that fit well for
+ msgid. We must signal an error if msgstr wants more arguments that msgid
+ accepts.
+ If msgstr wants fewer arguments than msgid, it wouldn't lead to a crash
+ at runtime, but we nevertheless give an error because
+ 1) this situation occurs typically after the programmer has added some
+ arguments to msgid, so we must make the translator specially aware
+ of it (more than just "fuzzy"),
+ 2) it is generally wrong if a translation wants to ignore arguments that
+ are used by other translations. */
+
+ struct formatstring_parser *parser = formatstring_parsers[i];
+ char *invalid_reason = NULL;
+ void *msgid_descr =
+ parser->parse (msgid_plural != NULL ? msgid_plural : msgid, false, NULL,
+ &invalid_reason);
+
+ if (msgid_descr != NULL)
+ {
+ char buf[18+1];
+ const char *pretty_msgstr = "msgstr";
+ bool has_plural_translations = (strlen (msgstr) + 1 < msgstr_len);
+ const char *p_end = msgstr + msgstr_len;
+ const char *p;
+ unsigned int j;
+
+ for (p = msgstr, j = 0; p < p_end; p += strlen (p) + 1, j++)
+ {
+ void *msgstr_descr;
+
+ if (msgid_plural != NULL)
+ {
+ sprintf (buf, "msgstr[%u]", j);
+ pretty_msgstr = buf;
+ }
+
+ msgstr_descr = parser->parse (p, true, NULL, &invalid_reason);
+
+ if (msgstr_descr != NULL)
+ {
+ /* Use strict checking (require same number of format
+ directives on both sides) if the message has no plurals,
+ or if msgid_plural exists but on the msgstr[] side
+ there is only msgstr[0], or if plural_distribution[j]
+ indicates that the variant applies to infinitely many
+ values of N.
+ Use relaxed checking when there are at least two
+ msgstr[] forms and the plural_distribution array does
+ not give more precise information. */
+ bool strict_checking =
+ (msgid_plural == NULL
+ || !has_plural_translations
+ || (plural_distribution != NULL
+ && j < plural_distribution_length
+ && plural_distribution[j]));
+
+ if (parser->check (msgid_descr, msgstr_descr,
+ strict_checking,
+ error_logger, pretty_msgstr))
+ seen_errors++;
+
+ parser->free (msgstr_descr);
+ }
+ else
+ {
+ error_logger (_("\
+'%s' is not a valid %s format string, unlike 'msgid'. Reason: %s"),
+ pretty_msgstr, format_language_pretty[i],
+ invalid_reason);
+ seen_errors++;
+ free (invalid_reason);
+ }
+ }
+
+ parser->free (msgid_descr);
+ }
+ else
+ free (invalid_reason);
+
+ return seen_errors;
+}
+
/* Check whether both formats strings contain compatible format
specifications.
PLURAL_DISTRIBUTION is either NULL or an array of nplurals elements,
{
int seen_errors = 0;
size_t i;
- unsigned int j;
/* We check only those messages for which the msgid's is_format flag
is one of 'yes' or 'possible'. We don't check msgids with is_format
anywhere where a translator wishes to use a percent sign. */
for (i = 0; i < NFORMATS; i++)
if (possible_format_p (is_format[i]))
- {
- /* At runtime, we can assume the program passes arguments that
- fit well for msgid. We must signal an error if msgstr wants
- more arguments that msgid accepts.
- If msgstr wants fewer arguments than msgid, it wouldn't lead
- to a crash at runtime, but we nevertheless give an error because
- 1) this situation occurs typically after the programmer has
- added some arguments to msgid, so we must make the translator
- specially aware of it (more than just "fuzzy"),
- 2) it is generally wrong if a translation wants to ignore
- arguments that are used by other translations. */
-
- struct formatstring_parser *parser = formatstring_parsers[i];
- char *invalid_reason = NULL;
- void *msgid_descr =
- parser->parse (msgid_plural != NULL ? msgid_plural : msgid,
- false, NULL, &invalid_reason);
-
- if (msgid_descr != NULL)
- {
- char buf[18+1];
- const char *pretty_msgstr = "msgstr";
- bool has_plural_translations = (strlen (msgstr) + 1 < msgstr_len);
- const char *p_end = msgstr + msgstr_len;
- const char *p;
-
- for (p = msgstr, j = 0; p < p_end; p += strlen (p) + 1, j++)
- {
- void *msgstr_descr;
-
- if (msgid_plural != NULL)
- {
- sprintf (buf, "msgstr[%u]", j);
- pretty_msgstr = buf;
- }
-
- msgstr_descr = parser->parse (p, true, NULL, &invalid_reason);
-
- if (msgstr_descr != NULL)
- {
- /* Use strict checking (require same number of format
- directives on both sides) if the message has no plurals,
- or if msgid_plural exists but on the msgstr[] side
- there is only msgstr[0], or if plural_distribution[j]
- indicates that the variant applies to infinitely many
- values of N.
- Use relaxed checking when there are at least two
- msgstr[] forms and the plural_distribution array does
- not give more precise information. */
- bool strict_checking =
- (msgid_plural == NULL
- || !has_plural_translations
- || (plural_distribution != NULL
- && j < plural_distribution_length
- && plural_distribution[j]));
-
- if (parser->check (msgid_descr, msgstr_descr,
- strict_checking,
- error_logger, pretty_msgstr))
- seen_errors++;
-
- parser->free (msgstr_descr);
- }
- else
- {
- error_logger (_("\
-'%s' is not a valid %s format string, unlike 'msgid'. Reason: %s"),
- pretty_msgstr, format_language_pretty[i],
- invalid_reason);
- seen_errors++;
- free (invalid_reason);
- }
- }
-
- parser->free (msgid_descr);
- }
- else
- free (invalid_reason);
- }
+ seen_errors += check_msgid_msgstr_format_i (msgid, msgid_plural,
+ msgstr, msgstr_len, i,
+ plural_distribution,
+ plural_distribution_length,
+ error_logger);
return seen_errors;
}
string. */
extern unsigned int get_python_format_unnamed_arg_count (const char *string);
+/* Check whether both formats strings contain compatible format
+ specifications for format type i (0 <= i < NFORMATS).
+ PLURAL_DISTRIBUTION is either NULL or an array of nplurals elements,
+ PLURAL_DISTRIBUTION[j] being true if the value j appears to be assumed
+ infinitely often by the plural formula.
+ Return the number of errors that were seen. */
+extern int
+ check_msgid_msgstr_format_i (const char *msgid, const char *msgid_plural,
+ const char *msgstr, size_t msgstr_len,
+ size_t i,
+ const unsigned char *plural_distribution,
+ unsigned long plural_distribution_length,
+ formatstring_error_logger_t error_logger);
+
/* Check whether both formats strings contain compatible format
specifications.
PLURAL_DISTRIBUTION is either NULL or an array of nplurals elements,
/* Check the values returned by plural_eval.
+ Signals the errors through po_xerror.
Return the number of errors that were seen.
If no errors, returns in *PLURAL_DISTRIBUTION either NULL or an array
of length NPLURALS_VALUE describing which plural formula values appear
infinitely often and in *PLURAL_DISTRIBUTION_LENGTH the length of this
array. */
-static int
-check_plural_eval (struct expression *plural_expr,
+int
+check_plural_eval (const struct expression *plural_expr,
unsigned long nplurals_value,
const message_ty *header,
unsigned char **plural_distribution,
const char *endp;
unsigned long int nplurals_value;
struct parse_args args;
- struct expression *plural_expr;
+ const struct expression *plural_expr;
/* First check the number. */
nplurals += 9;
#include "message.h"
#include "pos.h"
+#include "plural-eval.h"
#ifdef __cplusplus
#endif
+/* Check the values returned by plural_eval.
+ Signals the errors through po_xerror.
+ Return the number of errors that were seen.
+ If no errors, returns in *PLURAL_DISTRIBUTION either NULL or an array
+ of length NPLURALS_VALUE describing which plural formula values appear
+ infinitely often and in *PLURAL_DISTRIBUTION_LENGTH the length of this
+ array. */
+extern int check_plural_eval (const struct expression *plural_expr,
+ unsigned long nplurals_value,
+ const message_ty *header,
+ unsigned char **plural_distribution,
+ unsigned long *plural_distribution_length);
+
/* Perform all checks on a non-obsolete message.
PLURAL_DISTRIBUTION is either NULL or an array of nplurals elements,
PLURAL_DISTRIBUTION[j] being true if the value j appears to be assumed
#include "msgl-equal.h"
#include "msgl-fsearch.h"
#include "lock.h"
+#include "plural-exp.h"
#include "plural-count.h"
+#include "msgl-check.h"
+#include "po-xerror.h"
#include "backupfile.h"
#include "copy-file.h"
#include "propername.h"
}
-static bool
-msgfmt_check_pair_fails (const lex_pos_ty *pos,
- const char *msgid, const char *msgid_plural,
- const char *msgstr, size_t msgstr_len,
- size_t fmt)
+/* A silent error logger. We are only interested in knowing whether errors
+ occurred at all. */
+static void
+silent_error_logger (const char *format, ...)
+ __attribute__ ((__format__ (__printf__, 1, 2)));
+static void
+silent_error_logger (const char *format, ...)
{
- bool failure;
- struct formatstring_parser *parser = formatstring_parsers[fmt];
- char *invalid_reason = NULL;
- void *msgid_descr =
- parser->parse (msgid_plural != NULL ? msgid_plural : msgid, false, NULL,
- &invalid_reason);
-
- failure = false;
- if (msgid_descr != NULL)
- {
- const char *p_end = msgstr + msgstr_len;
- const char *p;
-
- for (p = msgstr; p < p_end; p += strlen (p) + 1)
- {
- void *msgstr_descr =
- parser->parse (msgstr, true, NULL, &invalid_reason);
-
- if (msgstr_descr != NULL)
- {
- failure = parser->check (msgid_descr, msgstr_descr,
- msgid_plural == NULL, NULL, NULL);
- parser->free (msgstr_descr);
- }
- else
- {
- failure = true;
- free (invalid_reason);
- }
-
- if (failure)
- break;
- }
+}
- parser->free (msgid_descr);
- }
- else
- free (invalid_reason);
- return failure;
+/* Another silent error logger. */
+static void
+silent_xerror (int severity,
+ const struct message_ty *message,
+ const char *filename, size_t lineno, size_t column,
+ int multiline_p, const char *message_text)
+{
}
static message_ty *
-message_merge (message_ty *def, message_ty *ref, bool force_fuzzy)
+message_merge (message_ty *def, message_ty *ref, bool force_fuzzy,
+ const unsigned char *plural_distribution,
+ unsigned long plural_distribution_length)
{
const char *msgstr;
size_t msgstr_len;
if (!result->is_fuzzy
&& possible_format_p (ref->is_format[i])
&& !possible_format_p (def->is_format[i])
- && msgfmt_check_pair_fails (&def->pos, ref->msgid, ref->msgid_plural,
- msgstr, msgstr_len, i))
+ && check_msgid_msgstr_format_i (ref->msgid, ref->msgid_plural,
+ msgstr, msgstr_len, i,
+ plural_distribution,
+ plural_distribution_length,
+ silent_error_logger) > 0)
result->is_fuzzy = true;
}
{
message_ty *header_entry;
unsigned long int nplurals;
+ const struct expression *plural_expr;
char *untranslated_plural_msgstr;
+ unsigned char *plural_distribution;
+ unsigned long plural_distribution_length;
struct search_result { message_ty *found; bool fuzzy; } *search_results;
size_t j;
header_entry =
message_list_search (definitions_current_list (definitions), NULL, "");
- nplurals = get_plural_count (header_entry ? header_entry->msgstr : NULL);
+ extract_plural_expression (header_entry ? header_entry->msgstr : NULL,
+ &plural_expr, &nplurals);
untranslated_plural_msgstr = XNMALLOC (nplurals, char);
memset (untranslated_plural_msgstr, '\0', nplurals);
+ /* Determine the plural distribution of the plural_expr formula. */
+ {
+ /* Disable error output temporarily. */
+ void (*old_po_xerror) (int, const struct message_ty *, const char *, size_t,
+ size_t, int, const char *)
+ = po_xerror;
+ po_xerror = silent_xerror;
+
+ if (check_plural_eval (plural_expr, nplurals, header_entry,
+ &plural_distribution,
+ &plural_distribution_length) > 0)
+ {
+ plural_distribution = NULL;
+ plural_distribution_length = 0;
+ }
+
+ po_xerror = old_po_xerror;
+ }
+
/* Most of the time is spent in definitions_search_fuzzy.
Perform it in a separate loop that can be parallelized by an OpenMP
capable compiler. */
#: comments from the reference, take the # comments from
the definition, take the msgstr from the definition. Add
this merged entry to the output message list. */
- message_ty *mp = message_merge (defmsg, refmsg, false);
+ message_ty *mp =
+ message_merge (defmsg, refmsg, false,
+ plural_distribution, plural_distribution_length);
message_list_append (resultmlp, mp);
#: comments from the reference, take the # comments from
the definition, take the msgstr from the definition. Add
this merged entry to the output message list. */
- mp = message_merge (defmsg, refmsg, true);
+ mp = message_merge (defmsg, refmsg, true,
+ plural_distribution,
+ plural_distribution_length);
message_list_append (resultmlp, mp);
+2007-10-20 Bruno Haible <bruno@clisp.org>
+
+ * msgmerge-21: New file.
+ * Makefile.am (TESTS): Add it.
+ Reported by Chusslove Illich (Часлав Илић) <caslav.ilic@gmx.net>.
+
2007-10-19 Bruno Haible <bruno@clisp.org>
Avoid test suite failures on Cygwin-hosted mingw.
msgmerge-1 msgmerge-2 msgmerge-3 msgmerge-4 msgmerge-5 msgmerge-6 \
msgmerge-7 msgmerge-8 msgmerge-9 msgmerge-10 msgmerge-11 msgmerge-12 \
msgmerge-13 msgmerge-14 msgmerge-15 msgmerge-16 msgmerge-17 \
- msgmerge-18 msgmerge-19 msgmerge-20 \
+ msgmerge-18 msgmerge-19 msgmerge-20 msgmerge-21 \
msgmerge-compendium-1 msgmerge-compendium-2 msgmerge-compendium-3 \
msgmerge-compendium-4 msgmerge-compendium-5 msgmerge-compendium-6 \
msgmerge-properties-1 msgmerge-properties-2 \
--- /dev/null
+#! /bin/sh
+
+# Test msgmerge when a message's flags have been changed from c-format to
+# kde-format. Reported by Chusslove Illich (Часлав Илић).
+
+tmpfiles=""
+trap 'rm -fr $tmpfiles' 1 2 3 15
+
+tmpfiles="$tmpfiles mm-test21.po"
+cat <<\EOF > mm-test21.po
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU gettext-tools 0.16\n"
+"Report-Msgid-Bugs-To: bug-gnu-gettext@gnu.org\n"
+"POT-Creation-Date: 2007-10-18 02:57+0200\n"
+"PO-Revision-Date: 2007-06-28 16:37+0200\n"
+"Last-Translator: Karl Eichwalder <ke@suse.de>\n"
+"Language-Team: German <translation-team-de@lists.sourceforge.net>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#, c-format
+msgid "Add resource from addressbook"
+msgid_plural "Add %n resources from addressbook"
+msgstr[0] "Engadir un recurso dende o libro de enderezos"
+msgstr[1] "Engadir %n recursos dende o libro de enderezos"
+EOF
+
+tmpfiles="$tmpfiles mm-test21.pot"
+cat <<\EOF > mm-test21.pot
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU gettext-tools 0.16\n"
+"Report-Msgid-Bugs-To: bug-gnu-gettext@gnu.org\n"
+"POT-Creation-Date: 2007-10-19 02:57+0200\n"
+"PO-Revision-Date: 2007-06-28 16:37+0200\n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#, kde-format
+msgid "Add resource from addressbook"
+msgid_plural "Add %1 resources from addressbook"
+msgstr[0] ""
+msgstr[1] ""
+EOF
+
+tmpfiles="$tmpfiles mm-test21.tmp.po mm-test21.new.po"
+: ${MSGMERGE=msgmerge}
+${MSGMERGE} -q -o mm-test21.tmp.po mm-test21.po mm-test21.pot
+test $? = 0 || { rm -fr $tmpfiles; exit 1; }
+tr -d '\r' < mm-test21.tmp.po > mm-test21.new.po
+test $? = 0 || { rm -fr $tmpfiles; exit 1; }
+
+tmpfiles="$tmpfiles mm-test21.ok"
+cat <<\EOF > mm-test21.ok
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU gettext-tools 0.16\n"
+"Report-Msgid-Bugs-To: bug-gnu-gettext@gnu.org\n"
+"POT-Creation-Date: 2007-10-19 02:57+0200\n"
+"PO-Revision-Date: 2007-06-28 16:37+0200\n"
+"Last-Translator: Karl Eichwalder <ke@suse.de>\n"
+"Language-Team: German <translation-team-de@lists.sourceforge.net>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#, fuzzy, kde-format
+msgid "Add resource from addressbook"
+msgid_plural "Add %1 resources from addressbook"
+msgstr[0] "Engadir un recurso dende o libro de enderezos"
+msgstr[1] "Engadir %n recursos dende o libro de enderezos"
+EOF
+
+: ${DIFF=diff}
+${DIFF} mm-test21.ok mm-test21.new.po
+test $? = 0 || { rm -fr $tmpfiles; exit 1; }
+
+tmpfiles="$tmpfiles mm-test21.mo"
+: ${MSGFMT=msgfmt}
+${MSGFMT} --check -o mm-test21.mo mm-test21.new.po
+test $? = 0 || { rm -fr $tmpfiles; exit 1; }
+
+rm -fr $tmpfiles
+
+exit 0