Home | History | Annotate | Line # | Download | only in src
      1 /* Checking of messages in PO files.
      2    Copyright (C) 1995-1998, 2000-2006 Free Software Foundation, Inc.
      3    Written by Ulrich Drepper <drepper (at) 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
     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 *
    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
    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
    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
    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
    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
    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
    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