+2008-10-04 Bruno Haible <bruno@clisp.org>
+
+ * plural-distrib.h (struct plural_distribution): New field 'histogram'.
+ * format.h (check_msgid_msgstr_format_i, check_msgid_msgstr_format):
+ Add range argument.
+ * format.c (check_msgid_msgstr_format_i): Add range argument. Set
+ strict_checking to false if, due to the range, the plural forms applies
+ to only one value of n.
+ (check_msgid_msgstr_format): Add range argument.
+ * msgl-check.c (plural_expression_histogram): New function.
+ (check_plural_eval, check_plural): Update.
+ (check_pair): Pass the range to check_msgid_msgstr_format.
+ (check_message_list): Update.
+ * msgmerge.c (message_merge): Pass the range to
+ check_msgid_msgstr_format_i.
+ Reported by Anatoly Techtonik <techtonik@gmail.com>
+ via <https://savannah.gnu.org/bugs/?24433>.
+
2008-10-04 Bruno Haible <bruno@clisp.org>
* message.h (struct argument_range): New type.
check_msgid_msgstr_format_i (const char *msgid, const char *msgid_plural,
const char *msgstr, size_t msgstr_len,
size_t i,
+ struct argument_range range,
const struct plural_distribution *distribution,
formatstring_error_logger_t error_logger)
{
/* 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]
+ there is only msgstr[0], or if distribution->often[j]
indicates that the variant applies to infinitely many
- values of N.
+ values of N and the N range is not restricted in a way
+ that the variant applies to only one N.
Use relaxed checking when there are at least two
- msgstr[] forms and the plural_distribution array does
- not give more precise information. */
+ msgstr[] forms and the distribution does not give more
+ precise information. */
bool strict_checking =
(msgid_plural == NULL
|| !has_plural_translations
|| (distribution != NULL
&& distribution->often != NULL
&& j < distribution->often_length
- && distribution->often[j]));
+ && distribution->often[j]
+ && !(has_range_p (range)
+ && distribution->histogram (distribution,
+ range.min, range.max, j)
+ <= 1)));
if (parser->check (msgid_descr, msgstr_descr,
strict_checking,
check_msgid_msgstr_format (const char *msgid, const char *msgid_plural,
const char *msgstr, size_t msgstr_len,
const enum is_format is_format[NFORMATS],
+ struct argument_range range,
const struct plural_distribution *distribution,
formatstring_error_logger_t error_logger)
{
if (possible_format_p (is_format[i]))
seen_errors += check_msgid_msgstr_format_i (msgid, msgid_plural,
msgstr, msgstr_len, i,
+ range,
distribution,
error_logger);
check_msgid_msgstr_format_i (const char *msgid, const char *msgid_plural,
const char *msgstr, size_t msgstr_len,
size_t i,
+ struct argument_range range,
const struct plural_distribution *distribution,
formatstring_error_logger_t error_logger);
check_msgid_msgstr_format (const char *msgid, const char *msgid_plural,
const char *msgstr, size_t msgstr_len,
const enum is_format is_format[NFORMATS],
+ struct argument_range range,
const struct plural_distribution *distribution,
formatstring_error_logger_t error_logger);
#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+/* Evaluates the plural formula for min <= n <= max
+ and returns the estimated number of times the value j was assumed. */
+static unsigned int
+plural_expression_histogram (const struct plural_distribution *self,
+ int min, int max, unsigned long j)
+{
+ if (min < 0)
+ min = 0;
+ /* Limit the number of evaluations. Nothing interesting happens beyond
+ 1000. */
+ if (max - min > 1000)
+ max = min + 1000;
+ if (min <= max)
+ {
+ const struct expression *expr = self->expr;
+ unsigned long n;
+ unsigned int count;
+
+ /* Protect against arithmetic exceptions. */
+ install_sigfpe_handler ();
+
+ count = 0;
+ for (n = min; n <= max; n++)
+ {
+ unsigned long val = plural_eval (expr, n);
+
+ if (val == j)
+ count++;
+ }
+
+ /* End of protection against arithmetic exceptions. */
+ uninstall_sigfpe_handler ();
+
+ return count;
+ }
+ else
+ return 0;
+}
+
+
/* Check the values returned by plural_eval.
Signals the errors through po_xerror.
Return the number of errors that were seen.
distribution->expr = plural_expr;
distribution->often = array;
distribution->often_length = (array != NULL ? nplurals_value : 0);
+ distribution->histogram = plural_expression_histogram;
return 0;
}
distribution.expr = NULL;
distribution.often = NULL;
distribution.often_length = 0;
+ distribution.histogram = NULL;
for (j = 0; j < mlp->nitems; j++)
{
message_ty *mp = mlp->item[j];
distribution.often = array;
}
distribution.often_length = 2;
+ distribution.histogram = plural_expression_histogram;
}
/* distribution is not needed if we report errors.
curr_msgid_pos = *msgid_pos;
seen_errors +=
check_msgid_msgstr_format (msgid, msgid_plural, msgstr, msgstr_len,
- is_format, distribution,
+ is_format, mp->range, distribution,
formatstring_error_logger);
}
distribution.expr = NULL;
distribution.often = NULL;
distribution.often_length = 0;
+ distribution.histogram = NULL;
if (check_header)
seen_errors += check_plural (mlp, &distribution);
&& possible_format_p (ref->is_format[i])
&& !possible_format_p (def->is_format[i])
&& check_msgid_msgstr_format_i (ref->msgid, ref->msgid_plural,
- msgstr, msgstr_len, i,
+ msgstr, msgstr_len, i, ref->range,
distribution, silent_error_logger)
> 0)
result->is_fuzzy = true;
/* The length of the OFTEN array. */
unsigned long often_length;
+
+ /* A function which evaluates the plural formula for min <= n <= max
+ and returns the estimated number of times the value j was assumed. */
+ unsigned int (*histogram) (const struct plural_distribution *self,
+ int min, int max, unsigned long j);
};