1 /* msgunfmt - converts binary .mo files to Uniforum style .po files
2 Copyright (C) 1995-1998, 2000-2007, 2009-2010, 2012, 2016, 2018-2020 Free Software
3 Foundation, Inc.
4 Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
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 <stdbool.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 "error.h"
35 #include "error-progname.h"
36 #include "progname.h"
37 #include "relocatable.h"
38 #include "basename-lgpl.h"
39 #include "message.h"
40 #include "msgunfmt.h"
41 #include "read-mo.h"
42 #include "read-java.h"
43 #include "read-csharp.h"
44 #include "read-resources.h"
45 #include "read-tcl.h"
46 #include "write-catalog.h"
47 #include "write-po.h"
48 #include "write-properties.h"
49 #include "write-stringtable.h"
50 #include "propername.h"
51 #include "gettext.h"
52
53 #define _(str) gettext (str)
54
55
56 /* Be more verbose. */
57 bool verbose;
58
59 /* Java mode input file specification. */
60 static bool java_mode;
61 static const char *java_resource_name;
62 static const char *java_locale_name;
63
64 /* C# mode input file specification. */
65 static bool csharp_mode;
66 static const char *csharp_resource_name;
67 static const char *csharp_locale_name;
68 static const char *csharp_base_directory;
69
70 /* C# resources mode input file specification. */
71 static bool csharp_resources_mode;
72
73 /* Tcl mode input file specification. */
74 static bool tcl_mode;
75 static const char *tcl_locale_name;
76 static const char *tcl_base_directory;
77
78 /* Force output of PO file even if empty. */
79 static int force_po;
80
81 /* Long options. */
82 static const struct option long_options[] =
83 {
84 { "color", optional_argument, NULL, CHAR_MAX + 6 },
85 { "csharp", no_argument, NULL, CHAR_MAX + 4 },
86 { "csharp-resources", no_argument, NULL, CHAR_MAX + 5 },
87 { "escape", no_argument, NULL, 'E' },
88 { "force-po", no_argument, &force_po, 1 },
89 { "help", no_argument, NULL, 'h' },
90 { "indent", no_argument, NULL, 'i' },
91 { "java", no_argument, NULL, 'j' },
92 { "locale", required_argument, NULL, 'l' },
93 { "no-escape", no_argument, NULL, 'e' },
94 { "no-wrap", no_argument, NULL, CHAR_MAX + 2 },
95 { "output-file", required_argument, NULL, 'o' },
96 { "properties-output", no_argument, NULL, 'p' },
97 { "resource", required_argument, NULL, 'r' },
98 { "sort-output", no_argument, NULL, 's' },
99 { "strict", no_argument, NULL, 'S' },
100 { "stringtable-output", no_argument, NULL, CHAR_MAX + 3 },
101 { "style", required_argument, NULL, CHAR_MAX + 7 },
102 { "tcl", no_argument, NULL, CHAR_MAX + 1 },
103 { "verbose", no_argument, NULL, 'v' },
104 { "version", no_argument, NULL, 'V' },
105 { "width", required_argument, NULL, 'w' },
106 { NULL, 0, NULL, 0 }
107 };
108
109
110 /* Forward declaration of local functions. */
111 _GL_NORETURN_FUNC static void usage (int status);
112 static void read_one_file (message_list_ty *mlp, const char *filename);
113
114
115 int
main(int argc,char ** argv)116 main (int argc, char **argv)
117 {
118 int optchar;
119 bool do_help = false;
120 bool do_version = false;
121 const char *output_file = "-";
122 msgdomain_list_ty *result;
123 catalog_output_format_ty output_syntax = &output_format_po;
124 bool sort_by_msgid = false;
125
126 /* Set program name for messages. */
127 set_program_name (argv[0]);
128 error_print_progname = maybe_print_progname;
129
130 /* Set locale via LC_ALL. */
131 setlocale (LC_ALL, "");
132
133 /* Set the text message domain. */
134 bindtextdomain (PACKAGE, relocate (LOCALEDIR));
135 bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
136 textdomain (PACKAGE);
137
138 /* Ensure that write errors on stdout are detected. */
139 atexit (close_stdout);
140
141 while ((optchar = getopt_long (argc, argv, "d:eEhijl:o:pr:svVw:",
142 long_options, NULL))
143 != EOF)
144 switch (optchar)
145 {
146 case '\0':
147 /* long option */
148 break;
149
150 case 'd':
151 csharp_base_directory = optarg;
152 tcl_base_directory = optarg;
153 break;
154
155 case 'e':
156 message_print_style_escape (false);
157 break;
158
159 case 'E':
160 message_print_style_escape (true);
161 break;
162
163 case 'h':
164 do_help = true;
165 break;
166
167 case 'i':
168 message_print_style_indent ();
169 break;
170
171 case 'j':
172 java_mode = true;
173 break;
174
175 case 'l':
176 java_locale_name = optarg;
177 csharp_locale_name = optarg;
178 tcl_locale_name = optarg;
179 break;
180
181 case 'o':
182 output_file = optarg;
183 break;
184
185 case 'p':
186 output_syntax = &output_format_properties;
187 break;
188
189 case 'r':
190 java_resource_name = optarg;
191 csharp_resource_name = optarg;
192 break;
193
194 case 's':
195 sort_by_msgid = true;
196 break;
197
198 case 'S':
199 message_print_style_uniforum ();
200 break;
201
202 case 'v':
203 verbose = true;
204 break;
205
206 case 'V':
207 do_version = true;
208 break;
209
210 case 'w':
211 {
212 int value;
213 char *endp;
214 value = strtol (optarg, &endp, 10);
215 if (endp != optarg)
216 message_page_width_set (value);
217 }
218 break;
219
220 case CHAR_MAX + 1: /* --tcl */
221 tcl_mode = true;
222 break;
223
224 case CHAR_MAX + 2: /* --no-wrap */
225 message_page_width_ignore ();
226 break;
227
228 case CHAR_MAX + 3: /* --stringtable-output */
229 output_syntax = &output_format_stringtable;
230 break;
231
232 case CHAR_MAX + 4: /* --csharp */
233 csharp_mode = true;
234 break;
235
236 case CHAR_MAX + 5: /* --csharp-resources */
237 csharp_resources_mode = true;
238 break;
239
240 case CHAR_MAX + 6: /* --color */
241 if (handle_color_option (optarg) || color_test_mode)
242 usage (EXIT_FAILURE);
243 break;
244
245 case CHAR_MAX + 7: /* --style */
246 handle_style_option (optarg);
247 break;
248
249 default:
250 usage (EXIT_FAILURE);
251 break;
252 }
253
254 /* Version information is requested. */
255 if (do_version)
256 {
257 printf ("%s (GNU %s) %s\n", last_component (program_name),
258 PACKAGE, VERSION);
259 /* xgettext: no-wrap */
260 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
261 License GPLv3+: GNU GPL version 3 or later <%s>\n\
262 This is free software: you are free to change and redistribute it.\n\
263 There is NO WARRANTY, to the extent permitted by law.\n\
264 "),
265 "1995-2020", "https://gnu.org/licenses/gpl.html");
266 printf (_("Written by %s.\n"), proper_name ("Ulrich Drepper"));
267 exit (EXIT_SUCCESS);
268 }
269
270 /* Help is requested. */
271 if (do_help)
272 usage (EXIT_SUCCESS);
273
274 /* Check for contradicting options. */
275 {
276 unsigned int modes =
277 (java_mode ? 1 : 0)
278 | (csharp_mode ? 2 : 0)
279 | (csharp_resources_mode ? 4 : 0)
280 | (tcl_mode ? 8 : 0);
281 static const char *mode_options[] =
282 { "--java", "--csharp", "--csharp-resources", "--tcl" };
283 /* More than one bit set? */
284 if (modes & (modes - 1))
285 {
286 const char *first_option;
287 const char *second_option;
288 unsigned int i;
289 for (i = 0; ; i++)
290 if (modes & (1 << i))
291 break;
292 first_option = mode_options[i];
293 for (i = i + 1; ; i++)
294 if (modes & (1 << i))
295 break;
296 second_option = mode_options[i];
297 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
298 first_option, second_option);
299 }
300 }
301 if (java_mode)
302 {
303 if (optind < argc)
304 {
305 error (EXIT_FAILURE, 0,
306 _("%s and explicit file names are mutually exclusive"),
307 "--java");
308 }
309 }
310 else if (csharp_mode)
311 {
312 if (optind < argc)
313 {
314 error (EXIT_FAILURE, 0,
315 _("%s and explicit file names are mutually exclusive"),
316 "--csharp");
317 }
318 if (csharp_locale_name == NULL)
319 {
320 error (EXIT_SUCCESS, 0,
321 _("%s requires a \"-l locale\" specification"),
322 "--csharp");
323 usage (EXIT_FAILURE);
324 }
325 if (csharp_base_directory == NULL)
326 {
327 error (EXIT_SUCCESS, 0,
328 _("%s requires a \"-d directory\" specification"),
329 "--csharp");
330 usage (EXIT_FAILURE);
331 }
332 }
333 else if (tcl_mode)
334 {
335 if (optind < argc)
336 {
337 error (EXIT_FAILURE, 0,
338 _("%s and explicit file names are mutually exclusive"),
339 "--tcl");
340 }
341 if (tcl_locale_name == NULL)
342 {
343 error (EXIT_SUCCESS, 0,
344 _("%s requires a \"-l locale\" specification"),
345 "--tcl");
346 usage (EXIT_FAILURE);
347 }
348 if (tcl_base_directory == NULL)
349 {
350 error (EXIT_SUCCESS, 0,
351 _("%s requires a \"-d directory\" specification"),
352 "--tcl");
353 usage (EXIT_FAILURE);
354 }
355 }
356 else
357 {
358 if (java_resource_name != NULL)
359 {
360 error (EXIT_SUCCESS, 0, _("%s is only valid with %s or %s"),
361 "--resource", "--java", "--csharp");
362 usage (EXIT_FAILURE);
363 }
364 if (java_locale_name != NULL)
365 {
366 error (EXIT_SUCCESS, 0, _("%s is only valid with %s or %s"),
367 "--locale", "--java", "--csharp");
368 usage (EXIT_FAILURE);
369 }
370 }
371
372 /* Read the given .mo file. */
373 if (java_mode)
374 {
375 result = msgdomain_read_java (java_resource_name, java_locale_name);
376 }
377 else if (csharp_mode)
378 {
379 result = msgdomain_read_csharp (csharp_resource_name, csharp_locale_name,
380 csharp_base_directory);
381 }
382 else if (tcl_mode)
383 {
384 result = msgdomain_read_tcl (tcl_locale_name, tcl_base_directory);
385 }
386 else
387 {
388 message_list_ty *mlp;
389
390 mlp = message_list_alloc (false);
391 if (optind < argc)
392 {
393 do
394 read_one_file (mlp, argv[optind]);
395 while (++optind < argc);
396 }
397 else
398 read_one_file (mlp, "-");
399
400 result = msgdomain_list_alloc (false);
401 result->item[0]->messages = mlp;
402 }
403
404 /* Sorting the list of messages. */
405 if (sort_by_msgid)
406 msgdomain_list_sort_by_msgid (result);
407
408 /* Write the resulting message list to the given .po file. */
409 msgdomain_list_print (result, output_file, output_syntax, force_po, false);
410
411 /* No problems. */
412 exit (EXIT_SUCCESS);
413 }
414
415
416 /* Display usage information and exit. */
417 static void
usage(int status)418 usage (int status)
419 {
420 if (status != EXIT_SUCCESS)
421 fprintf (stderr, _("Try '%s --help' for more information.\n"),
422 program_name);
423 else
424 {
425 printf (_("\
426 Usage: %s [OPTION] [FILE]...\n\
427 "), program_name);
428 printf ("\n");
429 printf (_("\
430 Convert binary message catalog to Uniforum style .po file.\n\
431 "));
432 printf ("\n");
433 printf (_("\
434 Mandatory arguments to long options are mandatory for short options too.\n"));
435 printf ("\n");
436 printf (_("\
437 Operation mode:\n"));
438 printf (_("\
439 -j, --java Java mode: input is a Java ResourceBundle class\n"));
440 printf (_("\
441 --csharp C# mode: input is a .NET .dll file\n"));
442 printf (_("\
443 --csharp-resources C# resources mode: input is a .NET .resources file\n"));
444 printf (_("\
445 --tcl Tcl mode: input is a tcl/msgcat .msg file\n"));
446 printf ("\n");
447 printf (_("\
448 Input file location:\n"));
449 printf (_("\
450 FILE ... input .mo files\n"));
451 printf (_("\
452 If no input file is given or if it is -, standard input is read.\n"));
453 printf ("\n");
454 printf (_("\
455 Input file location in Java mode:\n"));
456 printf (_("\
457 -r, --resource=RESOURCE resource name\n"));
458 printf (_("\
459 -l, --locale=LOCALE locale name, either language or language_COUNTRY\n"));
460 printf (_("\
461 The class name is determined by appending the locale name to the resource name,\n\
462 separated with an underscore. The class is located using the CLASSPATH.\n\
463 "));
464 printf ("\n");
465 printf (_("\
466 Input file location in C# mode:\n"));
467 printf (_("\
468 -r, --resource=RESOURCE resource name\n"));
469 printf (_("\
470 -l, --locale=LOCALE locale name, either language or language_COUNTRY\n"));
471 printf (_("\
472 -d DIRECTORY base directory for locale dependent .dll files\n"));
473 printf (_("\
474 The -l and -d options are mandatory. The .dll file is located in a\n\
475 subdirectory of the specified directory whose name depends on the locale.\n"));
476 printf ("\n");
477 printf (_("\
478 Input file location in Tcl mode:\n"));
479 printf (_("\
480 -l, --locale=LOCALE locale name, either language or language_COUNTRY\n"));
481 printf (_("\
482 -d DIRECTORY base directory of .msg message catalogs\n"));
483 printf (_("\
484 The -l and -d options are mandatory. The .msg file is located in the\n\
485 specified directory.\n"));
486 printf ("\n");
487 printf (_("\
488 Output file location:\n"));
489 printf (_("\
490 -o, --output-file=FILE write output to specified file\n"));
491 printf (_("\
492 The results are written to standard output if no output file is specified\n\
493 or if it is -.\n"));
494 printf ("\n");
495 printf (_("\
496 Output details:\n"));
497 printf (_("\
498 --color use colors and other text attributes always\n\
499 --color=WHEN use colors and other text attributes if WHEN.\n\
500 WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
501 printf (_("\
502 --style=STYLEFILE specify CSS style rule file for --color\n"));
503 printf (_("\
504 -e, --no-escape do not use C escapes in output (default)\n"));
505 printf (_("\
506 -E, --escape use C escapes in output, no extended chars\n"));
507 printf (_("\
508 --force-po write PO file even if empty\n"));
509 printf (_("\
510 -i, --indent write indented output style\n"));
511 printf (_("\
512 --strict write strict uniforum style\n"));
513 printf (_("\
514 -p, --properties-output write out a Java .properties file\n"));
515 printf (_("\
516 --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
517 printf (_("\
518 -w, --width=NUMBER set output page width\n"));
519 printf (_("\
520 --no-wrap do not break long message lines, longer than\n\
521 the output page width, into several lines\n"));
522 printf (_("\
523 -s, --sort-output generate sorted output\n"));
524 printf ("\n");
525 printf (_("\
526 Informative output:\n"));
527 printf (_("\
528 -h, --help display this help and exit\n"));
529 printf (_("\
530 -V, --version output version information and exit\n"));
531 printf (_("\
532 -v, --verbose increase verbosity level\n"));
533 printf ("\n");
534 /* TRANSLATORS: The first placeholder is the web address of the Savannah
535 project of this package. The second placeholder is the bug-reporting
536 email address for this package. Please add _another line_ saying
537 "Report translation bugs to <...>\n" with the address for translation
538 bugs (typically your translation team's web or email address). */
539 printf(_("\
540 Report bugs in the bug tracker at <%s>\n\
541 or by email to <%s>.\n"),
542 "https://savannah.gnu.org/projects/gettext",
543 "bug-gettext@gnu.org");
544 }
545
546 exit (status);
547 }
548
549
550 static void
read_one_file(message_list_ty * mlp,const char * filename)551 read_one_file (message_list_ty *mlp, const char *filename)
552 {
553 if (csharp_resources_mode)
554 read_resources_file (mlp, filename);
555 else
556 read_mo_file (mlp, filename);
557 }
558