1/* GNU gettext - internationalization aids
2   Copyright (C) 1997-1998, 2000-2007 Free Software Foundation, Inc.
3
4   This file was written by Peter Miller <millerp@canb.auug.org.au>
5
6   This program is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 3 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19#ifdef HAVE_CONFIG_H
20# include <config.h>
21#endif
22
23#include <getopt.h>
24#include <limits.h>
25#include <locale.h>
26#include <stdio.h>
27#include <stdlib.h>
28
29#include "closeout.h"
30#include "dir-list.h"
31#include "str-list.h"
32#include "file-list.h"
33#include "error.h"
34#include "error-progname.h"
35#include "progname.h"
36#include "relocatable.h"
37#include "basename.h"
38#include "message.h"
39#include "read-catalog.h"
40#include "read-po.h"
41#include "read-properties.h"
42#include "read-stringtable.h"
43#include "write-catalog.h"
44#include "write-po.h"
45#include "write-properties.h"
46#include "write-stringtable.h"
47#include "msgl-cat.h"
48#include "propername.h"
49#include "gettext.h"
50
51
52/* A convenience macro.  I don't like writing gettext() every time.  */
53#define _(str) gettext (str)
54
55
56/* Force output of PO file even if empty.  */
57static int force_po;
58
59/* Target encoding.  */
60static const char *to_code;
61
62/* Long options.  */
63static const struct option long_options[] =
64{
65  { "add-location", no_argument, &line_comment, 1 },
66  { "directory", required_argument, NULL, 'D' },
67  { "escape", no_argument, NULL, 'E' },
68  { "files-from", required_argument, NULL, 'f' },
69  { "force-po", no_argument, &force_po, 1 },
70  { "help", no_argument, NULL, 'h' },
71  { "indent", no_argument, NULL, 'i' },
72  { "no-escape", no_argument, NULL, 'e' },
73  { "no-location", no_argument, &line_comment, 0 },
74  { "no-wrap", no_argument, NULL, CHAR_MAX + 2 },
75  { "omit-header", no_argument, NULL, CHAR_MAX + 1 },
76  { "output", required_argument, NULL, 'o' }, /* for backward compatibility */
77  { "output-file", required_argument, NULL, 'o' },
78  { "properties-input", no_argument, NULL, 'P' },
79  { "properties-output", no_argument, NULL, 'p' },
80  { "sort-by-file", no_argument, NULL, 'F' },
81  { "sort-output", no_argument, NULL, 's' },
82  { "strict", no_argument, NULL, 'S' },
83  { "stringtable-input", no_argument, NULL, CHAR_MAX + 3 },
84  { "stringtable-output", no_argument, NULL, CHAR_MAX + 4 },
85  { "to-code", required_argument, NULL, 't' },
86  { "unique", no_argument, NULL, 'u' },
87  { "version", no_argument, NULL, 'V' },
88  { "width", required_argument, NULL, 'w', },
89  { "more-than", required_argument, NULL, '>', },
90  { "less-than", required_argument, NULL, '<', },
91  { NULL, 0, NULL, 0 }
92};
93
94
95/* Forward declaration of local functions.  */
96static void usage (int status)
97#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ > 4) || __GNUC__ > 2)
98	__attribute__ ((noreturn))
99#endif
100;
101
102
103int
104main (int argc, char *argv[])
105{
106  int cnt;
107  int optchar;
108  bool do_help = false;
109  bool do_version = false;
110  msgdomain_list_ty *result;
111  catalog_input_format_ty input_syntax = &input_format_po;
112  catalog_output_format_ty output_syntax = &output_format_po;
113  bool sort_by_msgid = false;
114  bool sort_by_filepos = false;
115  const char *files_from = NULL;
116  string_list_ty *file_list;
117  char *output_file = NULL;
118
119  /* Set program name for messages.  */
120  set_program_name (argv[0]);
121  error_print_progname = maybe_print_progname;
122
123#ifdef HAVE_SETLOCALE
124  /* Set locale via LC_ALL.  */
125  setlocale (LC_ALL, "");
126#endif
127
128  /* Set the text message domain.  */
129  bindtextdomain (PACKAGE, relocate (LOCALEDIR));
130  bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
131  textdomain (PACKAGE);
132
133  /* Ensure that write errors on stdout are detected.  */
134  atexit (close_stdout);
135
136  /* Set default values for variables.  */
137  more_than = -1;
138  less_than = -1;
139  use_first = false;
140
141  while ((optchar = getopt_long (argc, argv, "<:>:D:eEf:Fhino:pPst:uVw:",
142				 long_options, NULL)) != EOF)
143    switch (optchar)
144      {
145      case '\0':		/* Long option.  */
146	break;
147
148      case '>':
149	{
150	  int value;
151	  char *endp;
152	  value = strtol (optarg, &endp, 10);
153	  if (endp != optarg)
154	    more_than = value;
155	}
156	break;
157
158      case '<':
159	{
160	  int value;
161	  char *endp;
162	  value = strtol (optarg, &endp, 10);
163	  if (endp != optarg)
164	    less_than = value;
165	}
166	break;
167
168      case 'D':
169	dir_list_append (optarg);
170	break;
171
172      case 'e':
173	message_print_style_escape (false);
174	break;
175
176      case 'E':
177	message_print_style_escape (true);
178	break;
179
180      case 'f':
181	files_from = optarg;
182	break;
183
184      case 'F':
185	sort_by_filepos = true;
186	break;
187
188      case 'h':
189	do_help = true;
190	break;
191
192      case 'i':
193	message_print_style_indent ();
194	break;
195
196      case 'n':
197	line_comment = 1;
198	break;
199
200      case 'o':
201	output_file = optarg;
202	break;
203
204      case 'p':
205	output_syntax = &output_format_properties;
206	break;
207
208      case 'P':
209	input_syntax = &input_format_properties;
210	break;
211
212      case 's':
213	sort_by_msgid = true;
214	break;
215
216      case 'S':
217	message_print_style_uniforum ();
218	break;
219
220      case 't':
221	to_code = optarg;
222	break;
223
224      case 'u':
225	less_than = 2;
226	break;
227
228      case 'V':
229	do_version = true;
230	break;
231
232      case 'w':
233	{
234	  int value;
235	  char *endp;
236	  value = strtol (optarg, &endp, 10);
237	  if (endp != optarg)
238	    message_page_width_set (value);
239	}
240	break;
241
242      case CHAR_MAX + 1:
243	omit_header = true;
244	break;
245
246      case CHAR_MAX + 2: /* --no-wrap */
247	message_page_width_ignore ();
248	break;
249
250      case CHAR_MAX + 3: /* --stringtable-input */
251	input_syntax = &input_format_stringtable;
252	break;
253
254      case CHAR_MAX + 4: /* --stringtable-output */
255	output_syntax = &output_format_stringtable;
256	break;
257
258      default:
259	usage (EXIT_FAILURE);
260	/* NOTREACHED */
261      }
262
263  /* Version information requested.  */
264  if (do_version)
265    {
266      printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
267      /* xgettext: no-wrap */
268      printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
269License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
270This is free software: you are free to change and redistribute it.\n\
271There is NO WARRANTY, to the extent permitted by law.\n\
272"),
273	      "1995-1998, 2000-2007");
274      printf (_("Written by %s.\n"), proper_name ("Peter Miller"));
275      exit (EXIT_SUCCESS);
276    }
277
278  /* Help is requested.  */
279  if (do_help)
280    usage (EXIT_SUCCESS);
281
282  /* Verify selected options.  */
283  if (!line_comment && sort_by_filepos)
284    error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
285	   "--no-location", "--sort-by-file");
286
287  if (sort_by_msgid && sort_by_filepos)
288    error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
289	   "--sort-output", "--sort-by-file");
290
291  /* Determine list of files we have to process.  */
292  if (files_from != NULL)
293    file_list = read_names_from_file (files_from);
294  else
295    file_list = string_list_alloc ();
296  /* Append names from command line.  */
297  for (cnt = optind; cnt < argc; ++cnt)
298    string_list_append_unique (file_list, argv[cnt]);
299
300  /* Test whether sufficient input files were given.  */
301  if (file_list->nitems < 2)
302    {
303      error (EXIT_SUCCESS, 0, _("at least two files must be specified"));
304      usage (EXIT_FAILURE);
305    }
306
307  /* Default the message selection criteria, and check them for sanity.  */
308  if (more_than < 0)
309    more_than = (less_than < 0 ? 1 : 0);
310  if (less_than < 0)
311    less_than = INT_MAX;
312  if (more_than >= less_than || less_than < 2)
313    error (EXIT_FAILURE, 0,
314	   _("impossible selection criteria specified (%d < n < %d)"),
315	   more_than, less_than);
316
317  /* Read input files, then filter, convert and merge messages.  */
318  allow_duplicates = true;
319  msgcomm_mode = true;
320  result = catenate_msgdomain_list (file_list, input_syntax, to_code);
321
322  string_list_free (file_list);
323
324  /* Sorting the list of messages.  */
325  if (sort_by_filepos)
326    msgdomain_list_sort_by_filepos (result);
327  else if (sort_by_msgid)
328    msgdomain_list_sort_by_msgid (result);
329
330  /* Write the PO file.  */
331  msgdomain_list_print (result, output_file, output_syntax, force_po, false);
332
333  exit (EXIT_SUCCESS);
334}
335
336
337/* Display usage information and exit.  */
338static void
339usage (int status)
340{
341  if (status != EXIT_SUCCESS)
342    fprintf (stderr, _("Try `%s --help' for more information.\n"),
343	     program_name);
344  else
345    {
346      printf (_("\
347Usage: %s [OPTION] [INPUTFILE]...\n\
348"), program_name);
349      printf ("\n");
350      /* xgettext: no-wrap */
351      printf (_("\
352Find messages which are common to two or more of the specified PO files.\n\
353By using the --more-than option, greater commonality may be requested\n\
354before messages are printed.  Conversely, the --less-than option may be\n\
355used to specify less commonality before messages are printed (i.e.\n\
356--less-than=2 will only print the unique messages).  Translations,\n\
357comments and extract comments will be preserved, but only from the first\n\
358PO file to define them.  File positions from all PO files will be\n\
359cumulated.\n\
360"));
361      printf ("\n");
362      printf (_("\
363Mandatory arguments to long options are mandatory for short options too.\n"));
364      printf ("\n");
365      printf (_("\
366Input file location:\n"));
367      printf (_("\
368  INPUTFILE ...               input files\n"));
369      printf (_("\
370  -f, --files-from=FILE       get list of input files from FILE\n"));
371      printf (_("\
372  -D, --directory=DIRECTORY   add DIRECTORY to list for input files search\n"));
373      printf (_("\
374If input file is -, standard input is read.\n"));
375      printf ("\n");
376      printf (_("\
377Output file location:\n"));
378      printf (_("\
379  -o, --output-file=FILE      write output to specified file\n"));
380      printf (_("\
381The results are written to standard output if no output file is specified\n\
382or if it is -.\n"));
383      printf ("\n");
384      printf (_("\
385Message selection:\n"));
386      printf (_("\
387  -<, --less-than=NUMBER      print messages with less than this many\n\
388                              definitions, defaults to infinite if not set\n"));
389      printf (_("\
390  ->, --more-than=NUMBER      print messages with more than this many\n\
391                              definitions, defaults to 1 if not set\n"));
392      printf (_("\
393  -u, --unique                shorthand for --less-than=2, requests\n\
394                              that only unique messages be printed\n"));
395      printf ("\n");
396      printf (_("\
397Input file syntax:\n"));
398      printf (_("\
399  -P, --properties-input      input files are in Java .properties syntax\n"));
400      printf (_("\
401      --stringtable-input     input files are in NeXTstep/GNUstep .strings\n\
402                              syntax\n"));
403      printf ("\n");
404      printf (_("\
405Output details:\n"));
406      printf (_("\
407  -e, --no-escape             do not use C escapes in output (default)\n"));
408      printf (_("\
409  -E, --escape                use C escapes in output, no extended chars\n"));
410      printf (_("\
411      --force-po              write PO file even if empty\n"));
412      printf (_("\
413  -i, --indent                write the .po file using indented style\n"));
414      printf (_("\
415      --no-location           do not write '#: filename:line' lines\n"));
416      printf (_("\
417  -n, --add-location          generate '#: filename:line' lines (default)\n"));
418      printf (_("\
419      --strict                write out strict Uniforum conforming .po file\n"));
420      printf (_("\
421  -p, --properties-output     write out a Java .properties file\n"));
422      printf (_("\
423      --stringtable-output    write out a NeXTstep/GNUstep .strings file\n"));
424      printf (_("\
425  -w, --width=NUMBER          set output page width\n"));
426      printf (_("\
427      --no-wrap               do not break long message lines, longer than\n\
428                              the output page width, into several lines\n"));
429      printf (_("\
430  -s, --sort-output           generate sorted output\n"));
431      printf (_("\
432  -F, --sort-by-file          sort output by file location\n"));
433      printf (_("\
434      --omit-header           don't write header with `msgid \"\"' entry\n"));
435      printf ("\n");
436      printf (_("\
437Informative output:\n"));
438      printf (_("\
439  -h, --help                  display this help and exit\n"));
440      printf (_("\
441  -V, --version               output version information and exit\n"));
442      printf ("\n");
443      /* TRANSLATORS: The placeholder indicates the bug-reporting address
444         for this package.  Please add _another line_ saying
445         "Report translation bugs to <...>\n" with the address for translation
446         bugs (typically your translation team's web or email address).  */
447      fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
448	     stdout);
449    }
450
451  exit (status);
452}
453