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