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