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