1/* GNU gettext - internationalization aids 2 Copyright (C) 1997-1998, 2000-2007 Free Software Foundation, Inc. 3 4 This file was written by Peter Miller <millerp@canb.auug.org.au> 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 <http://www.gnu.org/licenses/>. */ 18 19#ifdef HAVE_CONFIG_H 20# include <config.h> 21#endif 22 23#include <getopt.h> 24#include <limits.h> 25#include <locale.h> 26#include <stdio.h> 27#include <stdlib.h> 28 29#include "closeout.h" 30#include "dir-list.h" 31#include "str-list.h" 32#include "file-list.h" 33#include "error.h" 34#include "error-progname.h" 35#include "progname.h" 36#include "relocatable.h" 37#include "basename.h" 38#include "message.h" 39#include "read-catalog.h" 40#include "read-po.h" 41#include "read-properties.h" 42#include "read-stringtable.h" 43#include "write-catalog.h" 44#include "write-po.h" 45#include "write-properties.h" 46#include "write-stringtable.h" 47#include "msgl-cat.h" 48#include "propername.h" 49#include "gettext.h" 50 51 52/* A convenience macro. I don't like writing gettext() every time. */ 53#define _(str) gettext (str) 54 55 56/* Force output of PO file even if empty. */ 57static int force_po; 58 59/* Target encoding. */ 60static const char *to_code; 61 62/* Long options. */ 63static const struct option long_options[] = 64{ 65 { "add-location", no_argument, &line_comment, 1 }, 66 { "directory", required_argument, NULL, 'D' }, 67 { "escape", no_argument, NULL, 'E' }, 68 { "files-from", required_argument, NULL, 'f' }, 69 { "force-po", no_argument, &force_po, 1 }, 70 { "help", no_argument, NULL, 'h' }, 71 { "indent", no_argument, NULL, 'i' }, 72 { "no-escape", no_argument, NULL, 'e' }, 73 { "no-location", no_argument, &line_comment, 0 }, 74 { "no-wrap", no_argument, NULL, CHAR_MAX + 2 }, 75 { "omit-header", no_argument, NULL, CHAR_MAX + 1 }, 76 { "output", required_argument, NULL, 'o' }, /* for backward compatibility */ 77 { "output-file", required_argument, NULL, 'o' }, 78 { "properties-input", no_argument, NULL, 'P' }, 79 { "properties-output", no_argument, NULL, 'p' }, 80 { "sort-by-file", no_argument, NULL, 'F' }, 81 { "sort-output", no_argument, NULL, 's' }, 82 { "strict", no_argument, NULL, 'S' }, 83 { "stringtable-input", no_argument, NULL, CHAR_MAX + 3 }, 84 { "stringtable-output", no_argument, NULL, CHAR_MAX + 4 }, 85 { "to-code", required_argument, NULL, 't' }, 86 { "unique", no_argument, NULL, 'u' }, 87 { "version", no_argument, NULL, 'V' }, 88 { "width", required_argument, NULL, 'w', }, 89 { "more-than", required_argument, NULL, '>', }, 90 { "less-than", required_argument, NULL, '<', }, 91 { NULL, 0, NULL, 0 } 92}; 93 94 95/* Forward declaration of local functions. */ 96static void usage (int status) 97#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ > 4) || __GNUC__ > 2) 98 __attribute__ ((noreturn)) 99#endif 100; 101 102 103int 104main (int argc, char *argv[]) 105{ 106 int cnt; 107 int optchar; 108 bool do_help = false; 109 bool do_version = false; 110 msgdomain_list_ty *result; 111 catalog_input_format_ty input_syntax = &input_format_po; 112 catalog_output_format_ty output_syntax = &output_format_po; 113 bool sort_by_msgid = false; 114 bool sort_by_filepos = false; 115 const char *files_from = NULL; 116 string_list_ty *file_list; 117 char *output_file = NULL; 118 119 /* Set program name for messages. */ 120 set_program_name (argv[0]); 121 error_print_progname = maybe_print_progname; 122 123#ifdef HAVE_SETLOCALE 124 /* Set locale via LC_ALL. */ 125 setlocale (LC_ALL, ""); 126#endif 127 128 /* Set the text message domain. */ 129 bindtextdomain (PACKAGE, relocate (LOCALEDIR)); 130 bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR)); 131 textdomain (PACKAGE); 132 133 /* Ensure that write errors on stdout are detected. */ 134 atexit (close_stdout); 135 136 /* Set default values for variables. */ 137 more_than = -1; 138 less_than = -1; 139 use_first = false; 140 141 while ((optchar = getopt_long (argc, argv, "<:>:D:eEf:Fhino:pPst:uVw:", 142 long_options, NULL)) != EOF) 143 switch (optchar) 144 { 145 case '\0': /* Long option. */ 146 break; 147 148 case '>': 149 { 150 int value; 151 char *endp; 152 value = strtol (optarg, &endp, 10); 153 if (endp != optarg) 154 more_than = value; 155 } 156 break; 157 158 case '<': 159 { 160 int value; 161 char *endp; 162 value = strtol (optarg, &endp, 10); 163 if (endp != optarg) 164 less_than = value; 165 } 166 break; 167 168 case 'D': 169 dir_list_append (optarg); 170 break; 171 172 case 'e': 173 message_print_style_escape (false); 174 break; 175 176 case 'E': 177 message_print_style_escape (true); 178 break; 179 180 case 'f': 181 files_from = optarg; 182 break; 183 184 case 'F': 185 sort_by_filepos = true; 186 break; 187 188 case 'h': 189 do_help = true; 190 break; 191 192 case 'i': 193 message_print_style_indent (); 194 break; 195 196 case 'n': 197 line_comment = 1; 198 break; 199 200 case 'o': 201 output_file = optarg; 202 break; 203 204 case 'p': 205 output_syntax = &output_format_properties; 206 break; 207 208 case 'P': 209 input_syntax = &input_format_properties; 210 break; 211 212 case 's': 213 sort_by_msgid = true; 214 break; 215 216 case 'S': 217 message_print_style_uniforum (); 218 break; 219 220 case 't': 221 to_code = optarg; 222 break; 223 224 case 'u': 225 less_than = 2; 226 break; 227 228 case 'V': 229 do_version = true; 230 break; 231 232 case 'w': 233 { 234 int value; 235 char *endp; 236 value = strtol (optarg, &endp, 10); 237 if (endp != optarg) 238 message_page_width_set (value); 239 } 240 break; 241 242 case CHAR_MAX + 1: 243 omit_header = true; 244 break; 245 246 case CHAR_MAX + 2: /* --no-wrap */ 247 message_page_width_ignore (); 248 break; 249 250 case CHAR_MAX + 3: /* --stringtable-input */ 251 input_syntax = &input_format_stringtable; 252 break; 253 254 case CHAR_MAX + 4: /* --stringtable-output */ 255 output_syntax = &output_format_stringtable; 256 break; 257 258 default: 259 usage (EXIT_FAILURE); 260 /* NOTREACHED */ 261 } 262 263 /* Version information requested. */ 264 if (do_version) 265 { 266 printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION); 267 /* xgettext: no-wrap */ 268 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\ 269License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\ 270This is free software: you are free to change and redistribute it.\n\ 271There is NO WARRANTY, to the extent permitted by law.\n\ 272"), 273 "1995-1998, 2000-2007"); 274 printf (_("Written by %s.\n"), proper_name ("Peter Miller")); 275 exit (EXIT_SUCCESS); 276 } 277 278 /* Help is requested. */ 279 if (do_help) 280 usage (EXIT_SUCCESS); 281 282 /* Verify selected options. */ 283 if (!line_comment && sort_by_filepos) 284 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"), 285 "--no-location", "--sort-by-file"); 286 287 if (sort_by_msgid && sort_by_filepos) 288 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"), 289 "--sort-output", "--sort-by-file"); 290 291 /* Determine list of files we have to process. */ 292 if (files_from != NULL) 293 file_list = read_names_from_file (files_from); 294 else 295 file_list = string_list_alloc (); 296 /* Append names from command line. */ 297 for (cnt = optind; cnt < argc; ++cnt) 298 string_list_append_unique (file_list, argv[cnt]); 299 300 /* Test whether sufficient input files were given. */ 301 if (file_list->nitems < 2) 302 { 303 error (EXIT_SUCCESS, 0, _("at least two files must be specified")); 304 usage (EXIT_FAILURE); 305 } 306 307 /* Default the message selection criteria, and check them for sanity. */ 308 if (more_than < 0) 309 more_than = (less_than < 0 ? 1 : 0); 310 if (less_than < 0) 311 less_than = INT_MAX; 312 if (more_than >= less_than || less_than < 2) 313 error (EXIT_FAILURE, 0, 314 _("impossible selection criteria specified (%d < n < %d)"), 315 more_than, less_than); 316 317 /* Read input files, then filter, convert and merge messages. */ 318 allow_duplicates = true; 319 msgcomm_mode = true; 320 result = catenate_msgdomain_list (file_list, input_syntax, to_code); 321 322 string_list_free (file_list); 323 324 /* Sorting the list of messages. */ 325 if (sort_by_filepos) 326 msgdomain_list_sort_by_filepos (result); 327 else if (sort_by_msgid) 328 msgdomain_list_sort_by_msgid (result); 329 330 /* Write the PO file. */ 331 msgdomain_list_print (result, output_file, output_syntax, force_po, false); 332 333 exit (EXIT_SUCCESS); 334} 335 336 337/* Display usage information and exit. */ 338static void 339usage (int status) 340{ 341 if (status != EXIT_SUCCESS) 342 fprintf (stderr, _("Try `%s --help' for more information.\n"), 343 program_name); 344 else 345 { 346 printf (_("\ 347Usage: %s [OPTION] [INPUTFILE]...\n\ 348"), program_name); 349 printf ("\n"); 350 /* xgettext: no-wrap */ 351 printf (_("\ 352Find messages which are common to two or more of the specified PO files.\n\ 353By using the --more-than option, greater commonality may be requested\n\ 354before messages are printed. Conversely, the --less-than option may be\n\ 355used to specify less commonality before messages are printed (i.e.\n\ 356--less-than=2 will only print the unique messages). Translations,\n\ 357comments and extract comments will be preserved, but only from the first\n\ 358PO file to define them. File positions from all PO files will be\n\ 359cumulated.\n\ 360")); 361 printf ("\n"); 362 printf (_("\ 363Mandatory arguments to long options are mandatory for short options too.\n")); 364 printf ("\n"); 365 printf (_("\ 366Input file location:\n")); 367 printf (_("\ 368 INPUTFILE ... input files\n")); 369 printf (_("\ 370 -f, --files-from=FILE get list of input files from FILE\n")); 371 printf (_("\ 372 -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n")); 373 printf (_("\ 374If input file is -, standard input is read.\n")); 375 printf ("\n"); 376 printf (_("\ 377Output file location:\n")); 378 printf (_("\ 379 -o, --output-file=FILE write output to specified file\n")); 380 printf (_("\ 381The results are written to standard output if no output file is specified\n\ 382or if it is -.\n")); 383 printf ("\n"); 384 printf (_("\ 385Message selection:\n")); 386 printf (_("\ 387 -<, --less-than=NUMBER print messages with less than this many\n\ 388 definitions, defaults to infinite if not set\n")); 389 printf (_("\ 390 ->, --more-than=NUMBER print messages with more than this many\n\ 391 definitions, defaults to 1 if not set\n")); 392 printf (_("\ 393 -u, --unique shorthand for --less-than=2, requests\n\ 394 that only unique messages be printed\n")); 395 printf ("\n"); 396 printf (_("\ 397Input file syntax:\n")); 398 printf (_("\ 399 -P, --properties-input input files are in Java .properties syntax\n")); 400 printf (_("\ 401 --stringtable-input input files are in NeXTstep/GNUstep .strings\n\ 402 syntax\n")); 403 printf ("\n"); 404 printf (_("\ 405Output details:\n")); 406 printf (_("\ 407 -e, --no-escape do not use C escapes in output (default)\n")); 408 printf (_("\ 409 -E, --escape use C escapes in output, no extended chars\n")); 410 printf (_("\ 411 --force-po write PO file even if empty\n")); 412 printf (_("\ 413 -i, --indent write the .po file using indented style\n")); 414 printf (_("\ 415 --no-location do not write '#: filename:line' lines\n")); 416 printf (_("\ 417 -n, --add-location generate '#: filename:line' lines (default)\n")); 418 printf (_("\ 419 --strict write out strict Uniforum conforming .po file\n")); 420 printf (_("\ 421 -p, --properties-output write out a Java .properties file\n")); 422 printf (_("\ 423 --stringtable-output write out a NeXTstep/GNUstep .strings file\n")); 424 printf (_("\ 425 -w, --width=NUMBER set output page width\n")); 426 printf (_("\ 427 --no-wrap do not break long message lines, longer than\n\ 428 the output page width, into several lines\n")); 429 printf (_("\ 430 -s, --sort-output generate sorted output\n")); 431 printf (_("\ 432 -F, --sort-by-file sort output by file location\n")); 433 printf (_("\ 434 --omit-header don't write header with `msgid \"\"' entry\n")); 435 printf ("\n"); 436 printf (_("\ 437Informative output:\n")); 438 printf (_("\ 439 -h, --help display this help and exit\n")); 440 printf (_("\ 441 -V, --version output version information and exit\n")); 442 printf ("\n"); 443 /* TRANSLATORS: The placeholder indicates the bug-reporting address 444 for this package. Please add _another line_ saying 445 "Report translation bugs to <...>\n" with the address for translation 446 bugs (typically your translation team's web or email address). */ 447 fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"), 448 stdout); 449 } 450 451 exit (status); 452} 453