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