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