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