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.  */
55bool verbose;
56
57/* Java mode input file specification.  */
58static bool java_mode;
59static const char *java_resource_name;
60static const char *java_locale_name;
61
62/* C# mode input file specification.  */
63static bool csharp_mode;
64static const char *csharp_resource_name;
65static const char *csharp_locale_name;
66static const char *csharp_base_directory;
67
68/* C# resources mode input file specification.  */
69static bool csharp_resources_mode;
70
71/* Tcl mode input file specification.  */
72static bool tcl_mode;
73static const char *tcl_locale_name;
74static const char *tcl_base_directory;
75
76/* Force output of PO file even if empty.  */
77static int force_po;
78
79/* Long options.  */
80static 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.  */
107static void usage (int status)
108#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
109	__attribute__ ((noreturn))
110#endif
111;
112static void read_one_file (message_list_ty *mlp, const char *filename);
113
114
115int
116main (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\
253This is free software; see the source for copying conditions.  There is NO\n\
254warranty; 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.  */
408static void
409usage (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 (_("\
417Usage: %s [OPTION] [FILE]...\n\
418"), program_name);
419      printf ("\n");
420      printf (_("\
421Convert binary message catalog to Uniforum style .po file.\n\
422"));
423      printf ("\n");
424      printf (_("\
425Mandatory arguments to long options are mandatory for short options too.\n"));
426      printf ("\n");
427      printf (_("\
428Operation 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 (_("\
439Input file location:\n"));
440      printf (_("\
441  FILE ...                    input .mo files\n"));
442      printf (_("\
443If no input file is given or if it is -, standard input is read.\n"));
444      printf ("\n");
445      printf (_("\
446Input 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 (_("\
452The class name is determined by appending the locale name to the resource name,\n\
453separated with an underscore.  The class is located using the CLASSPATH.\n\
454"));
455      printf ("\n");
456      printf (_("\
457Input 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 (_("\
465The -l and -d options are mandatory.  The .dll file is located in a\n\
466subdirectory of the specified directory whose name depends on the locale.\n"));
467      printf ("\n");
468      printf (_("\
469Input 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 (_("\
475The -l and -d options are mandatory.  The .msg file is located in the\n\
476specified directory.\n"));
477      printf ("\n");
478      printf (_("\
479Output file location:\n"));
480      printf (_("\
481  -o, --output-file=FILE      write output to specified file\n"));
482      printf (_("\
483The results are written to standard output if no output file is specified\n\
484or if it is -.\n"));
485      printf ("\n");
486      printf (_("\
487Output 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 (_("\
511Informative 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
527static void
528read_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