1 /* Converts a translation catalog to a different character encoding.
2 Copyright (C) 2001-2007, 2009-2010, 2012, 2014, 2016, 2018-2020 Free Software
3 Foundation, Inc.
4 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
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
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 <textstyle.h>
31
32 #include "noreturn.h"
33 #include "closeout.h"
34 #include "dir-list.h"
35 #include "error.h"
36 #include "error-progname.h"
37 #include "progname.h"
38 #include "relocatable.h"
39 #include "basename-lgpl.h"
40 #include "message.h"
41 #include "read-catalog.h"
42 #include "read-po.h"
43 #include "read-properties.h"
44 #include "read-stringtable.h"
45 #include "write-catalog.h"
46 #include "write-po.h"
47 #include "write-properties.h"
48 #include "write-stringtable.h"
49 #include "msgl-iconv.h"
50 #include "localcharset.h"
51 #include "propername.h"
52 #include "gettext.h"
53
54 #define _(str) gettext (str)
55
56
57 /* Force output of PO file even if empty. */
58 static int force_po;
59
60 /* Target encoding. */
61 static const char *to_code;
62
63 /* Long options. */
64 static const struct option long_options[] =
65 {
66 { "add-location", optional_argument, NULL, 'n' },
67 { "color", optional_argument, NULL, CHAR_MAX + 4 },
68 { "directory", required_argument, NULL, 'D' },
69 { "escape", no_argument, NULL, 'E' },
70 { "force-po", no_argument, &force_po, 1 },
71 { "help", no_argument, NULL, 'h' },
72 { "indent", no_argument, NULL, 'i' },
73 { "no-escape", no_argument, NULL, 'e' },
74 { "no-location", no_argument, NULL, CHAR_MAX + 6 },
75 { "no-wrap", no_argument, NULL, CHAR_MAX + 1 },
76 { "output-file", required_argument, NULL, 'o' },
77 { "properties-input", no_argument, NULL, 'P' },
78 { "properties-output", no_argument, NULL, 'p' },
79 { "sort-by-file", no_argument, NULL, 'F' },
80 { "sort-output", no_argument, NULL, 's' },
81 { "strict", no_argument, NULL, 'S' },
82 { "stringtable-input", no_argument, NULL, CHAR_MAX + 2 },
83 { "stringtable-output", no_argument, NULL, CHAR_MAX + 3 },
84 { "style", required_argument, NULL, CHAR_MAX + 5 },
85 { "to-code", required_argument, NULL, 't' },
86 { "version", no_argument, NULL, 'V' },
87 { "width", required_argument, NULL, 'w' },
88 { NULL, 0, NULL, 0 }
89 };
90
91
92 /* Forward declaration of local functions. */
93 _GL_NORETURN_FUNC static void usage (int status);
94
95
96 int
main(int argc,char ** argv)97 main (int argc, char **argv)
98 {
99 int opt;
100 bool do_help;
101 bool do_version;
102 char *output_file;
103 const char *input_file;
104 msgdomain_list_ty *result;
105 catalog_input_format_ty input_syntax = &input_format_po;
106 catalog_output_format_ty output_syntax = &output_format_po;
107 bool sort_by_filepos = false;
108 bool sort_by_msgid = false;
109
110 /* Set program name for messages. */
111 set_program_name (argv[0]);
112 error_print_progname = maybe_print_progname;
113
114 /* Set locale via LC_ALL. */
115 setlocale (LC_ALL, "");
116
117 /* Set the text message domain. */
118 bindtextdomain (PACKAGE, relocate (LOCALEDIR));
119 bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
120 textdomain (PACKAGE);
121
122 /* Ensure that write errors on stdout are detected. */
123 atexit (close_stdout);
124
125 /* Set default values for variables. */
126 do_help = false;
127 do_version = false;
128 output_file = NULL;
129 input_file = NULL;
130
131 while ((opt = getopt_long (argc, argv, "D:eEFhin:o:pPst:Vw:", long_options,
132 NULL))
133 != EOF)
134 switch (opt)
135 {
136 case '\0': /* Long option. */
137 break;
138
139 case 'D':
140 dir_list_append (optarg);
141 break;
142
143 case 'e':
144 message_print_style_escape (false);
145 break;
146
147 case 'E':
148 message_print_style_escape (true);
149 break;
150
151 case 'F':
152 sort_by_filepos = true;
153 break;
154
155 case 'h':
156 do_help = true;
157 break;
158
159 case 'i':
160 message_print_style_indent ();
161 break;
162
163 case 'n':
164 if (handle_filepos_comment_option (optarg))
165 usage (EXIT_FAILURE);
166 break;
167
168 case 'o':
169 output_file = optarg;
170 break;
171
172 case 'p':
173 output_syntax = &output_format_properties;
174 break;
175
176 case 'P':
177 input_syntax = &input_format_properties;
178 break;
179
180 case 's':
181 sort_by_msgid = true;
182 break;
183
184 case 'S':
185 message_print_style_uniforum ();
186 break;
187
188 case 't':
189 to_code = optarg;
190 break;
191
192 case 'V':
193 do_version = true;
194 break;
195
196 case 'w':
197 {
198 int value;
199 char *endp;
200 value = strtol (optarg, &endp, 10);
201 if (endp != optarg)
202 message_page_width_set (value);
203 }
204 break;
205
206 case CHAR_MAX + 1: /* --no-wrap */
207 message_page_width_ignore ();
208 break;
209
210 case CHAR_MAX + 2: /* --stringtable-input */
211 input_syntax = &input_format_stringtable;
212 break;
213
214 case CHAR_MAX + 3: /* --stringtable-output */
215 output_syntax = &output_format_stringtable;
216 break;
217
218 case CHAR_MAX + 4: /* --color */
219 if (handle_color_option (optarg) || color_test_mode)
220 usage (EXIT_FAILURE);
221 break;
222
223 case CHAR_MAX + 5: /* --style */
224 handle_style_option (optarg);
225 break;
226
227 case CHAR_MAX + 6: /* --no-location */
228 message_print_style_filepos (filepos_comment_none);
229 break;
230
231 default:
232 usage (EXIT_FAILURE);
233 break;
234 }
235
236 /* Version information is requested. */
237 if (do_version)
238 {
239 printf ("%s (GNU %s) %s\n", last_component (program_name),
240 PACKAGE, VERSION);
241 /* xgettext: no-wrap */
242 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
243 License GPLv3+: GNU GPL version 3 or later <%s>\n\
244 This is free software: you are free to change and redistribute it.\n\
245 There is NO WARRANTY, to the extent permitted by law.\n\
246 "),
247 "2001-2020", "https://gnu.org/licenses/gpl.html");
248 printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
249 exit (EXIT_SUCCESS);
250 }
251
252 /* Help is requested. */
253 if (do_help)
254 usage (EXIT_SUCCESS);
255
256 /* Test whether we have an .po file name as argument. */
257 if (optind == argc)
258 input_file = "-";
259 else if (optind + 1 == argc)
260 input_file = argv[optind];
261 else
262 {
263 error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
264 usage (EXIT_FAILURE);
265 }
266
267 /* Verify selected options. */
268 if (sort_by_msgid && sort_by_filepos)
269 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
270 "--sort-output", "--sort-by-file");
271
272 /* Default for target encoding is current locale's encoding. */
273 if (to_code == NULL)
274 to_code = locale_charset ();
275
276 /* Read input file. */
277 result = read_catalog_file (input_file, input_syntax);
278
279 /* Convert if and only if the output syntax supports different encodings. */
280 if (!output_syntax->requires_utf8)
281 result = iconv_msgdomain_list (result, to_code, true, input_file);
282
283 /* Sort the results. */
284 if (sort_by_filepos)
285 msgdomain_list_sort_by_filepos (result);
286 else if (sort_by_msgid)
287 msgdomain_list_sort_by_msgid (result);
288
289 /* Write the merged message list out. */
290 msgdomain_list_print (result, output_file, output_syntax, force_po, false);
291
292 exit (EXIT_SUCCESS);
293 }
294
295
296 /* Display usage information and exit. */
297 static void
usage(int status)298 usage (int status)
299 {
300 if (status != EXIT_SUCCESS)
301 fprintf (stderr, _("Try '%s --help' for more information.\n"),
302 program_name);
303 else
304 {
305 printf (_("\
306 Usage: %s [OPTION] [INPUTFILE]\n\
307 "), program_name);
308 printf ("\n");
309 printf (_("\
310 Converts a translation catalog to a different character encoding.\n\
311 "));
312 printf ("\n");
313 printf (_("\
314 Mandatory arguments to long options are mandatory for short options too.\n"));
315 printf ("\n");
316 printf (_("\
317 Input file location:\n"));
318 printf (_("\
319 INPUTFILE input PO file\n"));
320 printf (_("\
321 -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
322 printf (_("\
323 If no input file is given or if it is -, standard input is read.\n"));
324 printf ("\n");
325 printf (_("\
326 Output file location:\n"));
327 printf (_("\
328 -o, --output-file=FILE write output to specified file\n"));
329 printf (_("\
330 The results are written to standard output if no output file is specified\n\
331 or if it is -.\n"));
332 printf ("\n");
333 printf (_("\
334 Conversion target:\n"));
335 printf (_("\
336 -t, --to-code=NAME encoding for output\n"));
337 printf (_("\
338 The default encoding is the current locale's encoding.\n"));
339 printf ("\n");
340 printf (_("\
341 Input file syntax:\n"));
342 printf (_("\
343 -P, --properties-input input file is in Java .properties syntax\n"));
344 printf (_("\
345 --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
346 printf ("\n");
347 printf (_("\
348 Output details:\n"));
349 printf (_("\
350 --color use colors and other text attributes always\n\
351 --color=WHEN use colors and other text attributes if WHEN.\n\
352 WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
353 printf (_("\
354 --style=STYLEFILE specify CSS style rule file for --color\n"));
355 printf (_("\
356 -e, --no-escape do not use C escapes in output (default)\n"));
357 printf (_("\
358 -E, --escape use C escapes in output, no extended chars\n"));
359 printf (_("\
360 --force-po write PO file even if empty\n"));
361 printf (_("\
362 -i, --indent indented output style\n"));
363 printf (_("\
364 --no-location suppress '#: filename:line' lines\n"));
365 printf (_("\
366 -n, --add-location preserve '#: filename:line' lines (default)\n"));
367 printf (_("\
368 --strict strict Uniforum output style\n"));
369 printf (_("\
370 -p, --properties-output write out a Java .properties file\n"));
371 printf (_("\
372 --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
373 printf (_("\
374 -w, --width=NUMBER set output page width\n"));
375 printf (_("\
376 --no-wrap do not break long message lines, longer than\n\
377 the output page width, into several lines\n"));
378 printf (_("\
379 -s, --sort-output generate sorted output\n"));
380 printf (_("\
381 -F, --sort-by-file sort output by file location\n"));
382 printf ("\n");
383 printf (_("\
384 Informative output:\n"));
385 printf (_("\
386 -h, --help display this help and exit\n"));
387 printf (_("\
388 -V, --version output version information and exit\n"));
389 printf ("\n");
390 /* TRANSLATORS: The first placeholder is the web address of the Savannah
391 project of this package. The second placeholder is the bug-reporting
392 email address for this package. Please add _another line_ saying
393 "Report translation bugs to <...>\n" with the address for translation
394 bugs (typically your translation team's web or email address). */
395 printf(_("\
396 Report bugs in the bug tracker at <%s>\n\
397 or by email to <%s>.\n"),
398 "https://savannah.gnu.org/projects/gettext",
399 "bug-gettext@gnu.org");
400 }
401
402 exit (status);
403 }
404