1 /* Remove, select or merge duplicate translations.
2 Copyright (C) 2001-2006 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 2, or (at your option)
8 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, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include <getopt.h>
25 #include <limits.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <locale.h>
29
30 #include "closeout.h"
31 #include "dir-list.h"
32 #include "str-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 "msgl-cat.h"
48 #include "exit.h"
49 #include "propername.h"
50 #include "gettext.h"
51
52 #define _(str) gettext (str)
53
54
55 /* Force output of PO file even if empty. */
56 static int force_po;
57
58 /* Target encoding. */
59 static const char *to_code;
60
61 /* Long options. */
62 static const struct option long_options[] =
63 {
64 { "add-location", no_argument, &line_comment, 1 },
65 { "directory", required_argument, NULL, 'D' },
66 { "escape", no_argument, NULL, 'E' },
67 { "force-po", no_argument, &force_po, 1 },
68 { "help", no_argument, NULL, 'h' },
69 { "indent", no_argument, NULL, 'i' },
70 { "no-escape", no_argument, NULL, 'e' },
71 { "no-location", no_argument, &line_comment, 0 },
72 { "no-wrap", no_argument, NULL, CHAR_MAX + 2 },
73 { "output-file", required_argument, NULL, 'o' },
74 { "properties-input", no_argument, NULL, 'P' },
75 { "properties-output", no_argument, NULL, 'p' },
76 { "repeated", no_argument, NULL, 'd' },
77 { "sort-by-file", no_argument, NULL, 'F' },
78 { "sort-output", no_argument, NULL, 's' },
79 { "strict", no_argument, NULL, 'S' },
80 { "stringtable-input", no_argument, NULL, CHAR_MAX + 3 },
81 { "stringtable-output", no_argument, NULL, CHAR_MAX + 4 },
82 { "to-code", required_argument, NULL, 't' },
83 { "unique", no_argument, NULL, 'u' },
84 { "use-first", no_argument, NULL, CHAR_MAX + 1 },
85 { "version", no_argument, NULL, 'V' },
86 { "width", required_argument, NULL, 'w', },
87 { NULL, 0, NULL, 0 }
88 };
89
90
91 /* Forward declaration of local functions. */
92 static void usage (int status)
93 #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
94 __attribute__ ((noreturn))
95 #endif
96 ;
97
98
99 int
main(int argc,char ** argv)100 main (int argc, char **argv)
101 {
102 int optchar;
103 bool do_help;
104 bool do_version;
105 char *output_file;
106 const char *input_file;
107 string_list_ty *file_list;
108 msgdomain_list_ty *result;
109 catalog_input_format_ty input_syntax = &input_format_po;
110 catalog_output_format_ty output_syntax = &output_format_po;
111 bool sort_by_msgid = false;
112 bool sort_by_filepos = false;
113
114 /* Set program name for messages. */
115 set_program_name (argv[0]);
116 error_print_progname = maybe_print_progname;
117
118 #ifdef HAVE_SETLOCALE
119 /* Set locale via LC_ALL. */
120 setlocale (LC_ALL, "");
121 #endif
122
123 /* Set the text message domain. */
124 bindtextdomain (PACKAGE, relocate (LOCALEDIR));
125 bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
126 textdomain (PACKAGE);
127
128 /* Ensure that write errors on stdout are detected. */
129 atexit (close_stdout);
130
131 /* Set default values for variables. */
132 do_help = false;
133 do_version = false;
134 output_file = NULL;
135 input_file = NULL;
136 more_than = 0;
137 less_than = INT_MAX;
138 use_first = false;
139
140 while ((optchar = getopt_long (argc, argv, "dD:eEFhino:pPst:uVw:",
141 long_options, NULL)) != EOF)
142 switch (optchar)
143 {
144 case '\0': /* Long option. */
145 break;
146
147 case 'd':
148 more_than = 1;
149 less_than = INT_MAX;
150 break;
151
152 case 'D':
153 dir_list_append (optarg);
154 break;
155
156 case 'e':
157 message_print_style_escape (false);
158 break;
159
160 case 'E':
161 message_print_style_escape (true);
162 break;
163
164 case 'F':
165 sort_by_filepos = true;
166 break;
167
168 case 'h':
169 do_help = true;
170 break;
171
172 case 'i':
173 message_print_style_indent ();
174 break;
175
176 case 'n':
177 line_comment = 1;
178 break;
179
180 case 'o':
181 output_file = optarg;
182 break;
183
184 case 'p':
185 output_syntax = &output_format_properties;
186 break;
187
188 case 'P':
189 input_syntax = &input_format_properties;
190 break;
191
192 case 's':
193 sort_by_msgid = true;
194 break;
195
196 case 'S':
197 message_print_style_uniforum ();
198 break;
199
200 case 't':
201 to_code = optarg;
202 break;
203
204 case 'u':
205 more_than = 0;
206 less_than = 2;
207 break;
208
209 case 'V':
210 do_version = true;
211 break;
212
213 case 'w':
214 {
215 int value;
216 char *endp;
217 value = strtol (optarg, &endp, 10);
218 if (endp != optarg)
219 message_page_width_set (value);
220 }
221 break;
222
223 case CHAR_MAX + 1:
224 use_first = true;
225 break;
226
227 case CHAR_MAX + 2: /* --no-wrap */
228 message_page_width_ignore ();
229 break;
230
231 case CHAR_MAX + 3: /* --stringtable-input */
232 input_syntax = &input_format_stringtable;
233 break;
234
235 case CHAR_MAX + 4: /* --stringtable-output */
236 output_syntax = &output_format_stringtable;
237 break;
238
239 default:
240 usage (EXIT_FAILURE);
241 /* NOTREACHED */
242 }
243
244 /* Version information requested. */
245 if (do_version)
246 {
247 printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
248 /* xgettext: no-wrap */
249 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
250 This is free software; see the source for copying conditions. There is NO\n\
251 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
252 "),
253 "2001-2006");
254 printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
255 exit (EXIT_SUCCESS);
256 }
257
258 /* Help is requested. */
259 if (do_help)
260 usage (EXIT_SUCCESS);
261
262 /* Test whether we have an .po file name as argument. */
263 if (optind == argc)
264 input_file = "-";
265 else if (optind + 1 == argc)
266 input_file = argv[optind];
267 else
268 {
269 error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
270 usage (EXIT_FAILURE);
271 }
272
273 /* Verify selected options. */
274 if (!line_comment && sort_by_filepos)
275 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
276 "--no-location", "--sort-by-file");
277
278 if (sort_by_msgid && sort_by_filepos)
279 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
280 "--sort-output", "--sort-by-file");
281
282 /* Determine list of files we have to process: a single file. */
283 file_list = string_list_alloc ();
284 string_list_append (file_list, input_file);
285
286 /* Read input files, then filter, convert and merge messages. */
287 allow_duplicates = true;
288 result = catenate_msgdomain_list (file_list, input_syntax, to_code);
289
290 string_list_free (file_list);
291
292 /* Sorting the list of messages. */
293 if (sort_by_filepos)
294 msgdomain_list_sort_by_filepos (result);
295 else if (sort_by_msgid)
296 msgdomain_list_sort_by_msgid (result);
297
298 /* Write the PO file. */
299 msgdomain_list_print (result, output_file, output_syntax, force_po, false);
300
301 exit (EXIT_SUCCESS);
302 }
303
304
305 /* Display usage information and exit. */
306 static void
usage(int status)307 usage (int status)
308 {
309 if (status != EXIT_SUCCESS)
310 fprintf (stderr, _("Try `%s --help' for more information.\n"),
311 program_name);
312 else
313 {
314 printf (_("\
315 Usage: %s [OPTION] [INPUTFILE]\n\
316 "), program_name);
317 printf ("\n");
318 /* xgettext: no-wrap */
319 printf (_("\
320 Unifies duplicate translations in a translation catalog.\n\
321 Finds duplicate translations of the same message ID. Such duplicates are\n\
322 invalid input for other programs like msgfmt, msgmerge or msgcat. By\n\
323 default, duplicates are merged together. When using the --repeated option,\n\
324 only duplicates are output, and all other messages are discarded. Comments\n\
325 and extracted comments will be cumulated, except that if --use-first is\n\
326 specified, they will be taken from the first translation. File positions\n\
327 will be cumulated. When using the --unique option, duplicates are discarded.\n\
328 "));
329 printf ("\n");
330 printf (_("\
331 Mandatory arguments to long options are mandatory for short options too.\n"));
332 printf ("\n");
333 printf (_("\
334 Input file location:\n"));
335 printf (_("\
336 INPUTFILE input PO file\n"));
337 printf (_("\
338 -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
339 printf (_("\
340 If no input file is given or if it is -, standard input is read.\n"));
341 printf ("\n");
342 printf (_("\
343 Output file location:\n"));
344 printf (_("\
345 -o, --output-file=FILE write output to specified file\n"));
346 printf (_("\
347 The results are written to standard output if no output file is specified\n\
348 or if it is -.\n"));
349 printf ("\n");
350 printf (_("\
351 Message selection:\n"));
352 printf (_("\
353 -d, --repeated print only duplicates\n"));
354 printf (_("\
355 -u, --unique print only unique messages, discard duplicates\n"));
356 printf ("\n");
357 printf (_("\
358 Input file syntax:\n"));
359 printf (_("\
360 -P, --properties-input input file is in Java .properties syntax\n"));
361 printf (_("\
362 --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
363 printf ("\n");
364 printf (_("\
365 Output details:\n"));
366 printf (_("\
367 -t, --to-code=NAME encoding for output\n"));
368 printf (_("\
369 --use-first use first available translation for each\n\
370 message, don't merge several translations\n"));
371 printf (_("\
372 -e, --no-escape do not use C escapes in output (default)\n"));
373 printf (_("\
374 -E, --escape use C escapes in output, no extended chars\n"));
375 printf (_("\
376 --force-po write PO file even if empty\n"));
377 printf (_("\
378 -i, --indent write the .po file using indented style\n"));
379 printf (_("\
380 --no-location do not write '#: filename:line' lines\n"));
381 printf (_("\
382 -n, --add-location generate '#: filename:line' lines (default)\n"));
383 printf (_("\
384 --strict write out strict Uniforum conforming .po file\n"));
385 printf (_("\
386 -p, --properties-output write out a Java .properties file\n"));
387 printf (_("\
388 --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
389 printf (_("\
390 -w, --width=NUMBER set output page width\n"));
391 printf (_("\
392 --no-wrap do not break long message lines, longer than\n\
393 the output page width, into several lines\n"));
394 printf (_("\
395 -s, --sort-output generate sorted output\n"));
396 printf (_("\
397 -F, --sort-by-file sort output by file location\n"));
398 printf ("\n");
399 printf (_("\
400 Informative output:\n"));
401 printf (_("\
402 -h, --help display this help and exit\n"));
403 printf (_("\
404 -V, --version output version information and exit\n"));
405 printf ("\n");
406 fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
407 stdout);
408 }
409
410 exit (status);
411 }
412
413