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