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