Imported Upstream version 0.18.3.2
[platform/upstream/gettext.git] / gettext-tools / src / msgl-check.c
1 /* Checking of messages in PO files.
2    Copyright (C) 1995-1998, 2000-2008, 2010-2012 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,
284               int ignore_untranslated_messages,
285               int ignore_fuzzy_messages,
286               struct plural_distribution *distributionp)
287 {
288   int seen_errors = 0;
289   const message_ty *has_plural;
290   unsigned long min_nplurals;
291   const message_ty *min_pos;
292   unsigned long max_nplurals;
293   const message_ty *max_pos;
294   struct plural_distribution distribution;
295   size_t j;
296   message_ty *header;
297
298   /* Determine whether mlp has plural entries.  */
299   has_plural = NULL;
300   min_nplurals = ULONG_MAX;
301   min_pos = NULL;
302   max_nplurals = 0;
303   max_pos = NULL;
304   distribution.expr = NULL;
305   distribution.often = NULL;
306   distribution.often_length = 0;
307   distribution.histogram = NULL;
308   for (j = 0; j < mlp->nitems; j++)
309     {
310       message_ty *mp = mlp->item[j];
311
312       if (!mp->obsolete
313           && !(ignore_untranslated_messages && mp->msgstr[0] == '\0')
314           && !(ignore_fuzzy_messages && (mp->is_fuzzy && !is_header (mp)))
315           && mp->msgid_plural != NULL)
316         {
317           const char *p;
318           const char *p_end;
319           unsigned long n;
320
321           if (has_plural == NULL)
322             has_plural = mp;
323
324           n = 0;
325           for (p = mp->msgstr, p_end = p + mp->msgstr_len;
326                p < p_end;
327                p += strlen (p) + 1)
328             n++;
329           if (min_nplurals > n)
330             {
331               min_nplurals = n;
332               min_pos = mp;
333             }
334           if (max_nplurals < n)
335             {
336               max_nplurals = n;
337               max_pos = mp;
338             }
339         }
340     }
341
342   /* Look at the plural entry for this domain.
343      Cf, function extract_plural_expression.  */
344   header = message_list_search (mlp, NULL, "");
345   if (header != NULL && !header->obsolete)
346     {
347       const char *nullentry;
348       const char *plural;
349       const char *nplurals;
350
351       nullentry = header->msgstr;
352
353       plural = c_strstr (nullentry, "plural=");
354       nplurals = c_strstr (nullentry, "nplurals=");
355       if (plural == NULL && has_plural != NULL)
356         {
357           const char *msg1 =
358             _("message catalog has plural form translations");
359           const char *msg2 =
360             _("but header entry lacks a \"plural=EXPRESSION\" attribute");
361           char *help = plural_help (nullentry);
362
363           if (help != NULL)
364             {
365               char *msg2ext = xasprintf ("%s\n%s", msg2, help);
366               po_xerror2 (PO_SEVERITY_ERROR,
367                           has_plural, NULL, 0, 0, false, msg1,
368                           header, NULL, 0, 0, true, msg2ext);
369               free (msg2ext);
370               free (help);
371             }
372           else
373             po_xerror2 (PO_SEVERITY_ERROR,
374                         has_plural, NULL, 0, 0, false, msg1,
375                         header, NULL, 0, 0, false, msg2);
376
377           seen_errors++;
378         }
379       if (nplurals == NULL && has_plural != NULL)
380         {
381           const char *msg1 =
382             _("message catalog has plural form translations");
383           const char *msg2 =
384             _("but header entry lacks a \"nplurals=INTEGER\" attribute");
385           char *help = plural_help (nullentry);
386
387           if (help != NULL)
388             {
389               char *msg2ext = xasprintf ("%s\n%s", msg2, help);
390               po_xerror2 (PO_SEVERITY_ERROR,
391                           has_plural, NULL, 0, 0, false, msg1,
392                           header, NULL, 0, 0, true, msg2ext);
393               free (msg2ext);
394               free (help);
395             }
396           else
397             po_xerror2 (PO_SEVERITY_ERROR,
398                         has_plural, NULL, 0, 0, false, msg1,
399                         header, NULL, 0, 0, false, msg2);
400
401           seen_errors++;
402         }
403       if (plural != NULL && nplurals != NULL)
404         {
405           const char *endp;
406           unsigned long int nplurals_value;
407           struct parse_args args;
408           const struct expression *plural_expr;
409
410           /* First check the number.  */
411           nplurals += 9;
412           while (*nplurals != '\0' && c_isspace ((unsigned char) *nplurals))
413             ++nplurals;
414           endp = nplurals;
415           nplurals_value = 0;
416           if (*nplurals >= '0' && *nplurals <= '9')
417             nplurals_value = strtoul (nplurals, (char **) &endp, 10);
418           if (nplurals == endp)
419             {
420               const char *msg = _("invalid nplurals value");
421               char *help = plural_help (nullentry);
422
423               if (help != NULL)
424                 {
425                   char *msgext = xasprintf ("%s\n%s", msg, help);
426                   po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, true,
427                              msgext);
428                   free (msgext);
429                   free (help);
430                 }
431               else
432                 po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
433
434               seen_errors++;
435             }
436
437           /* Then check the expression.  */
438           plural += 7;
439           args.cp = plural;
440           if (parse_plural_expression (&args) != 0)
441             {
442               const char *msg = _("invalid plural expression");
443               char *help = plural_help (nullentry);
444
445               if (help != NULL)
446                 {
447                   char *msgext = xasprintf ("%s\n%s", msg, help);
448                   po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, true,
449                              msgext);
450                   free (msgext);
451                   free (help);
452                 }
453               else
454                 po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
455
456               seen_errors++;
457             }
458           plural_expr = args.res;
459
460           /* See whether nplurals and plural fit together.  */
461           if (!seen_errors)
462             seen_errors =
463               check_plural_eval (plural_expr, nplurals_value, header,
464                                  &distribution);
465
466           /* Check the number of plurals of the translations.  */
467           if (!seen_errors)
468             {
469               if (min_nplurals < nplurals_value)
470                 {
471                   char *msg1 =
472                     xasprintf (_("nplurals = %lu"), nplurals_value);
473                   char *msg2 =
474                     xasprintf (ngettext ("but some messages have only one plural form",
475                                          "but some messages have only %lu plural forms",
476                                          min_nplurals),
477                                min_nplurals);
478                   po_xerror2 (PO_SEVERITY_ERROR,
479                               header, NULL, 0, 0, false, msg1,
480                               min_pos, NULL, 0, 0, false, msg2);
481                   free (msg2);
482                   free (msg1);
483                   seen_errors++;
484                 }
485               else if (max_nplurals > nplurals_value)
486                 {
487                   char *msg1 =
488                     xasprintf (_("nplurals = %lu"), nplurals_value);
489                   char *msg2 =
490                     xasprintf (ngettext ("but some messages have one plural form",
491                                          "but some messages have %lu plural forms",
492                                          max_nplurals),
493                                max_nplurals);
494                   po_xerror2 (PO_SEVERITY_ERROR,
495                               header, NULL, 0, 0, false, msg1,
496                               max_pos, NULL, 0, 0, false, msg2);
497                   free (msg2);
498                   free (msg1);
499                   seen_errors++;
500                 }
501               /* The only valid case is max_nplurals <= n <= min_nplurals,
502                  which means either has_plural == NULL or
503                  max_nplurals = n = min_nplurals.  */
504             }
505         }
506       else
507         goto no_plural;
508     }
509   else
510     {
511       if (has_plural != NULL)
512         {
513           po_xerror (PO_SEVERITY_ERROR, has_plural, NULL, 0, 0, false,
514                      _("message catalog has plural form translations, but lacks a header entry with \"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\""));
515           seen_errors++;
516         }
517      no_plural:
518       /* By default, the Germanic formula (n != 1) is used.  */
519       distribution.expr = &germanic_plural;
520       {
521         unsigned char *array = XCALLOC (2, unsigned char);
522         array[1] = 1;
523         distribution.often = array;
524       }
525       distribution.often_length = 2;
526       distribution.histogram = plural_expression_histogram;
527     }
528
529   /* distribution is not needed if we report errors.
530      Also, if there was an error due to  max_nplurals > nplurals_value,
531      we must not use distribution because we would be doing out-of-bounds
532      array accesses.  */
533   if (seen_errors > 0)
534     free ((unsigned char *) distribution.often);
535   else
536     *distributionp = distribution;
537
538   return seen_errors;
539 }
540
541
542 /* Signal an error when checking format strings.  */
543 static const message_ty *curr_mp;
544 static lex_pos_ty curr_msgid_pos;
545 static void
546 formatstring_error_logger (const char *format, ...)
547 #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 7) || __GNUC__ > 2)
548      __attribute__ ((__format__ (__printf__, 1, 2)))
549 #endif
550 ;
551 static void
552 formatstring_error_logger (const char *format, ...)
553 {
554   va_list args;
555   char *msg;
556
557   va_start (args, format);
558   if (vasprintf (&msg, format, args) < 0)
559     error (EXIT_FAILURE, 0, _("memory exhausted"));
560   va_end (args);
561   po_xerror (PO_SEVERITY_ERROR,
562              curr_mp, curr_msgid_pos.file_name, curr_msgid_pos.line_number,
563              (size_t)(-1), false, msg);
564   free (msg);
565 }
566
567
568 /* Perform miscellaneous checks on a message.
569    PLURAL_DISTRIBUTION is either NULL or an array of nplurals elements,
570    PLURAL_DISTRIBUTION[j] being true if the value j appears to be assumed
571    infinitely often by the plural formula.
572    PLURAL_DISTRIBUTION_LENGTH is the length of the PLURAL_DISTRIBUTION
573    array.  */
574 static int
575 check_pair (const message_ty *mp,
576             const char *msgid,
577             const lex_pos_ty *msgid_pos,
578             const char *msgid_plural,
579             const char *msgstr, size_t msgstr_len,
580             const enum is_format is_format[NFORMATS],
581             int check_newlines,
582             int check_format_strings,
583             const struct plural_distribution *distribution,
584             int check_compatibility,
585             int check_accelerators, char accelerator_char)
586 {
587   int seen_errors;
588   int has_newline;
589   unsigned int j;
590
591   /* If the msgid string is empty we have the special entry reserved for
592      information about the translation.  */
593   if (msgid[0] == '\0')
594     return 0;
595
596   seen_errors = 0;
597
598   if (check_newlines)
599     {
600       /* Test 1: check whether all or none of the strings begin with a '\n'.  */
601       has_newline = (msgid[0] == '\n');
602 #define TEST_NEWLINE(p) (p[0] == '\n')
603       if (msgid_plural != NULL)
604         {
605           const char *p;
606
607           if (TEST_NEWLINE(msgid_plural) != has_newline)
608             {
609               po_xerror (PO_SEVERITY_ERROR,
610                          mp, msgid_pos->file_name, msgid_pos->line_number,
611                          (size_t)(-1), false, _("\
612 'msgid' and 'msgid_plural' entries do not both begin with '\\n'"));
613               seen_errors++;
614             }
615           for (p = msgstr, j = 0; p < msgstr + msgstr_len; p += strlen (p) + 1, j++)
616             if (TEST_NEWLINE(p) != has_newline)
617               {
618                 char *msg =
619                   xasprintf (_("\
620 'msgid' and 'msgstr[%u]' entries do not both begin with '\\n'"), j);
621                 po_xerror (PO_SEVERITY_ERROR,
622                            mp, msgid_pos->file_name, msgid_pos->line_number,
623                            (size_t)(-1), false, msg);
624                 free (msg);
625                 seen_errors++;
626               }
627         }
628       else
629         {
630           if (TEST_NEWLINE(msgstr) != has_newline)
631             {
632               po_xerror (PO_SEVERITY_ERROR,
633                          mp, msgid_pos->file_name, msgid_pos->line_number,
634                          (size_t)(-1), false, _("\
635 'msgid' and 'msgstr' entries do not both begin with '\\n'"));
636               seen_errors++;
637             }
638         }
639 #undef TEST_NEWLINE
640
641       /* Test 2: check whether all or none of the strings end with a '\n'.  */
642       has_newline = (msgid[strlen (msgid) - 1] == '\n');
643 #define TEST_NEWLINE(p) (p[0] != '\0' && p[strlen (p) - 1] == '\n')
644       if (msgid_plural != NULL)
645         {
646           const char *p;
647
648           if (TEST_NEWLINE(msgid_plural) != has_newline)
649             {
650               po_xerror (PO_SEVERITY_ERROR,
651                          mp, msgid_pos->file_name, msgid_pos->line_number,
652                          (size_t)(-1), false, _("\
653 'msgid' and 'msgid_plural' entries do not both end with '\\n'"));
654               seen_errors++;
655             }
656           for (p = msgstr, j = 0; p < msgstr + msgstr_len; p += strlen (p) + 1, j++)
657             if (TEST_NEWLINE(p) != has_newline)
658               {
659                 char *msg =
660                   xasprintf (_("\
661 'msgid' and 'msgstr[%u]' entries do not both end with '\\n'"), j);
662                 po_xerror (PO_SEVERITY_ERROR,
663                            mp, msgid_pos->file_name, msgid_pos->line_number,
664                            (size_t)(-1), false, msg);
665                 free (msg);
666                 seen_errors++;
667               }
668         }
669       else
670         {
671           if (TEST_NEWLINE(msgstr) != has_newline)
672             {
673               po_xerror (PO_SEVERITY_ERROR,
674                          mp, msgid_pos->file_name, msgid_pos->line_number,
675                          (size_t)(-1), false, _("\
676 'msgid' and 'msgstr' entries do not both end with '\\n'"));
677               seen_errors++;
678             }
679         }
680 #undef TEST_NEWLINE
681     }
682
683   if (check_compatibility && msgid_plural != NULL)
684     {
685       po_xerror (PO_SEVERITY_ERROR,
686                  mp, msgid_pos->file_name, msgid_pos->line_number,
687                  (size_t)(-1), false, _("\
688 plural handling is a GNU gettext extension"));
689       seen_errors++;
690     }
691
692   if (check_format_strings)
693     /* Test 3: Check whether both formats strings contain the same number
694        of format specifications.  */
695     {
696       curr_mp = mp;
697       curr_msgid_pos = *msgid_pos;
698       seen_errors +=
699         check_msgid_msgstr_format (msgid, msgid_plural, msgstr, msgstr_len,
700                                    is_format, mp->range, distribution,
701                                    formatstring_error_logger);
702     }
703
704   if (check_accelerators && msgid_plural == NULL)
705     /* Test 4: Check that if msgid is a menu item with a keyboard accelerator,
706        the msgstr has an accelerator as well.  A keyboard accelerator is
707        designated by an immediately preceding '&'.  We cannot check whether
708        two accelerators collide, only whether the translator has bothered
709        thinking about them.  */
710     {
711       const char *p;
712
713       /* We are only interested in msgids that contain exactly one '&'.  */
714       p = strchr (msgid, accelerator_char);
715       if (p != NULL && strchr (p + 1, accelerator_char) == NULL)
716         {
717           /* Count the number of '&' in msgstr, but ignore '&&'.  */
718           unsigned int count = 0;
719
720           for (p = msgstr; (p = strchr (p, accelerator_char)) != NULL; p++)
721             if (p[1] == accelerator_char)
722               p++;
723             else
724               count++;
725
726           if (count == 0)
727             {
728               char *msg =
729                 xasprintf (_("msgstr lacks the keyboard accelerator mark '%c'"),
730                            accelerator_char);
731               po_xerror (PO_SEVERITY_ERROR,
732                          mp, msgid_pos->file_name, msgid_pos->line_number,
733                          (size_t)(-1), false, msg);
734               free (msg);
735             }
736           else if (count > 1)
737             {
738               char *msg =
739                 xasprintf (_("msgstr has too many keyboard accelerator marks '%c'"),
740                            accelerator_char);
741               po_xerror (PO_SEVERITY_ERROR,
742                          mp, msgid_pos->file_name, msgid_pos->line_number,
743                          (size_t)(-1), false, msg);
744               free (msg);
745             }
746         }
747     }
748
749   return seen_errors;
750 }
751
752
753 /* Perform miscellaneous checks on a header entry.  */
754 static void
755 check_header_entry (const message_ty *mp, const char *msgstr_string)
756 {
757   static const char *required_fields[] =
758   {
759     "Project-Id-Version", "PO-Revision-Date", "Last-Translator",
760     "Language-Team", "MIME-Version", "Content-Type",
761     "Content-Transfer-Encoding",
762     /* These are recommended but not yet required.  */
763     "Language"
764   };
765   static const char *default_values[] =
766   {
767     "PACKAGE VERSION", "YEAR-MO-DA", "FULL NAME", "LANGUAGE", NULL,
768     "text/plain; charset=CHARSET", "ENCODING",
769     ""
770   };
771   const size_t nfields = SIZEOF (required_fields);
772   const size_t nrequiredfields = nfields - 1;
773   int cnt;
774
775   for (cnt = 0; cnt < nfields; ++cnt)
776     {
777       int severity =
778         (cnt < nrequiredfields ? PO_SEVERITY_ERROR : PO_SEVERITY_WARNING);
779       const char *field = required_fields[cnt];
780       size_t len = strlen (field);
781       const char *line;
782
783       for (line = msgstr_string; *line != '\0'; )
784         {
785           if (strncmp (line, field, len) == 0 && line[len] == ':')
786             {
787               const char *p = line + len + 1;
788
789               /* Test whether the field's value, starting at p, is the default
790                  value.  */
791               if (*p == ' ')
792                 p++;
793               if (default_values[cnt] != NULL
794                   && strncmp (p, default_values[cnt],
795                               strlen (default_values[cnt])) == 0)
796                 {
797                   p += strlen (default_values[cnt]);
798                   if (*p == '\0' || *p == '\n')
799                     {
800                       char *msg =
801                         xasprintf (_("header field '%s' still has the initial default value\n"),
802                                    field);
803                       po_xerror (severity, mp, NULL, 0, 0, true, msg);
804                       free (msg);
805                     }
806                 }
807               break;
808             }
809           line = strchrnul (line, '\n');
810           if (*line == '\n')
811             line++;
812         }
813       if (*line == '\0')
814         {
815           char *msg =
816             xasprintf (_("header field '%s' missing in header\n"),
817                        field);
818           po_xerror (severity, mp, NULL, 0, 0, true, msg);
819           free (msg);
820         }
821     }
822 }
823
824
825 /* Perform all checks on a non-obsolete message.
826    Return the number of errors that were seen.  */
827 int
828 check_message (const message_ty *mp,
829                const lex_pos_ty *msgid_pos,
830                int check_newlines,
831                int check_format_strings,
832                const struct plural_distribution *distribution,
833                int check_header,
834                int check_compatibility,
835                int check_accelerators, char accelerator_char)
836 {
837   if (check_header && is_header (mp))
838     check_header_entry (mp, mp->msgstr);
839
840   return check_pair (mp,
841                      mp->msgid, msgid_pos, mp->msgid_plural,
842                      mp->msgstr, mp->msgstr_len,
843                      mp->is_format,
844                      check_newlines,
845                      check_format_strings,
846                      distribution,
847                      check_compatibility,
848                      check_accelerators, accelerator_char);
849 }
850
851
852 /* Perform all checks on a message list.
853    Return the number of errors that were seen.  */
854 int
855 check_message_list (message_list_ty *mlp,
856                     int ignore_untranslated_messages,
857                     int ignore_fuzzy_messages,
858                     int check_newlines,
859                     int check_format_strings,
860                     int check_header,
861                     int check_compatibility,
862                     int check_accelerators, char accelerator_char)
863 {
864   int seen_errors = 0;
865   struct plural_distribution distribution;
866   size_t j;
867
868   distribution.expr = NULL;
869   distribution.often = NULL;
870   distribution.often_length = 0;
871   distribution.histogram = NULL;
872
873   if (check_header)
874     seen_errors += check_plural (mlp, ignore_untranslated_messages,
875                                  ignore_fuzzy_messages, &distribution);
876
877   for (j = 0; j < mlp->nitems; j++)
878     {
879       message_ty *mp = mlp->item[j];
880
881       if (!mp->obsolete
882           && !(ignore_untranslated_messages && mp->msgstr[0] == '\0')
883           && !(ignore_fuzzy_messages && (mp->is_fuzzy && !is_header (mp))))
884         seen_errors += check_message (mp, &mp->pos,
885                                       check_newlines,
886                                       check_format_strings,
887                                       &distribution,
888                                       check_header, check_compatibility,
889                                       check_accelerators, accelerator_char);
890     }
891
892   return seen_errors;
893 }