Imported Upstream version 0.18.1.1
[platform/upstream/gettext.git] / gettext-tools / src / msgl-check.c
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.
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 /* Evaluates the plural formula for min <= n <= max
51    and returns the estimated number of times the value j was assumed.  */
52 static unsigned int
53 plural_expression_histogram (const struct plural_distribution *self,
54                              int min, int max, unsigned long j)
55 {
56   if (min < 0)
57     min = 0;
58   /* Limit the number of evaluations.  Nothing interesting happens beyond
59      1000.  */
60   if (max - min > 1000)
61     max = min + 1000;
62   if (min <= max)
63     {
64       const struct expression *expr = self->expr;
65       unsigned long n;
66       unsigned int count;
67
68       /* Protect against arithmetic exceptions.  */
69       install_sigfpe_handler ();
70
71       count = 0;
72       for (n = min; n <= max; n++)
73         {
74           unsigned long val = plural_eval (expr, n);
75
76           if (val == j)
77             count++;
78         }
79
80       /* End of protection against arithmetic exceptions.  */
81       uninstall_sigfpe_handler ();
82
83       return count;
84     }
85   else
86     return 0;
87 }
88
89
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.  */
95 int
96 check_plural_eval (const struct expression *plural_expr,
97                    unsigned long nplurals_value,
98                    const message_ty *header,
99                    struct plural_distribution *distribution)
100 {
101   /* Do as if the plural formula assumes a value N infinitely often if it
102      assumes it at least 5 times.  */
103 #define OFTEN 5
104   unsigned char * volatile array;
105
106   /* Allocate a distribution array.  */
107   if (nplurals_value <= 100)
108     array = XCALLOC (nplurals_value, unsigned char);
109   else
110     /* nplurals_value is nonsense.  Don't risk an out-of-memory.  */
111     array = NULL;
112
113   if (sigsetjmp (sigfpe_exit, 1) == 0)
114     {
115       unsigned long n;
116
117       /* Protect against arithmetic exceptions.  */
118       install_sigfpe_handler ();
119
120       for (n = 0; n <= 1000; n++)
121         {
122           unsigned long val = plural_eval (plural_expr, n);
123
124           if ((long) val < 0)
125             {
126               /* End of protection against arithmetic exceptions.  */
127               uninstall_sigfpe_handler ();
128
129               po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false,
130                          _("plural expression can produce negative values"));
131               free (array);
132               return 1;
133             }
134           else if (val >= nplurals_value)
135             {
136               char *msg;
137
138               /* End of protection against arithmetic exceptions.  */
139               uninstall_sigfpe_handler ();
140
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);
144               free (msg);
145               free (array);
146               return 1;
147             }
148
149           if (array != NULL && array[val] < OFTEN)
150             array[val]++;
151         }
152
153       /* End of protection against arithmetic exceptions.  */
154       uninstall_sigfpe_handler ();
155
156       /* Normalize the array[val] statistics.  */
157       if (array != NULL)
158         {
159           unsigned long val;
160
161           for (val = 0; val < nplurals_value; val++)
162             array[val] = (array[val] == OFTEN ? 1 : 0);
163         }
164
165       distribution->expr = plural_expr;
166       distribution->often = array;
167       distribution->often_length = (array != NULL ? nplurals_value : 0);
168       distribution->histogram = plural_expression_histogram;
169
170       return 0;
171     }
172   else
173     {
174       /* Caught an arithmetic exception.  */
175       const char *msg;
176
177       /* End of protection against arithmetic exceptions.  */
178       uninstall_sigfpe_handler ();
179
180 #if USE_SIGINFO
181       switch (sigfpe_code)
182 #endif
183         {
184 #if USE_SIGINFO
185 # ifdef FPE_INTDIV
186         case FPE_INTDIV:
187           msg = _("plural expression can produce division by zero");
188           break;
189 # endif
190 # ifdef FPE_INTOVF
191         case FPE_INTOVF:
192           msg = _("plural expression can produce integer overflow");
193           break;
194 # endif
195         default:
196 #endif
197           msg = _("plural expression can produce arithmetic exceptions, possibly division by zero");
198         }
199
200       po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
201
202       free (array);
203
204       return 1;
205     }
206 #undef OFTEN
207 }
208
209
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.  */
212 static char *
213 plural_help (const char *nullentry)
214 {
215   struct plural_table_entry *ptentry = NULL;
216
217   {
218     const char *language;
219
220     language = c_strstr (nullentry, "Language: ");
221     if (language != NULL)
222       {
223         size_t len;
224
225         language += 10;
226         len = strcspn (language, " \t\n");
227         if (len > 0)
228           {
229             size_t j;
230
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)
234                 {
235                   ptentry = &plural_table[j];
236                   break;
237                 }
238           }
239       }
240   }
241
242   if (ptentry == NULL)
243     {
244       const char *language;
245
246       language = c_strstr (nullentry, "Language-Team: ");
247       if (language != NULL)
248         {
249           size_t j;
250
251           language += 15;
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)
256               {
257                 ptentry = &plural_table[j];
258                 break;
259               }
260         }
261     }
262
263   if (ptentry != NULL)
264     {
265       char *helpline1 =
266         xasprintf (_("Try using the following, valid for %s:"),
267                    ptentry->language);
268       char *help =
269         xasprintf ("%s\n\"Plural-Forms: %s\\n\"\n",
270                    helpline1, ptentry->value);
271       free (helpline1);
272       return help;
273     }
274   return NULL;
275 }
276
277
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.  */
282 static int
283 check_plural (message_list_ty *mlp, struct plural_distribution *distributionp)
284 {
285   int seen_errors = 0;
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;
292   size_t j;
293   message_ty *header;
294
295   /* Determine whether mlp has plural entries.  */
296   has_plural = NULL;
297   min_nplurals = ULONG_MAX;
298   min_pos = NULL;
299   max_nplurals = 0;
300   max_pos = NULL;
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++)
306     {
307       message_ty *mp = mlp->item[j];
308
309       if (!mp->obsolete && mp->msgid_plural != NULL)
310         {
311           const char *p;
312           const char *p_end;
313           unsigned long n;
314
315           if (has_plural == NULL)
316             has_plural = mp;
317
318           n = 0;
319           for (p = mp->msgstr, p_end = p + mp->msgstr_len;
320                p < p_end;
321                p += strlen (p) + 1)
322             n++;
323           if (min_nplurals > n)
324             {
325               min_nplurals = n;
326               min_pos = mp;
327             }
328           if (max_nplurals < n)
329             {
330               max_nplurals = n;
331               max_pos = mp;
332             }
333         }
334     }
335
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)
340     {
341       const char *nullentry;
342       const char *plural;
343       const char *nplurals;
344
345       nullentry = header->msgstr;
346
347       plural = c_strstr (nullentry, "plural=");
348       nplurals = c_strstr (nullentry, "nplurals=");
349       if (plural == NULL && has_plural != NULL)
350         {
351           const char *msg1 =
352             _("message catalog has plural form translations");
353           const char *msg2 =
354             _("but header entry lacks a \"plural=EXPRESSION\" attribute");
355           char *help = plural_help (nullentry);
356
357           if (help != NULL)
358             {
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);
363               free (msg2ext);
364               free (help);
365             }
366           else
367             po_xerror2 (PO_SEVERITY_ERROR,
368                         has_plural, NULL, 0, 0, false, msg1,
369                         header, NULL, 0, 0, false, msg2);
370
371           seen_errors++;
372         }
373       if (nplurals == NULL && has_plural != NULL)
374         {
375           const char *msg1 =
376             _("message catalog has plural form translations");
377           const char *msg2 =
378             _("but header entry lacks a \"nplurals=INTEGER\" attribute");
379           char *help = plural_help (nullentry);
380
381           if (help != NULL)
382             {
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);
387               free (msg2ext);
388               free (help);
389             }
390           else
391             po_xerror2 (PO_SEVERITY_ERROR,
392                         has_plural, NULL, 0, 0, false, msg1,
393                         header, NULL, 0, 0, false, msg2);
394
395           seen_errors++;
396         }
397       if (plural != NULL && nplurals != NULL)
398         {
399           const char *endp;
400           unsigned long int nplurals_value;
401           struct parse_args args;
402           const struct expression *plural_expr;
403
404           /* First check the number.  */
405           nplurals += 9;
406           while (*nplurals != '\0' && c_isspace ((unsigned char) *nplurals))
407             ++nplurals;
408           endp = nplurals;
409           nplurals_value = 0;
410           if (*nplurals >= '0' && *nplurals <= '9')
411             nplurals_value = strtoul (nplurals, (char **) &endp, 10);
412           if (nplurals == endp)
413             {
414               const char *msg = _("invalid nplurals value");
415               char *help = plural_help (nullentry);
416
417               if (help != NULL)
418                 {
419                   char *msgext = xasprintf ("%s\n%s", msg, help);
420                   po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, true,
421                              msgext);
422                   free (msgext);
423                   free (help);
424                 }
425               else
426                 po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
427
428               seen_errors++;
429             }
430
431           /* Then check the expression.  */
432           plural += 7;
433           args.cp = plural;
434           if (parse_plural_expression (&args) != 0)
435             {
436               const char *msg = _("invalid plural expression");
437               char *help = plural_help (nullentry);
438
439               if (help != NULL)
440                 {
441                   char *msgext = xasprintf ("%s\n%s", msg, help);
442                   po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, true,
443                              msgext);
444                   free (msgext);
445                   free (help);
446                 }
447               else
448                 po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
449
450               seen_errors++;
451             }
452           plural_expr = args.res;
453
454           /* See whether nplurals and plural fit together.  */
455           if (!seen_errors)
456             seen_errors =
457               check_plural_eval (plural_expr, nplurals_value, header,
458                                  &distribution);
459
460           /* Check the number of plurals of the translations.  */
461           if (!seen_errors)
462             {
463               if (min_nplurals < nplurals_value)
464                 {
465                   char *msg1 =
466                     xasprintf (_("nplurals = %lu"), nplurals_value);
467                   char *msg2 =
468                     xasprintf (ngettext ("but some messages have only one plural form",
469                                          "but some messages have only %lu plural forms",
470                                          min_nplurals),
471                                min_nplurals);
472                   po_xerror2 (PO_SEVERITY_ERROR,
473                               header, NULL, 0, 0, false, msg1,
474                               min_pos, NULL, 0, 0, false, msg2);
475                   free (msg2);
476                   free (msg1);
477                   seen_errors++;
478                 }
479               else if (max_nplurals > nplurals_value)
480                 {
481                   char *msg1 =
482                     xasprintf (_("nplurals = %lu"), nplurals_value);
483                   char *msg2 =
484                     xasprintf (ngettext ("but some messages have one plural form",
485                                          "but some messages have %lu plural forms",
486                                          max_nplurals),
487                                max_nplurals);
488                   po_xerror2 (PO_SEVERITY_ERROR,
489                               header, NULL, 0, 0, false, msg1,
490                               max_pos, NULL, 0, 0, false, msg2);
491                   free (msg2);
492                   free (msg1);
493                   seen_errors++;
494                 }
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.  */
498             }
499         }
500       else
501         goto no_plural;
502     }
503   else
504     {
505       if (has_plural != NULL)
506         {
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;\""));
509           seen_errors++;
510         }
511      no_plural:
512       /* By default, the Germanic formula (n != 1) is used.  */
513       distribution.expr = &germanic_plural;
514       {
515         unsigned char *array = XCALLOC (2, unsigned char);
516         array[1] = 1;
517         distribution.often = array;
518       }
519       distribution.often_length = 2;
520       distribution.histogram = plural_expression_histogram;
521     }
522
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
526      array accesses.  */
527   if (seen_errors > 0)
528     free ((unsigned char *) distribution.often);
529   else
530     *distributionp = distribution;
531
532   return seen_errors;
533 }
534
535
536 /* Signal an error when checking format strings.  */
537 static const message_ty *curr_mp;
538 static lex_pos_ty curr_msgid_pos;
539 static void
540 formatstring_error_logger (const char *format, ...)
541      __attribute__ ((__format__ (__printf__, 1, 2)));
542 static void
543 formatstring_error_logger (const char *format, ...)
544 {
545   va_list args;
546   char *msg;
547
548   va_start (args, format);
549   if (vasprintf (&msg, format, args) < 0)
550     error (EXIT_FAILURE, 0, _("memory exhausted"));
551   va_end (args);
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);
555   free (msg);
556 }
557
558
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
564    array.  */
565 static int
566 check_pair (const message_ty *mp,
567             const char *msgid,
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],
572             int check_newlines,
573             int check_format_strings,
574             const struct plural_distribution *distribution,
575             int check_compatibility,
576             int check_accelerators, char accelerator_char)
577 {
578   int seen_errors;
579   int has_newline;
580   unsigned int j;
581
582   /* If the msgid string is empty we have the special entry reserved for
583      information about the translation.  */
584   if (msgid[0] == '\0')
585     return 0;
586
587   seen_errors = 0;
588
589   if (check_newlines)
590     {
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)
595         {
596           const char *p;
597
598           if (TEST_NEWLINE(msgid_plural) != has_newline)
599             {
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'"));
604               seen_errors++;
605             }
606           for (p = msgstr, j = 0; p < msgstr + msgstr_len; p += strlen (p) + 1, j++)
607             if (TEST_NEWLINE(p) != has_newline)
608               {
609                 char *msg =
610                   xasprintf (_("\
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);
615                 free (msg);
616                 seen_errors++;
617               }
618         }
619       else
620         {
621           if (TEST_NEWLINE(msgstr) != has_newline)
622             {
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'"));
627               seen_errors++;
628             }
629         }
630 #undef TEST_NEWLINE
631
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)
636         {
637           const char *p;
638
639           if (TEST_NEWLINE(msgid_plural) != has_newline)
640             {
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'"));
645               seen_errors++;
646             }
647           for (p = msgstr, j = 0; p < msgstr + msgstr_len; p += strlen (p) + 1, j++)
648             if (TEST_NEWLINE(p) != has_newline)
649               {
650                 char *msg =
651                   xasprintf (_("\
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);
656                 free (msg);
657                 seen_errors++;
658               }
659         }
660       else
661         {
662           if (TEST_NEWLINE(msgstr) != has_newline)
663             {
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'"));
668               seen_errors++;
669             }
670         }
671 #undef TEST_NEWLINE
672     }
673
674   if (check_compatibility && msgid_plural != NULL)
675     {
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"));
680       seen_errors++;
681     }
682
683   if (check_format_strings)
684     /* Test 3: Check whether both formats strings contain the same number
685        of format specifications.  */
686     {
687       curr_mp = mp;
688       curr_msgid_pos = *msgid_pos;
689       seen_errors +=
690         check_msgid_msgstr_format (msgid, msgid_plural, msgstr, msgstr_len,
691                                    is_format, mp->range, distribution,
692                                    formatstring_error_logger);
693     }
694
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.  */
701     {
702       const char *p;
703
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)
707         {
708           /* Count the number of '&' in msgstr, but ignore '&&'.  */
709           unsigned int count = 0;
710
711           for (p = msgstr; (p = strchr (p, accelerator_char)) != NULL; p++)
712             if (p[1] == accelerator_char)
713               p++;
714             else
715               count++;
716
717           if (count == 0)
718             {
719               char *msg =
720                 xasprintf (_("msgstr lacks the keyboard accelerator mark '%c'"),
721                            accelerator_char);
722               po_xerror (PO_SEVERITY_ERROR,
723                          mp, msgid_pos->file_name, msgid_pos->line_number,
724                          (size_t)(-1), false, msg);
725               free (msg);
726             }
727           else if (count > 1)
728             {
729               char *msg =
730                 xasprintf (_("msgstr has too many keyboard accelerator marks '%c'"),
731                            accelerator_char);
732               po_xerror (PO_SEVERITY_ERROR,
733                          mp, msgid_pos->file_name, msgid_pos->line_number,
734                          (size_t)(-1), false, msg);
735               free (msg);
736             }
737         }
738     }
739
740   return seen_errors;
741 }
742
743
744 /* Perform miscellaneous checks on a header entry.  */
745 static void
746 check_header_entry (const message_ty *mp, const char *msgstr_string)
747 {
748   static const char *required_fields[] =
749   {
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.  */
754     "Language"
755   };
756   static const char *default_values[] =
757   {
758     "PACKAGE VERSION", "YEAR-MO-DA", "FULL NAME", "LANGUAGE", NULL,
759     "text/plain; charset=CHARSET", "ENCODING",
760     ""
761   };
762   const size_t nfields = SIZEOF (required_fields);
763   const size_t nrequiredfields = nfields - 1;
764   int initial = -1;
765   int cnt;
766
767   for (cnt = 0; cnt < nfields; ++cnt)
768     {
769       int severity =
770         (cnt < nrequiredfields ? PO_SEVERITY_ERROR : PO_SEVERITY_WARNING);
771       const char *endp = c_strstr (msgstr_string, required_fields[cnt]);
772
773       if (endp == NULL)
774         {
775           char *msg =
776             xasprintf (_("header field `%s' missing in header\n"),
777                        required_fields[cnt]);
778           po_xerror (severity, mp, NULL, 0, 0, true, msg);
779           free (msg);
780         }
781       else if (endp != msgstr_string && endp[-1] != '\n')
782         {
783           char *msg =
784             xasprintf (_("\
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);
788           free (msg);
789         }
790       else
791         {
792           const char *p = endp + strlen (required_fields[cnt]);
793           /* Test whether the field's value, starting at p, is the default
794              value.  */
795           if (*p == ':')
796             p++;
797           if (*p == ' ')
798             p++;
799           if (default_values[cnt] != NULL
800               && strncmp (p, default_values[cnt],
801                           strlen (default_values[cnt])) == 0)
802             {
803               p += strlen (default_values[cnt]);
804               if (*p == '\0' || *p == '\n')
805                 {
806                   if (initial != -1)
807                     {
808                       po_xerror (severity,
809                                  mp, NULL, 0, 0, true, _("\
810 some header fields still have the initial default value\n"));
811                       initial = -1;
812                       break;
813                     }
814                   else
815                     initial = cnt;
816                 }
817             }
818         }
819     }
820
821   if (initial != -1)
822     {
823       int severity =
824         (initial < nrequiredfields ? PO_SEVERITY_ERROR : PO_SEVERITY_WARNING);
825       char *msg =
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);
829       free (msg);
830     }
831 }
832
833
834 /* Perform all checks on a non-obsolete message.
835    Return the number of errors that were seen.  */
836 int
837 check_message (const message_ty *mp,
838                const lex_pos_ty *msgid_pos,
839                int check_newlines,
840                int check_format_strings,
841                const struct plural_distribution *distribution,
842                int check_header,
843                int check_compatibility,
844                int check_accelerators, char accelerator_char)
845 {
846   if (check_header && is_header (mp))
847     check_header_entry (mp, mp->msgstr);
848
849   return check_pair (mp,
850                      mp->msgid, msgid_pos, mp->msgid_plural,
851                      mp->msgstr, mp->msgstr_len,
852                      mp->is_format,
853                      check_newlines,
854                      check_format_strings,
855                      distribution,
856                      check_compatibility,
857                      check_accelerators, accelerator_char);
858 }
859
860
861 /* Perform all checks on a message list.
862    Return the number of errors that were seen.  */
863 int
864 check_message_list (message_list_ty *mlp,
865                     int check_newlines,
866                     int check_format_strings,
867                     int check_header,
868                     int check_compatibility,
869                     int check_accelerators, char accelerator_char)
870 {
871   int seen_errors = 0;
872   struct plural_distribution distribution;
873   size_t j;
874
875   distribution.expr = NULL;
876   distribution.often = NULL;
877   distribution.often_length = 0;
878   distribution.histogram = NULL;
879
880   if (check_header)
881     seen_errors += check_plural (mlp, &distribution);
882
883   for (j = 0; j < mlp->nitems; j++)
884     {
885       message_ty *mp = mlp->item[j];
886
887       if (!mp->obsolete)
888         seen_errors += check_message (mp, &mp->pos,
889                                       check_newlines,
890                                       check_format_strings,
891                                       &distribution,
892                                       check_header, check_compatibility,
893                                       check_accelerators, accelerator_char);
894     }
895
896   return seen_errors;
897 }