• 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/* Manipulates attributes of messages in translation catalogs.
2   Copyright (C) 2001-2007 Free Software Foundation, Inc.
3   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
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
19#ifdef HAVE_CONFIG_H
20# include "config.h"
21#endif
22
23#include <getopt.h>
24#include <limits.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 "write-catalog.h"
42#include "write-po.h"
43#include "write-properties.h"
44#include "write-stringtable.h"
45#include "propername.h"
46#include "gettext.h"
47
48#define _(str) gettext (str)
49
50
51/* Force output of PO file even if empty.  */
52static int force_po;
53
54/* Bit mask of subsets to remove.  */
55enum
56{
57  REMOVE_UNTRANSLATED	= 1 << 0,
58  REMOVE_TRANSLATED	= 1 << 1,
59  REMOVE_FUZZY		= 1 << 2,
60  REMOVE_NONFUZZY	= 1 << 3,
61  REMOVE_OBSOLETE	= 1 << 4,
62  REMOVE_NONOBSOLETE	= 1 << 5
63};
64static int to_remove;
65
66/* Bit mask of actions to perform on all messages.  */
67enum
68{
69  SET_FUZZY		= 1 << 0,
70  RESET_FUZZY		= 1 << 1,
71  SET_OBSOLETE		= 1 << 2,
72  RESET_OBSOLETE	= 1 << 3,
73  REMOVE_PREV		= 1 << 4
74};
75static int to_change;
76
77/* Long options.  */
78static const struct option long_options[] =
79{
80  { "add-location", no_argument, &line_comment, 1 },
81  { "clear-fuzzy", no_argument, NULL, CHAR_MAX + 8 },
82  { "clear-obsolete", no_argument, NULL, CHAR_MAX + 10 },
83  { "clear-previous", no_argument, NULL, CHAR_MAX + 18 },
84  { "directory", required_argument, NULL, 'D' },
85  { "escape", no_argument, NULL, 'E' },
86  { "force-po", no_argument, &force_po, 1 },
87  { "fuzzy", no_argument, NULL, CHAR_MAX + 11 },
88  { "help", no_argument, NULL, 'h' },
89  { "ignore-file", required_argument, NULL, CHAR_MAX + 15 },
90  { "indent", no_argument, NULL, 'i' },
91  { "no-escape", no_argument, NULL, 'e' },
92  { "no-fuzzy", no_argument, NULL, CHAR_MAX + 3 },
93  { "no-location", no_argument, &line_comment, 0 },
94  { "no-obsolete", no_argument, NULL, CHAR_MAX + 5 },
95  { "no-wrap", no_argument, NULL, CHAR_MAX + 13 },
96  { "obsolete", no_argument, NULL, CHAR_MAX + 12 },
97  { "only-file", required_argument, NULL, CHAR_MAX + 14 },
98  { "only-fuzzy", no_argument, NULL, CHAR_MAX + 4 },
99  { "only-obsolete", no_argument, NULL, CHAR_MAX + 6 },
100  { "output-file", required_argument, NULL, 'o' },
101  { "properties-input", no_argument, NULL, 'P' },
102  { "properties-output", no_argument, NULL, 'p' },
103  { "set-fuzzy", no_argument, NULL, CHAR_MAX + 7 },
104  { "set-obsolete", no_argument, NULL, CHAR_MAX + 9 },
105  { "sort-by-file", no_argument, NULL, 'F' },
106  { "sort-output", no_argument, NULL, 's' },
107  { "stringtable-input", no_argument, NULL, CHAR_MAX + 16 },
108  { "stringtable-output", no_argument, NULL, CHAR_MAX + 17 },
109  { "strict", no_argument, NULL, 'S' },
110  { "translated", no_argument, NULL, CHAR_MAX + 1 },
111  { "untranslated", no_argument, NULL, CHAR_MAX + 2 },
112  { "version", no_argument, NULL, 'V' },
113  { "width", required_argument, NULL, 'w', },
114  { NULL, 0, NULL, 0 }
115};
116
117
118/* Forward declaration of local functions.  */
119static void usage (int status)
120#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
121	__attribute__ ((noreturn))
122#endif
123;
124static msgdomain_list_ty *process_msgdomain_list (msgdomain_list_ty *mdlp,
125						  msgdomain_list_ty *only_mdlp,
126						msgdomain_list_ty *ignore_mdlp);
127
128
129int
130main (int argc, char **argv)
131{
132  int optchar;
133  bool do_help;
134  bool do_version;
135  char *output_file;
136  const char *input_file;
137  const char *only_file;
138  const char *ignore_file;
139  msgdomain_list_ty *only_mdlp;
140  msgdomain_list_ty *ignore_mdlp;
141  msgdomain_list_ty *result;
142  catalog_input_format_ty input_syntax = &input_format_po;
143  catalog_output_format_ty output_syntax = &output_format_po;
144  bool sort_by_msgid = false;
145  bool sort_by_filepos = false;
146
147  /* Set program name for messages.  */
148  set_program_name (argv[0]);
149  error_print_progname = maybe_print_progname;
150
151#ifdef HAVE_SETLOCALE
152  /* Set locale via LC_ALL.  */
153  setlocale (LC_ALL, "");
154#endif
155
156  /* Set the text message domain.  */
157  bindtextdomain (PACKAGE, relocate (LOCALEDIR));
158  bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
159  textdomain (PACKAGE);
160
161  /* Ensure that write errors on stdout are detected.  */
162  atexit (close_stdout);
163
164  /* Set default values for variables.  */
165  do_help = false;
166  do_version = false;
167  output_file = NULL;
168  input_file = NULL;
169  only_file = NULL;
170  ignore_file = NULL;
171
172  while ((optchar = getopt_long (argc, argv, "D:eEFhino:pPsVw:", long_options,
173				 NULL)) != EOF)
174    switch (optchar)
175      {
176      case '\0':		/* Long option.  */
177	break;
178
179      case 'D':
180	dir_list_append (optarg);
181	break;
182
183      case 'e':
184	message_print_style_escape (false);
185	break;
186
187      case 'E':
188	message_print_style_escape (true);
189	break;
190
191      case 'F':
192	sort_by_filepos = true;
193	break;
194
195      case 'h':
196	do_help = true;
197	break;
198
199      case 'i':
200	message_print_style_indent ();
201	break;
202
203      case 'n':
204	line_comment = 1;
205	break;
206
207      case 'o':
208	output_file = optarg;
209	break;
210
211      case 'p':
212	output_syntax = &output_format_properties;
213	break;
214
215      case 'P':
216	input_syntax = &input_format_properties;
217	break;
218
219      case 's':
220	sort_by_msgid = true;
221	break;
222
223      case 'S':
224	message_print_style_uniforum ();
225	break;
226
227      case 'V':
228	do_version = true;
229	break;
230
231      case 'w':
232	{
233	  int value;
234	  char *endp;
235	  value = strtol (optarg, &endp, 10);
236	  if (endp != optarg)
237	    message_page_width_set (value);
238	}
239	break;
240
241      case CHAR_MAX + 1: /* --translated */
242	to_remove |= REMOVE_UNTRANSLATED;
243	break;
244
245      case CHAR_MAX + 2: /* --untranslated */
246	to_remove |= REMOVE_TRANSLATED;
247	break;
248
249      case CHAR_MAX + 3: /* --no-fuzzy */
250	to_remove |= REMOVE_FUZZY;
251	break;
252
253      case CHAR_MAX + 4: /* --only-fuzzy */
254	to_remove |= REMOVE_NONFUZZY;
255	break;
256
257      case CHAR_MAX + 5: /* --no-obsolete */
258	to_remove |= REMOVE_OBSOLETE;
259	break;
260
261      case CHAR_MAX + 6: /* --only-obsolete */
262	to_remove |= REMOVE_NONOBSOLETE;
263	break;
264
265      case CHAR_MAX + 7: /* --set-fuzzy */
266	to_change |= SET_FUZZY;
267	break;
268
269      case CHAR_MAX + 8: /* --clear-fuzzy */
270	to_change |= RESET_FUZZY;
271	break;
272
273      case CHAR_MAX + 9: /* --set-obsolete */
274	to_change |= SET_OBSOLETE;
275	break;
276
277      case CHAR_MAX + 10: /* --clear-obsolete */
278	to_change |= RESET_OBSOLETE;
279	break;
280
281      case CHAR_MAX + 11: /* --fuzzy */
282	to_remove |= REMOVE_NONFUZZY;
283	to_change |= RESET_FUZZY;
284	break;
285
286      case CHAR_MAX + 12: /* --obsolete */
287	to_remove |= REMOVE_NONOBSOLETE;
288	to_change |= RESET_OBSOLETE;
289	break;
290
291      case CHAR_MAX + 13: /* --no-wrap */
292	message_page_width_ignore ();
293	break;
294
295      case CHAR_MAX + 14: /* --only-file */
296	only_file = optarg;
297	break;
298
299      case CHAR_MAX + 15: /* --ignore-file */
300	ignore_file = optarg;
301	break;
302
303      case CHAR_MAX + 16: /* --stringtable-input */
304	input_syntax = &input_format_stringtable;
305	break;
306
307      case CHAR_MAX + 17: /* --stringtable-output */
308	output_syntax = &output_format_stringtable;
309	break;
310
311      case CHAR_MAX + 18: /* --clear-previous */
312	to_change |= REMOVE_PREV;
313	break;
314
315      default:
316	usage (EXIT_FAILURE);
317	/* NOTREACHED */
318      }
319
320  /* Version information requested.  */
321  if (do_version)
322    {
323      printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
324      /* xgettext: no-wrap */
325      printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
326License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
327This is free software: you are free to change and redistribute it.\n\
328There is NO WARRANTY, to the extent permitted by law.\n\
329"),
330	      "2001-2007");
331      printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
332      exit (EXIT_SUCCESS);
333    }
334
335  /* Help is requested.  */
336  if (do_help)
337    usage (EXIT_SUCCESS);
338
339  /* Test whether we have an .po file name as argument.  */
340  if (optind == argc)
341    input_file = "-";
342  else if (optind + 1 == argc)
343    input_file = argv[optind];
344  else
345    {
346      error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
347      usage (EXIT_FAILURE);
348    }
349
350  /* Verify selected options.  */
351  if (!line_comment && sort_by_filepos)
352    error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
353	   "--no-location", "--sort-by-file");
354
355  if (sort_by_msgid && sort_by_filepos)
356    error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
357	   "--sort-output", "--sort-by-file");
358
359  /* Read input file.  */
360  result = read_catalog_file (input_file, input_syntax);
361
362  /* Read optional files that limit the extent of the attribute changes.  */
363  only_mdlp = (only_file != NULL
364	       ? read_catalog_file (only_file, input_syntax)
365	       : NULL);
366  ignore_mdlp = (ignore_file != NULL
367		 ? read_catalog_file (ignore_file, input_syntax)
368		 : NULL);
369
370  /* Filter the messages and manipulate the attributes.  */
371  result = process_msgdomain_list (result, only_mdlp, ignore_mdlp);
372
373  /* Sorting the list of messages.  */
374  if (sort_by_filepos)
375    msgdomain_list_sort_by_filepos (result);
376  else if (sort_by_msgid)
377    msgdomain_list_sort_by_msgid (result);
378
379  /* Write the PO file.  */
380  msgdomain_list_print (result, output_file, output_syntax, force_po, false);
381
382  exit (EXIT_SUCCESS);
383}
384
385
386/* Display usage information and exit.  */
387static void
388usage (int status)
389{
390  if (status != EXIT_SUCCESS)
391    fprintf (stderr, _("Try `%s --help' for more information.\n"),
392	     program_name);
393  else
394    {
395      printf (_("\
396Usage: %s [OPTION] [INPUTFILE]\n\
397"), program_name);
398      printf ("\n");
399      /* xgettext: no-wrap */
400      printf (_("\
401Filters the messages of a translation catalog according to their attributes,\n\
402and manipulates the attributes.\n"));
403      printf ("\n");
404      printf (_("\
405Mandatory arguments to long options are mandatory for short options too.\n"));
406      printf ("\n");
407      printf (_("\
408Input file location:\n"));
409      printf (_("\
410  INPUTFILE                   input PO file\n"));
411      printf (_("\
412  -D, --directory=DIRECTORY   add DIRECTORY to list for input files search\n"));
413      printf (_("\
414If no input file is given or if it is -, standard input is read.\n"));
415      printf ("\n");
416      printf (_("\
417Output file location:\n"));
418      printf (_("\
419  -o, --output-file=FILE      write output to specified file\n"));
420      printf (_("\
421The results are written to standard output if no output file is specified\n\
422or if it is -.\n"));
423      printf ("\n");
424      printf (_("\
425Message selection:\n"));
426      printf (_("\
427      --translated            keep translated, remove untranslated messages\n"));
428      printf (_("\
429      --untranslated          keep untranslated, remove translated messages\n"));
430      printf (_("\
431      --no-fuzzy              remove 'fuzzy' marked messages\n"));
432      printf (_("\
433      --only-fuzzy            keep 'fuzzy' marked messages\n"));
434      printf (_("\
435      --no-obsolete           remove obsolete #~ messages\n"));
436      printf (_("\
437      --only-obsolete         keep obsolete #~ messages\n"));
438      printf ("\n");
439      printf (_("\
440Attribute manipulation:\n"));
441      printf (_("\
442      --set-fuzzy             set all messages 'fuzzy'\n"));
443      printf (_("\
444      --clear-fuzzy           set all messages non-'fuzzy'\n"));
445      printf (_("\
446      --set-obsolete          set all messages obsolete\n"));
447      printf (_("\
448      --clear-obsolete        set all messages non-obsolete\n"));
449      printf (_("\
450      --clear-previous        remove the \"previous msgid\" from all messages\n"));
451      printf (_("\
452      --only-file=FILE.po     manipulate only entries listed in FILE.po\n"));
453      printf (_("\
454      --ignore-file=FILE.po   manipulate only entries not listed in FILE.po\n"));
455      printf (_("\
456      --fuzzy                 synonym for --only-fuzzy --clear-fuzzy\n"));
457      printf (_("\
458      --obsolete              synonym for --only-obsolete --clear-obsolete\n"));
459      printf ("\n");
460      printf (_("\
461Input file syntax:\n"));
462      printf (_("\
463  -P, --properties-input      input file is in Java .properties syntax\n"));
464      printf (_("\
465      --stringtable-input     input file is in NeXTstep/GNUstep .strings syntax\n"));
466      printf ("\n");
467      printf (_("\
468Output details:\n"));
469      printf (_("\
470  -e, --no-escape             do not use C escapes in output (default)\n"));
471      printf (_("\
472  -E, --escape                use C escapes in output, no extended chars\n"));
473      printf (_("\
474      --force-po              write PO file even if empty\n"));
475      printf (_("\
476  -i, --indent                write the .po file using indented style\n"));
477      printf (_("\
478      --no-location           do not write '#: filename:line' lines\n"));
479      printf (_("\
480  -n, --add-location          generate '#: filename:line' lines (default)\n"));
481      printf (_("\
482      --strict                write out strict Uniforum conforming .po file\n"));
483      printf (_("\
484  -p, --properties-output     write out a Java .properties file\n"));
485      printf (_("\
486      --stringtable-output    write out a NeXTstep/GNUstep .strings file\n"));
487      printf (_("\
488  -w, --width=NUMBER          set output page width\n"));
489      printf (_("\
490      --no-wrap               do not break long message lines, longer than\n\
491                              the output page width, into several lines\n"));
492      printf (_("\
493  -s, --sort-output           generate sorted output\n"));
494      printf (_("\
495  -F, --sort-by-file          sort output by file location\n"));
496      printf ("\n");
497      printf (_("\
498Informative output:\n"));
499      printf (_("\
500  -h, --help                  display this help and exit\n"));
501      printf (_("\
502  -V, --version               output version information and exit\n"));
503      printf ("\n");
504      /* TRANSLATORS: The placeholder indicates the bug-reporting address
505         for this package.  Please add _another line_ saying
506         "Report translation bugs to <...>\n" with the address for translation
507         bugs (typically your translation team's web or email address).  */
508      fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
509	     stdout);
510    }
511
512  exit (status);
513}
514
515
516/* Return true if a message should be kept.  */
517static bool
518is_message_selected (const message_ty *mp)
519{
520  /* Always keep the header entry.  */
521  if (is_header (mp))
522    return true;
523
524  if ((to_remove & (REMOVE_UNTRANSLATED | REMOVE_TRANSLATED))
525      && (mp->msgstr[0] == '\0'
526	  ? to_remove & REMOVE_UNTRANSLATED
527	  : to_remove & REMOVE_TRANSLATED))
528    return false;
529
530  if ((to_remove & (REMOVE_FUZZY | REMOVE_NONFUZZY))
531      && (mp->is_fuzzy
532	  ? to_remove & REMOVE_FUZZY
533	  : to_remove & REMOVE_NONFUZZY))
534    return false;
535
536  if ((to_remove & (REMOVE_OBSOLETE | REMOVE_NONOBSOLETE))
537      && (mp->obsolete
538	  ? to_remove & REMOVE_OBSOLETE
539	  : to_remove & REMOVE_NONOBSOLETE))
540    return false;
541
542  return true;
543}
544
545
546static void
547process_message_list (message_list_ty *mlp,
548		      message_list_ty *only_mlp, message_list_ty *ignore_mlp)
549{
550  /* Keep only the selected messages.  */
551  message_list_remove_if_not (mlp, is_message_selected);
552
553  /* Change the attributes.  */
554  if (to_change)
555    {
556      size_t j;
557
558      for (j = 0; j < mlp->nitems; j++)
559	{
560	  message_ty *mp = mlp->item[j];
561
562	  /* Attribute changes only affect messages listed in --only-file
563	     and not listed in --ignore-file.  */
564	  if ((only_mlp
565	       ? message_list_search (only_mlp, mp->msgctxt, mp->msgid) != NULL
566	       : true)
567	      && (ignore_mlp
568		  ? message_list_search (ignore_mlp, mp->msgctxt, mp->msgid) == NULL
569		  : true))
570	    {
571	      if (to_change & SET_FUZZY)
572		mp->is_fuzzy = true;
573	      if (to_change & RESET_FUZZY)
574		mp->is_fuzzy = false;
575	      /* Always keep the header entry non-obsolete.  */
576	      if ((to_change & SET_OBSOLETE) && !is_header (mp))
577		mp->obsolete = true;
578	      if (to_change & RESET_OBSOLETE)
579		mp->obsolete = false;
580	      if (to_change & REMOVE_PREV)
581		{
582		  mp->prev_msgctxt = NULL;
583		  mp->prev_msgid = NULL;
584		  mp->prev_msgid_plural = NULL;
585		}
586	    }
587	}
588    }
589}
590
591
592static msgdomain_list_ty *
593process_msgdomain_list (msgdomain_list_ty *mdlp,
594			msgdomain_list_ty *only_mdlp,
595			msgdomain_list_ty *ignore_mdlp)
596{
597  size_t k;
598
599  for (k = 0; k < mdlp->nitems; k++)
600    process_message_list (mdlp->item[k]->messages,
601			  only_mdlp
602			  ? msgdomain_list_sublist (only_mdlp,
603						    mdlp->item[k]->domain,
604						    true)
605			  : NULL,
606			  ignore_mdlp
607			  ? msgdomain_list_sublist (ignore_mdlp,
608						    mdlp->item[k]->domain,
609						    false)
610			  : NULL);
611
612  return mdlp;
613}
614