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