Add a plural formula check to msgfmt.
+2001-09-23 Bruno Haible <haible@clisp.cons.org>
+
+ * configure.in: Call gt_SIGINFO.
+
2001-10-20 Bruno Haible <haible@clisp.cons.org>
* DISCLAIM: Update from
gt_TMPDIR
gt_FUNC_MKDTEMP
gt_SIGNALBLOCKING
+gt_SIGINFO
gt_FUNC_SETENV
AM_FUNC_ERROR_AT_LINE
+2001-09-22 Bruno Haible <haible@clisp.cons.org>
+
+ * plural-eval.c: New file, extracted from dcigettext.c.
+ * plural-exp.h (PLURAL_EVAL): New declaration.
+ * dcigettext.c (plural_eval): Remove function, moved to plural-eval.c.
+ (plural_lookup): Call PLURAL_EVAL instead of plural_eval.
+ * Makefile.in (COMSRCS): Add plural-eval.c.
+ (OBJECTS): Add plural-eval.$lo.
+
2001-09-22 Bruno Haible <haible@clisp.cons.org>
* plural-exp.c (EXTRACT_PLURAL_EXPRESSION): Reject numbers that don't
COMSRCS = bindtextdom.c dcgettext.c dgettext.c gettext.c \
finddomain.c loadmsgcat.c localealias.c textdomain.c l10nflist.c \
explodename.c dcigettext.c dcngettext.c dngettext.c ngettext.c plural.y \
-plural-exp.c localcharset.c
+plural-exp.c plural-eval.c localcharset.c
OBJECTS = @INTLOBJS@ bindtextdom.$lo dcgettext.$lo dgettext.$lo gettext.$lo \
finddomain.$lo loadmsgcat.$lo localealias.$lo textdomain.$lo l10nflist.$lo \
explodename.$lo dcigettext.$lo dcngettext.$lo dngettext.$lo ngettext.$lo \
-plural.$lo plural-exp.$lo localcharset.$lo
+plural.$lo plural-exp.$lo plural-eval.$lo localcharset.$lo
GETTOBJS = intl-compat.$lo
DISTFILES.common = Makefile.in \
config.charset locale.alias ref-add.sin ref-del.sin $(HEADERS) $(SOURCES)
bindtextdom.$lo dcgettext.$lo dcigettext.$lo dcngettext.$lo dgettext.$lo dngettext.$lo finddomain.$lo gettext.$lo intl-compat.$lo loadmsgcat.$lo localealias.$lo ngettext.$lo textdomain.$lo: gettextP.h gettext.h loadinfo.h
dcigettext.$lo: hash-string.h
explodename.$lo l10nflist.$lo: loadinfo.h
-dcigettext.$lo loadmsgcat.$lo plural.$lo plural-exp.$lo: plural-exp.h
+dcigettext.$lo loadmsgcat.$lo plural.$lo plural-exp.$lo plural-eval.$lo: plural-exp.h
tags: TAGS
const char *translation,
size_t translation_len))
internal_function;
-static unsigned long int plural_eval PARAMS ((struct expression *pexp,
- unsigned long int n))
- internal_function;
static const char *category_to_name PARAMS ((int category)) internal_function;
static const char *guess_category_value PARAMS ((int category,
const char *categoryname))
unsigned long int index;
const char *p;
- index = plural_eval (domaindata->plural, n);
+ index = PLURAL_EVAL (domaindata->plural, n);
if (index >= domaindata->nplurals)
/* This should never happen. It means the plural expression and the
given maximum value do not match. */
}
-/* Function to evaluate the plural expression and return an index value. */
-static unsigned long int
-internal_function
-plural_eval (pexp, n)
- struct expression *pexp;
- unsigned long int n;
-{
- switch (pexp->nargs)
- {
- case 0:
- switch (pexp->operation)
- {
- case var:
- return n;
- case num:
- return pexp->val.num;
- default:
- break;
- }
- /* NOTREACHED */
- break;
- case 1:
- {
- /* pexp->operation must be lnot. */
- unsigned long int arg = plural_eval (pexp->val.args[0], n);
- return ! arg;
- }
- case 2:
- {
- unsigned long int leftarg = plural_eval (pexp->val.args[0], n);
- if (pexp->operation == lor)
- return leftarg || plural_eval (pexp->val.args[1], n);
- else if (pexp->operation == land)
- return leftarg && plural_eval (pexp->val.args[1], n);
- else
- {
- unsigned long int rightarg = plural_eval (pexp->val.args[1], n);
-
- switch (pexp->operation)
- {
- case mult:
- return leftarg * rightarg;
- case divide:
- return leftarg / rightarg;
- case module:
- return leftarg % rightarg;
- case plus:
- return leftarg + rightarg;
- case minus:
- return leftarg - rightarg;
- case less_than:
- return leftarg < rightarg;
- case greater_than:
- return leftarg > rightarg;
- case less_or_equal:
- return leftarg <= rightarg;
- case greater_or_equal:
- return leftarg >= rightarg;
- case equal:
- return leftarg == rightarg;
- case not_equal:
- return leftarg != rightarg;
- default:
- break;
- }
- }
- /* NOTREACHED */
- break;
- }
- case 3:
- {
- /* pexp->operation must be qmop. */
- unsigned long int boolarg = plural_eval (pexp->val.args[0], n);
- return plural_eval (pexp->val.args[boolarg ? 1 : 2], n);
- }
- }
- /* NOTREACHED */
- return 0;
-}
-
-
/* Return string representation of locale CATEGORY. */
static const char *
internal_function
--- /dev/null
+/* Plural expression evaluation.
+ Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "plural-exp.h"
+
+unsigned long int
+internal_function
+PLURAL_EVAL (pexp, n)
+ struct expression *pexp;
+ unsigned long int n;
+{
+ switch (pexp->nargs)
+ {
+ case 0:
+ switch (pexp->operation)
+ {
+ case var:
+ return n;
+ case num:
+ return pexp->val.num;
+ default:
+ break;
+ }
+ /* NOTREACHED */
+ break;
+ case 1:
+ {
+ /* pexp->operation must be lnot. */
+ unsigned long int arg = PLURAL_EVAL (pexp->val.args[0], n);
+ return ! arg;
+ }
+ case 2:
+ {
+ unsigned long int leftarg = PLURAL_EVAL (pexp->val.args[0], n);
+ if (pexp->operation == lor)
+ return leftarg || PLURAL_EVAL (pexp->val.args[1], n);
+ else if (pexp->operation == land)
+ return leftarg && PLURAL_EVAL (pexp->val.args[1], n);
+ else
+ {
+ unsigned long int rightarg = PLURAL_EVAL (pexp->val.args[1], n);
+
+ switch (pexp->operation)
+ {
+ case mult:
+ return leftarg * rightarg;
+ case divide:
+ return leftarg / rightarg;
+ case module:
+ return leftarg % rightarg;
+ case plus:
+ return leftarg + rightarg;
+ case minus:
+ return leftarg - rightarg;
+ case less_than:
+ return leftarg < rightarg;
+ case greater_than:
+ return leftarg > rightarg;
+ case less_or_equal:
+ return leftarg <= rightarg;
+ case greater_or_equal:
+ return leftarg >= rightarg;
+ case equal:
+ return leftarg == rightarg;
+ case not_equal:
+ return leftarg != rightarg;
+ default:
+ break;
+ }
+ }
+ /* NOTREACHED */
+ break;
+ }
+ case 3:
+ {
+ /* pexp->operation must be qmop. */
+ unsigned long int boolarg = PLURAL_EVAL (pexp->val.args[0], n);
+ return PLURAL_EVAL (pexp->val.args[boolarg ? 1 : 2], n);
+ }
+ }
+ /* NOTREACHED */
+ return 0;
+}
-/* Expression parsing for plural form selection.
+/* Expression parsing and evaluation for plural form selection.
Copyright (C) 2000, 2001 Free Software Foundation, Inc.
Written by Ulrich Drepper <drepper@cygnus.com>, 2000.
# define FREE_EXPRESSION __gettext_free_exp
# define PLURAL_PARSE __gettextparse
# define EXTRACT_PLURAL_EXPRESSION __gettext_extract_plural
+# define PLURAL_EVAL __gettext_plural_eval
#elif defined (IN_LIBINTL)
# define FREE_EXPRESSION gettext_free_exp__
# define PLURAL_PARSE gettextparse__
# define EXTRACT_PLURAL_EXPRESSION gettext_extract_plural__
+# define PLURAL_EVAL gettext_plural_eval__
#else
# define FREE_EXPRESSION free_plural_expression
# define PLURAL_PARSE parse_plural_expression
# define EXTRACT_PLURAL_EXPRESSION extract_plural_expression
+# define PLURAL_EVAL plural_eval
#endif
+
extern void FREE_EXPRESSION PARAMS ((struct expression *exp))
internal_function;
extern int PLURAL_PARSE PARAMS ((void *arg));
unsigned long int *npluralsp))
internal_function;
+/* Evaluate the plural expression and return an index value. */
+extern unsigned long int PLURAL_EVAL PARAMS ((struct expression *pexp,
+ unsigned long int n))
+ internal_function;
+
#endif /* _PLURAL_EXP_H */
+2001-09-23 Bruno Haible <haible@clisp.cons.org>
+
+ * siginfo.m4: New file.
+ * Makefile.am (EXTRA_DIST): Add it.
+
2001-09-18 Bruno Haible <haible@clisp.cons.org>
* gettext.m4 (AM_WITH_NLS): Test for msgmerge which understands the
backupfile.m4 c-bs-a.m4 codeset.m4 flex.m4 getline.m4 gettext.m4 \
glibc21.m4 iconv.m4 inttypes_h.m4 isc-posix.m4 javacomp.m4 javaexec.m4 \
lcmessage.m4 libtool.m4 mbrtowc.m4 mbstate_t.m4 mbswidth.m4 mkdtemp.m4 \
-progtest.m4 setenv.m4 setlocale.m4 signalblocking.m4 signed.m4 ssize_t.m4 \
-stdbool.m4 tmpdir.m4 uintmax_t.m4 ulonglong.m4 unionwait.m4
+progtest.m4 setenv.m4 setlocale.m4 siginfo.m4 signalblocking.m4 signed.m4 \
+ssize_t.m4 stdbool.m4 tmpdir.m4 uintmax_t.m4 ulonglong.m4 unionwait.m4
--- /dev/null
+#serial 1
+
+# Determine how to determine the precise cause of a signal, for example
+# division by zero.
+# - SUSV2 and POSIX specify the use of sigaction with SA_SIGINFO and a member
+# void (*)(int sig, siginfo_t *info, void *context) sa_sigaction.
+# Linux (2.2.x and newer) and Solaris implement this.
+# Linux (2.4.x and newer) on i386, m68k, sparc, sparc64, ia64 actually
+# deliver FPE_INTDIV.
+# - Without SA_SIGINFO:
+# - Linux on m68k calls the handler as
+# void (*)(int sig, int code, struct sigcontext* scp).
+# For division by zero, code would be VEC_ZERODIV<<2.
+# - Linux on sparc calls the handler either as
+# void (*)(int sig, int code, struct sigcontext* scp),
+# code for division by zero would be SUBSIG_IDIVZERO, or as
+# void (*)(int sig, siginfo_t *info, void *context).
+# Which one depends on a process specific flag in the kernel.
+# - Linux on sparc64 always calls the handler as
+# void (*)(int sig, siginfo_t *info, void *context).
+# - FreeBSD on i386 calls the handler as
+# void (*)(int sig, int code, void* scp, char* addr).
+# For division by zero, code would be FPE_INTDIV.
+# - SunOS 4 calls the handler as
+# void (*)(int sig, int code, void* scp, char* addr).
+# - Solaris?
+# - Irix 5, OSF/1, AIX call the handler as
+# void (*)(int sig, int code, struct sigcontext *scp).
+# These are so many OS and CPU dependencies that we don't bother, and rely
+# only on SA_SIGINFO.
+AC_DEFUN([gt_SIGINFO],
+[
+ AC_CACHE_CHECK([for signal handlers with siginfo_t], gt_cv_siginfo_t,
+ [AC_TRY_COMPILE([
+#include <signal.h>], [
+struct sigaction action;
+siginfo_t info;
+action.sa_flags = SA_SIGINFO;
+action.sa_sigaction = (void *) 0;
+], gt_cv_siginfo_t=yes, gt_cv_siginfo_t=no)])
+ if test $gt_cv_siginfo_t = yes; then
+ AC_DEFINE(HAVE_SIGINFO, 1,
+ [Define to 1 if <signal.h> defines the siginfo_t type,
+ and struct sigaction has the sa_sigaction member and the SA_SIGINFO flag.])
+ fi
+])
+2001-09-23 Bruno Haible <haible@clisp.cons.org>
+
+ * plural-eval.c: New file.
+ * msgfmt.c: Include plural-exp.h.
+ (sigfpe_exit, sigfpe_code): New variables.
+ (sigfpe_handler, install_sigfpe_handler, uninstall_sigfpe_handler,
+ check_plural_eval, check_plural): New functions.
+ (main): Call it if --check-header is enabled.
+ * Makefile.am (msgfmt_SOURCES): Add plural-eval.c.
+
2001-10-04 Tommy Johansson <tommy.johansson@kanalen.org>
* x-java.l (object_list): New type.
gettext_SOURCES = gettext.c
ngettext_SOURCES = ngettext.c
msgcmp_SOURCES = msgcmp.c $(COMMON_SOURCES)
-msgfmt_SOURCES = msgfmt.c $(COMMON_SOURCES) msgl-ascii.c msgl-iconv.c write-mo.c write-java.c plural.c $(FORMAT_SOURCES)
+msgfmt_SOURCES = msgfmt.c $(COMMON_SOURCES) msgl-ascii.c msgl-iconv.c write-mo.c write-java.c plural.c plural-eval.c $(FORMAT_SOURCES)
msgmerge_SOURCES = msgmerge.c $(COMMON_SOURCES) msgl-ascii.c write-po.c read-po.c msgl-equal.c
msgunfmt_SOURCES = msgunfmt.c $(COMMON_SOURCES) msgl-ascii.c write-po.c read-po.c read-mo.c read-java.c
xgettext_SOURCES = xgettext.c $(COMMON_SOURCES) msgl-ascii.c write-po.c file-list.c x-c.c x-po.c x-java.l x-ycp.c x-rst.c $(FORMAT_SOURCES)
# include <config.h>
#endif
+#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
+#include <setjmp.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include "getline.h"
#include "format.h"
#include "xmalloc.h"
+#include "plural-exp.h"
#include "strstr.h"
#include "system.h"
#include "msgfmt.h"
static const char *add_mo_suffix PARAMS ((const char *));
static struct msg_domain *new_domain PARAMS ((const char *name,
const char *file_name));
+#if HAVE_SIGINFO
+static void sigfpe_handler PARAMS ((int sig, siginfo_t *sip, void *scp));
+#else
+static void sigfpe_handler PARAMS ((int sig));
+#endif
+static void install_sigfpe_handler PARAMS ((void));
+static void uninstall_sigfpe_handler PARAMS ((void));
+static void check_plural_eval PARAMS ((struct expression *plural_expr,
+ unsigned long nplurals_value,
+ const lex_pos_ty *header_pos));
+static void check_plural PARAMS ((message_list_ty *mlp));
static void check_pair PARAMS ((const char *msgid, const lex_pos_ty *msgid_pos,
const char *msgid_plural,
const char *msgstr, size_t msgstr_len,
translations must be reported. */
po_lex_pass_comments (true);
- /* Now write out all domains. */
/* Process all given .po files. */
while (argc > optind)
{
++optind;
}
+ /* Check the plural expression is present if needed and has valid syntax. */
+ if (check_header)
+ for (domain = domain_list; domain != NULL; domain = domain->next)
+ check_plural (domain->mlp);
+
+ /* Now write out all domains. */
for (domain = domain_list; domain != NULL; domain = domain->next)
{
if (java_mode)
}
+static sigjmp_buf sigfpe_exit;
+
+#if HAVE_SIGINFO
+
+static int sigfpe_code;
+
+/* Signal handler called in case of arithmetic exception (e.g. division
+ by zero) during plural_eval. */
+static void
+sigfpe_handler (sig, sip, scp)
+ int sig;
+ siginfo_t *sip;
+ void *scp;
+{
+ sigfpe_code = sip->si_code;
+ siglongjmp (sigfpe_exit, 1);
+}
+
+#else
+
+/* Signal handler called in case of arithmetic exception (e.g. division
+ by zero) during plural_eval. */
+static void
+sigfpe_handler (sig)
+ int sig;
+{
+ siglongjmp (sigfpe_exit, 1);
+}
+
+#endif
+
+static void
+install_sigfpe_handler ()
+{
+#if HAVE_SIGINFO
+ struct sigaction action;
+ action.sa_sigaction = sigfpe_handler;
+ action.sa_flags = SA_SIGINFO;
+ sigemptyset (&action.sa_mask);
+ sigaction (SIGFPE, &action, (struct sigaction *) NULL);
+#else
+ signal (SIGFPE, sigfpe_handler);
+#endif
+}
+
+static void
+uninstall_sigfpe_handler ()
+{
+#if HAVE_SIGINFO
+ struct sigaction action;
+ action.sa_handler = SIG_DFL;
+ action.sa_flags = 0;
+ sigemptyset (&action.sa_mask);
+ sigaction (SIGFPE, &action, (struct sigaction *) NULL);
+#else
+ signal (SIGFPE, SIG_DFL);
+#endif
+}
+
+/* Check the values returned by plural_eval. */
+static void
+check_plural_eval (plural_expr, nplurals_value, header_pos)
+ struct expression *plural_expr;
+ unsigned long nplurals_value;
+ const lex_pos_ty *header_pos;
+{
+ if (sigsetjmp (sigfpe_exit, 1) == 0)
+ {
+ unsigned long n;
+
+ /* Protect against arithmetic exceptions. */
+ install_sigfpe_handler ();
+
+ for (n = 0; n <= 1000; n++)
+ {
+ unsigned long val = plural_eval (plural_expr, n);
+
+ if ((long) val < 0)
+ {
+ /* End of protection against arithmetic exceptions. */
+ uninstall_sigfpe_handler ();
+
+ error_with_progname = false;
+ error_at_line (0, 0,
+ header_pos->file_name, header_pos->line_number,
+ _("plural expression can produce negative values"));
+ error_with_progname = true;
+ exit_status = EXIT_FAILURE;
+ return;
+ }
+ else if (val >= nplurals_value)
+ {
+ /* End of protection against arithmetic exceptions. */
+ uninstall_sigfpe_handler ();
+
+ error_with_progname = false;
+ error_at_line (0, 0,
+ header_pos->file_name, header_pos->line_number,
+ _("nplurals = %lu but plural expression can produce values as large as %lu"),
+ nplurals_value, val);
+ error_with_progname = true;
+ exit_status = EXIT_FAILURE;
+ return;
+ }
+ }
+
+ /* End of protection against arithmetic exceptions. */
+ uninstall_sigfpe_handler ();
+ }
+ else
+ {
+ /* Caught an arithmetic exception. */
+ const char *msg;
+
+ /* End of protection against arithmetic exceptions. */
+ uninstall_sigfpe_handler ();
+
+#if HAVE_SIGINFO
+ switch (sigfpe_code)
+#endif
+ {
+#if HAVE_SIGINFO
+# ifdef FPE_INTDIV
+ case FPE_INTDIV:
+ msg = _("plural expression can produce division by zero");
+ break;
+# endif
+# ifdef FPE_INTOVF
+ case FPE_INTOVF:
+ msg = _("plural expression can produce integer overflow");
+ break;
+# endif
+ default:
+ msg = _("plural expression can produce arithmetic exceptions, possibly division by zero");
+#endif
+ }
+
+ error_with_progname = false;
+ error_at_line (0, 0, header_pos->file_name, header_pos->line_number, msg);
+ error_with_progname = true;
+ exit_status = EXIT_FAILURE;
+ }
+}
+
+
+/* Perform plural expression checking. */
+static void
+check_plural (mlp)
+ message_list_ty *mlp;
+{
+ const lex_pos_ty *has_plural;
+ unsigned long min_nplurals;
+ const lex_pos_ty *min_pos;
+ unsigned long max_nplurals;
+ const lex_pos_ty *max_pos;
+ size_t j;
+ message_ty *header;
+
+ /* Determine whether mlp has plural entries. */
+ has_plural = NULL;
+ min_nplurals = ULONG_MAX;
+ min_pos = NULL;
+ max_nplurals = 0;
+ max_pos = NULL;
+ for (j = 0; j < mlp->nitems; j++) {
+ message_ty *mp = mlp->item[j];
+
+ if (mp->msgid_plural != NULL) {
+ const char *p;
+ const char *p_end;
+ unsigned long n;
+
+ if (has_plural == NULL)
+ has_plural = &mp->pos;
+
+ n = 0;
+ for (p = mp->msgstr, p_end = p + mp->msgstr_len;
+ p < p_end;
+ p += strlen (p) + 1)
+ n++;
+ if (min_nplurals > n) {
+ min_nplurals = n;
+ min_pos = &mp->pos;
+ }
+ if (max_nplurals > n) {
+ max_nplurals = n;
+ min_pos = &mp->pos;
+ }
+ }
+ }
+
+ /* Look at the plural entry for this domain.
+ Cf, function extract_plural_expression. */
+ header = message_list_search (mlp, "");
+ if (header != NULL)
+ {
+ const char *nullentry;
+ const char *plural;
+ const char *nplurals;
+
+ nullentry = header->msgstr;
+
+ plural = strstr (nullentry, "plural=");
+ nplurals = strstr (nullentry, "nplurals=");
+ if (plural == NULL && has_plural != NULL)
+ {
+ error_with_progname = false;
+ error_at_line (0, 0, has_plural->file_name, has_plural->line_number,
+ _("message catalog has plural form translations..."));
+ --error_message_count;
+ error_at_line (0, 0, header->pos.file_name, header->pos.line_number,
+ _("...but header entry lacks a \"plural=EXPRESSION\" attribute"));
+ error_with_progname = true;
+ exit_status = EXIT_FAILURE;
+ }
+ if (nplurals == NULL && has_plural != NULL)
+ {
+ error_with_progname = false;
+ error_at_line (0, 0, has_plural->file_name, has_plural->line_number,
+ _("message catalog has plural form translations..."));
+ --error_message_count;
+ error_at_line (0, 0, header->pos.file_name, header->pos.line_number,
+ _("...but header entry lacks a \"nplurals=INTEGER\" attribute"));
+ error_with_progname = true;
+ exit_status = EXIT_FAILURE;
+ }
+ if (plural != NULL && nplurals != NULL)
+ {
+ const char *endp;
+ unsigned long int nplurals_value;
+ struct parse_args args;
+ struct expression *plural_expr;
+
+ /* First check the number. */
+ nplurals += 9;
+ while (*nplurals != '\0' && isspace ((unsigned char) *nplurals))
+ ++nplurals;
+ endp = nplurals;
+ nplurals_value = 0;
+ if (*nplurals >= '0' && *nplurals <= '9')
+ nplurals_value = strtoul (nplurals, (char **) &endp, 10);
+ if (nplurals == endp)
+ {
+ error_with_progname = false;
+ error_at_line (0, 0,
+ header->pos.file_name, header->pos.line_number,
+ _("invalid nplurals value"));
+ error_with_progname = true;
+ exit_status = EXIT_FAILURE;
+ }
+
+ /* Then check the expression. */
+ plural += 7;
+ args.cp = plural;
+ if (parse_plural_expression (&args) != 0)
+ {
+ error_with_progname = false;
+ error_at_line (0, 0,
+ header->pos.file_name, header->pos.line_number,
+ _("invalid plural expression"));
+ error_with_progname = true;
+ exit_status = EXIT_FAILURE;
+ }
+ plural_expr = args.res;
+
+ /* See whether nplurals and plural fit together. */
+ if (exit_status != EXIT_FAILURE)
+ check_plural_eval (plural_expr, nplurals_value, &header->pos);
+
+ /* Check the number of plurals of the translations. */
+ if (exit_status != EXIT_FAILURE)
+ {
+ if (min_nplurals < nplurals_value)
+ {
+ error_with_progname = false;
+ error_at_line (0, 0,
+ header->pos.file_name, header->pos.line_number,
+ _("nplurals = %lu..."), nplurals_value);
+ --error_message_count;
+ error_at_line (0, 0, min_pos->file_name, min_pos->line_number,
+ _("...but some messages have only %lu plural forms"),
+ min_nplurals);
+ error_with_progname = true;
+ exit_status = EXIT_FAILURE;
+ }
+ else if (max_nplurals > nplurals_value)
+ {
+ error_with_progname = false;
+ error_at_line (0, 0,
+ header->pos.file_name, header->pos.line_number,
+ _("nplurals = %lu..."), nplurals_value);
+ --error_message_count;
+ error_at_line (0, 0, max_pos->file_name, max_pos->line_number,
+ _("...but some messages have %lu plural forms"),
+ max_nplurals);
+ error_with_progname = true;
+ exit_status = EXIT_FAILURE;
+ }
+ /* The only valid case is max_nplurals <= n <= min_nplurals,
+ which means either has_plural == NULL or
+ max_nplurals = n = min_nplurals. */
+ }
+ }
+ }
+ else if (has_plural != NULL)
+ {
+ error_with_progname = false;
+ error_at_line (0, 0, has_plural->file_name, has_plural->line_number,
+ _("message catalog has plural form translations, but lacks a header entry with \"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\""));
+ error_with_progname = true;
+ exit_status = EXIT_FAILURE;
+ }
+}
+
+
/* Perform miscellaneous checks on a message. */
static void
check_pair (msgid, msgid_pos, msgid_plural, msgstr, msgstr_len, msgstr_pos,
--- /dev/null
+/* Expression evaluation for plural form selection.
+ Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@cygnus.com>, 2000.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Include the expression evaluation code from libintl, with different function
+ names. */
+#include "../intl/plural-eval.c"
+2001-09-23 Bruno Haible <haible@clisp.cons.org>
+
+ * msgfmt-6: New file.
+ * Makefile.am (TESTS): Add it.
+
2001-10-22 Bruno Haible <haible@clisp.cons.org>
* Makefile.am (TESTS): Add xgettext-15, xgettext-16.
msgcomm-8 msgcomm-9 msgcomm-10 msgcomm-11 msgcomm-12 msgcomm-13 \
msgcomm-14 msgcomm-15 msgcomm-16 msgcomm-17 msgcomm-18 msgcomm-19 \
msgcomm-20 msgcomm-21 msgcomm-22 msgcomm-23 \
- msgfmt-1 msgfmt-2 msgfmt-3 msgfmt-4 msgfmt-5 \
+ msgfmt-1 msgfmt-2 msgfmt-3 msgfmt-4 msgfmt-5 msgfmt-6 \
msgmerge-1 msgmerge-2 msgmerge-3 msgmerge-4 msgmerge-5 msgmerge-6 \
msgmerge-7 msgmerge-8 msgmerge-9 msgmerge-10 msgmerge-11 msgmerge-12 \
msgunfmt-1 \
--- /dev/null
+#! /bin/sh
+
+# Test catching of division by zero in plural expression.
+
+tmpfiles=""
+trap 'rm -fr $tmpfiles' 1 2 3 15
+
+tmpfiles="$tmpfiles mf-6.po"
+cat <<EOF > mf-6.po
+msgid ""
+msgstr ""
+"Project-Id-Version: msgfmt test 4\n"
+"PO-Revision-Date: 2001-09-23 15:03+0200\n"
+"Last-Translator: Bruno Haible <haible@clisp.cons.org>\n"
+"Language-Team: test <test@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ASCII\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(1 + (1 / (n - 257)));\n"
+EOF
+
+tmpfiles="$tmpfiles mf-6.mo core *.core"
+: ${MSGFMT=msgfmt}
+${MSGFMT} --check -o mf-6.mo mf-6.po 2>/dev/null
+# Exit code must be 1.
+# If the division by zero didn't get unnoticed, it would be 0.
+# If it produced a core dump, it would be 136 (= 128 + SIGFPE).
+test $? = 1
+result=$?
+
+rm -fr $tmpfiles
+
+exit $result