1 /* Checking of messages in PO files.
2 Copyright (C) 1995-1998, 2000-2008, 2010-2012 Free Software Foundation, Inc.
3 Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
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.
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, see <http://www.gnu.org/licenses/>. */
23 #include "msgl-check.h"
35 #include "xvasprintf.h"
36 #include "po-xerror.h"
38 #include "plural-exp.h"
39 #include "plural-eval.h"
40 #include "plural-table.h"
45 #define _(str) gettext (str)
47 #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
50 /* Evaluates the plural formula for min <= n <= max
51 and returns the estimated number of times the value j was assumed. */
53 plural_expression_histogram (const struct plural_distribution *self,
54 int min, int max, unsigned long j)
58 /* Limit the number of evaluations. Nothing interesting happens beyond
64 const struct expression *expr = self->expr;
68 /* Protect against arithmetic exceptions. */
69 install_sigfpe_handler ();
72 for (n = min; n <= max; n++)
74 unsigned long val = plural_eval (expr, n);
80 /* End of protection against arithmetic exceptions. */
81 uninstall_sigfpe_handler ();
90 /* Check the values returned by plural_eval.
91 Signals the errors through po_xerror.
92 Return the number of errors that were seen.
93 If no errors, returns in *DISTRIBUTION information about the plural_eval
94 values distribution. */
96 check_plural_eval (const struct expression *plural_expr,
97 unsigned long nplurals_value,
98 const message_ty *header,
99 struct plural_distribution *distribution)
101 /* Do as if the plural formula assumes a value N infinitely often if it
102 assumes it at least 5 times. */
104 unsigned char * volatile array;
106 /* Allocate a distribution array. */
107 if (nplurals_value <= 100)
108 array = XCALLOC (nplurals_value, unsigned char);
110 /* nplurals_value is nonsense. Don't risk an out-of-memory. */
113 if (sigsetjmp (sigfpe_exit, 1) == 0)
117 /* Protect against arithmetic exceptions. */
118 install_sigfpe_handler ();
120 for (n = 0; n <= 1000; n++)
122 unsigned long val = plural_eval (plural_expr, n);
126 /* End of protection against arithmetic exceptions. */
127 uninstall_sigfpe_handler ();
129 po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false,
130 _("plural expression can produce negative values"));
134 else if (val >= nplurals_value)
138 /* End of protection against arithmetic exceptions. */
139 uninstall_sigfpe_handler ();
141 msg = xasprintf (_("nplurals = %lu but plural expression can produce values as large as %lu"),
142 nplurals_value, val);
143 po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
149 if (array != NULL && array[val] < OFTEN)
153 /* End of protection against arithmetic exceptions. */
154 uninstall_sigfpe_handler ();
156 /* Normalize the array[val] statistics. */
161 for (val = 0; val < nplurals_value; val++)
162 array[val] = (array[val] == OFTEN ? 1 : 0);
165 distribution->expr = plural_expr;
166 distribution->often = array;
167 distribution->often_length = (array != NULL ? nplurals_value : 0);
168 distribution->histogram = plural_expression_histogram;
174 /* Caught an arithmetic exception. */
177 /* End of protection against arithmetic exceptions. */
178 uninstall_sigfpe_handler ();
187 msg = _("plural expression can produce division by zero");
192 msg = _("plural expression can produce integer overflow");
197 msg = _("plural expression can produce arithmetic exceptions, possibly division by zero");
200 po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
210 /* Try to help the translator by looking up the right plural formula for her.
211 Return a freshly allocated multiline help string, or NULL. */
213 plural_help (const char *nullentry)
215 struct plural_table_entry *ptentry = NULL;
218 const char *language;
220 language = c_strstr (nullentry, "Language: ");
221 if (language != NULL)
226 len = strcspn (language, " \t\n");
231 for (j = 0; j < plural_table_size; j++)
232 if (len == strlen (plural_table[j].lang)
233 && strncmp (language, plural_table[j].lang, len) == 0)
235 ptentry = &plural_table[j];
244 const char *language;
246 language = c_strstr (nullentry, "Language-Team: ");
247 if (language != NULL)
252 for (j = 0; j < plural_table_size; j++)
253 if (strncmp (language,
254 plural_table[j].language,
255 strlen (plural_table[j].language)) == 0)
257 ptentry = &plural_table[j];
266 xasprintf (_("Try using the following, valid for %s:"),
269 xasprintf ("%s\n\"Plural-Forms: %s\\n\"\n",
270 helpline1, ptentry->value);
278 /* Perform plural expression checking.
279 Return the number of errors that were seen.
280 If no errors, returns in *DISTRIBUTION information about the plural_eval
281 values distribution. */
283 check_plural (message_list_ty *mlp,
284 int ignore_untranslated_messages,
285 int ignore_fuzzy_messages,
286 struct plural_distribution *distributionp)
289 const message_ty *has_plural;
290 unsigned long min_nplurals;
291 const message_ty *min_pos;
292 unsigned long max_nplurals;
293 const message_ty *max_pos;
294 struct plural_distribution distribution;
298 /* Determine whether mlp has plural entries. */
300 min_nplurals = ULONG_MAX;
304 distribution.expr = NULL;
305 distribution.often = NULL;
306 distribution.often_length = 0;
307 distribution.histogram = NULL;
308 for (j = 0; j < mlp->nitems; j++)
310 message_ty *mp = mlp->item[j];
313 && !(ignore_untranslated_messages && mp->msgstr[0] == '\0')
314 && !(ignore_fuzzy_messages && (mp->is_fuzzy && !is_header (mp)))
315 && mp->msgid_plural != NULL)
321 if (has_plural == NULL)
325 for (p = mp->msgstr, p_end = p + mp->msgstr_len;
329 if (min_nplurals > n)
334 if (max_nplurals < n)
342 /* Look at the plural entry for this domain.
343 Cf, function extract_plural_expression. */
344 header = message_list_search (mlp, NULL, "");
345 if (header != NULL && !header->obsolete)
347 const char *nullentry;
349 const char *nplurals;
351 nullentry = header->msgstr;
353 plural = c_strstr (nullentry, "plural=");
354 nplurals = c_strstr (nullentry, "nplurals=");
355 if (plural == NULL && has_plural != NULL)
358 _("message catalog has plural form translations");
360 _("but header entry lacks a \"plural=EXPRESSION\" attribute");
361 char *help = plural_help (nullentry);
365 char *msg2ext = xasprintf ("%s\n%s", msg2, help);
366 po_xerror2 (PO_SEVERITY_ERROR,
367 has_plural, NULL, 0, 0, false, msg1,
368 header, NULL, 0, 0, true, msg2ext);
373 po_xerror2 (PO_SEVERITY_ERROR,
374 has_plural, NULL, 0, 0, false, msg1,
375 header, NULL, 0, 0, false, msg2);
379 if (nplurals == NULL && has_plural != NULL)
382 _("message catalog has plural form translations");
384 _("but header entry lacks a \"nplurals=INTEGER\" attribute");
385 char *help = plural_help (nullentry);
389 char *msg2ext = xasprintf ("%s\n%s", msg2, help);
390 po_xerror2 (PO_SEVERITY_ERROR,
391 has_plural, NULL, 0, 0, false, msg1,
392 header, NULL, 0, 0, true, msg2ext);
397 po_xerror2 (PO_SEVERITY_ERROR,
398 has_plural, NULL, 0, 0, false, msg1,
399 header, NULL, 0, 0, false, msg2);
403 if (plural != NULL && nplurals != NULL)
406 unsigned long int nplurals_value;
407 struct parse_args args;
408 const struct expression *plural_expr;
410 /* First check the number. */
412 while (*nplurals != '\0' && c_isspace ((unsigned char) *nplurals))
416 if (*nplurals >= '0' && *nplurals <= '9')
417 nplurals_value = strtoul (nplurals, (char **) &endp, 10);
418 if (nplurals == endp)
420 const char *msg = _("invalid nplurals value");
421 char *help = plural_help (nullentry);
425 char *msgext = xasprintf ("%s\n%s", msg, help);
426 po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, true,
432 po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
437 /* Then check the expression. */
440 if (parse_plural_expression (&args) != 0)
442 const char *msg = _("invalid plural expression");
443 char *help = plural_help (nullentry);
447 char *msgext = xasprintf ("%s\n%s", msg, help);
448 po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, true,
454 po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
458 plural_expr = args.res;
460 /* See whether nplurals and plural fit together. */
463 check_plural_eval (plural_expr, nplurals_value, header,
466 /* Check the number of plurals of the translations. */
469 if (min_nplurals < nplurals_value)
472 xasprintf (_("nplurals = %lu"), nplurals_value);
474 xasprintf (ngettext ("but some messages have only one plural form",
475 "but some messages have only %lu plural forms",
478 po_xerror2 (PO_SEVERITY_ERROR,
479 header, NULL, 0, 0, false, msg1,
480 min_pos, NULL, 0, 0, false, msg2);
485 else if (max_nplurals > nplurals_value)
488 xasprintf (_("nplurals = %lu"), nplurals_value);
490 xasprintf (ngettext ("but some messages have one plural form",
491 "but some messages have %lu plural forms",
494 po_xerror2 (PO_SEVERITY_ERROR,
495 header, NULL, 0, 0, false, msg1,
496 max_pos, NULL, 0, 0, false, msg2);
501 /* The only valid case is max_nplurals <= n <= min_nplurals,
502 which means either has_plural == NULL or
503 max_nplurals = n = min_nplurals. */
511 if (has_plural != NULL)
513 po_xerror (PO_SEVERITY_ERROR, has_plural, NULL, 0, 0, false,
514 _("message catalog has plural form translations, but lacks a header entry with \"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\""));
518 /* By default, the Germanic formula (n != 1) is used. */
519 distribution.expr = &germanic_plural;
521 unsigned char *array = XCALLOC (2, unsigned char);
523 distribution.often = array;
525 distribution.often_length = 2;
526 distribution.histogram = plural_expression_histogram;
529 /* distribution is not needed if we report errors.
530 Also, if there was an error due to max_nplurals > nplurals_value,
531 we must not use distribution because we would be doing out-of-bounds
534 free ((unsigned char *) distribution.often);
536 *distributionp = distribution;
542 /* Signal an error when checking format strings. */
543 static const message_ty *curr_mp;
544 static lex_pos_ty curr_msgid_pos;
546 formatstring_error_logger (const char *format, ...)
547 #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 7) || __GNUC__ > 2)
548 __attribute__ ((__format__ (__printf__, 1, 2)))
552 formatstring_error_logger (const char *format, ...)
557 va_start (args, format);
558 if (vasprintf (&msg, format, args) < 0)
559 error (EXIT_FAILURE, 0, _("memory exhausted"));
561 po_xerror (PO_SEVERITY_ERROR,
562 curr_mp, curr_msgid_pos.file_name, curr_msgid_pos.line_number,
563 (size_t)(-1), false, msg);
568 /* Perform miscellaneous checks on a message.
569 PLURAL_DISTRIBUTION is either NULL or an array of nplurals elements,
570 PLURAL_DISTRIBUTION[j] being true if the value j appears to be assumed
571 infinitely often by the plural formula.
572 PLURAL_DISTRIBUTION_LENGTH is the length of the PLURAL_DISTRIBUTION
575 check_pair (const message_ty *mp,
577 const lex_pos_ty *msgid_pos,
578 const char *msgid_plural,
579 const char *msgstr, size_t msgstr_len,
580 const enum is_format is_format[NFORMATS],
582 int check_format_strings,
583 const struct plural_distribution *distribution,
584 int check_compatibility,
585 int check_accelerators, char accelerator_char)
591 /* If the msgid string is empty we have the special entry reserved for
592 information about the translation. */
593 if (msgid[0] == '\0')
600 /* Test 1: check whether all or none of the strings begin with a '\n'. */
601 has_newline = (msgid[0] == '\n');
602 #define TEST_NEWLINE(p) (p[0] == '\n')
603 if (msgid_plural != NULL)
607 if (TEST_NEWLINE(msgid_plural) != has_newline)
609 po_xerror (PO_SEVERITY_ERROR,
610 mp, msgid_pos->file_name, msgid_pos->line_number,
611 (size_t)(-1), false, _("\
612 'msgid' and 'msgid_plural' entries do not both begin with '\\n'"));
615 for (p = msgstr, j = 0; p < msgstr + msgstr_len; p += strlen (p) + 1, j++)
616 if (TEST_NEWLINE(p) != has_newline)
620 'msgid' and 'msgstr[%u]' entries do not both begin with '\\n'"), j);
621 po_xerror (PO_SEVERITY_ERROR,
622 mp, msgid_pos->file_name, msgid_pos->line_number,
623 (size_t)(-1), false, msg);
630 if (TEST_NEWLINE(msgstr) != has_newline)
632 po_xerror (PO_SEVERITY_ERROR,
633 mp, msgid_pos->file_name, msgid_pos->line_number,
634 (size_t)(-1), false, _("\
635 'msgid' and 'msgstr' entries do not both begin with '\\n'"));
641 /* Test 2: check whether all or none of the strings end with a '\n'. */
642 has_newline = (msgid[strlen (msgid) - 1] == '\n');
643 #define TEST_NEWLINE(p) (p[0] != '\0' && p[strlen (p) - 1] == '\n')
644 if (msgid_plural != NULL)
648 if (TEST_NEWLINE(msgid_plural) != has_newline)
650 po_xerror (PO_SEVERITY_ERROR,
651 mp, msgid_pos->file_name, msgid_pos->line_number,
652 (size_t)(-1), false, _("\
653 'msgid' and 'msgid_plural' entries do not both end with '\\n'"));
656 for (p = msgstr, j = 0; p < msgstr + msgstr_len; p += strlen (p) + 1, j++)
657 if (TEST_NEWLINE(p) != has_newline)
661 'msgid' and 'msgstr[%u]' entries do not both end with '\\n'"), j);
662 po_xerror (PO_SEVERITY_ERROR,
663 mp, msgid_pos->file_name, msgid_pos->line_number,
664 (size_t)(-1), false, msg);
671 if (TEST_NEWLINE(msgstr) != has_newline)
673 po_xerror (PO_SEVERITY_ERROR,
674 mp, msgid_pos->file_name, msgid_pos->line_number,
675 (size_t)(-1), false, _("\
676 'msgid' and 'msgstr' entries do not both end with '\\n'"));
683 if (check_compatibility && msgid_plural != NULL)
685 po_xerror (PO_SEVERITY_ERROR,
686 mp, msgid_pos->file_name, msgid_pos->line_number,
687 (size_t)(-1), false, _("\
688 plural handling is a GNU gettext extension"));
692 if (check_format_strings)
693 /* Test 3: Check whether both formats strings contain the same number
694 of format specifications. */
697 curr_msgid_pos = *msgid_pos;
699 check_msgid_msgstr_format (msgid, msgid_plural, msgstr, msgstr_len,
700 is_format, mp->range, distribution,
701 formatstring_error_logger);
704 if (check_accelerators && msgid_plural == NULL)
705 /* Test 4: Check that if msgid is a menu item with a keyboard accelerator,
706 the msgstr has an accelerator as well. A keyboard accelerator is
707 designated by an immediately preceding '&'. We cannot check whether
708 two accelerators collide, only whether the translator has bothered
709 thinking about them. */
713 /* We are only interested in msgids that contain exactly one '&'. */
714 p = strchr (msgid, accelerator_char);
715 if (p != NULL && strchr (p + 1, accelerator_char) == NULL)
717 /* Count the number of '&' in msgstr, but ignore '&&'. */
718 unsigned int count = 0;
720 for (p = msgstr; (p = strchr (p, accelerator_char)) != NULL; p++)
721 if (p[1] == accelerator_char)
729 xasprintf (_("msgstr lacks the keyboard accelerator mark '%c'"),
731 po_xerror (PO_SEVERITY_ERROR,
732 mp, msgid_pos->file_name, msgid_pos->line_number,
733 (size_t)(-1), false, msg);
739 xasprintf (_("msgstr has too many keyboard accelerator marks '%c'"),
741 po_xerror (PO_SEVERITY_ERROR,
742 mp, msgid_pos->file_name, msgid_pos->line_number,
743 (size_t)(-1), false, msg);
753 /* Perform miscellaneous checks on a header entry. */
755 check_header_entry (const message_ty *mp, const char *msgstr_string)
757 static const char *required_fields[] =
759 "Project-Id-Version", "PO-Revision-Date", "Last-Translator",
760 "Language-Team", "MIME-Version", "Content-Type",
761 "Content-Transfer-Encoding",
762 /* These are recommended but not yet required. */
765 static const char *default_values[] =
767 "PACKAGE VERSION", "YEAR-MO-DA", "FULL NAME", "LANGUAGE", NULL,
768 "text/plain; charset=CHARSET", "ENCODING",
771 const size_t nfields = SIZEOF (required_fields);
772 const size_t nrequiredfields = nfields - 1;
775 for (cnt = 0; cnt < nfields; ++cnt)
778 (cnt < nrequiredfields ? PO_SEVERITY_ERROR : PO_SEVERITY_WARNING);
779 const char *field = required_fields[cnt];
780 size_t len = strlen (field);
783 for (line = msgstr_string; *line != '\0'; )
785 if (strncmp (line, field, len) == 0 && line[len] == ':')
787 const char *p = line + len + 1;
789 /* Test whether the field's value, starting at p, is the default
793 if (default_values[cnt] != NULL
794 && strncmp (p, default_values[cnt],
795 strlen (default_values[cnt])) == 0)
797 p += strlen (default_values[cnt]);
798 if (*p == '\0' || *p == '\n')
801 xasprintf (_("header field '%s' still has the initial default value\n"),
803 po_xerror (severity, mp, NULL, 0, 0, true, msg);
809 line = strchrnul (line, '\n');
816 xasprintf (_("header field '%s' missing in header\n"),
818 po_xerror (severity, mp, NULL, 0, 0, true, msg);
825 /* Perform all checks on a non-obsolete message.
826 Return the number of errors that were seen. */
828 check_message (const message_ty *mp,
829 const lex_pos_ty *msgid_pos,
831 int check_format_strings,
832 const struct plural_distribution *distribution,
834 int check_compatibility,
835 int check_accelerators, char accelerator_char)
837 if (check_header && is_header (mp))
838 check_header_entry (mp, mp->msgstr);
840 return check_pair (mp,
841 mp->msgid, msgid_pos, mp->msgid_plural,
842 mp->msgstr, mp->msgstr_len,
845 check_format_strings,
848 check_accelerators, accelerator_char);
852 /* Perform all checks on a message list.
853 Return the number of errors that were seen. */
855 check_message_list (message_list_ty *mlp,
856 int ignore_untranslated_messages,
857 int ignore_fuzzy_messages,
859 int check_format_strings,
861 int check_compatibility,
862 int check_accelerators, char accelerator_char)
865 struct plural_distribution distribution;
868 distribution.expr = NULL;
869 distribution.often = NULL;
870 distribution.often_length = 0;
871 distribution.histogram = NULL;
874 seen_errors += check_plural (mlp, ignore_untranslated_messages,
875 ignore_fuzzy_messages, &distribution);
877 for (j = 0; j < mlp->nitems; j++)
879 message_ty *mp = mlp->item[j];
882 && !(ignore_untranslated_messages && mp->msgstr[0] == '\0')
883 && !(ignore_fuzzy_messages && (mp->is_fuzzy && !is_header (mp))))
884 seen_errors += check_message (mp, &mp->pos,
886 check_format_strings,
888 check_header, check_compatibility,
889 check_accelerators, accelerator_char);