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