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