xref: /netbsd-src/external/gpl2/gettext/dist/gettext-tools/src/msgcmp.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1*946379e7Schristos /* GNU gettext - internationalization aids
2*946379e7Schristos    Copyright (C) 1995-1998, 2000-2006 Free Software Foundation, Inc.
3*946379e7Schristos    This file was written by Peter Miller <millerp@canb.auug.org.au>
4*946379e7Schristos 
5*946379e7Schristos    This program is free software; you can redistribute it and/or modify
6*946379e7Schristos    it under the terms of the GNU General Public License as published by
7*946379e7Schristos    the Free Software Foundation; either version 2, or (at your option)
8*946379e7Schristos    any later version.
9*946379e7Schristos 
10*946379e7Schristos    This program is distributed in the hope that it will be useful,
11*946379e7Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
12*946379e7Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13*946379e7Schristos    GNU General Public License for more details.
14*946379e7Schristos 
15*946379e7Schristos    You should have received a copy of the GNU General Public License
16*946379e7Schristos    along with this program; if not, write to the Free Software Foundation,
17*946379e7Schristos    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18*946379e7Schristos 
19*946379e7Schristos #ifdef HAVE_CONFIG_H
20*946379e7Schristos # include <config.h>
21*946379e7Schristos #endif
22*946379e7Schristos 
23*946379e7Schristos #include <getopt.h>
24*946379e7Schristos #include <limits.h>
25*946379e7Schristos #include <stdbool.h>
26*946379e7Schristos #include <stdio.h>
27*946379e7Schristos #include <stdlib.h>
28*946379e7Schristos #include <locale.h>
29*946379e7Schristos 
30*946379e7Schristos #include "closeout.h"
31*946379e7Schristos #include "dir-list.h"
32*946379e7Schristos #include "error.h"
33*946379e7Schristos #include "error-progname.h"
34*946379e7Schristos #include "progname.h"
35*946379e7Schristos #include "relocatable.h"
36*946379e7Schristos #include "basename.h"
37*946379e7Schristos #include "message.h"
38*946379e7Schristos #include "exit.h"
39*946379e7Schristos #include "read-catalog.h"
40*946379e7Schristos #include "read-po.h"
41*946379e7Schristos #include "read-properties.h"
42*946379e7Schristos #include "read-stringtable.h"
43*946379e7Schristos #include "msgl-iconv.h"
44*946379e7Schristos #include "c-strstr.h"
45*946379e7Schristos #include "c-strcase.h"
46*946379e7Schristos #include "propername.h"
47*946379e7Schristos #include "gettext.h"
48*946379e7Schristos 
49*946379e7Schristos #define _(str) gettext (str)
50*946379e7Schristos 
51*946379e7Schristos 
52*946379e7Schristos /* Apply the .pot file to each of the domains in the PO file.  */
53*946379e7Schristos static bool multi_domain_mode = false;
54*946379e7Schristos 
55*946379e7Schristos /* Whether to consider fuzzy messages as translations.  */
56*946379e7Schristos static bool include_fuzzies = false;
57*946379e7Schristos 
58*946379e7Schristos /* Whether to consider untranslated messages as translations.  */
59*946379e7Schristos static bool include_untranslated = false;
60*946379e7Schristos 
61*946379e7Schristos /* Long options.  */
62*946379e7Schristos static const struct option long_options[] =
63*946379e7Schristos {
64*946379e7Schristos   { "directory", required_argument, NULL, 'D' },
65*946379e7Schristos   { "help", no_argument, NULL, 'h' },
66*946379e7Schristos   { "multi-domain", no_argument, NULL, 'm' },
67*946379e7Schristos   { "properties-input", no_argument, NULL, 'P' },
68*946379e7Schristos   { "stringtable-input", no_argument, NULL, CHAR_MAX + 1 },
69*946379e7Schristos   { "use-fuzzy", no_argument, NULL, CHAR_MAX + 2 },
70*946379e7Schristos   { "use-untranslated", no_argument, NULL, CHAR_MAX + 3 },
71*946379e7Schristos   { "version", no_argument, NULL, 'V' },
72*946379e7Schristos   { NULL, 0, NULL, 0 }
73*946379e7Schristos };
74*946379e7Schristos 
75*946379e7Schristos 
76*946379e7Schristos /* Forward declaration of local functions.  */
77*946379e7Schristos static void usage (int status)
78*946379e7Schristos #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
79*946379e7Schristos 	__attribute__ ((noreturn))
80*946379e7Schristos #endif
81*946379e7Schristos ;
82*946379e7Schristos static void compare (const char *fn1, const char *fn2,
83*946379e7Schristos 		     catalog_input_format_ty input_syntax);
84*946379e7Schristos 
85*946379e7Schristos 
86*946379e7Schristos int
main(int argc,char * argv[])87*946379e7Schristos main (int argc, char *argv[])
88*946379e7Schristos {
89*946379e7Schristos   int optchar;
90*946379e7Schristos   bool do_help;
91*946379e7Schristos   bool do_version;
92*946379e7Schristos   catalog_input_format_ty input_syntax = &input_format_po;
93*946379e7Schristos 
94*946379e7Schristos   /* Set program name for messages.  */
95*946379e7Schristos   set_program_name (argv[0]);
96*946379e7Schristos   error_print_progname = maybe_print_progname;
97*946379e7Schristos   gram_max_allowed_errors = UINT_MAX;
98*946379e7Schristos 
99*946379e7Schristos #ifdef HAVE_SETLOCALE
100*946379e7Schristos   /* Set locale via LC_ALL.  */
101*946379e7Schristos   setlocale (LC_ALL, "");
102*946379e7Schristos #endif
103*946379e7Schristos 
104*946379e7Schristos   /* Set the text message domain.  */
105*946379e7Schristos   bindtextdomain (PACKAGE, relocate (LOCALEDIR));
106*946379e7Schristos   bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
107*946379e7Schristos   textdomain (PACKAGE);
108*946379e7Schristos 
109*946379e7Schristos   /* Ensure that write errors on stdout are detected.  */
110*946379e7Schristos   atexit (close_stdout);
111*946379e7Schristos 
112*946379e7Schristos   do_help = false;
113*946379e7Schristos   do_version = false;
114*946379e7Schristos   while ((optchar = getopt_long (argc, argv, "D:hmPV", long_options, NULL))
115*946379e7Schristos 	 != EOF)
116*946379e7Schristos     switch (optchar)
117*946379e7Schristos       {
118*946379e7Schristos       case '\0':		/* long option */
119*946379e7Schristos 	break;
120*946379e7Schristos 
121*946379e7Schristos       case 'D':
122*946379e7Schristos 	dir_list_append (optarg);
123*946379e7Schristos 	break;
124*946379e7Schristos 
125*946379e7Schristos       case 'h':
126*946379e7Schristos 	do_help = true;
127*946379e7Schristos 	break;
128*946379e7Schristos 
129*946379e7Schristos       case 'm':
130*946379e7Schristos 	multi_domain_mode = true;
131*946379e7Schristos 	break;
132*946379e7Schristos 
133*946379e7Schristos       case 'P':
134*946379e7Schristos 	input_syntax = &input_format_properties;
135*946379e7Schristos 	break;
136*946379e7Schristos 
137*946379e7Schristos       case 'V':
138*946379e7Schristos 	do_version = true;
139*946379e7Schristos 	break;
140*946379e7Schristos 
141*946379e7Schristos       case CHAR_MAX + 1:	/* --stringtable-input */
142*946379e7Schristos 	input_syntax = &input_format_stringtable;
143*946379e7Schristos 	break;
144*946379e7Schristos 
145*946379e7Schristos       case CHAR_MAX + 2:	/* --use-fuzzy */
146*946379e7Schristos 	include_fuzzies = true;
147*946379e7Schristos 	break;
148*946379e7Schristos 
149*946379e7Schristos       case CHAR_MAX + 3:	/* --use-untranslated */
150*946379e7Schristos 	include_untranslated = true;
151*946379e7Schristos 	break;
152*946379e7Schristos 
153*946379e7Schristos       default:
154*946379e7Schristos 	usage (EXIT_FAILURE);
155*946379e7Schristos 	break;
156*946379e7Schristos       }
157*946379e7Schristos 
158*946379e7Schristos   /* Version information is requested.  */
159*946379e7Schristos   if (do_version)
160*946379e7Schristos     {
161*946379e7Schristos       printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
162*946379e7Schristos       /* xgettext: no-wrap */
163*946379e7Schristos       printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
164*946379e7Schristos This is free software; see the source for copying conditions.  There is NO\n\
165*946379e7Schristos warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
166*946379e7Schristos "),
167*946379e7Schristos 	      "1995-1998, 2000-2006");
168*946379e7Schristos       printf (_("Written by %s.\n"), proper_name ("Peter Miller"));
169*946379e7Schristos       exit (EXIT_SUCCESS);
170*946379e7Schristos     }
171*946379e7Schristos 
172*946379e7Schristos   /* Help is requested.  */
173*946379e7Schristos   if (do_help)
174*946379e7Schristos     usage (EXIT_SUCCESS);
175*946379e7Schristos 
176*946379e7Schristos   /* Test whether we have an .po file name as argument.  */
177*946379e7Schristos   if (optind >= argc)
178*946379e7Schristos     {
179*946379e7Schristos       error (EXIT_SUCCESS, 0, _("no input files given"));
180*946379e7Schristos       usage (EXIT_FAILURE);
181*946379e7Schristos     }
182*946379e7Schristos   if (optind + 2 != argc)
183*946379e7Schristos     {
184*946379e7Schristos       error (EXIT_SUCCESS, 0, _("exactly 2 input files required"));
185*946379e7Schristos       usage (EXIT_FAILURE);
186*946379e7Schristos     }
187*946379e7Schristos 
188*946379e7Schristos   /* compare the two files */
189*946379e7Schristos   compare (argv[optind], argv[optind + 1], input_syntax);
190*946379e7Schristos   exit (EXIT_SUCCESS);
191*946379e7Schristos }
192*946379e7Schristos 
193*946379e7Schristos 
194*946379e7Schristos /* Display usage information and exit.  */
195*946379e7Schristos static void
usage(int status)196*946379e7Schristos usage (int status)
197*946379e7Schristos {
198*946379e7Schristos   if (status != EXIT_SUCCESS)
199*946379e7Schristos     fprintf (stderr, _("Try `%s --help' for more information.\n"),
200*946379e7Schristos 	     program_name);
201*946379e7Schristos   else
202*946379e7Schristos     {
203*946379e7Schristos       printf (_("\
204*946379e7Schristos Usage: %s [OPTION] def.po ref.pot\n\
205*946379e7Schristos "), program_name);
206*946379e7Schristos       printf ("\n");
207*946379e7Schristos       /* xgettext: no-wrap */
208*946379e7Schristos       printf (_("\
209*946379e7Schristos Compare two Uniforum style .po files to check that both contain the same\n\
210*946379e7Schristos set of msgid strings.  The def.po file is an existing PO file with the\n\
211*946379e7Schristos translations.  The ref.pot file is the last created PO file, or a PO Template\n\
212*946379e7Schristos file (generally created by xgettext).  This is useful for checking that\n\
213*946379e7Schristos you have translated each and every message in your program.  Where an exact\n\
214*946379e7Schristos match cannot be found, fuzzy matching is used to produce better diagnostics.\n\
215*946379e7Schristos "));
216*946379e7Schristos       printf ("\n");
217*946379e7Schristos       printf (_("\
218*946379e7Schristos Mandatory arguments to long options are mandatory for short options too.\n"));
219*946379e7Schristos       printf ("\n");
220*946379e7Schristos       printf (_("\
221*946379e7Schristos Input file location:\n"));
222*946379e7Schristos       printf (_("\
223*946379e7Schristos   def.po                      translations\n"));
224*946379e7Schristos       printf (_("\
225*946379e7Schristos   ref.pot                     references to the sources\n"));
226*946379e7Schristos       printf (_("\
227*946379e7Schristos   -D, --directory=DIRECTORY   add DIRECTORY to list for input files search\n"));
228*946379e7Schristos       printf ("\n");
229*946379e7Schristos       printf (_("\
230*946379e7Schristos Operation modifiers:\n"));
231*946379e7Schristos       printf (_("\
232*946379e7Schristos   -m, --multi-domain          apply ref.pot to each of the domains in def.po\n"));
233*946379e7Schristos       printf (_("\
234*946379e7Schristos       --use-fuzzy             consider fuzzy entries\n"));
235*946379e7Schristos       printf (_("\
236*946379e7Schristos       --use-untranslated      consider untranslated entries\n"));
237*946379e7Schristos       printf ("\n");
238*946379e7Schristos       printf (_("\
239*946379e7Schristos Input file syntax:\n"));
240*946379e7Schristos       printf (_("\
241*946379e7Schristos   -P, --properties-input      input files are in Java .properties syntax\n"));
242*946379e7Schristos       printf (_("\
243*946379e7Schristos       --stringtable-input     input files are in NeXTstep/GNUstep .strings\n\
244*946379e7Schristos                               syntax\n"));
245*946379e7Schristos       printf ("\n");
246*946379e7Schristos       printf (_("\
247*946379e7Schristos Informative output:\n"));
248*946379e7Schristos       printf (_("\
249*946379e7Schristos   -h, --help                  display this help and exit\n"));
250*946379e7Schristos       printf (_("\
251*946379e7Schristos   -V, --version               output version information and exit\n"));
252*946379e7Schristos       printf ("\n");
253*946379e7Schristos       fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"), stdout);
254*946379e7Schristos     }
255*946379e7Schristos 
256*946379e7Schristos   exit (status);
257*946379e7Schristos }
258*946379e7Schristos 
259*946379e7Schristos 
260*946379e7Schristos /* Return true if a message should be kept.  */
261*946379e7Schristos static bool
is_message_selected(const message_ty * mp)262*946379e7Schristos is_message_selected (const message_ty *mp)
263*946379e7Schristos {
264*946379e7Schristos   /* Always keep the header entry.  */
265*946379e7Schristos   if (is_header (mp))
266*946379e7Schristos     return true;
267*946379e7Schristos 
268*946379e7Schristos   return !mp->obsolete;
269*946379e7Schristos }
270*946379e7Schristos 
271*946379e7Schristos 
272*946379e7Schristos /* Remove obsolete messages from a message list.  Return the modified list.  */
273*946379e7Schristos static msgdomain_list_ty *
remove_obsoletes(msgdomain_list_ty * mdlp)274*946379e7Schristos remove_obsoletes (msgdomain_list_ty *mdlp)
275*946379e7Schristos {
276*946379e7Schristos   size_t k;
277*946379e7Schristos 
278*946379e7Schristos   for (k = 0; k < mdlp->nitems; k++)
279*946379e7Schristos     message_list_remove_if_not (mdlp->item[k]->messages, is_message_selected);
280*946379e7Schristos 
281*946379e7Schristos   return mdlp;
282*946379e7Schristos }
283*946379e7Schristos 
284*946379e7Schristos 
285*946379e7Schristos static void
match_domain(const char * fn1,const char * fn2,message_list_ty * defmlp,message_list_ty * refmlp,int * nerrors)286*946379e7Schristos match_domain (const char *fn1, const char *fn2,
287*946379e7Schristos 	      message_list_ty *defmlp, message_list_ty *refmlp,
288*946379e7Schristos 	      int *nerrors)
289*946379e7Schristos {
290*946379e7Schristos   size_t j;
291*946379e7Schristos 
292*946379e7Schristos   for (j = 0; j < refmlp->nitems; j++)
293*946379e7Schristos     {
294*946379e7Schristos       message_ty *refmsg;
295*946379e7Schristos       message_ty *defmsg;
296*946379e7Schristos 
297*946379e7Schristos       refmsg = refmlp->item[j];
298*946379e7Schristos 
299*946379e7Schristos       /* See if it is in the other file.  */
300*946379e7Schristos       defmsg = message_list_search (defmlp, refmsg->msgctxt, refmsg->msgid);
301*946379e7Schristos       if (defmsg)
302*946379e7Schristos 	{
303*946379e7Schristos 	  if (!include_untranslated && defmsg->msgstr[0] == '\0')
304*946379e7Schristos 	    {
305*946379e7Schristos 	      (*nerrors)++;
306*946379e7Schristos 	      po_gram_error_at_line (&defmsg->pos, _("\
307*946379e7Schristos this message is untranslated"));
308*946379e7Schristos 	    }
309*946379e7Schristos 	  else if (!include_fuzzies && defmsg->is_fuzzy && !is_header (defmsg))
310*946379e7Schristos 	    {
311*946379e7Schristos 	      (*nerrors)++;
312*946379e7Schristos 	      po_gram_error_at_line (&defmsg->pos, _("\
313*946379e7Schristos this message needs to be reviewed by the translator"));
314*946379e7Schristos 	    }
315*946379e7Schristos 	  else
316*946379e7Schristos 	    defmsg->used = 1;
317*946379e7Schristos 	}
318*946379e7Schristos       else
319*946379e7Schristos 	{
320*946379e7Schristos 	  /* If the message was not defined at all, try to find a very
321*946379e7Schristos 	     similar message, it could be a typo, or the suggestion may
322*946379e7Schristos 	     help.  */
323*946379e7Schristos 	  (*nerrors)++;
324*946379e7Schristos 	  defmsg =
325*946379e7Schristos 	    message_list_search_fuzzy (defmlp, refmsg->msgctxt, refmsg->msgid);
326*946379e7Schristos 	  if (defmsg)
327*946379e7Schristos 	    {
328*946379e7Schristos 	      po_gram_error_at_line (&refmsg->pos, _("\
329*946379e7Schristos this message is used but not defined..."));
330*946379e7Schristos 	      error_message_count--;
331*946379e7Schristos 	      po_gram_error_at_line (&defmsg->pos, _("\
332*946379e7Schristos ...but this definition is similar"));
333*946379e7Schristos 	      defmsg->used = 1;
334*946379e7Schristos 	    }
335*946379e7Schristos 	  else
336*946379e7Schristos 	    po_gram_error_at_line (&refmsg->pos, _("\
337*946379e7Schristos this message is used but not defined in %s"), fn1);
338*946379e7Schristos 	}
339*946379e7Schristos     }
340*946379e7Schristos }
341*946379e7Schristos 
342*946379e7Schristos 
343*946379e7Schristos static void
compare(const char * fn1,const char * fn2,catalog_input_format_ty input_syntax)344*946379e7Schristos compare (const char *fn1, const char *fn2, catalog_input_format_ty input_syntax)
345*946379e7Schristos {
346*946379e7Schristos   msgdomain_list_ty *def;
347*946379e7Schristos   msgdomain_list_ty *ref;
348*946379e7Schristos   int nerrors;
349*946379e7Schristos   size_t j, k;
350*946379e7Schristos   message_list_ty *empty_list;
351*946379e7Schristos 
352*946379e7Schristos   /* This is the master file, created by a human.  */
353*946379e7Schristos   def = remove_obsoletes (read_catalog_file (fn1, input_syntax));
354*946379e7Schristos 
355*946379e7Schristos   /* This is the generated file, created by groping the sources with
356*946379e7Schristos      the xgettext program.  */
357*946379e7Schristos   ref = remove_obsoletes (read_catalog_file (fn2, input_syntax));
358*946379e7Schristos 
359*946379e7Schristos   /* The references file can be either in ASCII or in UTF-8.  If it is
360*946379e7Schristos      in UTF-8, we have to convert the definitions to UTF-8 as well.  */
361*946379e7Schristos   {
362*946379e7Schristos     bool was_utf8 = false;
363*946379e7Schristos     for (k = 0; k < ref->nitems; k++)
364*946379e7Schristos       {
365*946379e7Schristos 	message_list_ty *mlp = ref->item[k]->messages;
366*946379e7Schristos 
367*946379e7Schristos 	for (j = 0; j < mlp->nitems; j++)
368*946379e7Schristos 	  if (is_header (mlp->item[j]) /* && !mlp->item[j]->obsolete */)
369*946379e7Schristos 	    {
370*946379e7Schristos 	      const char *header = mlp->item[j]->msgstr;
371*946379e7Schristos 
372*946379e7Schristos 	      if (header != NULL)
373*946379e7Schristos 		{
374*946379e7Schristos 		  const char *charsetstr = c_strstr (header, "charset=");
375*946379e7Schristos 
376*946379e7Schristos 		  if (charsetstr != NULL)
377*946379e7Schristos 		    {
378*946379e7Schristos 		      size_t len;
379*946379e7Schristos 
380*946379e7Schristos 		      charsetstr += strlen ("charset=");
381*946379e7Schristos 		      len = strcspn (charsetstr, " \t\n");
382*946379e7Schristos 		      if (len == strlen ("UTF-8")
383*946379e7Schristos 			  && c_strncasecmp (charsetstr, "UTF-8", len) == 0)
384*946379e7Schristos 			was_utf8 = true;
385*946379e7Schristos 		    }
386*946379e7Schristos 		}
387*946379e7Schristos 	    }
388*946379e7Schristos 	}
389*946379e7Schristos     if (was_utf8)
390*946379e7Schristos       def = iconv_msgdomain_list (def, "UTF-8", fn1);
391*946379e7Schristos   }
392*946379e7Schristos 
393*946379e7Schristos   empty_list = message_list_alloc (false);
394*946379e7Schristos 
395*946379e7Schristos   /* Every entry in the xgettext generated file must be matched by a
396*946379e7Schristos      (single) entry in the human created file.  */
397*946379e7Schristos   nerrors = 0;
398*946379e7Schristos   if (!multi_domain_mode)
399*946379e7Schristos     for (k = 0; k < ref->nitems; k++)
400*946379e7Schristos       {
401*946379e7Schristos 	const char *domain = ref->item[k]->domain;
402*946379e7Schristos 	message_list_ty *refmlp = ref->item[k]->messages;
403*946379e7Schristos 	message_list_ty *defmlp;
404*946379e7Schristos 
405*946379e7Schristos 	defmlp = msgdomain_list_sublist (def, domain, false);
406*946379e7Schristos 	if (defmlp == NULL)
407*946379e7Schristos 	  defmlp = empty_list;
408*946379e7Schristos 
409*946379e7Schristos 	match_domain (fn1, fn2, defmlp, refmlp, &nerrors);
410*946379e7Schristos       }
411*946379e7Schristos   else
412*946379e7Schristos     {
413*946379e7Schristos       /* Apply the references messages in the default domain to each of
414*946379e7Schristos 	 the definition domains.  */
415*946379e7Schristos       message_list_ty *refmlp = ref->item[0]->messages;
416*946379e7Schristos 
417*946379e7Schristos       for (k = 0; k < def->nitems; k++)
418*946379e7Schristos 	{
419*946379e7Schristos 	  message_list_ty *defmlp = def->item[k]->messages;
420*946379e7Schristos 
421*946379e7Schristos 	  /* Ignore the default message domain if it has no messages.  */
422*946379e7Schristos 	  if (k > 0 || defmlp->nitems > 0)
423*946379e7Schristos 	    match_domain (fn1, fn2, defmlp, refmlp, &nerrors);
424*946379e7Schristos 	}
425*946379e7Schristos     }
426*946379e7Schristos 
427*946379e7Schristos   /* Look for messages in the definition file, which are not present
428*946379e7Schristos      in the reference file, indicating messages which defined but not
429*946379e7Schristos      used in the program.  */
430*946379e7Schristos   for (k = 0; k < def->nitems; ++k)
431*946379e7Schristos     {
432*946379e7Schristos       message_list_ty *defmlp = def->item[k]->messages;
433*946379e7Schristos 
434*946379e7Schristos       for (j = 0; j < defmlp->nitems; j++)
435*946379e7Schristos 	{
436*946379e7Schristos 	  message_ty *defmsg = defmlp->item[j];
437*946379e7Schristos 
438*946379e7Schristos 	  if (!defmsg->used)
439*946379e7Schristos 	    po_gram_error_at_line (&defmsg->pos,
440*946379e7Schristos 				   _("warning: this message is not used"));
441*946379e7Schristos 	}
442*946379e7Schristos     }
443*946379e7Schristos 
444*946379e7Schristos   /* Exit with status 1 on any error.  */
445*946379e7Schristos   if (nerrors > 0)
446*946379e7Schristos     error (EXIT_FAILURE, 0,
447*946379e7Schristos 	   ngettext ("found %d fatal error", "found %d fatal errors", nerrors),
448*946379e7Schristos 	   nerrors);
449*946379e7Schristos }
450