• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500-V1.0.1.40_1.0.68/ap/gpl/timemachine/gettext-0.17/gettext-tools/src/
1/* Checking of messages in PO files.
2   Copyright (C) 1995-1998, 2000-2007 Free Software Foundation, Inc.
3   Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
4
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.
9
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.
14
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/>.  */
17
18#ifdef HAVE_CONFIG_H
19# include <config.h>
20#endif
21
22/* Specification.  */
23#include "msgl-check.h"
24
25#include <limits.h>
26#include <setjmp.h>
27#include <signal.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <stdarg.h>
32
33#include "c-ctype.h"
34#include "xalloc.h"
35#include "xvasprintf.h"
36#include "po-xerror.h"
37#include "format.h"
38#include "plural-exp.h"
39#include "plural-eval.h"
40#include "plural-table.h"
41#include "c-strstr.h"
42#include "message.h"
43#include "gettext.h"
44
45#define _(str) gettext (str)
46
47#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
48
49
50/* Check the values returned by plural_eval.
51   Signals the errors through po_xerror.
52   Return the number of errors that were seen.
53   If no errors, returns in *PLURAL_DISTRIBUTION either NULL or an array
54   of length NPLURALS_VALUE describing which plural formula values appear
55   infinitely often and in *PLURAL_DISTRIBUTION_LENGTH the length of this
56   array.  */
57int
58check_plural_eval (const struct expression *plural_expr,
59		   unsigned long nplurals_value,
60		   const message_ty *header,
61		   unsigned char **plural_distribution,
62		   unsigned long *plural_distribution_length)
63{
64  /* Do as if the plural formula assumes a value N infinitely often if it
65     assumes it at least 5 times.  */
66#define OFTEN 5
67  unsigned char * volatile distribution;
68
69  /* Allocate a distribution array.  */
70  if (nplurals_value <= 100)
71    distribution = XCALLOC (nplurals_value, unsigned char);
72  else
73    /* nplurals_value is nonsense.  Don't risk an out-of-memory.  */
74    distribution = NULL;
75
76  if (sigsetjmp (sigfpe_exit, 1) == 0)
77    {
78      unsigned long n;
79
80      /* Protect against arithmetic exceptions.  */
81      install_sigfpe_handler ();
82
83      for (n = 0; n <= 1000; n++)
84	{
85	  unsigned long val = plural_eval (plural_expr, n);
86
87	  if ((long) val < 0)
88	    {
89	      /* End of protection against arithmetic exceptions.  */
90	      uninstall_sigfpe_handler ();
91
92	      po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false,
93			 _("plural expression can produce negative values"));
94	      return 1;
95	    }
96	  else if (val >= nplurals_value)
97	    {
98	      char *msg;
99
100	      /* End of protection against arithmetic exceptions.  */
101	      uninstall_sigfpe_handler ();
102
103	      msg = xasprintf (_("nplurals = %lu but plural expression can produce values as large as %lu"),
104			       nplurals_value, val);
105	      po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
106	      free (msg);
107	      return 1;
108	    }
109
110	  if (distribution != NULL && distribution[val] < OFTEN)
111	    distribution[val]++;
112	}
113
114      /* End of protection against arithmetic exceptions.  */
115      uninstall_sigfpe_handler ();
116
117      /* Normalize the distribution[val] statistics.  */
118      if (distribution != NULL)
119	{
120	  unsigned long val;
121
122	  for (val = 0; val < nplurals_value; val++)
123	    distribution[val] = (distribution[val] == OFTEN ? 1 : 0);
124	  *plural_distribution_length = nplurals_value;
125	}
126      else
127	*plural_distribution_length = 0;
128      *plural_distribution = distribution;
129
130      return 0;
131    }
132  else
133    {
134      /* Caught an arithmetic exception.  */
135      const char *msg;
136
137      /* End of protection against arithmetic exceptions.  */
138      uninstall_sigfpe_handler ();
139
140#if USE_SIGINFO
141      switch (sigfpe_code)
142#endif
143	{
144#if USE_SIGINFO
145# ifdef FPE_INTDIV
146	case FPE_INTDIV:
147	  msg = _("plural expression can produce division by zero");
148	  break;
149# endif
150# ifdef FPE_INTOVF
151	case FPE_INTOVF:
152	  msg = _("plural expression can produce integer overflow");
153	  break;
154# endif
155	default:
156#endif
157	  msg = _("plural expression can produce arithmetic exceptions, possibly division by zero");
158	}
159
160      po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
161
162      if (distribution != NULL)
163	free (distribution);
164
165      return 1;
166    }
167#undef OFTEN
168}
169
170
171/* Try to help the translator by looking up the right plural formula for her.
172   Return a freshly allocated multiline help string, or NULL.  */
173static char *
174plural_help (const char *nullentry)
175{
176  const char *language;
177  size_t j;
178
179  language = c_strstr (nullentry, "Language-Team: ");
180  if (language != NULL)
181    {
182      language += 15;
183      for (j = 0; j < plural_table_size; j++)
184	if (strncmp (language,
185		     plural_table[j].language,
186		     strlen (plural_table[j].language)) == 0)
187	  {
188	    char *helpline1 =
189	      xasprintf (_("Try using the following, valid for %s:"),
190			 plural_table[j].language);
191	    char *help =
192	      xasprintf ("%s\n\"Plural-Forms: %s\\n\"\n",
193			 helpline1, plural_table[j].value);
194	    free (helpline1);
195	    return help;
196	  }
197    }
198  return NULL;
199}
200
201
202/* Perform plural expression checking.
203   Return the number of errors that were seen.
204   If no errors, returns in *PLURAL_DISTRIBUTION either NULL or an array
205   describing which plural formula values appear infinitely often and in
206   *PLURAL_DISTRIBUTION_LENGTH the length of this array.  */
207static int
208check_plural (message_list_ty *mlp,
209	      unsigned char **plural_distribution,
210	      unsigned long *plural_distribution_length)
211{
212  int seen_errors = 0;
213  const message_ty *has_plural;
214  unsigned long min_nplurals;
215  const message_ty *min_pos;
216  unsigned long max_nplurals;
217  const message_ty *max_pos;
218  size_t j;
219  message_ty *header;
220  unsigned char *distribution = NULL;
221  unsigned long distribution_length = 0;
222
223  /* Determine whether mlp has plural entries.  */
224  has_plural = NULL;
225  min_nplurals = ULONG_MAX;
226  min_pos = NULL;
227  max_nplurals = 0;
228  max_pos = NULL;
229  for (j = 0; j < mlp->nitems; j++)
230    {
231      message_ty *mp = mlp->item[j];
232
233      if (!mp->obsolete && mp->msgid_plural != NULL)
234	{
235	  const char *p;
236	  const char *p_end;
237	  unsigned long n;
238
239	  if (has_plural == NULL)
240	    has_plural = mp;
241
242	  n = 0;
243	  for (p = mp->msgstr, p_end = p + mp->msgstr_len;
244	       p < p_end;
245	       p += strlen (p) + 1)
246	    n++;
247	  if (min_nplurals > n)
248	    {
249	      min_nplurals = n;
250	      min_pos = mp;
251	    }
252	  if (max_nplurals < n)
253	    {
254	      max_nplurals = n;
255	      max_pos = mp;
256	    }
257	}
258    }
259
260  /* Look at the plural entry for this domain.
261     Cf, function extract_plural_expression.  */
262  header = message_list_search (mlp, NULL, "");
263  if (header != NULL && !header->obsolete)
264    {
265      const char *nullentry;
266      const char *plural;
267      const char *nplurals;
268
269      nullentry = header->msgstr;
270
271      plural = c_strstr (nullentry, "plural=");
272      nplurals = c_strstr (nullentry, "nplurals=");
273      if (plural == NULL && has_plural != NULL)
274	{
275	  const char *msg1 =
276	    _("message catalog has plural form translations");
277	  const char *msg2 =
278	    _("but header entry lacks a \"plural=EXPRESSION\" attribute");
279	  char *help = plural_help (nullentry);
280
281	  if (help != NULL)
282	    {
283	      char *msg2ext = xasprintf ("%s\n%s", msg2, help);
284	      po_xerror2 (PO_SEVERITY_ERROR,
285			  has_plural, NULL, 0, 0, false, msg1,
286			  header, NULL, 0, 0, true, msg2ext);
287	      free (msg2ext);
288	      free (help);
289	    }
290	  else
291	    po_xerror2 (PO_SEVERITY_ERROR,
292			has_plural, NULL, 0, 0, false, msg1,
293			header, NULL, 0, 0, false, msg2);
294
295	  seen_errors++;
296	}
297      if (nplurals == NULL && has_plural != NULL)
298	{
299	  const char *msg1 =
300	    _("message catalog has plural form translations");
301	  const char *msg2 =
302	    _("but header entry lacks a \"nplurals=INTEGER\" attribute");
303	  char *help = plural_help (nullentry);
304
305	  if (help != NULL)
306	    {
307	      char *msg2ext = xasprintf ("%s\n%s", msg2, help);
308	      po_xerror2 (PO_SEVERITY_ERROR,
309			  has_plural, NULL, 0, 0, false, msg1,
310			  header, NULL, 0, 0, true, msg2ext);
311	      free (msg2ext);
312	      free (help);
313	    }
314	  else
315	    po_xerror2 (PO_SEVERITY_ERROR,
316			has_plural, NULL, 0, 0, false, msg1,
317			header, NULL, 0, 0, false, msg2);
318
319	  seen_errors++;
320	}
321      if (plural != NULL && nplurals != NULL)
322	{
323	  const char *endp;
324	  unsigned long int nplurals_value;
325	  struct parse_args args;
326	  const struct expression *plural_expr;
327
328	  /* First check the number.  */
329	  nplurals += 9;
330	  while (*nplurals != '\0' && c_isspace ((unsigned char) *nplurals))
331	    ++nplurals;
332	  endp = nplurals;
333	  nplurals_value = 0;
334	  if (*nplurals >= '0' && *nplurals <= '9')
335	    nplurals_value = strtoul (nplurals, (char **) &endp, 10);
336	  if (nplurals == endp)
337	    {
338	      const char *msg = _("invalid nplurals value");
339	      char *help = plural_help (nullentry);
340
341	      if (help != NULL)
342		{
343		  char *msgext = xasprintf ("%s\n%s", msg, help);
344		  po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, true,
345			     msgext);
346		  free (msgext);
347		  free (help);
348		}
349	      else
350		po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
351
352	      seen_errors++;
353	    }
354
355	  /* Then check the expression.  */
356	  plural += 7;
357	  args.cp = plural;
358	  if (parse_plural_expression (&args) != 0)
359	    {
360	      const char *msg = _("invalid plural expression");
361	      char *help = plural_help (nullentry);
362
363	      if (help != NULL)
364		{
365		  char *msgext = xasprintf ("%s\n%s", msg, help);
366		  po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, true,
367			     msgext);
368		  free (msgext);
369		  free (help);
370		}
371	      else
372		po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
373
374	      seen_errors++;
375	    }
376	  plural_expr = args.res;
377
378	  /* See whether nplurals and plural fit together.  */
379	  if (!seen_errors)
380	    seen_errors =
381	      check_plural_eval (plural_expr, nplurals_value, header,
382				 &distribution, &distribution_length);
383
384	  /* Check the number of plurals of the translations.  */
385	  if (!seen_errors)
386	    {
387	      if (min_nplurals < nplurals_value)
388		{
389		  char *msg1 =
390		    xasprintf (_("nplurals = %lu"), nplurals_value);
391		  char *msg2 =
392		    xasprintf (ngettext ("but some messages have only one plural form",
393					 "but some messages have only %lu plural forms",
394					 min_nplurals),
395			       min_nplurals);
396		  po_xerror2 (PO_SEVERITY_ERROR,
397			      header, NULL, 0, 0, false, msg1,
398			      min_pos, NULL, 0, 0, false, msg2);
399		  free (msg2);
400		  free (msg1);
401		  seen_errors++;
402		}
403	      else if (max_nplurals > nplurals_value)
404		{
405		  char *msg1 =
406		    xasprintf (_("nplurals = %lu"), nplurals_value);
407		  char *msg2 =
408		    xasprintf (ngettext ("but some messages have one plural form",
409					 "but some messages have %lu plural forms",
410					 max_nplurals),
411			       max_nplurals);
412		  po_xerror2 (PO_SEVERITY_ERROR,
413			      header, NULL, 0, 0, false, msg1,
414			      max_pos, NULL, 0, 0, false, msg2);
415		  free (msg2);
416		  free (msg1);
417		  seen_errors++;
418		}
419	      /* The only valid case is max_nplurals <= n <= min_nplurals,
420		 which means either has_plural == NULL or
421		 max_nplurals = n = min_nplurals.  */
422	    }
423	}
424      else
425	goto no_plural;
426    }
427  else
428    {
429      if (has_plural != NULL)
430	{
431	  po_xerror (PO_SEVERITY_ERROR, has_plural, NULL, 0, 0, false,
432		     _("message catalog has plural form translations, but lacks a header entry with \"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\""));
433	  seen_errors++;
434	}
435     no_plural:
436      /* By default, the Germanic formula (n != 1) is used.  */
437      distribution = XCALLOC (2, unsigned char);
438      distribution[1] = 1;
439      distribution_length = 2;
440    }
441
442  /* distribution is not needed if we report errors.
443     Also, if there was an error due to  max_nplurals > nplurals_value,
444     we must not use distribution because we would be doing out-of-bounds
445     array accesses.  */
446  if (seen_errors > 0 && distribution != NULL)
447    {
448      free (distribution);
449      distribution = NULL;
450      distribution_length = 0;
451    }
452  *plural_distribution = distribution;
453  *plural_distribution_length = distribution_length;
454
455  return seen_errors;
456}
457
458
459/* Signal an error when checking format strings.  */
460static const message_ty *curr_mp;
461static lex_pos_ty curr_msgid_pos;
462static void
463formatstring_error_logger (const char *format, ...)
464     __attribute__ ((__format__ (__printf__, 1, 2)));
465static void
466formatstring_error_logger (const char *format, ...)
467{
468  va_list args;
469  char *msg;
470
471  va_start (args, format);
472  if (vasprintf (&msg, format, args) < 0)
473    error (EXIT_FAILURE, 0, _("memory exhausted"));
474  va_end (args);
475  po_xerror (PO_SEVERITY_ERROR,
476	     curr_mp, curr_msgid_pos.file_name, curr_msgid_pos.line_number,
477	     (size_t)(-1), false, msg);
478  free (msg);
479}
480
481
482/* Perform miscellaneous checks on a message.
483   PLURAL_DISTRIBUTION is either NULL or an array of nplurals elements,
484   PLURAL_DISTRIBUTION[j] being true if the value j appears to be assumed
485   infinitely often by the plural formula.
486   PLURAL_DISTRIBUTION_LENGTH is the length of the PLURAL_DISTRIBUTION
487   array.  */
488static int
489check_pair (const message_ty *mp,
490	    const char *msgid,
491	    const lex_pos_ty *msgid_pos,
492	    const char *msgid_plural,
493	    const char *msgstr, size_t msgstr_len,
494	    const enum is_format is_format[NFORMATS],
495	    int check_newlines,
496	    int check_format_strings,
497	    const unsigned char *plural_distribution,
498	    unsigned long plural_distribution_length,
499	    int check_compatibility,
500	    int check_accelerators, char accelerator_char)
501{
502  int seen_errors;
503  int has_newline;
504  unsigned int j;
505
506  /* If the msgid string is empty we have the special entry reserved for
507     information about the translation.  */
508  if (msgid[0] == '\0')
509    return 0;
510
511  seen_errors = 0;
512
513  if (check_newlines)
514    {
515      /* Test 1: check whether all or none of the strings begin with a '\n'.  */
516      has_newline = (msgid[0] == '\n');
517#define TEST_NEWLINE(p) (p[0] == '\n')
518      if (msgid_plural != NULL)
519	{
520	  const char *p;
521
522	  if (TEST_NEWLINE(msgid_plural) != has_newline)
523	    {
524	      po_xerror (PO_SEVERITY_ERROR,
525			 mp, msgid_pos->file_name, msgid_pos->line_number,
526			 (size_t)(-1), false, _("\
527`msgid' and `msgid_plural' entries do not both begin with '\\n'"));
528	      seen_errors++;
529	    }
530	  for (p = msgstr, j = 0; p < msgstr + msgstr_len; p += strlen (p) + 1, j++)
531	    if (TEST_NEWLINE(p) != has_newline)
532	      {
533		char *msg =
534		  xasprintf (_("\
535`msgid' and `msgstr[%u]' entries do not both begin with '\\n'"), j);
536		po_xerror (PO_SEVERITY_ERROR,
537			   mp, msgid_pos->file_name, msgid_pos->line_number,
538			   (size_t)(-1), false, msg);
539		free (msg);
540		seen_errors++;
541	      }
542	}
543      else
544	{
545	  if (TEST_NEWLINE(msgstr) != has_newline)
546	    {
547	      po_xerror (PO_SEVERITY_ERROR,
548			 mp, msgid_pos->file_name, msgid_pos->line_number,
549			 (size_t)(-1), false, _("\
550`msgid' and `msgstr' entries do not both begin with '\\n'"));
551	      seen_errors++;
552	    }
553	}
554#undef TEST_NEWLINE
555
556      /* Test 2: check whether all or none of the strings end with a '\n'.  */
557      has_newline = (msgid[strlen (msgid) - 1] == '\n');
558#define TEST_NEWLINE(p) (p[0] != '\0' && p[strlen (p) - 1] == '\n')
559      if (msgid_plural != NULL)
560	{
561	  const char *p;
562
563	  if (TEST_NEWLINE(msgid_plural) != has_newline)
564	    {
565	      po_xerror (PO_SEVERITY_ERROR,
566			 mp, msgid_pos->file_name, msgid_pos->line_number,
567			 (size_t)(-1), false, _("\
568`msgid' and `msgid_plural' entries do not both end with '\\n'"));
569	      seen_errors++;
570	    }
571	  for (p = msgstr, j = 0; p < msgstr + msgstr_len; p += strlen (p) + 1, j++)
572	    if (TEST_NEWLINE(p) != has_newline)
573	      {
574		char *msg =
575		  xasprintf (_("\
576`msgid' and `msgstr[%u]' entries do not both end with '\\n'"), j);
577		po_xerror (PO_SEVERITY_ERROR,
578			   mp, msgid_pos->file_name, msgid_pos->line_number,
579			   (size_t)(-1), false, msg);
580		free (msg);
581		seen_errors++;
582	      }
583	}
584      else
585	{
586	  if (TEST_NEWLINE(msgstr) != has_newline)
587	    {
588	      po_xerror (PO_SEVERITY_ERROR,
589			 mp, msgid_pos->file_name, msgid_pos->line_number,
590			 (size_t)(-1), false, _("\
591`msgid' and `msgstr' entries do not both end with '\\n'"));
592	      seen_errors++;
593	    }
594	}
595#undef TEST_NEWLINE
596    }
597
598  if (check_compatibility && msgid_plural != NULL)
599    {
600      po_xerror (PO_SEVERITY_ERROR,
601		 mp, msgid_pos->file_name, msgid_pos->line_number,
602		 (size_t)(-1), false, _("\
603plural handling is a GNU gettext extension"));
604      seen_errors++;
605    }
606
607  if (check_format_strings)
608    /* Test 3: Check whether both formats strings contain the same number
609       of format specifications.  */
610    {
611      curr_mp = mp;
612      curr_msgid_pos = *msgid_pos;
613      seen_errors +=
614	check_msgid_msgstr_format (msgid, msgid_plural, msgstr, msgstr_len,
615				   is_format, plural_distribution,
616				   plural_distribution_length,
617				   formatstring_error_logger);
618    }
619
620  if (check_accelerators && msgid_plural == NULL)
621    /* Test 4: Check that if msgid is a menu item with a keyboard accelerator,
622       the msgstr has an accelerator as well.  A keyboard accelerator is
623       designated by an immediately preceding '&'.  We cannot check whether
624       two accelerators collide, only whether the translator has bothered
625       thinking about them.  */
626    {
627      const char *p;
628
629      /* We are only interested in msgids that contain exactly one '&'.  */
630      p = strchr (msgid, accelerator_char);
631      if (p != NULL && strchr (p + 1, accelerator_char) == NULL)
632	{
633	  /* Count the number of '&' in msgstr, but ignore '&&'.  */
634	  unsigned int count = 0;
635
636	  for (p = msgstr; (p = strchr (p, accelerator_char)) != NULL; p++)
637	    if (p[1] == accelerator_char)
638	      p++;
639	    else
640	      count++;
641
642	  if (count == 0)
643	    {
644	      char *msg =
645		xasprintf (_("msgstr lacks the keyboard accelerator mark '%c'"),
646			   accelerator_char);
647	      po_xerror (PO_SEVERITY_ERROR,
648			 mp, msgid_pos->file_name, msgid_pos->line_number,
649			 (size_t)(-1), false, msg);
650	      free (msg);
651	    }
652	  else if (count > 1)
653	    {
654	      char *msg =
655		xasprintf (_("msgstr has too many keyboard accelerator marks '%c'"),
656			   accelerator_char);
657	      po_xerror (PO_SEVERITY_ERROR,
658			 mp, msgid_pos->file_name, msgid_pos->line_number,
659			 (size_t)(-1), false, msg);
660	      free (msg);
661	    }
662	}
663    }
664
665  return seen_errors;
666}
667
668
669/* Perform miscellaneous checks on a header entry.  */
670static void
671check_header_entry (const message_ty *mp, const char *msgstr_string)
672{
673  static const char *required_fields[] =
674  {
675    "Project-Id-Version", "PO-Revision-Date", "Last-Translator",
676    "Language-Team", "MIME-Version", "Content-Type",
677    "Content-Transfer-Encoding"
678  };
679  static const char *default_values[] =
680  {
681    "PACKAGE VERSION", "YEAR-MO-DA", "FULL NAME", "LANGUAGE", NULL,
682    "text/plain; charset=CHARSET", "ENCODING"
683  };
684  const size_t nfields = SIZEOF (required_fields);
685  int initial = -1;
686  int cnt;
687
688  for (cnt = 0; cnt < nfields; ++cnt)
689    {
690      char *endp = c_strstr (msgstr_string, required_fields[cnt]);
691
692      if (endp == NULL)
693	{
694	  char *msg =
695	    xasprintf (_("headerfield `%s' missing in header\n"),
696		       required_fields[cnt]);
697	  po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0, true, msg);
698	  free (msg);
699	}
700      else if (endp != msgstr_string && endp[-1] != '\n')
701	{
702	  char *msg =
703	    xasprintf (_("\
704header field `%s' should start at beginning of line\n"),
705		       required_fields[cnt]);
706	  po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0, true, msg);
707	  free (msg);
708	}
709      else if (default_values[cnt] != NULL
710	       && strncmp (default_values[cnt],
711			   endp + strlen (required_fields[cnt]) + 2,
712			   strlen (default_values[cnt])) == 0)
713	{
714	  if (initial != -1)
715	    {
716	      po_xerror (PO_SEVERITY_ERROR,
717			 mp, NULL, 0, 0, true, _("\
718some header fields still have the initial default value\n"));
719	      initial = -1;
720	      break;
721	    }
722	  else
723	    initial = cnt;
724	}
725    }
726
727  if (initial != -1)
728    {
729      char *msg =
730	xasprintf (_("field `%s' still has initial default value\n"),
731		   required_fields[initial]);
732      po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0, true, msg);
733      free (msg);
734    }
735}
736
737
738/* Perform all checks on a non-obsolete message.
739   PLURAL_DISTRIBUTION is either NULL or an array of nplurals elements,
740   PLURAL_DISTRIBUTION[j] being true if the value j appears to be assumed
741   infinitely often by the plural formula.
742   PLURAL_DISTRIBUTION_LENGTH is the length of the PLURAL_DISTRIBUTION array.
743   Return the number of errors that were seen.  */
744int
745check_message (const message_ty *mp,
746	       const lex_pos_ty *msgid_pos,
747	       int check_newlines,
748	       int check_format_strings,
749	       const unsigned char *plural_distribution,
750	       unsigned long plural_distribution_length,
751	       int check_header,
752	       int check_compatibility,
753	       int check_accelerators, char accelerator_char)
754{
755  if (check_header && is_header (mp))
756    check_header_entry (mp, mp->msgstr);
757
758  return check_pair (mp,
759		     mp->msgid, msgid_pos, mp->msgid_plural,
760		     mp->msgstr, mp->msgstr_len,
761		     mp->is_format,
762		     check_newlines,
763		     check_format_strings,
764		     plural_distribution, plural_distribution_length,
765		     check_compatibility,
766		     check_accelerators, accelerator_char);
767}
768
769
770/* Perform all checks on a message list.
771   Return the number of errors that were seen.  */
772int
773check_message_list (message_list_ty *mlp,
774		    int check_newlines,
775		    int check_format_strings,
776		    int check_header,
777		    int check_compatibility,
778		    int check_accelerators, char accelerator_char)
779{
780  int seen_errors = 0;
781  unsigned char *plural_distribution = NULL;
782  unsigned long plural_distribution_length = 0;
783  size_t j;
784
785  if (check_header)
786    seen_errors +=
787      check_plural (mlp, &plural_distribution, &plural_distribution_length);
788
789  for (j = 0; j < mlp->nitems; j++)
790    {
791      message_ty *mp = mlp->item[j];
792
793      if (!mp->obsolete)
794	seen_errors += check_message (mp, &mp->pos,
795				      check_newlines,
796				      check_format_strings,
797				      plural_distribution,
798				      plural_distribution_length,
799				      check_header, check_compatibility,
800				      check_accelerators, accelerator_char);
801    }
802
803  return seen_errors;
804}
805