1/* Extracts strings from C source file to Uniforum style .po file. 2 Copyright (C) 1995-1998, 2000-2007 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 3 of the License, or 8 (at your option) 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, see <http://www.gnu.org/licenses/>. */ 17 18#ifdef HAVE_CONFIG_H 19# include <config.h> 20#endif 21#include <alloca.h> 22 23#include <ctype.h> 24#include <errno.h> 25#include <getopt.h> 26#include <stdio.h> 27#include <time.h> 28#include <stdlib.h> 29#include <stdbool.h> 30#include <string.h> 31#include <locale.h> 32#include <limits.h> 33 34#include "xgettext.h" 35#include "closeout.h" 36#include "dir-list.h" 37#include "file-list.h" 38#include "str-list.h" 39#include "error.h" 40#include "error-progname.h" 41#include "progname.h" 42#include "relocatable.h" 43#include "basename.h" 44#include "xerror.h" 45#include "xvasprintf.h" 46#include "xsize.h" 47#include "xalloc.h" 48#include "xmalloca.h" 49#include "c-strstr.h" 50#include "xerror.h" 51#include "filename.h" 52#include "c-strcase.h" 53#include "open-catalog.h" 54#include "read-catalog-abstract.h" 55#include "read-po.h" 56#include "message.h" 57#include "po-charset.h" 58#include "msgl-iconv.h" 59#include "msgl-ascii.h" 60#include "po-time.h" 61#include "write-catalog.h" 62#include "write-po.h" 63#include "write-properties.h" 64#include "write-stringtable.h" 65#include "format.h" 66#include "propername.h" 67#include "gettext.h" 68 69/* A convenience macro. I don't like writing gettext() every time. */ 70#define _(str) gettext (str) 71 72 73#include "x-c.h" 74#include "x-po.h" 75#include "x-sh.h" 76#include "x-python.h" 77#include "x-lisp.h" 78#include "x-elisp.h" 79#include "x-librep.h" 80#include "x-scheme.h" 81#include "x-smalltalk.h" 82#include "x-java.h" 83#include "x-properties.h" 84#include "x-csharp.h" 85#include "x-awk.h" 86#include "x-ycp.h" 87#include "x-tcl.h" 88#include "x-perl.h" 89#include "x-php.h" 90#include "x-stringtable.h" 91#include "x-rst.h" 92#include "x-glade.h" 93 94 95/* If nonzero add all comments immediately preceding one of the keywords. */ 96static bool add_all_comments = false; 97 98/* Tag used in comment of prevailing domain. */ 99static char *comment_tag; 100 101/* Name of default domain file. If not set defaults to messages.po. */ 102static const char *default_domain; 103 104/* If called with --debug option the output reflects whether format 105 string recognition is done automatically or forced by the user. */ 106static int do_debug; 107 108/* Content of .po files with symbols to be excluded. */ 109message_list_ty *exclude; 110 111/* Force output of PO file even if empty. */ 112static int force_po; 113 114/* Copyright holder of the output file and the translations. */ 115static const char *copyright_holder = "THE PACKAGE'S COPYRIGHT HOLDER"; 116 117/* Package name. */ 118static const char *package_name = NULL; 119 120/* Package version. */ 121static const char *package_version = NULL; 122 123/* Email address or URL for reports of bugs in msgids. */ 124static const char *msgid_bugs_address = NULL; 125 126/* String used as prefix for msgstr. */ 127static const char *msgstr_prefix; 128 129/* String used as suffix for msgstr. */ 130static const char *msgstr_suffix; 131 132/* Directory in which output files are created. */ 133static char *output_dir; 134 135/* The output syntax: .pot or .properties or .strings. */ 136static catalog_output_format_ty output_syntax = &output_format_po; 137 138/* If nonzero omit header with information about this run. */ 139int xgettext_omit_header; 140 141/* Table of flag_context_list_ty tables. */ 142static flag_context_list_table_ty flag_table_c; 143static flag_context_list_table_ty flag_table_cxx_qt; 144static flag_context_list_table_ty flag_table_cxx_kde; 145static flag_context_list_table_ty flag_table_cxx_boost; 146static flag_context_list_table_ty flag_table_objc; 147static flag_context_list_table_ty flag_table_gcc_internal; 148static flag_context_list_table_ty flag_table_sh; 149static flag_context_list_table_ty flag_table_python; 150static flag_context_list_table_ty flag_table_lisp; 151static flag_context_list_table_ty flag_table_elisp; 152static flag_context_list_table_ty flag_table_librep; 153static flag_context_list_table_ty flag_table_scheme; 154static flag_context_list_table_ty flag_table_java; 155static flag_context_list_table_ty flag_table_csharp; 156static flag_context_list_table_ty flag_table_awk; 157static flag_context_list_table_ty flag_table_ycp; 158static flag_context_list_table_ty flag_table_tcl; 159static flag_context_list_table_ty flag_table_perl; 160static flag_context_list_table_ty flag_table_php; 161 162/* If true, recognize Qt format strings. */ 163static bool recognize_format_qt; 164 165/* If true, recognize KDE format strings. */ 166static bool recognize_format_kde; 167 168/* If true, recognize Boost format strings. */ 169static bool recognize_format_boost; 170 171/* Canonicalized encoding name for all input files. */ 172const char *xgettext_global_source_encoding; 173 174#if HAVE_ICONV 175/* Converter from xgettext_global_source_encoding to UTF-8 (except from 176 ASCII or UTF-8, when this conversion is a no-op). */ 177iconv_t xgettext_global_source_iconv; 178#endif 179 180/* Canonicalized encoding name for the current input file. */ 181const char *xgettext_current_source_encoding; 182 183#if HAVE_ICONV 184/* Converter from xgettext_current_source_encoding to UTF-8 (except from 185 ASCII or UTF-8, when this conversion is a no-op). */ 186iconv_t xgettext_current_source_iconv; 187#endif 188 189/* Long options. */ 190static const struct option long_options[] = 191{ 192 { "add-comments", optional_argument, NULL, 'c' }, 193 { "add-location", no_argument, &line_comment, 1 }, 194 { "boost", no_argument, NULL, CHAR_MAX + 11 }, 195 { "c++", no_argument, NULL, 'C' }, 196 { "copyright-holder", required_argument, NULL, CHAR_MAX + 1 }, 197 { "debug", no_argument, &do_debug, 1 }, 198 { "default-domain", required_argument, NULL, 'd' }, 199 { "directory", required_argument, NULL, 'D' }, 200 { "escape", no_argument, NULL, 'E' }, 201 { "exclude-file", required_argument, NULL, 'x' }, 202 { "extract-all", no_argument, NULL, 'a' }, 203 { "files-from", required_argument, NULL, 'f' }, 204 { "flag", required_argument, NULL, CHAR_MAX + 8 }, 205 { "force-po", no_argument, &force_po, 1 }, 206 { "foreign-user", no_argument, NULL, CHAR_MAX + 2 }, 207 { "from-code", required_argument, NULL, CHAR_MAX + 3 }, 208 { "help", no_argument, NULL, 'h' }, 209 { "indent", no_argument, NULL, 'i' }, 210 { "join-existing", no_argument, NULL, 'j' }, 211 { "kde", no_argument, NULL, CHAR_MAX + 10 }, 212 { "keyword", optional_argument, NULL, 'k' }, 213 { "language", required_argument, NULL, 'L' }, 214 { "msgid-bugs-address", required_argument, NULL, CHAR_MAX + 5 }, 215 { "msgstr-prefix", optional_argument, NULL, 'm' }, 216 { "msgstr-suffix", optional_argument, NULL, 'M' }, 217 { "no-escape", no_argument, NULL, 'e' }, 218 { "no-location", no_argument, &line_comment, 0 }, 219 { "no-wrap", no_argument, NULL, CHAR_MAX + 4 }, 220 { "omit-header", no_argument, &xgettext_omit_header, 1 }, 221 { "output", required_argument, NULL, 'o' }, 222 { "output-dir", required_argument, NULL, 'p' }, 223 { "package-name", required_argument, NULL, CHAR_MAX + 12 }, 224 { "package-version", required_argument, NULL, CHAR_MAX + 13 }, 225 { "properties-output", no_argument, NULL, CHAR_MAX + 6 }, 226 { "qt", no_argument, NULL, CHAR_MAX + 9 }, 227 { "sort-by-file", no_argument, NULL, 'F' }, 228 { "sort-output", no_argument, NULL, 's' }, 229 { "strict", no_argument, NULL, 'S' }, 230 { "string-limit", required_argument, NULL, 'l' }, 231 { "stringtable-output", no_argument, NULL, CHAR_MAX + 7 }, 232 { "trigraphs", no_argument, NULL, 'T' }, 233 { "version", no_argument, NULL, 'V' }, 234 { "width", required_argument, NULL, 'w', }, 235 { NULL, 0, NULL, 0 } 236}; 237 238 239/* The extractors must all be functions returning void and taking three 240 arguments designating the input stream and one message domain list argument 241 in which to add the messages. */ 242typedef void (*extractor_func) (FILE *fp, const char *real_filename, 243 const char *logical_filename, 244 flag_context_list_table_ty *flag_table, 245 msgdomain_list_ty *mdlp); 246 247typedef struct extractor_ty extractor_ty; 248struct extractor_ty 249{ 250 extractor_func func; 251 flag_context_list_table_ty *flag_table; 252 struct formatstring_parser *formatstring_parser1; 253 struct formatstring_parser *formatstring_parser2; 254}; 255 256 257/* Forward declaration of local functions. */ 258static void usage (int status) 259#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ > 4) || __GNUC__ > 2) 260 __attribute__ ((noreturn)) 261#endif 262; 263static void read_exclusion_file (char *file_name); 264static void extract_from_file (const char *file_name, extractor_ty extractor, 265 msgdomain_list_ty *mdlp); 266static message_ty *construct_header (void); 267static void finalize_header (msgdomain_list_ty *mdlp); 268static extractor_ty language_to_extractor (const char *name); 269static const char *extension_to_language (const char *extension); 270 271 272int 273main (int argc, char *argv[]) 274{ 275 int optchar; 276 bool do_help = false; 277 bool do_version = false; 278 msgdomain_list_ty *mdlp; 279 bool join_existing = false; 280 bool no_default_keywords = false; 281 bool some_additional_keywords = false; 282 bool sort_by_msgid = false; 283 bool sort_by_filepos = false; 284 const char *file_name; 285 const char *files_from = NULL; 286 string_list_ty *file_list; 287 char *output_file = NULL; 288 const char *language = NULL; 289 extractor_ty extractor = { NULL, NULL, NULL, NULL }; 290 int cnt; 291 size_t i; 292 293 /* Set program name for messages. */ 294 set_program_name (argv[0]); 295 error_print_progname = maybe_print_progname; 296 297#ifdef HAVE_SETLOCALE 298 /* Set locale via LC_ALL. */ 299 setlocale (LC_ALL, ""); 300#endif 301 302 /* Set the text message domain. */ 303 bindtextdomain (PACKAGE, relocate (LOCALEDIR)); 304 bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR)); 305 textdomain (PACKAGE); 306 307 /* Ensure that write errors on stdout are detected. */ 308 atexit (close_stdout); 309 310 /* Set initial value of variables. */ 311 default_domain = MESSAGE_DOMAIN_DEFAULT; 312 xgettext_global_source_encoding = po_charset_ascii; 313 init_flag_table_c (); 314 init_flag_table_objc (); 315 init_flag_table_gcc_internal (); 316 init_flag_table_sh (); 317 init_flag_table_python (); 318 init_flag_table_lisp (); 319 init_flag_table_elisp (); 320 init_flag_table_librep (); 321 init_flag_table_scheme (); 322 init_flag_table_java (); 323 init_flag_table_csharp (); 324 init_flag_table_awk (); 325 init_flag_table_ycp (); 326 init_flag_table_tcl (); 327 init_flag_table_perl (); 328 init_flag_table_php (); 329 330 while ((optchar = getopt_long (argc, argv, 331 "ac::Cd:D:eEf:Fhijk::l:L:m::M::no:p:sTVw:x:", 332 long_options, NULL)) != EOF) 333 switch (optchar) 334 { 335 case '\0': /* Long option. */ 336 break; 337 case 'a': 338 x_c_extract_all (); 339 x_sh_extract_all (); 340 x_python_extract_all (); 341 x_lisp_extract_all (); 342 x_elisp_extract_all (); 343 x_librep_extract_all (); 344 x_scheme_extract_all (); 345 x_java_extract_all (); 346 x_csharp_extract_all (); 347 x_awk_extract_all (); 348 x_tcl_extract_all (); 349 x_perl_extract_all (); 350 x_php_extract_all (); 351 x_glade_extract_all (); 352 break; 353 case 'c': 354 if (optarg == NULL) 355 { 356 add_all_comments = true; 357 comment_tag = NULL; 358 } 359 else 360 { 361 add_all_comments = false; 362 comment_tag = optarg; 363 /* We ignore leading white space. */ 364 while (isspace ((unsigned char) *comment_tag)) 365 ++comment_tag; 366 } 367 break; 368 case 'C': 369 language = "C++"; 370 break; 371 case 'd': 372 default_domain = optarg; 373 break; 374 case 'D': 375 dir_list_append (optarg); 376 break; 377 case 'e': 378 message_print_style_escape (false); 379 break; 380 case 'E': 381 message_print_style_escape (true); 382 break; 383 case 'f': 384 files_from = optarg; 385 break; 386 case 'F': 387 sort_by_filepos = true; 388 break; 389 case 'h': 390 do_help = true; 391 break; 392 case 'i': 393 message_print_style_indent (); 394 break; 395 case 'j': 396 join_existing = true; 397 break; 398 case 'k': 399 if (optarg != NULL && *optarg == '\0') 400 /* Make "--keyword=" work like "--keyword" and "-k". */ 401 optarg = NULL; 402 x_c_keyword (optarg); 403 x_objc_keyword (optarg); 404 x_sh_keyword (optarg); 405 x_python_keyword (optarg); 406 x_lisp_keyword (optarg); 407 x_elisp_keyword (optarg); 408 x_librep_keyword (optarg); 409 x_scheme_keyword (optarg); 410 x_java_keyword (optarg); 411 x_csharp_keyword (optarg); 412 x_awk_keyword (optarg); 413 x_tcl_keyword (optarg); 414 x_perl_keyword (optarg); 415 x_php_keyword (optarg); 416 x_glade_keyword (optarg); 417 if (optarg == NULL) 418 no_default_keywords = true; 419 else 420 some_additional_keywords = true; 421 break; 422 case 'l': 423 /* Accepted for backward compatibility with 0.10.35. */ 424 break; 425 case 'L': 426 language = optarg; 427 break; 428 case 'm': 429 /* -m takes an optional argument. If none is given "" is assumed. */ 430 msgstr_prefix = optarg == NULL ? "" : optarg; 431 break; 432 case 'M': 433 /* -M takes an optional argument. If none is given "" is assumed. */ 434 msgstr_suffix = optarg == NULL ? "" : optarg; 435 break; 436 case 'n': 437 line_comment = 1; 438 break; 439 case 'o': 440 output_file = optarg; 441 break; 442 case 'p': 443 { 444 size_t len = strlen (optarg); 445 446 if (output_dir != NULL) 447 free (output_dir); 448 449 if (optarg[len - 1] == '/') 450 output_dir = xstrdup (optarg); 451 else 452 output_dir = xasprintf ("%s/", optarg); 453 } 454 break; 455 case 's': 456 sort_by_msgid = true; 457 break; 458 case 'S': 459 message_print_style_uniforum (); 460 break; 461 case 'T': 462 x_c_trigraphs (); 463 break; 464 case 'V': 465 do_version = true; 466 break; 467 case 'w': 468 { 469 int value; 470 char *endp; 471 value = strtol (optarg, &endp, 10); 472 if (endp != optarg) 473 message_page_width_set (value); 474 } 475 break; 476 case 'x': 477 read_exclusion_file (optarg); 478 break; 479 case CHAR_MAX + 1: /* --copyright-holder */ 480 copyright_holder = optarg; 481 break; 482 case CHAR_MAX + 2: /* --foreign-user */ 483 copyright_holder = ""; 484 break; 485 case CHAR_MAX + 3: /* --from-code */ 486 xgettext_global_source_encoding = po_charset_canonicalize (optarg); 487 if (xgettext_global_source_encoding == NULL) 488 xgettext_global_source_encoding = po_charset_ascii; 489 break; 490 case CHAR_MAX + 4: /* --no-wrap */ 491 message_page_width_ignore (); 492 break; 493 case CHAR_MAX + 5: /* --msgid-bugs-address */ 494 msgid_bugs_address = optarg; 495 break; 496 case CHAR_MAX + 6: /* --properties-output */ 497 output_syntax = &output_format_properties; 498 break; 499 case CHAR_MAX + 7: /* --stringtable-output */ 500 output_syntax = &output_format_stringtable; 501 break; 502 case CHAR_MAX + 8: /* --flag */ 503 xgettext_record_flag (optarg); 504 break; 505 case CHAR_MAX + 9: /* --qt */ 506 recognize_format_qt = true; 507 break; 508 case CHAR_MAX + 10: /* --kde */ 509 recognize_format_kde = true; 510 break; 511 case CHAR_MAX + 11: /* --boost */ 512 recognize_format_boost = true; 513 break; 514 case CHAR_MAX + 12: /* --package-name */ 515 package_name = optarg; 516 break; 517 case CHAR_MAX + 13: /* --package-version */ 518 package_version = optarg; 519 break; 520 default: 521 usage (EXIT_FAILURE); 522 /* NOTREACHED */ 523 } 524 525 /* Version information requested. */ 526 if (do_version) 527 { 528 printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION); 529 /* xgettext: no-wrap */ 530 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\ 531License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\ 532This is free software: you are free to change and redistribute it.\n\ 533There is NO WARRANTY, to the extent permitted by law.\n\ 534"), 535 "1995-1998, 2000-2007"); 536 printf (_("Written by %s.\n"), proper_name ("Ulrich Drepper")); 537 exit (EXIT_SUCCESS); 538 } 539 540 /* Help is requested. */ 541 if (do_help) 542 usage (EXIT_SUCCESS); 543 544 /* Verify selected options. */ 545 if (!line_comment && sort_by_filepos) 546 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"), 547 "--no-location", "--sort-by-file"); 548 549 if (sort_by_msgid && sort_by_filepos) 550 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"), 551 "--sort-output", "--sort-by-file"); 552 553 /* We cannot support both Qt and KDE, or Qt and Boost, or KDE and Boost 554 format strings, because there are only two formatstring parsers per 555 language, and formatstring_c is the first one for C++. */ 556 if (recognize_format_qt && recognize_format_kde) 557 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"), 558 "--qt", "--kde"); 559 if (recognize_format_qt && recognize_format_boost) 560 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"), 561 "--qt", "--boost"); 562 if (recognize_format_kde && recognize_format_boost) 563 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"), 564 "--kde", "--boost"); 565 566 if (join_existing && strcmp (default_domain, "-") == 0) 567 error (EXIT_FAILURE, 0, _("\ 568--join-existing cannot be used when output is written to stdout")); 569 570 if (no_default_keywords && !some_additional_keywords) 571 { 572 error (0, 0, _("\ 573xgettext cannot work without keywords to look for")); 574 usage (EXIT_FAILURE); 575 } 576 577 /* Test whether we have some input files given. */ 578 if (files_from == NULL && optind >= argc) 579 { 580 error (EXIT_SUCCESS, 0, _("no input file given")); 581 usage (EXIT_FAILURE); 582 } 583 584 /* Determine extractor from language. */ 585 if (language != NULL) 586 extractor = language_to_extractor (language); 587 588 /* Canonize msgstr prefix/suffix. */ 589 if (msgstr_prefix != NULL && msgstr_suffix == NULL) 590 msgstr_suffix = ""; 591 else if (msgstr_prefix == NULL && msgstr_suffix != NULL) 592 msgstr_prefix = ""; 593 594 /* Default output directory is the current directory. */ 595 if (output_dir == NULL) 596 output_dir = "."; 597 598 /* Construct the name of the output file. If the default domain has 599 the special name "-" we write to stdout. */ 600 if (output_file) 601 { 602 if (IS_ABSOLUTE_PATH (output_file) || strcmp (output_file, "-") == 0) 603 file_name = xstrdup (output_file); 604 else 605 /* Please do NOT add a .po suffix! */ 606 file_name = concatenated_filename (output_dir, output_file, NULL); 607 } 608 else if (strcmp (default_domain, "-") == 0) 609 file_name = "-"; 610 else 611 file_name = concatenated_filename (output_dir, default_domain, ".po"); 612 613 /* Determine list of files we have to process. */ 614 if (files_from != NULL) 615 file_list = read_names_from_file (files_from); 616 else 617 file_list = string_list_alloc (); 618 /* Append names from command line. */ 619 for (cnt = optind; cnt < argc; ++cnt) 620 string_list_append_unique (file_list, argv[cnt]); 621 622 /* Allocate converter from xgettext_global_source_encoding to UTF-8 (except 623 from ASCII or UTF-8, when this conversion is a no-op). */ 624 if (xgettext_global_source_encoding != po_charset_ascii 625 && xgettext_global_source_encoding != po_charset_utf8) 626 { 627#if HAVE_ICONV 628 iconv_t cd; 629 630 /* Avoid glibc-2.1 bug with EUC-KR. */ 631# if (__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) && !defined _LIBICONV_VERSION 632 if (strcmp (xgettext_global_source_encoding, "EUC-KR") == 0) 633 cd = (iconv_t)(-1); 634 else 635# endif 636 cd = iconv_open (po_charset_utf8, xgettext_global_source_encoding); 637 if (cd == (iconv_t)(-1)) 638 error (EXIT_FAILURE, 0, _("\ 639Cannot convert from \"%s\" to \"%s\". %s relies on iconv(), \ 640and iconv() does not support this conversion."), 641 xgettext_global_source_encoding, po_charset_utf8, 642 basename (program_name)); 643 xgettext_global_source_iconv = cd; 644#else 645 error (EXIT_FAILURE, 0, _("\ 646Cannot convert from \"%s\" to \"%s\". %s relies on iconv(). \ 647This version was built without iconv()."), 648 xgettext_global_source_encoding, po_charset_utf8, 649 basename (program_name)); 650#endif 651 } 652 653 /* Allocate a message list to remember all the messages. */ 654 mdlp = msgdomain_list_alloc (true); 655 656 /* Generate a header, so that we know how and when this PO file was 657 created. */ 658 if (!xgettext_omit_header) 659 message_list_append (mdlp->item[0]->messages, construct_header ()); 660 661 /* Read in the old messages, so that we can add to them. */ 662 if (join_existing) 663 { 664 /* Temporarily reset the directory list to empty, because file_name 665 is an output file and therefore should not be searched for. */ 666 void *saved_directory_list = dir_list_save_reset (); 667 extractor_ty po_extractor = { extract_po, NULL, NULL, NULL }; 668 669 extract_from_file (file_name, po_extractor, mdlp); 670 if (!is_ascii_msgdomain_list (mdlp)) 671 mdlp = iconv_msgdomain_list (mdlp, "UTF-8", true, file_name); 672 673 dir_list_restore (saved_directory_list); 674 } 675 676 /* Process all input files. */ 677 for (i = 0; i < file_list->nitems; i++) 678 { 679 const char *filename; 680 extractor_ty this_file_extractor; 681 682 filename = file_list->item[i]; 683 684 if (extractor.func) 685 this_file_extractor = extractor; 686 else 687 { 688 const char *base; 689 char *reduced; 690 const char *extension; 691 const char *language; 692 693 base = strrchr (filename, '/'); 694 if (!base) 695 base = filename; 696 697 reduced = xstrdup (base); 698 /* Remove a trailing ".in" - it's a generic suffix. */ 699 if (strlen (reduced) >= 3 700 && memcmp (reduced + strlen (reduced) - 3, ".in", 3) == 0) 701 reduced[strlen (reduced) - 3] = '\0'; 702 703 /* Work out what the file extension is. */ 704 extension = strrchr (reduced, '.'); 705 if (extension) 706 ++extension; 707 else 708 extension = ""; 709 710 /* Derive the language from the extension, and the extractor 711 function from the language. */ 712 language = extension_to_language (extension); 713 if (language == NULL) 714 { 715 error (0, 0, _("\ 716warning: file `%s' extension `%s' is unknown; will try C"), filename, extension); 717 language = "C"; 718 } 719 this_file_extractor = language_to_extractor (language); 720 721 free (reduced); 722 } 723 724 /* Extract the strings from the file. */ 725 extract_from_file (filename, this_file_extractor, mdlp); 726 } 727 string_list_free (file_list); 728 729 /* Finalize the constructed header. */ 730 if (!xgettext_omit_header) 731 finalize_header (mdlp); 732 733 /* Free the allocated converter. */ 734#if HAVE_ICONV 735 if (xgettext_global_source_encoding != po_charset_ascii 736 && xgettext_global_source_encoding != po_charset_utf8) 737 iconv_close (xgettext_global_source_iconv); 738#endif 739 740 /* Sorting the list of messages. */ 741 if (sort_by_filepos) 742 msgdomain_list_sort_by_filepos (mdlp); 743 else if (sort_by_msgid) 744 msgdomain_list_sort_by_msgid (mdlp); 745 746 /* Write the PO file. */ 747 msgdomain_list_print (mdlp, file_name, output_syntax, force_po, do_debug); 748 749 exit (EXIT_SUCCESS); 750} 751 752 753/* Display usage information and exit. */ 754static void 755usage (int status) 756{ 757 if (status != EXIT_SUCCESS) 758 fprintf (stderr, _("Try `%s --help' for more information.\n"), 759 program_name); 760 else 761 { 762 printf (_("\ 763Usage: %s [OPTION] [INPUTFILE]...\n\ 764"), program_name); 765 printf ("\n"); 766 printf (_("\ 767Extract translatable strings from given input files.\n\ 768")); 769 printf ("\n"); 770 /* xgettext: no-wrap */ 771 printf (_("\ 772Mandatory arguments to long options are mandatory for short options too.\n\ 773Similarly for optional arguments.\n\ 774")); 775 printf ("\n"); 776 printf (_("\ 777Input file location:\n")); 778 printf (_("\ 779 INPUTFILE ... input files\n")); 780 printf (_("\ 781 -f, --files-from=FILE get list of input files from FILE\n")); 782 printf (_("\ 783 -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n")); 784 printf (_("\ 785If input file is -, standard input is read.\n")); 786 printf ("\n"); 787 printf (_("\ 788Output file location:\n")); 789 printf (_("\ 790 -d, --default-domain=NAME use NAME.po for output (instead of messages.po)\n")); 791 printf (_("\ 792 -o, --output=FILE write output to specified file\n")); 793 printf (_("\ 794 -p, --output-dir=DIR output files will be placed in directory DIR\n")); 795 printf (_("\ 796If output file is -, output is written to standard output.\n")); 797 printf ("\n"); 798 printf (_("\ 799Choice of input file language:\n")); 800 printf (_("\ 801 -L, --language=NAME recognise the specified language\n\ 802 (C, C++, ObjectiveC, PO, Shell, Python, Lisp,\n\ 803 EmacsLisp, librep, Scheme, Smalltalk, Java,\n\ 804 JavaProperties, C#, awk, YCP, Tcl, Perl, PHP,\n\ 805 GCC-source, NXStringTable, RST, Glade)\n")); 806 printf (_("\ 807 -C, --c++ shorthand for --language=C++\n")); 808 printf (_("\ 809By default the language is guessed depending on the input file name extension.\n")); 810 printf ("\n"); 811 printf (_("\ 812Input file interpretation:\n")); 813 printf (_("\ 814 --from-code=NAME encoding of input files\n\ 815 (except for Python, Tcl, Glade)\n")); 816 printf (_("\ 817By default the input files are assumed to be in ASCII.\n")); 818 printf ("\n"); 819 printf (_("\ 820Operation mode:\n")); 821 printf (_("\ 822 -j, --join-existing join messages with existing file\n")); 823 printf (_("\ 824 -x, --exclude-file=FILE.po entries from FILE.po are not extracted\n")); 825 printf (_("\ 826 -c, --add-comments[=TAG] place comment block with TAG (or those\n\ 827 preceding keyword lines) in output file\n")); 828 printf ("\n"); 829 printf (_("\ 830Language specific options:\n")); 831 printf (_("\ 832 -a, --extract-all extract all strings\n")); 833 printf (_("\ 834 (only languages C, C++, ObjectiveC, Shell,\n\ 835 Python, Lisp, EmacsLisp, librep, Scheme, Java,\n\ 836 C#, awk, Tcl, Perl, PHP, GCC-source, Glade)\n")); 837 printf (_("\ 838 -k, --keyword[=WORD] additional keyword to be looked for (without\n\ 839 WORD means not to use default keywords)\n")); 840 printf (_("\ 841 (only languages C, C++, ObjectiveC, Shell,\n\ 842 Python, Lisp, EmacsLisp, librep, Scheme, Java,\n\ 843 C#, awk, Tcl, Perl, PHP, GCC-source, Glade)\n")); 844 printf (_("\ 845 --flag=WORD:ARG:FLAG additional flag for strings inside the argument\n\ 846 number ARG of keyword WORD\n")); 847 printf (_("\ 848 (only languages C, C++, ObjectiveC, Shell,\n\ 849 Python, Lisp, EmacsLisp, librep, Scheme, Java,\n\ 850 C#, awk, YCP, Tcl, Perl, PHP, GCC-source)\n")); 851 printf (_("\ 852 -T, --trigraphs understand ANSI C trigraphs for input\n")); 853 printf (_("\ 854 (only languages C, C++, ObjectiveC)\n")); 855 printf (_("\ 856 --qt recognize Qt format strings\n")); 857 printf (_("\ 858 (only language C++)\n")); 859 printf (_("\ 860 --kde recognize KDE 4 format strings\n")); 861 printf (_("\ 862 (only language C++)\n")); 863 printf (_("\ 864 --boost recognize Boost format strings\n")); 865 printf (_("\ 866 (only language C++)\n")); 867 printf (_("\ 868 --debug more detailed formatstring recognition result\n")); 869 printf ("\n"); 870 printf (_("\ 871Output details:\n")); 872 printf (_("\ 873 -e, --no-escape do not use C escapes in output (default)\n")); 874 printf (_("\ 875 -E, --escape use C escapes in output, no extended chars\n")); 876 printf (_("\ 877 --force-po write PO file even if empty\n")); 878 printf (_("\ 879 -i, --indent write the .po file using indented style\n")); 880 printf (_("\ 881 --no-location do not write '#: filename:line' lines\n")); 882 printf (_("\ 883 -n, --add-location generate '#: filename:line' lines (default)\n")); 884 printf (_("\ 885 --strict write out strict Uniforum conforming .po file\n")); 886 printf (_("\ 887 --properties-output write out a Java .properties file\n")); 888 printf (_("\ 889 --stringtable-output write out a NeXTstep/GNUstep .strings file\n")); 890 printf (_("\ 891 -w, --width=NUMBER set output page width\n")); 892 printf (_("\ 893 --no-wrap do not break long message lines, longer than\n\ 894 the output page width, into several lines\n")); 895 printf (_("\ 896 -s, --sort-output generate sorted output\n")); 897 printf (_("\ 898 -F, --sort-by-file sort output by file location\n")); 899 printf (_("\ 900 --omit-header don't write header with `msgid \"\"' entry\n")); 901 printf (_("\ 902 --copyright-holder=STRING set copyright holder in output\n")); 903 printf (_("\ 904 --foreign-user omit FSF copyright in output for foreign user\n")); 905 printf (_("\ 906 --package-name=PACKAGE set package name in output\n")); 907 printf (_("\ 908 --package-version=VERSION set package version in output\n")); 909 printf (_("\ 910 --msgid-bugs-address=EMAIL@ADDRESS set report address for msgid bugs\n")); 911 printf (_("\ 912 -m, --msgstr-prefix[=STRING] use STRING or \"\" as prefix for msgstr entries\n")); 913 printf (_("\ 914 -M, --msgstr-suffix[=STRING] use STRING or \"\" as suffix for msgstr entries\n")); 915 printf ("\n"); 916 printf (_("\ 917Informative output:\n")); 918 printf (_("\ 919 -h, --help display this help and exit\n")); 920 printf (_("\ 921 -V, --version output version information and exit\n")); 922 printf ("\n"); 923 /* TRANSLATORS: The placeholder indicates the bug-reporting address 924 for this package. Please add _another line_ saying 925 "Report translation bugs to <...>\n" with the address for translation 926 bugs (typically your translation team's web or email address). */ 927 fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"), 928 stdout); 929 } 930 931 exit (status); 932} 933 934 935static void 936exclude_directive_domain (abstract_catalog_reader_ty *pop, char *name) 937{ 938 po_gram_error_at_line (&gram_pos, 939 _("this file may not contain domain directives")); 940} 941 942 943static void 944exclude_directive_message (abstract_catalog_reader_ty *pop, 945 char *msgctxt, 946 char *msgid, 947 lex_pos_ty *msgid_pos, 948 char *msgid_plural, 949 char *msgstr, size_t msgstr_len, 950 lex_pos_ty *msgstr_pos, 951 char *prev_msgctxt, 952 char *prev_msgid, 953 char *prev_msgid_plural, 954 bool force_fuzzy, bool obsolete) 955{ 956 message_ty *mp; 957 958 /* See if this message ID has been seen before. */ 959 if (exclude == NULL) 960 exclude = message_list_alloc (true); 961 mp = message_list_search (exclude, msgctxt, msgid); 962 if (mp != NULL) 963 free (msgid); 964 else 965 { 966 mp = message_alloc (msgctxt, msgid, msgid_plural, "", 1, msgstr_pos); 967 /* Do not free msgid. */ 968 message_list_append (exclude, mp); 969 } 970 971 /* All we care about is the msgid. Throw the msgstr away. 972 Don't even check for duplicate msgids. */ 973 free (msgstr); 974} 975 976 977/* So that the one parser can be used for multiple programs, and also 978 use good data hiding and encapsulation practices, an object 979 oriented approach has been taken. An object instance is allocated, 980 and all actions resulting from the parse will be through 981 invocations of method functions of that object. */ 982 983static abstract_catalog_reader_class_ty exclude_methods = 984{ 985 sizeof (abstract_catalog_reader_ty), 986 NULL, /* constructor */ 987 NULL, /* destructor */ 988 NULL, /* parse_brief */ 989 NULL, /* parse_debrief */ 990 exclude_directive_domain, 991 exclude_directive_message, 992 NULL, /* comment */ 993 NULL, /* comment_dot */ 994 NULL, /* comment_filepos */ 995 NULL, /* comment_special */ 996}; 997 998 999static void 1000read_exclusion_file (char *filename) 1001{ 1002 char *real_filename; 1003 FILE *fp = open_catalog_file (filename, &real_filename, true); 1004 abstract_catalog_reader_ty *pop; 1005 1006 pop = catalog_reader_alloc (&exclude_methods); 1007 catalog_reader_parse (pop, fp, real_filename, filename, &input_format_po); 1008 catalog_reader_free (pop); 1009 1010 if (fp != stdin) 1011 fclose (fp); 1012} 1013 1014 1015void 1016split_keywordspec (const char *spec, 1017 const char **endp, struct callshape *shapep) 1018{ 1019 const char *p; 1020 int argnum1 = 0; 1021 int argnum2 = 0; 1022 int argnumc = 0; 1023 bool argnum1_glib_context = false; 1024 bool argnum2_glib_context = false; 1025 int argtotal = 0; 1026 string_list_ty xcomments; 1027 1028 string_list_init (&xcomments); 1029 1030 /* Start parsing from the end. */ 1031 p = spec + strlen (spec); 1032 while (p > spec) 1033 { 1034 if (isdigit ((unsigned char) p[-1]) 1035 || ((p[-1] == 'c' || p[-1] == 'g' || p[-1] == 't') 1036 && p - 1 > spec && isdigit ((unsigned char) p[-2]))) 1037 { 1038 bool contextp = (p[-1] == 'c'); 1039 bool glibp = (p[-1] == 'g'); 1040 bool totalp = (p[-1] == 't'); 1041 1042 do 1043 p--; 1044 while (p > spec && isdigit ((unsigned char) p[-1])); 1045 1046 if (p > spec && (p[-1] == ',' || p[-1] == ':')) 1047 { 1048 char *dummy; 1049 int arg = strtol (p, &dummy, 10); 1050 1051 if (contextp) 1052 { 1053 if (argnumc != 0) 1054 /* Only one context argument can be given. */ 1055 break; 1056 argnumc = arg; 1057 } 1058 else if (totalp) 1059 { 1060 if (argtotal != 0) 1061 /* Only one total number of arguments can be given. */ 1062 break; 1063 argtotal = arg; 1064 } 1065 else 1066 { 1067 if (argnum2 != 0) 1068 /* At most two normal arguments can be given. */ 1069 break; 1070 argnum2 = argnum1; 1071 argnum2_glib_context = argnum1_glib_context; 1072 argnum1 = arg; 1073 argnum1_glib_context = glibp; 1074 } 1075 } 1076 else 1077 break; 1078 } 1079 else if (p[-1] == '"') 1080 { 1081 const char *xcomment_end; 1082 1083 p--; 1084 xcomment_end = p; 1085 1086 while (p > spec && p[-1] != '"') 1087 p--; 1088 1089 if (p > spec /* && p[-1] == '"' */) 1090 { 1091 const char *xcomment_start; 1092 1093 xcomment_start = p; 1094 p--; 1095 if (p > spec && (p[-1] == ',' || p[-1] == ':')) 1096 { 1097 size_t xcomment_len = xcomment_end - xcomment_start; 1098 char *xcomment = XNMALLOC (xcomment_len + 1, char); 1099 1100 memcpy (xcomment, xcomment_start, xcomment_len); 1101 xcomment[xcomment_len] = '\0'; 1102 string_list_append (&xcomments, xcomment); 1103 } 1104 else 1105 break; 1106 } 1107 else 1108 break; 1109 } 1110 else 1111 break; 1112 1113 /* Here an element of the comma-separated list has been parsed. */ 1114 if (!(p > spec && (p[-1] == ',' || p[-1] == ':'))) 1115 abort (); 1116 p--; 1117 if (*p == ':') 1118 { 1119 size_t i; 1120 1121 if (argnum1 == 0 && argnum2 == 0) 1122 /* At least one non-context argument must be given. */ 1123 break; 1124 if (argnumc != 0 1125 && (argnum1_glib_context || argnum2_glib_context)) 1126 /* Incompatible ways to specify the context. */ 1127 break; 1128 *endp = p; 1129 shapep->argnum1 = (argnum1 > 0 ? argnum1 : 1); 1130 shapep->argnum2 = argnum2; 1131 shapep->argnumc = argnumc; 1132 shapep->argnum1_glib_context = argnum1_glib_context; 1133 shapep->argnum2_glib_context = argnum2_glib_context; 1134 shapep->argtotal = argtotal; 1135 /* Reverse the order of the xcomments. */ 1136 string_list_init (&shapep->xcomments); 1137 for (i = xcomments.nitems; i > 0; ) 1138 string_list_append (&shapep->xcomments, xcomments.item[--i]); 1139 string_list_destroy (&xcomments); 1140 return; 1141 } 1142 } 1143 1144 /* Couldn't parse the desired syntax. */ 1145 *endp = spec + strlen (spec); 1146 shapep->argnum1 = 1; 1147 shapep->argnum2 = 0; 1148 shapep->argnumc = 0; 1149 shapep->argnum1_glib_context = false; 1150 shapep->argnum2_glib_context = false; 1151 shapep->argtotal = 0; 1152 string_list_init (&shapep->xcomments); 1153 string_list_destroy (&xcomments); 1154} 1155 1156 1157void 1158insert_keyword_callshape (hash_table *table, 1159 const char *keyword, size_t keyword_len, 1160 const struct callshape *shape) 1161{ 1162 void *old_value; 1163 1164 if (hash_find_entry (table, keyword, keyword_len, &old_value)) 1165 { 1166 /* Create a one-element 'struct callshapes'. */ 1167 struct callshapes *shapes = XMALLOC (struct callshapes); 1168 shapes->nshapes = 1; 1169 shapes->shapes[0] = *shape; 1170 keyword = 1171 (const char *) hash_insert_entry (table, keyword, keyword_len, shapes); 1172 if (keyword == NULL) 1173 abort (); 1174 shapes->keyword = keyword; 1175 shapes->keyword_len = keyword_len; 1176 } 1177 else 1178 { 1179 /* Found a 'struct callshapes'. See whether it already contains the 1180 desired shape. */ 1181 struct callshapes *old_shapes = (struct callshapes *) old_value; 1182 bool found; 1183 size_t i; 1184 1185 found = false; 1186 for (i = 0; i < old_shapes->nshapes; i++) 1187 if (old_shapes->shapes[i].argnum1 == shape->argnum1 1188 && old_shapes->shapes[i].argnum2 == shape->argnum2 1189 && old_shapes->shapes[i].argnumc == shape->argnumc 1190 && old_shapes->shapes[i].argnum1_glib_context 1191 == shape->argnum1_glib_context 1192 && old_shapes->shapes[i].argnum2_glib_context 1193 == shape->argnum2_glib_context 1194 && old_shapes->shapes[i].argtotal == shape->argtotal) 1195 { 1196 old_shapes->shapes[i].xcomments = shape->xcomments; 1197 found = true; 1198 break; 1199 } 1200 1201 if (!found) 1202 { 1203 /* Replace the existing 'struct callshapes' with a new one. */ 1204 struct callshapes *shapes = 1205 (struct callshapes *) 1206 xmalloc (xsum (sizeof (struct callshapes), 1207 xtimes (old_shapes->nshapes, 1208 sizeof (struct callshape)))); 1209 1210 shapes->keyword = old_shapes->keyword; 1211 shapes->keyword_len = old_shapes->keyword_len; 1212 shapes->nshapes = old_shapes->nshapes + 1; 1213 for (i = 0; i < old_shapes->nshapes; i++) 1214 shapes->shapes[i] = old_shapes->shapes[i]; 1215 shapes->shapes[i] = *shape; 1216 if (hash_set_value (table, keyword, keyword_len, shapes)) 1217 abort (); 1218 free (old_shapes); 1219 } 1220 } 1221} 1222 1223 1224/* Null context. */ 1225flag_context_ty null_context = { undecided, false, undecided, false }; 1226 1227/* Transparent context. */ 1228flag_context_ty passthrough_context = { undecided, true, undecided, true }; 1229 1230 1231flag_context_ty 1232inherited_context (flag_context_ty outer_context, 1233 flag_context_ty modifier_context) 1234{ 1235 flag_context_ty result = modifier_context; 1236 1237 if (result.pass_format1) 1238 { 1239 result.is_format1 = outer_context.is_format1; 1240 result.pass_format1 = false; 1241 } 1242 if (result.pass_format2) 1243 { 1244 result.is_format2 = outer_context.is_format2; 1245 result.pass_format2 = false; 1246 } 1247 return result; 1248} 1249 1250 1251/* Null context list iterator. */ 1252flag_context_list_iterator_ty null_context_list_iterator = { 1, NULL }; 1253 1254/* Transparent context list iterator. */ 1255static flag_context_list_ty passthrough_context_circular_list = 1256 { 1257 1, 1258 { undecided, true, undecided, true }, 1259 &passthrough_context_circular_list 1260 }; 1261flag_context_list_iterator_ty passthrough_context_list_iterator = 1262 { 1263 1, 1264 &passthrough_context_circular_list 1265 }; 1266 1267 1268flag_context_list_iterator_ty 1269flag_context_list_iterator (flag_context_list_ty *list) 1270{ 1271 flag_context_list_iterator_ty result; 1272 1273 result.argnum = 1; 1274 result.head = list; 1275 return result; 1276} 1277 1278 1279flag_context_ty 1280flag_context_list_iterator_advance (flag_context_list_iterator_ty *iter) 1281{ 1282 if (iter->head == NULL) 1283 return null_context; 1284 if (iter->argnum == iter->head->argnum) 1285 { 1286 flag_context_ty result = iter->head->flags; 1287 1288 /* Special casing of circular list. */ 1289 if (iter->head != iter->head->next) 1290 { 1291 iter->head = iter->head->next; 1292 iter->argnum++; 1293 } 1294 1295 return result; 1296 } 1297 else 1298 { 1299 iter->argnum++; 1300 return null_context; 1301 } 1302} 1303 1304 1305flag_context_list_ty * 1306flag_context_list_table_lookup (flag_context_list_table_ty *flag_table, 1307 const void *key, size_t keylen) 1308{ 1309 void *entry; 1310 1311 if (flag_table->table != NULL 1312 && hash_find_entry (flag_table, key, keylen, &entry) == 0) 1313 return (flag_context_list_ty *) entry; 1314 else 1315 return NULL; 1316} 1317 1318 1319static void 1320flag_context_list_table_insert (flag_context_list_table_ty *table, 1321 unsigned int index, 1322 const char *name_start, const char *name_end, 1323 int argnum, enum is_format value, bool pass) 1324{ 1325 char *allocated_name = NULL; 1326 1327 if (table == &flag_table_lisp) 1328 { 1329 /* Convert NAME to upper case. */ 1330 size_t name_len = name_end - name_start; 1331 char *name = allocated_name = (char *) xmalloca (name_len); 1332 size_t i; 1333 1334 for (i = 0; i < name_len; i++) 1335 name[i] = (name_start[i] >= 'a' && name_start[i] <= 'z' 1336 ? name_start[i] - 'a' + 'A' 1337 : name_start[i]); 1338 name_start = name; 1339 name_end = name + name_len; 1340 } 1341 else if (table == &flag_table_tcl) 1342 { 1343 /* Remove redundant "::" prefix. */ 1344 if (name_end - name_start > 2 1345 && name_start[0] == ':' && name_start[1] == ':') 1346 name_start += 2; 1347 } 1348 1349 /* Insert the pair (VALUE, PASS) at INDEX in the element numbered ARGNUM 1350 of the list corresponding to NAME in the TABLE. */ 1351 if (table->table == NULL) 1352 hash_init (table, 100); 1353 { 1354 void *entry; 1355 1356 if (hash_find_entry (table, name_start, name_end - name_start, &entry) != 0) 1357 { 1358 /* Create new hash table entry. */ 1359 flag_context_list_ty *list = XMALLOC (flag_context_list_ty); 1360 list->argnum = argnum; 1361 memset (&list->flags, '\0', sizeof (list->flags)); 1362 switch (index) 1363 { 1364 case 0: 1365 list->flags.is_format1 = value; 1366 list->flags.pass_format1 = pass; 1367 break; 1368 case 1: 1369 list->flags.is_format2 = value; 1370 list->flags.pass_format2 = pass; 1371 break; 1372 default: 1373 abort (); 1374 } 1375 list->next = NULL; 1376 hash_insert_entry (table, name_start, name_end - name_start, list); 1377 } 1378 else 1379 { 1380 flag_context_list_ty *list = (flag_context_list_ty *)entry; 1381 flag_context_list_ty **lastp = NULL; 1382 1383 while (list != NULL && list->argnum < argnum) 1384 { 1385 lastp = &list->next; 1386 list = *lastp; 1387 } 1388 if (list != NULL && list->argnum == argnum) 1389 { 1390 /* Add this flag to the current argument number. */ 1391 switch (index) 1392 { 1393 case 0: 1394 list->flags.is_format1 = value; 1395 list->flags.pass_format1 = pass; 1396 break; 1397 case 1: 1398 list->flags.is_format2 = value; 1399 list->flags.pass_format2 = pass; 1400 break; 1401 default: 1402 abort (); 1403 } 1404 } 1405 else if (lastp != NULL) 1406 { 1407 /* Add a new list entry for this argument number. */ 1408 list = XMALLOC (flag_context_list_ty); 1409 list->argnum = argnum; 1410 memset (&list->flags, '\0', sizeof (list->flags)); 1411 switch (index) 1412 { 1413 case 0: 1414 list->flags.is_format1 = value; 1415 list->flags.pass_format1 = pass; 1416 break; 1417 case 1: 1418 list->flags.is_format2 = value; 1419 list->flags.pass_format2 = pass; 1420 break; 1421 default: 1422 abort (); 1423 } 1424 list->next = *lastp; 1425 *lastp = list; 1426 } 1427 else 1428 { 1429 /* Add a new list entry for this argument number, at the beginning 1430 of the list. Since we don't have an API for replacing the 1431 value of a key in the hash table, we have to copy the first 1432 list element. */ 1433 flag_context_list_ty *copy = XMALLOC (flag_context_list_ty); 1434 *copy = *list; 1435 1436 list->argnum = argnum; 1437 memset (&list->flags, '\0', sizeof (list->flags)); 1438 switch (index) 1439 { 1440 case 0: 1441 list->flags.is_format1 = value; 1442 list->flags.pass_format1 = pass; 1443 break; 1444 case 1: 1445 list->flags.is_format2 = value; 1446 list->flags.pass_format2 = pass; 1447 break; 1448 default: 1449 abort (); 1450 } 1451 list->next = copy; 1452 } 1453 } 1454 } 1455 1456 if (allocated_name != NULL) 1457 freea (allocated_name); 1458} 1459 1460 1461void 1462xgettext_record_flag (const char *optionstring) 1463{ 1464 /* Check the string has at least two colons. (Colons in the name are 1465 allowed, needed for the Lisp and the Tcl backends.) */ 1466 const char *colon1; 1467 const char *colon2; 1468 1469 for (colon2 = optionstring + strlen (optionstring); ; ) 1470 { 1471 if (colon2 == optionstring) 1472 goto err; 1473 colon2--; 1474 if (*colon2 == ':') 1475 break; 1476 } 1477 for (colon1 = colon2; ; ) 1478 { 1479 if (colon1 == optionstring) 1480 goto err; 1481 colon1--; 1482 if (*colon1 == ':') 1483 break; 1484 } 1485 { 1486 const char *name_start = optionstring; 1487 const char *name_end = colon1; 1488 const char *argnum_start = colon1 + 1; 1489 const char *argnum_end = colon2; 1490 const char *flag = colon2 + 1; 1491 int argnum; 1492 1493 /* Check the parts' syntax. */ 1494 if (name_end == name_start) 1495 goto err; 1496 if (argnum_end == argnum_start) 1497 goto err; 1498 { 1499 char *endp; 1500 argnum = strtol (argnum_start, &endp, 10); 1501 if (endp != argnum_end) 1502 goto err; 1503 } 1504 if (argnum <= 0) 1505 goto err; 1506 1507 /* Analyze the flag part. */ 1508 { 1509 bool pass; 1510 1511 pass = false; 1512 if (strlen (flag) >= 5 && memcmp (flag, "pass-", 5) == 0) 1513 { 1514 pass = true; 1515 flag += 5; 1516 } 1517 1518 /* Unlike po_parse_comment_special(), we don't accept "fuzzy" or "wrap" 1519 here - it has no sense. */ 1520 if (strlen (flag) >= 7 1521 && memcmp (flag + strlen (flag) - 7, "-format", 7) == 0) 1522 { 1523 const char *p; 1524 size_t n; 1525 enum is_format value; 1526 size_t type; 1527 1528 p = flag; 1529 n = strlen (flag) - 7; 1530 1531 if (n >= 3 && memcmp (p, "no-", 3) == 0) 1532 { 1533 p += 3; 1534 n -= 3; 1535 value = no; 1536 } 1537 else if (n >= 9 && memcmp (p, "possible-", 9) == 0) 1538 { 1539 p += 9; 1540 n -= 9; 1541 value = possible; 1542 } 1543 else if (n >= 11 && memcmp (p, "impossible-", 11) == 0) 1544 { 1545 p += 11; 1546 n -= 11; 1547 value = impossible; 1548 } 1549 else 1550 value = yes_according_to_context; 1551 1552 for (type = 0; type < NFORMATS; type++) 1553 if (strlen (format_language[type]) == n 1554 && memcmp (format_language[type], p, n) == 0) 1555 { 1556 switch (type) 1557 { 1558 case format_c: 1559 flag_context_list_table_insert (&flag_table_c, 0, 1560 name_start, name_end, 1561 argnum, value, pass); 1562 flag_context_list_table_insert (&flag_table_cxx_qt, 0, 1563 name_start, name_end, 1564 argnum, value, pass); 1565 flag_context_list_table_insert (&flag_table_cxx_kde, 0, 1566 name_start, name_end, 1567 argnum, value, pass); 1568 flag_context_list_table_insert (&flag_table_cxx_boost, 0, 1569 name_start, name_end, 1570 argnum, value, pass); 1571 flag_context_list_table_insert (&flag_table_objc, 0, 1572 name_start, name_end, 1573 argnum, value, pass); 1574 break; 1575 case format_objc: 1576 flag_context_list_table_insert (&flag_table_objc, 1, 1577 name_start, name_end, 1578 argnum, value, pass); 1579 break; 1580 case format_sh: 1581 flag_context_list_table_insert (&flag_table_sh, 0, 1582 name_start, name_end, 1583 argnum, value, pass); 1584 break; 1585 case format_python: 1586 flag_context_list_table_insert (&flag_table_python, 0, 1587 name_start, name_end, 1588 argnum, value, pass); 1589 break; 1590 case format_lisp: 1591 flag_context_list_table_insert (&flag_table_lisp, 0, 1592 name_start, name_end, 1593 argnum, value, pass); 1594 break; 1595 case format_elisp: 1596 flag_context_list_table_insert (&flag_table_elisp, 0, 1597 name_start, name_end, 1598 argnum, value, pass); 1599 break; 1600 case format_librep: 1601 flag_context_list_table_insert (&flag_table_librep, 0, 1602 name_start, name_end, 1603 argnum, value, pass); 1604 break; 1605 case format_scheme: 1606 flag_context_list_table_insert (&flag_table_scheme, 0, 1607 name_start, name_end, 1608 argnum, value, pass); 1609 break; 1610 case format_smalltalk: 1611 break; 1612 case format_java: 1613 flag_context_list_table_insert (&flag_table_java, 0, 1614 name_start, name_end, 1615 argnum, value, pass); 1616 break; 1617 case format_csharp: 1618 flag_context_list_table_insert (&flag_table_csharp, 0, 1619 name_start, name_end, 1620 argnum, value, pass); 1621 break; 1622 case format_awk: 1623 flag_context_list_table_insert (&flag_table_awk, 0, 1624 name_start, name_end, 1625 argnum, value, pass); 1626 break; 1627 case format_pascal: 1628 break; 1629 case format_ycp: 1630 flag_context_list_table_insert (&flag_table_ycp, 0, 1631 name_start, name_end, 1632 argnum, value, pass); 1633 break; 1634 case format_tcl: 1635 flag_context_list_table_insert (&flag_table_tcl, 0, 1636 name_start, name_end, 1637 argnum, value, pass); 1638 break; 1639 case format_perl: 1640 flag_context_list_table_insert (&flag_table_perl, 0, 1641 name_start, name_end, 1642 argnum, value, pass); 1643 break; 1644 case format_perl_brace: 1645 flag_context_list_table_insert (&flag_table_perl, 1, 1646 name_start, name_end, 1647 argnum, value, pass); 1648 break; 1649 case format_php: 1650 flag_context_list_table_insert (&flag_table_php, 0, 1651 name_start, name_end, 1652 argnum, value, pass); 1653 break; 1654 case format_gcc_internal: 1655 flag_context_list_table_insert (&flag_table_gcc_internal, 0, 1656 name_start, name_end, 1657 argnum, value, pass); 1658 break; 1659 case format_qt: 1660 flag_context_list_table_insert (&flag_table_cxx_qt, 1, 1661 name_start, name_end, 1662 argnum, value, pass); 1663 break; 1664 case format_kde: 1665 flag_context_list_table_insert (&flag_table_cxx_kde, 1, 1666 name_start, name_end, 1667 argnum, value, pass); 1668 break; 1669 case format_boost: 1670 flag_context_list_table_insert (&flag_table_cxx_boost, 1, 1671 name_start, name_end, 1672 argnum, value, pass); 1673 break; 1674 default: 1675 abort (); 1676 } 1677 return; 1678 } 1679 /* If the flag is not among the valid values, the optionstring is 1680 invalid. */ 1681 } 1682 } 1683 } 1684 1685err: 1686 error (EXIT_FAILURE, 0, _("\ 1687A --flag argument doesn't have the <keyword>:<argnum>:[pass-]<flag> syntax: %s"), 1688 optionstring); 1689} 1690 1691 1692/* Comment handling: There is a list of automatic comments that may be appended 1693 to the next message. Used by remember_a_message(). */ 1694 1695static string_list_ty *comment; 1696 1697static void 1698xgettext_comment_add (const char *str) 1699{ 1700 if (comment == NULL) 1701 comment = string_list_alloc (); 1702 string_list_append (comment, str); 1703} 1704 1705static const char * 1706xgettext_comment (size_t n) 1707{ 1708 if (comment == NULL || n >= comment->nitems) 1709 return NULL; 1710 return comment->item[n]; 1711} 1712 1713static void 1714xgettext_comment_reset () 1715{ 1716 if (comment != NULL) 1717 { 1718 string_list_free (comment); 1719 comment = NULL; 1720 } 1721} 1722 1723 1724refcounted_string_list_ty *savable_comment; 1725 1726void 1727savable_comment_add (const char *str) 1728{ 1729 if (savable_comment == NULL) 1730 { 1731 savable_comment = XMALLOC (refcounted_string_list_ty); 1732 savable_comment->refcount = 1; 1733 string_list_init (&savable_comment->contents); 1734 } 1735 else if (savable_comment->refcount > 1) 1736 { 1737 /* Unshare the list by making copies. */ 1738 struct string_list_ty *oldcontents; 1739 size_t i; 1740 1741 savable_comment->refcount--; 1742 oldcontents = &savable_comment->contents; 1743 1744 savable_comment = XMALLOC (refcounted_string_list_ty); 1745 savable_comment->refcount = 1; 1746 string_list_init (&savable_comment->contents); 1747 for (i = 0; i < oldcontents->nitems; i++) 1748 string_list_append (&savable_comment->contents, oldcontents->item[i]); 1749 } 1750 string_list_append (&savable_comment->contents, str); 1751} 1752 1753void 1754savable_comment_reset () 1755{ 1756 drop_reference (savable_comment); 1757 savable_comment = NULL; 1758} 1759 1760static void 1761savable_comment_to_xgettext_comment (refcounted_string_list_ty *rslp) 1762{ 1763 xgettext_comment_reset (); 1764 if (rslp != NULL) 1765 { 1766 size_t i; 1767 1768 for (i = 0; i < rslp->contents.nitems; i++) 1769 xgettext_comment_add (rslp->contents.item[i]); 1770 } 1771} 1772 1773 1774 1775static FILE * 1776xgettext_open (const char *fn, 1777 char **logical_file_name_p, char **real_file_name_p) 1778{ 1779 FILE *fp; 1780 char *new_name; 1781 char *logical_file_name; 1782 1783 if (strcmp (fn, "-") == 0) 1784 { 1785 new_name = xstrdup (_("standard input")); 1786 logical_file_name = xstrdup (new_name); 1787 fp = stdin; 1788 } 1789 else if (IS_ABSOLUTE_PATH (fn)) 1790 { 1791 new_name = xstrdup (fn); 1792 fp = fopen (fn, "r"); 1793 if (fp == NULL) 1794 error (EXIT_FAILURE, errno, _("\ 1795error while opening \"%s\" for reading"), fn); 1796 logical_file_name = xstrdup (new_name); 1797 } 1798 else 1799 { 1800 int j; 1801 1802 for (j = 0; ; ++j) 1803 { 1804 const char *dir = dir_list_nth (j); 1805 1806 if (dir == NULL) 1807 error (EXIT_FAILURE, ENOENT, _("\ 1808error while opening \"%s\" for reading"), fn); 1809 1810 new_name = concatenated_filename (dir, fn, NULL); 1811 1812 fp = fopen (new_name, "r"); 1813 if (fp != NULL) 1814 break; 1815 1816 if (errno != ENOENT) 1817 error (EXIT_FAILURE, errno, _("\ 1818error while opening \"%s\" for reading"), new_name); 1819 free (new_name); 1820 } 1821 1822 /* Note that the NEW_NAME variable contains the actual file name 1823 and the logical file name is what is reported by xgettext. In 1824 this case NEW_NAME is set to the file which was found along the 1825 directory search path, and LOGICAL_FILE_NAME is is set to the 1826 file name which was searched for. */ 1827 logical_file_name = xstrdup (fn); 1828 } 1829 1830 *logical_file_name_p = logical_file_name; 1831 *real_file_name_p = new_name; 1832 return fp; 1833} 1834 1835 1836/* Language dependent format string parser. 1837 NULL if the language has no notion of format strings. */ 1838static struct formatstring_parser *current_formatstring_parser1; 1839static struct formatstring_parser *current_formatstring_parser2; 1840 1841 1842static void 1843extract_from_file (const char *file_name, extractor_ty extractor, 1844 msgdomain_list_ty *mdlp) 1845{ 1846 char *logical_file_name; 1847 char *real_file_name; 1848 FILE *fp = xgettext_open (file_name, &logical_file_name, &real_file_name); 1849 1850 /* Set the default for the source file encoding. May be overridden by 1851 the extractor function. */ 1852 xgettext_current_source_encoding = xgettext_global_source_encoding; 1853#if HAVE_ICONV 1854 xgettext_current_source_iconv = xgettext_global_source_iconv; 1855#endif 1856 1857 current_formatstring_parser1 = extractor.formatstring_parser1; 1858 current_formatstring_parser2 = extractor.formatstring_parser2; 1859 extractor.func (fp, real_file_name, logical_file_name, extractor.flag_table, 1860 mdlp); 1861 1862 if (fp != stdin) 1863 fclose (fp); 1864 free (logical_file_name); 1865 free (real_file_name); 1866} 1867 1868 1869 1870/* Convert the given string from xgettext_current_source_encoding to 1871 the output file encoding (i.e. ASCII or UTF-8). 1872 The resulting string is either the argument string, or freshly allocated. 1873 The file_name and line_number are only used for error message purposes. */ 1874char * 1875from_current_source_encoding (const char *string, 1876 const char *file_name, size_t line_number) 1877{ 1878 if (xgettext_current_source_encoding == po_charset_ascii) 1879 { 1880 if (!is_ascii_string (string)) 1881 { 1882 char buffer[21]; 1883 1884 if (line_number == (size_t)(-1)) 1885 buffer[0] = '\0'; 1886 else 1887 sprintf (buffer, ":%ld", (long) line_number); 1888 multiline_error (xstrdup (""), 1889 xasprintf (_("\ 1890Non-ASCII string at %s%s.\n\ 1891Please specify the source encoding through --from-code.\n"), 1892 file_name, buffer)); 1893 exit (EXIT_FAILURE); 1894 } 1895 } 1896 else if (xgettext_current_source_encoding != po_charset_utf8) 1897 { 1898#if HAVE_ICONV 1899 struct conversion_context context; 1900 1901 context.from_code = xgettext_current_source_encoding; 1902 context.to_code = po_charset_utf8; 1903 context.from_filename = file_name; 1904 context.message = NULL; 1905 1906 string = convert_string (xgettext_current_source_iconv, string, &context); 1907#else 1908 /* If we don't have iconv(), the only supported values for 1909 xgettext_global_source_encoding and thus also for 1910 xgettext_current_source_encoding are ASCII and UTF-8. 1911 convert_string() should not be called in this case. */ 1912 abort (); 1913#endif 1914 } 1915 1916 return (char *) string; 1917} 1918 1919#define CONVERT_STRING(string) \ 1920 string = from_current_source_encoding (string, pos->file_name, \ 1921 pos->line_number); 1922 1923 1924/* Update the is_format[] flags depending on the information given in the 1925 context. */ 1926static void 1927set_format_flags_from_context (enum is_format is_format[NFORMATS], 1928 flag_context_ty context, const char *string, 1929 lex_pos_ty *pos, const char *pretty_msgstr) 1930{ 1931 size_t i; 1932 1933 if (context.is_format1 != undecided || context.is_format2 != undecided) 1934 for (i = 0; i < NFORMATS; i++) 1935 { 1936 if (is_format[i] == undecided) 1937 { 1938 if (formatstring_parsers[i] == current_formatstring_parser1 1939 && context.is_format1 != undecided) 1940 is_format[i] = (enum is_format) context.is_format1; 1941 if (formatstring_parsers[i] == current_formatstring_parser2 1942 && context.is_format2 != undecided) 1943 is_format[i] = (enum is_format) context.is_format2; 1944 } 1945 if (possible_format_p (is_format[i])) 1946 { 1947 struct formatstring_parser *parser = formatstring_parsers[i]; 1948 char *invalid_reason = NULL; 1949 void *descr = parser->parse (string, false, NULL, &invalid_reason); 1950 1951 if (descr != NULL) 1952 parser->free (descr); 1953 else 1954 { 1955 /* The string is not a valid format string. */ 1956 if (is_format[i] != possible) 1957 { 1958 char buffer[21]; 1959 1960 error_with_progname = false; 1961 if (pos->line_number == (size_t)(-1)) 1962 buffer[0] = '\0'; 1963 else 1964 sprintf (buffer, ":%ld", (long) pos->line_number); 1965 multiline_warning (xasprintf (_("%s%s: warning: "), 1966 pos->file_name, buffer), 1967 xasprintf (is_format[i] == yes_according_to_context 1968 ? _("Although being used in a format string position, the %s is not a valid %s format string. Reason: %s\n") 1969 : _("Although declared as such, the %s is not a valid %s format string. Reason: %s\n"), 1970 pretty_msgstr, 1971 format_language_pretty[i], 1972 invalid_reason)); 1973 error_with_progname = true; 1974 } 1975 1976 is_format[i] = impossible; 1977 free (invalid_reason); 1978 } 1979 } 1980 } 1981} 1982 1983 1984static void 1985warn_format_string (enum is_format is_format[NFORMATS], const char *string, 1986 lex_pos_ty *pos, const char *pretty_msgstr) 1987{ 1988 if (possible_format_p (is_format[format_python]) 1989 && get_python_format_unnamed_arg_count (string) > 1) 1990 { 1991 char buffer[21]; 1992 1993 error_with_progname = false; 1994 if (pos->line_number == (size_t)(-1)) 1995 buffer[0] = '\0'; 1996 else 1997 sprintf (buffer, ":%ld", (long) pos->line_number); 1998 multiline_warning (xasprintf (_("%s%s: warning: "), 1999 pos->file_name, buffer), 2000 xasprintf (_("\ 2001'%s' format string with unnamed arguments cannot be properly localized:\n\ 2002The translator cannot reorder the arguments.\n\ 2003Please consider using a format string with named arguments,\n\ 2004and a mapping instead of a tuple for the arguments.\n"), 2005 pretty_msgstr)); 2006 error_with_progname = true; 2007 } 2008} 2009 2010 2011message_ty * 2012remember_a_message (message_list_ty *mlp, char *msgctxt, char *msgid, 2013 flag_context_ty context, lex_pos_ty *pos, 2014 refcounted_string_list_ty *comment) 2015{ 2016 enum is_format is_format[NFORMATS]; 2017 enum is_wrap do_wrap; 2018 message_ty *mp; 2019 char *msgstr; 2020 size_t i; 2021 2022 /* See whether we shall exclude this message. */ 2023 if (exclude != NULL && message_list_search (exclude, msgctxt, msgid) != NULL) 2024 { 2025 /* Tell the lexer to reset its comment buffer, so that the next 2026 message gets the correct comments. */ 2027 xgettext_comment_reset (); 2028 savable_comment_reset (); 2029 2030 if (msgctxt != NULL) 2031 free (msgctxt); 2032 free (msgid); 2033 2034 return NULL; 2035 } 2036 2037 savable_comment_to_xgettext_comment (comment); 2038 2039 for (i = 0; i < NFORMATS; i++) 2040 is_format[i] = undecided; 2041 do_wrap = undecided; 2042 2043 if (msgctxt != NULL) 2044 CONVERT_STRING (msgctxt); 2045 CONVERT_STRING (msgid); 2046 2047 if (msgctxt == NULL && msgid[0] == '\0' && !xgettext_omit_header) 2048 { 2049 char buffer[21]; 2050 2051 error_with_progname = false; 2052 if (pos->line_number == (size_t)(-1)) 2053 buffer[0] = '\0'; 2054 else 2055 sprintf (buffer, ":%ld", (long) pos->line_number); 2056 multiline_warning (xasprintf (_("%s%s: warning: "), pos->file_name, 2057 buffer), 2058 xstrdup (_("\ 2059Empty msgid. It is reserved by GNU gettext:\n\ 2060gettext(\"\") returns the header entry with\n\ 2061meta information, not the empty string.\n"))); 2062 error_with_progname = true; 2063 } 2064 2065 /* See if we have seen this message before. */ 2066 mp = message_list_search (mlp, msgctxt, msgid); 2067 if (mp != NULL) 2068 { 2069 if (msgctxt != NULL) 2070 free (msgctxt); 2071 free (msgid); 2072 for (i = 0; i < NFORMATS; i++) 2073 is_format[i] = mp->is_format[i]; 2074 do_wrap = mp->do_wrap; 2075 } 2076 else 2077 { 2078 /* Construct the msgstr from the prefix and suffix, otherwise use the 2079 empty string. */ 2080 if (msgstr_prefix) 2081 msgstr = xasprintf ("%s%s%s", msgstr_prefix, msgid, msgstr_suffix); 2082 else 2083 msgstr = ""; 2084 2085 /* Allocate a new message and append the message to the list. */ 2086 mp = message_alloc (msgctxt, msgid, NULL, msgstr, strlen (msgstr) + 1, 2087 pos); 2088 /* Do not free msgctxt and msgid. */ 2089 message_list_append (mlp, mp); 2090 } 2091 2092 /* Determine whether the context specifies that the msgid is a format 2093 string. */ 2094 set_format_flags_from_context (is_format, context, mp->msgid, pos, "msgid"); 2095 2096 /* Ask the lexer for the comments it has seen. */ 2097 { 2098 size_t nitems_before; 2099 size_t nitems_after; 2100 int j; 2101 bool add_all_remaining_comments; 2102 2103 nitems_before = (mp->comment_dot != NULL ? mp->comment_dot->nitems : 0); 2104 2105 add_all_remaining_comments = add_all_comments; 2106 for (j = 0; ; ++j) 2107 { 2108 const char *s = xgettext_comment (j); 2109 const char *t; 2110 if (s == NULL) 2111 break; 2112 2113 CONVERT_STRING (s); 2114 2115 /* To reduce the possibility of unwanted matches we do a two 2116 step match: the line must contain `xgettext:' and one of 2117 the possible format description strings. */ 2118 if ((t = c_strstr (s, "xgettext:")) != NULL) 2119 { 2120 bool tmp_fuzzy; 2121 enum is_format tmp_format[NFORMATS]; 2122 enum is_wrap tmp_wrap; 2123 bool interesting; 2124 2125 t += strlen ("xgettext:"); 2126 2127 po_parse_comment_special (t, &tmp_fuzzy, tmp_format, &tmp_wrap); 2128 2129 interesting = false; 2130 for (i = 0; i < NFORMATS; i++) 2131 if (tmp_format[i] != undecided) 2132 { 2133 is_format[i] = tmp_format[i]; 2134 interesting = true; 2135 } 2136 if (tmp_wrap != undecided) 2137 { 2138 do_wrap = tmp_wrap; 2139 interesting = true; 2140 } 2141 2142 /* If the "xgettext:" marker was followed by an interesting 2143 keyword, and we updated our is_format/do_wrap variables, 2144 we don't print the comment as a #. comment. */ 2145 if (interesting) 2146 continue; 2147 } 2148 /* When the comment tag is seen, it drags in not only the line 2149 which it starts, but all remaining comment lines. */ 2150 if (add_all_remaining_comments 2151 || (add_all_remaining_comments = 2152 (comment_tag != NULL 2153 && strncmp (s, comment_tag, strlen (comment_tag)) == 0))) 2154 message_comment_dot_append (mp, s); 2155 } 2156 2157 nitems_after = (mp->comment_dot != NULL ? mp->comment_dot->nitems : 0); 2158 2159 /* Don't add the comments if they are a repetition of the tail of the 2160 already present comments. This avoids unneeded duplication if the 2161 same message appears several times, each time with the same comment. */ 2162 if (nitems_before < nitems_after) 2163 { 2164 size_t added = nitems_after - nitems_before; 2165 2166 if (added <= nitems_before) 2167 { 2168 bool repeated = true; 2169 2170 for (i = 0; i < added; i++) 2171 if (strcmp (mp->comment_dot->item[nitems_before - added + i], 2172 mp->comment_dot->item[nitems_before + i]) != 0) 2173 { 2174 repeated = false; 2175 break; 2176 } 2177 2178 if (repeated) 2179 { 2180 for (i = 0; i < added; i++) 2181 free ((char *) mp->comment_dot->item[nitems_before + i]); 2182 mp->comment_dot->nitems = nitems_before; 2183 } 2184 } 2185 } 2186 } 2187 2188 /* If it is not already decided, through programmer comments, whether the 2189 msgid is a format string, examine the msgid. This is a heuristic. */ 2190 for (i = 0; i < NFORMATS; i++) 2191 { 2192 if (is_format[i] == undecided 2193 && (formatstring_parsers[i] == current_formatstring_parser1 2194 || formatstring_parsers[i] == current_formatstring_parser2) 2195 /* But avoid redundancy: objc-format is stronger than c-format. */ 2196 && !(i == format_c && possible_format_p (is_format[format_objc])) 2197 && !(i == format_objc && possible_format_p (is_format[format_c])) 2198 /* Avoid flagging a string as c-format when it's known to be a 2199 qt-format or kde-format or boost-format string. */ 2200 && !(i == format_c 2201 && (possible_format_p (is_format[format_qt]) 2202 || possible_format_p (is_format[format_kde]) 2203 || possible_format_p (is_format[format_boost])))) 2204 { 2205 struct formatstring_parser *parser = formatstring_parsers[i]; 2206 char *invalid_reason = NULL; 2207 void *descr = parser->parse (mp->msgid, false, NULL, &invalid_reason); 2208 2209 if (descr != NULL) 2210 { 2211 /* msgid is a valid format string. We mark only those msgids 2212 as format strings which contain at least one format directive 2213 and thus are format strings with a high probability. We 2214 don't mark strings without directives as format strings, 2215 because that would force the programmer to add 2216 "xgettext: no-c-format" anywhere where a translator wishes 2217 to use a percent sign. So, the msgfmt checking will not be 2218 perfect. Oh well. */ 2219 if (parser->get_number_of_directives (descr) > 0 2220 && !(parser->is_unlikely_intentional != NULL 2221 && parser->is_unlikely_intentional (descr))) 2222 is_format[i] = possible; 2223 2224 parser->free (descr); 2225 } 2226 else 2227 { 2228 /* msgid is not a valid format string. */ 2229 is_format[i] = impossible; 2230 free (invalid_reason); 2231 } 2232 } 2233 mp->is_format[i] = is_format[i]; 2234 } 2235 2236 mp->do_wrap = do_wrap == no ? no : yes; /* By default we wrap. */ 2237 2238 /* Warn about the use of non-reorderable format strings when the programming 2239 language also provides reorderable format strings. */ 2240 warn_format_string (is_format, mp->msgid, pos, "msgid"); 2241 2242 /* Remember where we saw this msgid. */ 2243 if (line_comment) 2244 message_comment_filepos (mp, pos->file_name, pos->line_number); 2245 2246 /* Tell the lexer to reset its comment buffer, so that the next 2247 message gets the correct comments. */ 2248 xgettext_comment_reset (); 2249 savable_comment_reset (); 2250 2251 return mp; 2252} 2253 2254 2255void 2256remember_a_message_plural (message_ty *mp, char *string, 2257 flag_context_ty context, lex_pos_ty *pos, 2258 refcounted_string_list_ty *comment) 2259{ 2260 char *msgid_plural; 2261 char *msgstr1; 2262 size_t msgstr1_len; 2263 char *msgstr; 2264 size_t i; 2265 2266 msgid_plural = string; 2267 2268 savable_comment_to_xgettext_comment (comment); 2269 2270 CONVERT_STRING (msgid_plural); 2271 2272 /* See if the message is already a plural message. */ 2273 if (mp->msgid_plural == NULL) 2274 { 2275 mp->msgid_plural = msgid_plural; 2276 2277 /* Construct the first plural form from the prefix and suffix, 2278 otherwise use the empty string. The translator will have to 2279 provide additional plural forms. */ 2280 if (msgstr_prefix) 2281 msgstr1 = 2282 xasprintf ("%s%s%s", msgstr_prefix, msgid_plural, msgstr_suffix); 2283 else 2284 msgstr1 = ""; 2285 msgstr1_len = strlen (msgstr1) + 1; 2286 msgstr = XNMALLOC (mp->msgstr_len + msgstr1_len, char); 2287 memcpy (msgstr, mp->msgstr, mp->msgstr_len); 2288 memcpy (msgstr + mp->msgstr_len, msgstr1, msgstr1_len); 2289 mp->msgstr = msgstr; 2290 mp->msgstr_len = mp->msgstr_len + msgstr1_len; 2291 if (msgstr_prefix) 2292 free (msgstr1); 2293 2294 /* Determine whether the context specifies that the msgid_plural is a 2295 format string. */ 2296 set_format_flags_from_context (mp->is_format, context, mp->msgid_plural, 2297 pos, "msgid_plural"); 2298 2299 /* If it is not already decided, through programmer comments or 2300 the msgid, whether the msgid is a format string, examine the 2301 msgid_plural. This is a heuristic. */ 2302 for (i = 0; i < NFORMATS; i++) 2303 if ((formatstring_parsers[i] == current_formatstring_parser1 2304 || formatstring_parsers[i] == current_formatstring_parser2) 2305 && (mp->is_format[i] == undecided || mp->is_format[i] == possible) 2306 /* But avoid redundancy: objc-format is stronger than c-format. */ 2307 && !(i == format_c 2308 && possible_format_p (mp->is_format[format_objc])) 2309 && !(i == format_objc 2310 && possible_format_p (mp->is_format[format_c])) 2311 /* Avoid flagging a string as c-format when it's known to be a 2312 qt-format or boost-format string. */ 2313 && !(i == format_c 2314 && (possible_format_p (mp->is_format[format_qt]) 2315 || possible_format_p (mp->is_format[format_kde]) 2316 || possible_format_p (mp->is_format[format_boost])))) 2317 { 2318 struct formatstring_parser *parser = formatstring_parsers[i]; 2319 char *invalid_reason = NULL; 2320 void *descr = 2321 parser->parse (mp->msgid_plural, false, NULL, &invalid_reason); 2322 2323 if (descr != NULL) 2324 { 2325 /* Same heuristic as in remember_a_message. */ 2326 if (parser->get_number_of_directives (descr) > 0 2327 && !(parser->is_unlikely_intentional != NULL 2328 && parser->is_unlikely_intentional (descr))) 2329 mp->is_format[i] = possible; 2330 2331 parser->free (descr); 2332 } 2333 else 2334 { 2335 /* msgid_plural is not a valid format string. */ 2336 mp->is_format[i] = impossible; 2337 free (invalid_reason); 2338 } 2339 } 2340 2341 /* Warn about the use of non-reorderable format strings when the programming 2342 language also provides reorderable format strings. */ 2343 warn_format_string (mp->is_format, mp->msgid_plural, pos, "msgid_plural"); 2344 } 2345 else 2346 free (msgid_plural); 2347 2348 /* Tell the lexer to reset its comment buffer, so that the next 2349 message gets the correct comments. */ 2350 xgettext_comment_reset (); 2351 savable_comment_reset (); 2352} 2353 2354 2355struct arglist_parser * 2356arglist_parser_alloc (message_list_ty *mlp, const struct callshapes *shapes) 2357{ 2358 if (shapes == NULL || shapes->nshapes == 0) 2359 { 2360 struct arglist_parser *ap = 2361 (struct arglist_parser *) 2362 xmalloc (offsetof (struct arglist_parser, alternative[0])); 2363 2364 ap->mlp = mlp; 2365 ap->keyword = NULL; 2366 ap->keyword_len = 0; 2367 ap->nalternatives = 0; 2368 2369 return ap; 2370 } 2371 else 2372 { 2373 struct arglist_parser *ap = 2374 (struct arglist_parser *) 2375 xmalloc (xsum (sizeof (struct arglist_parser), 2376 xtimes (shapes->nshapes - 1, 2377 sizeof (struct partial_call)))); 2378 size_t i; 2379 2380 ap->mlp = mlp; 2381 ap->keyword = shapes->keyword; 2382 ap->keyword_len = shapes->keyword_len; 2383 ap->nalternatives = shapes->nshapes; 2384 for (i = 0; i < shapes->nshapes; i++) 2385 { 2386 ap->alternative[i].argnumc = shapes->shapes[i].argnumc; 2387 ap->alternative[i].argnum1 = shapes->shapes[i].argnum1; 2388 ap->alternative[i].argnum2 = shapes->shapes[i].argnum2; 2389 ap->alternative[i].argnum1_glib_context = 2390 shapes->shapes[i].argnum1_glib_context; 2391 ap->alternative[i].argnum2_glib_context = 2392 shapes->shapes[i].argnum2_glib_context; 2393 ap->alternative[i].argtotal = shapes->shapes[i].argtotal; 2394 ap->alternative[i].xcomments = shapes->shapes[i].xcomments; 2395 ap->alternative[i].msgctxt = NULL; 2396 ap->alternative[i].msgctxt_pos.file_name = NULL; 2397 ap->alternative[i].msgctxt_pos.line_number = (size_t)(-1); 2398 ap->alternative[i].msgid = NULL; 2399 ap->alternative[i].msgid_context = null_context; 2400 ap->alternative[i].msgid_pos.file_name = NULL; 2401 ap->alternative[i].msgid_pos.line_number = (size_t)(-1); 2402 ap->alternative[i].msgid_comment = NULL; 2403 ap->alternative[i].msgid_plural = NULL; 2404 ap->alternative[i].msgid_plural_context = null_context; 2405 ap->alternative[i].msgid_plural_pos.file_name = NULL; 2406 ap->alternative[i].msgid_plural_pos.line_number = (size_t)(-1); 2407 } 2408 2409 return ap; 2410 } 2411} 2412 2413 2414struct arglist_parser * 2415arglist_parser_clone (struct arglist_parser *ap) 2416{ 2417 struct arglist_parser *copy = 2418 (struct arglist_parser *) 2419 xmalloc (xsum (sizeof (struct arglist_parser) - sizeof (struct partial_call), 2420 xtimes (ap->nalternatives, sizeof (struct partial_call)))); 2421 size_t i; 2422 2423 copy->mlp = ap->mlp; 2424 copy->keyword = ap->keyword; 2425 copy->keyword_len = ap->keyword_len; 2426 copy->nalternatives = ap->nalternatives; 2427 for (i = 0; i < ap->nalternatives; i++) 2428 { 2429 const struct partial_call *cp = &ap->alternative[i]; 2430 struct partial_call *ccp = ©->alternative[i]; 2431 2432 ccp->argnumc = cp->argnumc; 2433 ccp->argnum1 = cp->argnum1; 2434 ccp->argnum2 = cp->argnum2; 2435 ccp->argnum1_glib_context = cp->argnum1_glib_context; 2436 ccp->argnum2_glib_context = cp->argnum2_glib_context; 2437 ccp->argtotal = cp->argtotal; 2438 ccp->xcomments = cp->xcomments; 2439 ccp->msgctxt = (cp->msgctxt != NULL ? xstrdup (cp->msgctxt) : NULL); 2440 ccp->msgctxt_pos = cp->msgctxt_pos; 2441 ccp->msgid = (cp->msgid != NULL ? xstrdup (cp->msgid) : NULL); 2442 ccp->msgid_context = cp->msgid_context; 2443 ccp->msgid_pos = cp->msgctxt_pos; 2444 ccp->msgid_comment = add_reference (cp->msgid_comment); 2445 ccp->msgid_plural = 2446 (cp->msgid_plural != NULL ? xstrdup (cp->msgid_plural) : NULL); 2447 ccp->msgid_plural_context = cp->msgid_plural_context; 2448 ccp->msgid_plural_pos = cp->msgid_plural_pos; 2449 } 2450 2451 return copy; 2452} 2453 2454 2455void 2456arglist_parser_remember (struct arglist_parser *ap, 2457 int argnum, char *string, 2458 flag_context_ty context, 2459 char *file_name, size_t line_number, 2460 refcounted_string_list_ty *comment) 2461{ 2462 bool stored_string = false; 2463 size_t nalternatives = ap->nalternatives; 2464 size_t i; 2465 2466 if (!(argnum > 0)) 2467 abort (); 2468 for (i = 0; i < nalternatives; i++) 2469 { 2470 struct partial_call *cp = &ap->alternative[i]; 2471 2472 if (argnum == cp->argnumc) 2473 { 2474 cp->msgctxt = string; 2475 cp->msgctxt_pos.file_name = file_name; 2476 cp->msgctxt_pos.line_number = line_number; 2477 stored_string = true; 2478 /* Mark msgctxt as done. */ 2479 cp->argnumc = 0; 2480 } 2481 else if (argnum == cp->argnum1) 2482 { 2483 cp->msgid = string; 2484 cp->msgid_context = context; 2485 cp->msgid_pos.file_name = file_name; 2486 cp->msgid_pos.line_number = line_number; 2487 cp->msgid_comment = add_reference (comment); 2488 stored_string = true; 2489 /* Mark msgid as done. */ 2490 cp->argnum1 = 0; 2491 } 2492 else if (argnum == cp->argnum2) 2493 { 2494 cp->msgid_plural = string; 2495 cp->msgid_plural_context = context; 2496 cp->msgid_plural_pos.file_name = file_name; 2497 cp->msgid_plural_pos.line_number = line_number; 2498 stored_string = true; 2499 /* Mark msgid_plural as done. */ 2500 cp->argnum2 = 0; 2501 } 2502 } 2503 /* Note: There is a memory leak here: When string was stored but is later 2504 not used by arglist_parser_done, we don't free it. */ 2505 if (!stored_string) 2506 free (string); 2507} 2508 2509 2510bool 2511arglist_parser_decidedp (struct arglist_parser *ap, int argnum) 2512{ 2513 size_t i; 2514 2515 /* Test whether all alternatives are decided. 2516 Note: A decided alternative can be complete 2517 cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 2518 && cp->argtotal == 0 2519 or it can be failed if no literal strings were found at the specified 2520 argument positions: 2521 cp->argnumc <= argnum && cp->argnum1 <= argnum && cp->argnum2 <= argnum 2522 or it can be failed if the number of arguments is exceeded: 2523 cp->argtotal > 0 && cp->argtotal < argnum 2524 */ 2525 for (i = 0; i < ap->nalternatives; i++) 2526 { 2527 struct partial_call *cp = &ap->alternative[i]; 2528 2529 if (!((cp->argnumc <= argnum 2530 && cp->argnum1 <= argnum 2531 && cp->argnum2 <= argnum) 2532 || (cp->argtotal > 0 && cp->argtotal < argnum))) 2533 /* cp is still undecided. */ 2534 return false; 2535 } 2536 return true; 2537} 2538 2539 2540void 2541arglist_parser_done (struct arglist_parser *ap, int argnum) 2542{ 2543 size_t ncomplete; 2544 size_t i; 2545 2546 /* Determine the number of complete calls. */ 2547 ncomplete = 0; 2548 for (i = 0; i < ap->nalternatives; i++) 2549 { 2550 struct partial_call *cp = &ap->alternative[i]; 2551 2552 if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 2553 && (cp->argtotal == 0 || cp->argtotal == argnum)) 2554 ncomplete++; 2555 } 2556 2557 if (ncomplete > 0) 2558 { 2559 struct partial_call *best_cp = NULL; 2560 bool ambiguous = false; 2561 2562 /* Find complete calls where msgctxt, msgid, msgid_plural are all 2563 provided. */ 2564 for (i = 0; i < ap->nalternatives; i++) 2565 { 2566 struct partial_call *cp = &ap->alternative[i]; 2567 2568 if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 2569 && (cp->argtotal == 0 || cp->argtotal == argnum) 2570 && cp->msgctxt != NULL 2571 && cp->msgid != NULL 2572 && cp->msgid_plural != NULL) 2573 { 2574 if (best_cp != NULL) 2575 { 2576 ambiguous = true; 2577 break; 2578 } 2579 best_cp = cp; 2580 } 2581 } 2582 2583 if (best_cp == NULL) 2584 { 2585 struct partial_call *best_cp1 = NULL; 2586 struct partial_call *best_cp2 = NULL; 2587 2588 /* Find complete calls where msgctxt, msgid are provided. */ 2589 for (i = 0; i < ap->nalternatives; i++) 2590 { 2591 struct partial_call *cp = &ap->alternative[i]; 2592 2593 if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 2594 && (cp->argtotal == 0 || cp->argtotal == argnum) 2595 && cp->msgctxt != NULL 2596 && cp->msgid != NULL) 2597 { 2598 if (best_cp1 != NULL) 2599 { 2600 ambiguous = true; 2601 break; 2602 } 2603 best_cp1 = cp; 2604 } 2605 } 2606 2607 /* Find complete calls where msgid, msgid_plural are provided. */ 2608 for (i = 0; i < ap->nalternatives; i++) 2609 { 2610 struct partial_call *cp = &ap->alternative[i]; 2611 2612 if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 2613 && (cp->argtotal == 0 || cp->argtotal == argnum) 2614 && cp->msgid != NULL 2615 && cp->msgid_plural != NULL) 2616 { 2617 if (best_cp2 != NULL) 2618 { 2619 ambiguous = true; 2620 break; 2621 } 2622 best_cp2 = cp; 2623 } 2624 } 2625 2626 if (best_cp1 != NULL) 2627 best_cp = best_cp1; 2628 if (best_cp2 != NULL) 2629 { 2630 if (best_cp != NULL) 2631 ambiguous = true; 2632 else 2633 best_cp = best_cp2; 2634 } 2635 } 2636 2637 if (best_cp == NULL) 2638 { 2639 /* Find complete calls where msgid is provided. */ 2640 for (i = 0; i < ap->nalternatives; i++) 2641 { 2642 struct partial_call *cp = &ap->alternative[i]; 2643 2644 if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 2645 && (cp->argtotal == 0 || cp->argtotal == argnum) 2646 && cp->msgid != NULL) 2647 { 2648 if (best_cp != NULL) 2649 { 2650 ambiguous = true; 2651 break; 2652 } 2653 best_cp = cp; 2654 } 2655 } 2656 } 2657 2658 if (ambiguous) 2659 { 2660 error_with_progname = false; 2661 error_at_line (0, 0, 2662 best_cp->msgid_pos.file_name, 2663 best_cp->msgid_pos.line_number, 2664 _("ambiguous argument specification for keyword '%.*s'"), 2665 (int) ap->keyword_len, ap->keyword); 2666 error_with_progname = true; 2667 } 2668 2669 if (best_cp != NULL) 2670 { 2671 /* best_cp indicates the best found complete call. 2672 Now call remember_a_message. */ 2673 message_ty *mp; 2674 2675 /* Split strings in the GNOME glib syntax "msgctxt|msgid". */ 2676 if (best_cp->argnum1_glib_context || best_cp->argnum2_glib_context) 2677 /* split_keywordspec should not allow the context to be specified 2678 in two different ways. */ 2679 if (best_cp->msgctxt != NULL) 2680 abort (); 2681 if (best_cp->argnum1_glib_context) 2682 { 2683 const char *separator = strchr (best_cp->msgid, '|'); 2684 2685 if (separator == NULL) 2686 { 2687 error_with_progname = false; 2688 error_at_line (0, 0, 2689 best_cp->msgid_pos.file_name, 2690 best_cp->msgid_pos.line_number, 2691 _("warning: missing context for keyword '%.*s'"), 2692 (int) ap->keyword_len, ap->keyword); 2693 error_with_progname = true; 2694 } 2695 else 2696 { 2697 size_t ctxt_len = separator - best_cp->msgid; 2698 char *ctxt = XNMALLOC (ctxt_len + 1, char); 2699 2700 memcpy (ctxt, best_cp->msgid, ctxt_len); 2701 ctxt[ctxt_len] = '\0'; 2702 best_cp->msgctxt = ctxt; 2703 best_cp->msgid = xstrdup (separator + 1); 2704 } 2705 } 2706 if (best_cp->msgid_plural != NULL && best_cp->argnum2_glib_context) 2707 { 2708 const char *separator = strchr (best_cp->msgid_plural, '|'); 2709 2710 if (separator == NULL) 2711 { 2712 error_with_progname = false; 2713 error_at_line (0, 0, 2714 best_cp->msgid_plural_pos.file_name, 2715 best_cp->msgid_plural_pos.line_number, 2716 _("warning: missing context for plural argument of keyword '%.*s'"), 2717 (int) ap->keyword_len, ap->keyword); 2718 error_with_progname = true; 2719 } 2720 else 2721 { 2722 size_t ctxt_len = separator - best_cp->msgid_plural; 2723 char *ctxt = XNMALLOC (ctxt_len + 1, char); 2724 2725 memcpy (ctxt, best_cp->msgid_plural, ctxt_len); 2726 ctxt[ctxt_len] = '\0'; 2727 if (best_cp->msgctxt == NULL) 2728 best_cp->msgctxt = ctxt; 2729 else 2730 { 2731 if (strcmp (ctxt, best_cp->msgctxt) != 0) 2732 { 2733 error_with_progname = false; 2734 error_at_line (0, 0, 2735 best_cp->msgid_plural_pos.file_name, 2736 best_cp->msgid_plural_pos.line_number, 2737 _("context mismatch between singular and plural form")); 2738 error_with_progname = true; 2739 } 2740 free (ctxt); 2741 } 2742 best_cp->msgid_plural = xstrdup (separator + 1); 2743 } 2744 } 2745 2746 mp = remember_a_message (ap->mlp, best_cp->msgctxt, best_cp->msgid, 2747 best_cp->msgid_context, 2748 &best_cp->msgid_pos, 2749 best_cp->msgid_comment); 2750 if (best_cp->msgid_plural != NULL) 2751 remember_a_message_plural (mp, best_cp->msgid_plural, 2752 best_cp->msgid_plural_context, 2753 &best_cp->msgid_plural_pos, 2754 NULL); 2755 if (best_cp->xcomments.nitems > 0) 2756 { 2757 /* Add best_cp->xcomments to mp->comment_dot, unless already 2758 present. */ 2759 size_t i; 2760 2761 for (i = 0; i < best_cp->xcomments.nitems; i++) 2762 { 2763 const char *xcomment = best_cp->xcomments.item[i]; 2764 bool found = false; 2765 2766 if (mp->comment_dot != NULL) 2767 { 2768 size_t j; 2769 2770 for (j = 0; j < mp->comment_dot->nitems; j++) 2771 if (strcmp (xcomment, mp->comment_dot->item[j]) == 0) 2772 { 2773 found = true; 2774 break; 2775 } 2776 } 2777 if (!found) 2778 message_comment_dot_append (mp, xcomment); 2779 } 2780 } 2781 } 2782 } 2783 else 2784 { 2785 /* No complete call was parsed. */ 2786 /* Note: There is a memory leak here: When there is more than one 2787 alternative, the same string can be stored in multiple alternatives, 2788 and it's not easy to free all strings reliably. */ 2789 if (ap->nalternatives == 1) 2790 { 2791 if (ap->alternative[0].msgctxt != NULL) 2792 free (ap->alternative[0].msgctxt); 2793 if (ap->alternative[0].msgid != NULL) 2794 free (ap->alternative[0].msgid); 2795 if (ap->alternative[0].msgid_plural != NULL) 2796 free (ap->alternative[0].msgid_plural); 2797 } 2798 } 2799 2800 for (i = 0; i < ap->nalternatives; i++) 2801 drop_reference (ap->alternative[i].msgid_comment); 2802 free (ap); 2803} 2804 2805 2806static message_ty * 2807construct_header () 2808{ 2809 char *project_id_version; 2810 time_t now; 2811 char *timestring; 2812 message_ty *mp; 2813 char *msgstr; 2814 static lex_pos_ty pos = { __FILE__, __LINE__ }; 2815 2816 if (package_name != NULL) 2817 { 2818 if (package_version != NULL) 2819 project_id_version = xasprintf ("%s %s", package_name, package_version); 2820 else 2821 project_id_version = xasprintf ("%s", package_name); 2822 } 2823 else 2824 project_id_version = xstrdup ("PACKAGE VERSION"); 2825 2826 if (msgid_bugs_address != NULL && msgid_bugs_address[0] == '\0') 2827 multiline_warning (xasprintf (_("warning: ")), 2828 xstrdup (_("\ 2829The option --msgid-bugs-address was not specified.\n\ 2830If you are using a `Makevars' file, please specify\n\ 2831the MSGID_BUGS_ADDRESS variable there; otherwise please\n\ 2832specify an --msgid-bugs-address command line option.\n\ 2833"))); 2834 2835 time (&now); 2836 timestring = po_strftime (&now); 2837 2838 msgstr = xasprintf ("\ 2839Project-Id-Version: %s\n\ 2840Report-Msgid-Bugs-To: %s\n\ 2841POT-Creation-Date: %s\n\ 2842PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n\ 2843Last-Translator: FULL NAME <EMAIL@ADDRESS>\n\ 2844Language-Team: LANGUAGE <LL@li.org>\n\ 2845MIME-Version: 1.0\n\ 2846Content-Type: text/plain; charset=CHARSET\n\ 2847Content-Transfer-Encoding: 8bit\n", 2848 project_id_version, 2849 msgid_bugs_address != NULL ? msgid_bugs_address : "", 2850 timestring); 2851 free (timestring); 2852 free (project_id_version); 2853 2854 mp = message_alloc (NULL, "", NULL, msgstr, strlen (msgstr) + 1, &pos); 2855 2856 message_comment_append (mp, 2857 copyright_holder[0] != '\0' 2858 ? xasprintf ("\ 2859SOME DESCRIPTIVE TITLE.\n\ 2860Copyright (C) YEAR %s\n\ 2861This file is distributed under the same license as the PACKAGE package.\n\ 2862FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n", 2863 copyright_holder) 2864 : "\ 2865SOME DESCRIPTIVE TITLE.\n\ 2866This file is put in the public domain.\n\ 2867FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n"); 2868 2869 mp->is_fuzzy = true; 2870 2871 return mp; 2872} 2873 2874static void 2875finalize_header (msgdomain_list_ty *mdlp) 2876{ 2877 /* If the generated PO file has plural forms, add a Plural-Forms template 2878 to the constructed header. */ 2879 { 2880 bool has_plural; 2881 size_t i, j; 2882 2883 has_plural = false; 2884 for (i = 0; i < mdlp->nitems; i++) 2885 { 2886 message_list_ty *mlp = mdlp->item[i]->messages; 2887 2888 for (j = 0; j < mlp->nitems; j++) 2889 { 2890 message_ty *mp = mlp->item[j]; 2891 2892 if (mp->msgid_plural != NULL) 2893 { 2894 has_plural = true; 2895 break; 2896 } 2897 } 2898 if (has_plural) 2899 break; 2900 } 2901 2902 if (has_plural) 2903 { 2904 message_ty *header = 2905 message_list_search (mdlp->item[0]->messages, NULL, ""); 2906 if (header != NULL 2907 && c_strstr (header->msgstr, "Plural-Forms:") == NULL) 2908 { 2909 size_t insertpos = strlen (header->msgstr); 2910 const char *suffix; 2911 size_t suffix_len; 2912 char *new_msgstr; 2913 2914 suffix = "\nPlural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"; 2915 if (insertpos == 0 || header->msgstr[insertpos-1] == '\n') 2916 suffix++; 2917 suffix_len = strlen (suffix); 2918 new_msgstr = XNMALLOC (header->msgstr_len + suffix_len, char); 2919 memcpy (new_msgstr, header->msgstr, insertpos); 2920 memcpy (new_msgstr + insertpos, suffix, suffix_len); 2921 memcpy (new_msgstr + insertpos + suffix_len, 2922 header->msgstr + insertpos, 2923 header->msgstr_len - insertpos); 2924 header->msgstr = new_msgstr; 2925 header->msgstr_len = header->msgstr_len + suffix_len; 2926 } 2927 } 2928 } 2929 2930 /* If not all the strings were plain ASCII, or if the output syntax 2931 requires a charset conversion, set the charset in the header to UTF-8. 2932 All messages have already been converted to UTF-8 in remember_a_message 2933 and remember_a_message_plural. */ 2934 { 2935 bool has_nonascii = false; 2936 size_t i; 2937 2938 for (i = 0; i < mdlp->nitems; i++) 2939 { 2940 message_list_ty *mlp = mdlp->item[i]->messages; 2941 2942 if (!is_ascii_message_list (mlp)) 2943 has_nonascii = true; 2944 } 2945 2946 if (has_nonascii || output_syntax->requires_utf8) 2947 { 2948 message_list_ty *mlp = mdlp->item[0]->messages; 2949 2950 iconv_message_list (mlp, po_charset_utf8, po_charset_utf8, NULL); 2951 } 2952 } 2953} 2954 2955 2956#define SIZEOF(a) (sizeof(a) / sizeof(a[0])) 2957#define ENDOF(a) ((a) + SIZEOF(a)) 2958 2959 2960static extractor_ty 2961language_to_extractor (const char *name) 2962{ 2963 struct table_ty 2964 { 2965 const char *name; 2966 extractor_func func; 2967 flag_context_list_table_ty *flag_table; 2968 struct formatstring_parser *formatstring_parser1; 2969 struct formatstring_parser *formatstring_parser2; 2970 }; 2971 typedef struct table_ty table_ty; 2972 2973 static table_ty table[] = 2974 { 2975 SCANNERS_C 2976 SCANNERS_PO 2977 SCANNERS_SH 2978 SCANNERS_PYTHON 2979 SCANNERS_LISP 2980 SCANNERS_ELISP 2981 SCANNERS_LIBREP 2982 SCANNERS_SCHEME 2983 SCANNERS_SMALLTALK 2984 SCANNERS_JAVA 2985 SCANNERS_PROPERTIES 2986 SCANNERS_CSHARP 2987 SCANNERS_AWK 2988 SCANNERS_YCP 2989 SCANNERS_TCL 2990 SCANNERS_PERL 2991 SCANNERS_PHP 2992 SCANNERS_STRINGTABLE 2993 SCANNERS_RST 2994 SCANNERS_GLADE 2995 /* Here may follow more languages and their scanners: pike, etc... 2996 Make sure new scanners honor the --exclude-file option. */ 2997 }; 2998 2999 table_ty *tp; 3000 3001 for (tp = table; tp < ENDOF(table); ++tp) 3002 if (c_strcasecmp (name, tp->name) == 0) 3003 { 3004 extractor_ty result; 3005 3006 result.func = tp->func; 3007 result.flag_table = tp->flag_table; 3008 result.formatstring_parser1 = tp->formatstring_parser1; 3009 result.formatstring_parser2 = tp->formatstring_parser2; 3010 3011 /* Handle --qt. It's preferrable to handle this facility here rather 3012 than through an option --language=C++/Qt because the latter would 3013 conflict with the language "C++" regarding the file extensions. */ 3014 if (recognize_format_qt && strcmp (tp->name, "C++") == 0) 3015 { 3016 result.flag_table = &flag_table_cxx_qt; 3017 result.formatstring_parser2 = &formatstring_qt; 3018 } 3019 /* Likewise for --kde. */ 3020 if (recognize_format_kde && strcmp (tp->name, "C++") == 0) 3021 { 3022 result.flag_table = &flag_table_cxx_kde; 3023 result.formatstring_parser2 = &formatstring_kde; 3024 } 3025 /* Likewise for --boost. */ 3026 if (recognize_format_boost && strcmp (tp->name, "C++") == 0) 3027 { 3028 result.flag_table = &flag_table_cxx_boost; 3029 result.formatstring_parser2 = &formatstring_boost; 3030 } 3031 3032 return result; 3033 } 3034 3035 error (EXIT_FAILURE, 0, _("language `%s' unknown"), name); 3036 /* NOTREACHED */ 3037 { 3038 extractor_ty result = { NULL, NULL, NULL, NULL }; 3039 return result; 3040 } 3041} 3042 3043 3044static const char * 3045extension_to_language (const char *extension) 3046{ 3047 struct table_ty 3048 { 3049 const char *extension; 3050 const char *language; 3051 }; 3052 typedef struct table_ty table_ty; 3053 3054 static table_ty table[] = 3055 { 3056 EXTENSIONS_C 3057 EXTENSIONS_PO 3058 EXTENSIONS_SH 3059 EXTENSIONS_PYTHON 3060 EXTENSIONS_LISP 3061 EXTENSIONS_ELISP 3062 EXTENSIONS_LIBREP 3063 EXTENSIONS_SCHEME 3064 EXTENSIONS_SMALLTALK 3065 EXTENSIONS_JAVA 3066 EXTENSIONS_PROPERTIES 3067 EXTENSIONS_CSHARP 3068 EXTENSIONS_AWK 3069 EXTENSIONS_YCP 3070 EXTENSIONS_TCL 3071 EXTENSIONS_PERL 3072 EXTENSIONS_PHP 3073 EXTENSIONS_STRINGTABLE 3074 EXTENSIONS_RST 3075 EXTENSIONS_GLADE 3076 /* Here may follow more file extensions... */ 3077 }; 3078 3079 table_ty *tp; 3080 3081 for (tp = table; tp < ENDOF(table); ++tp) 3082 if (strcmp (extension, tp->extension) == 0) 3083 return tp->language; 3084 return NULL; 3085} 3086