• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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