1 /* Checking of messages in PO files.
2 Copyright (C) 1995-1998, 2000-2008 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, struct plural_distribution *distributionp)
286 const message_ty *has_plural;
287 unsigned long min_nplurals;
288 const message_ty *min_pos;
289 unsigned long max_nplurals;
290 const message_ty *max_pos;
291 struct plural_distribution distribution;
295 /* Determine whether mlp has plural entries. */
297 min_nplurals = ULONG_MAX;
301 distribution.expr = NULL;
302 distribution.often = NULL;
303 distribution.often_length = 0;
304 distribution.histogram = NULL;
305 for (j = 0; j < mlp->nitems; j++)
307 message_ty *mp = mlp->item[j];
309 if (!mp->obsolete && mp->msgid_plural != NULL)
315 if (has_plural == NULL)
319 for (p = mp->msgstr, p_end = p + mp->msgstr_len;
323 if (min_nplurals > n)
328 if (max_nplurals < n)
336 /* Look at the plural entry for this domain.
337 Cf, function extract_plural_expression. */
338 header = message_list_search (mlp, NULL, "");
339 if (header != NULL && !header->obsolete)
341 const char *nullentry;
343 const char *nplurals;
345 nullentry = header->msgstr;
347 plural = c_strstr (nullentry, "plural=");
348 nplurals = c_strstr (nullentry, "nplurals=");
349 if (plural == NULL && has_plural != NULL)
352 _("message catalog has plural form translations");
354 _("but header entry lacks a \"plural=EXPRESSION\" attribute");
355 char *help = plural_help (nullentry);
359 char *msg2ext = xasprintf ("%s\n%s", msg2, help);
360 po_xerror2 (PO_SEVERITY_ERROR,
361 has_plural, NULL, 0, 0, false, msg1,
362 header, NULL, 0, 0, true, msg2ext);
367 po_xerror2 (PO_SEVERITY_ERROR,
368 has_plural, NULL, 0, 0, false, msg1,
369 header, NULL, 0, 0, false, msg2);
373 if (nplurals == NULL && has_plural != NULL)
376 _("message catalog has plural form translations");
378 _("but header entry lacks a \"nplurals=INTEGER\" attribute");
379 char *help = plural_help (nullentry);
383 char *msg2ext = xasprintf ("%s\n%s", msg2, help);
384 po_xerror2 (PO_SEVERITY_ERROR,
385 has_plural, NULL, 0, 0, false, msg1,
386 header, NULL, 0, 0, true, msg2ext);
391 po_xerror2 (PO_SEVERITY_ERROR,
392 has_plural, NULL, 0, 0, false, msg1,
393 header, NULL, 0, 0, false, msg2);
397 if (plural != NULL && nplurals != NULL)
400 unsigned long int nplurals_value;
401 struct parse_args args;
402 const struct expression *plural_expr;
404 /* First check the number. */
406 while (*nplurals != '\0' && c_isspace ((unsigned char) *nplurals))
410 if (*nplurals >= '0' && *nplurals <= '9')
411 nplurals_value = strtoul (nplurals, (char **) &endp, 10);
412 if (nplurals == endp)
414 const char *msg = _("invalid nplurals value");
415 char *help = plural_help (nullentry);
419 char *msgext = xasprintf ("%s\n%s", msg, help);
420 po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, true,
426 po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
431 /* Then check the expression. */
434 if (parse_plural_expression (&args) != 0)
436 const char *msg = _("invalid plural expression");
437 char *help = plural_help (nullentry);
441 char *msgext = xasprintf ("%s\n%s", msg, help);
442 po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, true,
448 po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
452 plural_expr = args.res;
454 /* See whether nplurals and plural fit together. */
457 check_plural_eval (plural_expr, nplurals_value, header,
460 /* Check the number of plurals of the translations. */
463 if (min_nplurals < nplurals_value)
466 xasprintf (_("nplurals = %lu"), nplurals_value);
468 xasprintf (ngettext ("but some messages have only one plural form",
469 "but some messages have only %lu plural forms",
472 po_xerror2 (PO_SEVERITY_ERROR,
473 header, NULL, 0, 0, false, msg1,
474 min_pos, NULL, 0, 0, false, msg2);
479 else if (max_nplurals > nplurals_value)
482 xasprintf (_("nplurals = %lu"), nplurals_value);
484 xasprintf (ngettext ("but some messages have one plural form",
485 "but some messages have %lu plural forms",
488 po_xerror2 (PO_SEVERITY_ERROR,
489 header, NULL, 0, 0, false, msg1,
490 max_pos, NULL, 0, 0, false, msg2);
495 /* The only valid case is max_nplurals <= n <= min_nplurals,
496 which means either has_plural == NULL or
497 max_nplurals = n = min_nplurals. */
505 if (has_plural != NULL)
507 po_xerror (PO_SEVERITY_ERROR, has_plural, NULL, 0, 0, false,
508 _("message catalog has plural form translations, but lacks a header entry with \"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\""));
512 /* By default, the Germanic formula (n != 1) is used. */
513 distribution.expr = &germanic_plural;
515 unsigned char *array = XCALLOC (2, unsigned char);
517 distribution.often = array;
519 distribution.often_length = 2;
520 distribution.histogram = plural_expression_histogram;
523 /* distribution is not needed if we report errors.
524 Also, if there was an error due to max_nplurals > nplurals_value,
525 we must not use distribution because we would be doing out-of-bounds
528 free ((unsigned char *) distribution.often);
530 *distributionp = distribution;
536 /* Signal an error when checking format strings. */
537 static const message_ty *curr_mp;
538 static lex_pos_ty curr_msgid_pos;
540 formatstring_error_logger (const char *format, ...)
541 __attribute__ ((__format__ (__printf__, 1, 2)));
543 formatstring_error_logger (const char *format, ...)
548 va_start (args, format);
549 if (vasprintf (&msg, format, args) < 0)
550 error (EXIT_FAILURE, 0, _("memory exhausted"));
552 po_xerror (PO_SEVERITY_ERROR,
553 curr_mp, curr_msgid_pos.file_name, curr_msgid_pos.line_number,
554 (size_t)(-1), false, msg);
559 /* Perform miscellaneous checks on a message.
560 PLURAL_DISTRIBUTION is either NULL or an array of nplurals elements,
561 PLURAL_DISTRIBUTION[j] being true if the value j appears to be assumed
562 infinitely often by the plural formula.
563 PLURAL_DISTRIBUTION_LENGTH is the length of the PLURAL_DISTRIBUTION
566 check_pair (const message_ty *mp,
568 const lex_pos_ty *msgid_pos,
569 const char *msgid_plural,
570 const char *msgstr, size_t msgstr_len,
571 const enum is_format is_format[NFORMATS],
573 int check_format_strings,
574 const struct plural_distribution *distribution,
575 int check_compatibility,
576 int check_accelerators, char accelerator_char)
582 /* If the msgid string is empty we have the special entry reserved for
583 information about the translation. */
584 if (msgid[0] == '\0')
591 /* Test 1: check whether all or none of the strings begin with a '\n'. */
592 has_newline = (msgid[0] == '\n');
593 #define TEST_NEWLINE(p) (p[0] == '\n')
594 if (msgid_plural != NULL)
598 if (TEST_NEWLINE(msgid_plural) != has_newline)
600 po_xerror (PO_SEVERITY_ERROR,
601 mp, msgid_pos->file_name, msgid_pos->line_number,
602 (size_t)(-1), false, _("\
603 `msgid' and `msgid_plural' entries do not both begin with '\\n'"));
606 for (p = msgstr, j = 0; p < msgstr + msgstr_len; p += strlen (p) + 1, j++)
607 if (TEST_NEWLINE(p) != has_newline)
611 `msgid' and `msgstr[%u]' entries do not both begin with '\\n'"), j);
612 po_xerror (PO_SEVERITY_ERROR,
613 mp, msgid_pos->file_name, msgid_pos->line_number,
614 (size_t)(-1), false, msg);
621 if (TEST_NEWLINE(msgstr) != has_newline)
623 po_xerror (PO_SEVERITY_ERROR,
624 mp, msgid_pos->file_name, msgid_pos->line_number,
625 (size_t)(-1), false, _("\
626 `msgid' and `msgstr' entries do not both begin with '\\n'"));
632 /* Test 2: check whether all or none of the strings end with a '\n'. */
633 has_newline = (msgid[strlen (msgid) - 1] == '\n');
634 #define TEST_NEWLINE(p) (p[0] != '\0' && p[strlen (p) - 1] == '\n')
635 if (msgid_plural != NULL)
639 if (TEST_NEWLINE(msgid_plural) != has_newline)
641 po_xerror (PO_SEVERITY_ERROR,
642 mp, msgid_pos->file_name, msgid_pos->line_number,
643 (size_t)(-1), false, _("\
644 `msgid' and `msgid_plural' entries do not both end with '\\n'"));
647 for (p = msgstr, j = 0; p < msgstr + msgstr_len; p += strlen (p) + 1, j++)
648 if (TEST_NEWLINE(p) != has_newline)
652 `msgid' and `msgstr[%u]' entries do not both end with '\\n'"), j);
653 po_xerror (PO_SEVERITY_ERROR,
654 mp, msgid_pos->file_name, msgid_pos->line_number,
655 (size_t)(-1), false, msg);
662 if (TEST_NEWLINE(msgstr) != has_newline)
664 po_xerror (PO_SEVERITY_ERROR,
665 mp, msgid_pos->file_name, msgid_pos->line_number,
666 (size_t)(-1), false, _("\
667 `msgid' and `msgstr' entries do not both end with '\\n'"));
674 if (check_compatibility && msgid_plural != NULL)
676 po_xerror (PO_SEVERITY_ERROR,
677 mp, msgid_pos->file_name, msgid_pos->line_number,
678 (size_t)(-1), false, _("\
679 plural handling is a GNU gettext extension"));
683 if (check_format_strings)
684 /* Test 3: Check whether both formats strings contain the same number
685 of format specifications. */
688 curr_msgid_pos = *msgid_pos;
690 check_msgid_msgstr_format (msgid, msgid_plural, msgstr, msgstr_len,
691 is_format, mp->range, distribution,
692 formatstring_error_logger);
695 if (check_accelerators && msgid_plural == NULL)
696 /* Test 4: Check that if msgid is a menu item with a keyboard accelerator,
697 the msgstr has an accelerator as well. A keyboard accelerator is
698 designated by an immediately preceding '&'. We cannot check whether
699 two accelerators collide, only whether the translator has bothered
700 thinking about them. */
704 /* We are only interested in msgids that contain exactly one '&'. */
705 p = strchr (msgid, accelerator_char);
706 if (p != NULL && strchr (p + 1, accelerator_char) == NULL)
708 /* Count the number of '&' in msgstr, but ignore '&&'. */
709 unsigned int count = 0;
711 for (p = msgstr; (p = strchr (p, accelerator_char)) != NULL; p++)
712 if (p[1] == accelerator_char)
720 xasprintf (_("msgstr lacks the keyboard accelerator mark '%c'"),
722 po_xerror (PO_SEVERITY_ERROR,
723 mp, msgid_pos->file_name, msgid_pos->line_number,
724 (size_t)(-1), false, msg);
730 xasprintf (_("msgstr has too many keyboard accelerator marks '%c'"),
732 po_xerror (PO_SEVERITY_ERROR,
733 mp, msgid_pos->file_name, msgid_pos->line_number,
734 (size_t)(-1), false, msg);
744 /* Perform miscellaneous checks on a header entry. */
746 check_header_entry (const message_ty *mp, const char *msgstr_string)
748 static const char *required_fields[] =
750 "Project-Id-Version", "PO-Revision-Date", "Last-Translator",
751 "Language-Team", "MIME-Version", "Content-Type",
752 "Content-Transfer-Encoding",
753 /* These are recommended but not yet required. */
756 static const char *default_values[] =
758 "PACKAGE VERSION", "YEAR-MO-DA", "FULL NAME", "LANGUAGE", NULL,
759 "text/plain; charset=CHARSET", "ENCODING",
762 const size_t nfields = SIZEOF (required_fields);
763 const size_t nrequiredfields = nfields - 1;
767 for (cnt = 0; cnt < nfields; ++cnt)
770 (cnt < nrequiredfields ? PO_SEVERITY_ERROR : PO_SEVERITY_WARNING);
771 const char *endp = c_strstr (msgstr_string, required_fields[cnt]);
776 xasprintf (_("header field `%s' missing in header\n"),
777 required_fields[cnt]);
778 po_xerror (severity, mp, NULL, 0, 0, true, msg);
781 else if (endp != msgstr_string && endp[-1] != '\n')
785 header field `%s' should start at beginning of line\n"),
786 required_fields[cnt]);
787 po_xerror (severity, mp, NULL, 0, 0, true, msg);
792 const char *p = endp + strlen (required_fields[cnt]);
793 /* Test whether the field's value, starting at p, is the default
799 if (default_values[cnt] != NULL
800 && strncmp (p, default_values[cnt],
801 strlen (default_values[cnt])) == 0)
803 p += strlen (default_values[cnt]);
804 if (*p == '\0' || *p == '\n')
809 mp, NULL, 0, 0, true, _("\
810 some header fields still have the initial default value\n"));
824 (initial < nrequiredfields ? PO_SEVERITY_ERROR : PO_SEVERITY_WARNING);
826 xasprintf (_("header field `%s' still has the initial default value\n"),
827 required_fields[initial]);
828 po_xerror (severity, mp, NULL, 0, 0, true, msg);
834 /* Perform all checks on a non-obsolete message.
835 Return the number of errors that were seen. */
837 check_message (const message_ty *mp,
838 const lex_pos_ty *msgid_pos,
840 int check_format_strings,
841 const struct plural_distribution *distribution,
843 int check_compatibility,
844 int check_accelerators, char accelerator_char)
846 if (check_header && is_header (mp))
847 check_header_entry (mp, mp->msgstr);
849 return check_pair (mp,
850 mp->msgid, msgid_pos, mp->msgid_plural,
851 mp->msgstr, mp->msgstr_len,
854 check_format_strings,
857 check_accelerators, accelerator_char);
861 /* Perform all checks on a message list.
862 Return the number of errors that were seen. */
864 check_message_list (message_list_ty *mlp,
866 int check_format_strings,
868 int check_compatibility,
869 int check_accelerators, char accelerator_char)
872 struct plural_distribution distribution;
875 distribution.expr = NULL;
876 distribution.often = NULL;
877 distribution.often_length = 0;
878 distribution.histogram = NULL;
881 seen_errors += check_plural (mlp, &distribution);
883 for (j = 0; j < mlp->nitems; j++)
885 message_ty *mp = mlp->item[j];
888 seen_errors += check_message (mp, &mp->pos,
890 check_format_strings,
892 check_header, check_compatibility,
893 check_accelerators, accelerator_char);