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