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