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