1 /* Extract some translations of a translation catalog. 2 Copyright (C) 2001-2006 Free Software Foundation, Inc. 3 Written by Bruno Haible <haible@clisp.cons.org>, 2001. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2, or (at your option) 8 any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software Foundation, 17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 18 19 20 #ifdef HAVE_CONFIG_H 21 # include "config.h" 22 #endif 23 #include <alloca.h> 24 25 #include <assert.h> 26 #include <errno.h> 27 #include <getopt.h> 28 #include <limits.h> 29 #include <locale.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 34 #include <unistd.h> 35 #if defined _MSC_VER || defined __MINGW32__ 36 # include <io.h> 37 #endif 38 39 #include <fnmatch.h> 40 41 #include "closeout.h" 42 #include "dir-list.h" 43 #include "error.h" 44 #include "error-progname.h" 45 #include "progname.h" 46 #include "relocatable.h" 47 #include "basename.h" 48 #include "message.h" 49 #include "read-catalog.h" 50 #include "read-po.h" 51 #include "read-properties.h" 52 #include "read-stringtable.h" 53 #include "write-catalog.h" 54 #include "write-po.h" 55 #include "write-properties.h" 56 #include "write-stringtable.h" 57 #include "str-list.h" 58 #include "msgl-charset.h" 59 #include "xalloc.h" 60 #include "xallocsa.h" 61 #include "exit.h" 62 #include "libgrep.h" 63 #include "propername.h" 64 #include "gettext.h" 65 66 #define _(str) gettext (str) 67 68 69 /* Force output of PO file even if empty. */ 70 static int force_po; 71 72 /* Output only non-matching messages. */ 73 static bool invert_match = false; 74 75 /* Selected source files. */ 76 static string_list_ty *location_files; 77 78 /* Selected domain names. */ 79 static string_list_ty *domain_names; 80 81 /* Task for each grep pass. */ 82 struct grep_task { 83 matcher_t *matcher; 84 size_t pattern_count; 85 char *patterns; 86 size_t patterns_size; 87 bool case_insensitive; 88 void *compiled_patterns; 89 }; 90 static struct grep_task grep_task[5]; 91 92 /* Long options. */ 93 static const struct option long_options[] = 94 { 95 { "add-location", no_argument, &line_comment, 1 }, 96 { "comment", no_argument, NULL, 'C' }, 97 { "directory", required_argument, NULL, 'D' }, 98 { "domain", required_argument, NULL, 'M' }, 99 { "escape", no_argument, NULL, CHAR_MAX + 1 }, 100 { "extended-regexp", no_argument, NULL, 'E' }, 101 { "extracted-comment", no_argument, NULL, 'X' }, 102 { "file", required_argument, NULL, 'f' }, 103 { "fixed-strings", no_argument, NULL, 'F' }, 104 { "force-po", no_argument, &force_po, 1 }, 105 { "help", no_argument, NULL, 'h' }, 106 { "ignore-case", no_argument, NULL, 'i' }, 107 { "indent", no_argument, NULL, CHAR_MAX + 2 }, 108 { "invert-match", no_argument, NULL, 'v' }, 109 { "location", required_argument, NULL, 'N' }, 110 { "msgctxt", no_argument, NULL, 'J' }, 111 { "msgid", no_argument, NULL, 'K' }, 112 { "msgstr", no_argument, NULL, 'T' }, 113 { "no-escape", no_argument, NULL, CHAR_MAX + 3 }, 114 { "no-location", no_argument, &line_comment, 0 }, 115 { "no-wrap", no_argument, NULL, CHAR_MAX + 6 }, 116 { "output-file", required_argument, NULL, 'o' }, 117 { "properties-input", no_argument, NULL, 'P' }, 118 { "properties-output", no_argument, NULL, 'p' }, 119 { "regexp", required_argument, NULL, 'e' }, 120 { "sort-by-file", no_argument, NULL, CHAR_MAX + 4 }, 121 { "sort-output", no_argument, NULL, CHAR_MAX + 5 }, 122 { "strict", no_argument, NULL, 'S' }, 123 { "stringtable-input", no_argument, NULL, CHAR_MAX + 7 }, 124 { "stringtable-output", no_argument, NULL, CHAR_MAX + 8 }, 125 { "version", no_argument, NULL, 'V' }, 126 { "width", required_argument, NULL, 'w' }, 127 { NULL, 0, NULL, 0 } 128 }; 129 130 131 /* Forward declaration of local functions. */ 132 static void no_pass (int opt) 133 #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2) 134 __attribute__ ((noreturn)) 135 #endif 136 ; 137 static void usage (int status) 138 #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2) 139 __attribute__ ((noreturn)) 140 #endif 141 ; 142 static msgdomain_list_ty *process_msgdomain_list (msgdomain_list_ty *mdlp); 143 144 145 int 146 main (int argc, char **argv) 147 { 148 int opt; 149 bool do_help; 150 bool do_version; 151 char *output_file; 152 const char *input_file; 153 int grep_pass; 154 msgdomain_list_ty *result; 155 catalog_input_format_ty input_syntax = &input_format_po; 156 catalog_output_format_ty output_syntax = &output_format_po; 157 bool sort_by_filepos = false; 158 bool sort_by_msgid = false; 159 size_t i; 160 161 /* Set program name for messages. */ 162 set_program_name (argv[0]); 163 error_print_progname = maybe_print_progname; 164 165 #ifdef HAVE_SETLOCALE 166 /* Set locale via LC_ALL. */ 167 setlocale (LC_ALL, ""); 168 #endif 169 170 /* Set the text message domain. */ 171 bindtextdomain (PACKAGE, relocate (LOCALEDIR)); 172 bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR)); 173 textdomain (PACKAGE); 174 175 /* Ensure that write errors on stdout are detected. */ 176 atexit (close_stdout); 177 178 /* Set default values for variables. */ 179 do_help = false; 180 do_version = false; 181 output_file = NULL; 182 input_file = NULL; 183 grep_pass = -1; 184 location_files = string_list_alloc (); 185 domain_names = string_list_alloc (); 186 187 for (i = 0; i < 5; i++) 188 { 189 struct grep_task *gt = &grep_task[i]; 190 191 gt->matcher = &matcher_grep; 192 gt->pattern_count = 0; 193 gt->patterns = NULL; 194 gt->patterns_size = 0; 195 gt->case_insensitive = false; 196 } 197 198 while ((opt = getopt_long (argc, argv, "CD:e:Ef:FhiJKM:N:o:pPTvVw:X", 199 long_options, NULL)) 200 != EOF) 201 switch (opt) 202 { 203 case '\0': /* Long option. */ 204 break; 205 206 case 'C': 207 grep_pass = 3; 208 break; 209 210 case 'D': 211 dir_list_append (optarg); 212 break; 213 214 case 'e': 215 if (grep_pass < 0) 216 no_pass (opt); 217 { 218 struct grep_task *gt = &grep_task[grep_pass]; 219 /* Append optarg and a newline to gt->patterns. */ 220 size_t len = strlen (optarg); 221 gt->patterns = 222 (char *) xrealloc (gt->patterns, gt->patterns_size + len + 1); 223 memcpy (gt->patterns + gt->patterns_size, optarg, len); 224 gt->patterns_size += len; 225 *(gt->patterns + gt->patterns_size) = '\n'; 226 gt->patterns_size += 1; 227 gt->pattern_count++; 228 } 229 break; 230 231 case 'E': 232 if (grep_pass < 0) 233 no_pass (opt); 234 grep_task[grep_pass].matcher = &matcher_egrep; 235 break; 236 237 case 'f': 238 if (grep_pass < 0) 239 no_pass (opt); 240 { 241 struct grep_task *gt = &grep_task[grep_pass]; 242 /* Append the contents of the specified file to gt->patterns. */ 243 FILE *fp = fopen (optarg, "r"); 244 245 if (fp == NULL) 246 error (EXIT_FAILURE, errno, _("\ 247 error while opening \"%s\" for reading"), optarg); 248 249 while (!feof (fp)) 250 { 251 char buf[4096]; 252 size_t count = fread (buf, 1, sizeof buf, fp); 253 254 if (count == 0) 255 { 256 if (ferror (fp)) 257 error (EXIT_FAILURE, errno, _("\ 258 error while reading \"%s\""), optarg); 259 /* EOF reached. */ 260 break; 261 } 262 263 gt->patterns = 264 (char *) xrealloc (gt->patterns, gt->patterns_size + count); 265 memcpy (gt->patterns + gt->patterns_size, buf, count); 266 gt->patterns_size += count; 267 } 268 269 /* Append a final newline if file ended in a non-newline. */ 270 if (gt->patterns_size > 0 271 && *(gt->patterns + gt->patterns_size - 1) != '\n') 272 { 273 gt->patterns = 274 (char *) xrealloc (gt->patterns, gt->patterns_size + 1); 275 *(gt->patterns + gt->patterns_size) = '\n'; 276 gt->patterns_size += 1; 277 } 278 279 fclose (fp); 280 gt->pattern_count++; 281 } 282 break; 283 284 case 'F': 285 if (grep_pass < 0) 286 no_pass (opt); 287 grep_task[grep_pass].matcher = &matcher_fgrep; 288 break; 289 290 case 'h': 291 do_help = true; 292 break; 293 294 case 'i': 295 if (grep_pass < 0) 296 no_pass (opt); 297 grep_task[grep_pass].case_insensitive = true; 298 break; 299 300 case 'J': 301 grep_pass = 0; 302 break; 303 304 case 'K': 305 grep_pass = 1; 306 break; 307 308 case 'M': 309 string_list_append (domain_names, optarg); 310 break; 311 312 case 'N': 313 string_list_append (location_files, optarg); 314 break; 315 316 case 'o': 317 output_file = optarg; 318 break; 319 320 case 'p': 321 output_syntax = &output_format_properties; 322 break; 323 324 case 'P': 325 input_syntax = &input_format_properties; 326 break; 327 328 case 'S': 329 message_print_style_uniforum (); 330 break; 331 332 case 'T': 333 grep_pass = 2; 334 break; 335 336 case 'v': 337 invert_match = true; 338 break; 339 340 case 'V': 341 do_version = true; 342 break; 343 344 case 'w': 345 { 346 int value; 347 char *endp; 348 value = strtol (optarg, &endp, 10); 349 if (endp != optarg) 350 message_page_width_set (value); 351 } 352 break; 353 354 case 'X': 355 grep_pass = 4; 356 break; 357 358 case CHAR_MAX + 1: 359 message_print_style_escape (true); 360 break; 361 362 case CHAR_MAX + 2: 363 message_print_style_indent (); 364 break; 365 366 case CHAR_MAX + 3: 367 message_print_style_escape (false); 368 break; 369 370 case CHAR_MAX + 4: 371 sort_by_filepos = true; 372 break; 373 374 case CHAR_MAX + 5: 375 sort_by_msgid = true; 376 break; 377 378 case CHAR_MAX + 6: /* --no-wrap */ 379 message_page_width_ignore (); 380 break; 381 382 case CHAR_MAX + 7: /* --stringtable-input */ 383 input_syntax = &input_format_stringtable; 384 break; 385 386 case CHAR_MAX + 8: /* --stringtable-output */ 387 output_syntax = &output_format_stringtable; 388 break; 389 390 default: 391 usage (EXIT_FAILURE); 392 break; 393 } 394 395 /* Version information is requested. */ 396 if (do_version) 397 { 398 printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION); 399 /* xgettext: no-wrap */ 400 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\ 401 This is free software; see the source for copying conditions. There is NO\n\ 402 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ 403 "), 404 "2001-2006"); 405 printf (_("Written by %s.\n"), proper_name ("Bruno Haible")); 406 exit (EXIT_SUCCESS); 407 } 408 409 /* Help is requested. */ 410 if (do_help) 411 usage (EXIT_SUCCESS); 412 413 /* Test whether we have an .po file name as argument. */ 414 if (optind == argc) 415 input_file = "-"; 416 else if (optind + 1 == argc) 417 input_file = argv[optind]; 418 else 419 { 420 error (EXIT_SUCCESS, 0, _("at most one input file allowed")); 421 usage (EXIT_FAILURE); 422 } 423 424 /* Verify selected options. */ 425 if (!line_comment && sort_by_filepos) 426 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"), 427 "--no-location", "--sort-by-file"); 428 429 if (sort_by_msgid && sort_by_filepos) 430 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"), 431 "--sort-output", "--sort-by-file"); 432 433 /* Compile the patterns. */ 434 for (grep_pass = 0; grep_pass < 5; grep_pass++) 435 { 436 struct grep_task *gt = &grep_task[grep_pass]; 437 438 if (gt->pattern_count > 0) 439 { 440 if (gt->patterns_size > 0) 441 { 442 /* Strip trailing newline. */ 443 assert (gt->patterns[gt->patterns_size - 1] == '\n'); 444 gt->patterns_size--; 445 } 446 gt->compiled_patterns = 447 gt->matcher->compile (gt->patterns, gt->patterns_size, 448 gt->case_insensitive, false, false, '\n'); 449 } 450 } 451 452 /* Read input file. */ 453 result = read_catalog_file (input_file, input_syntax); 454 455 if (grep_task[0].pattern_count > 0 456 || grep_task[1].pattern_count > 0 457 || grep_task[2].pattern_count > 0 458 || grep_task[3].pattern_count > 0 459 || grep_task[4].pattern_count > 0) 460 { 461 /* Warn if the current locale is not suitable for this PO file. */ 462 compare_po_locale_charsets (result); 463 } 464 465 /* Select the messages. */ 466 result = process_msgdomain_list (result); 467 468 /* Sort the results. */ 469 if (sort_by_filepos) 470 msgdomain_list_sort_by_filepos (result); 471 else if (sort_by_msgid) 472 msgdomain_list_sort_by_msgid (result); 473 474 /* Write the merged message list out. */ 475 msgdomain_list_print (result, output_file, output_syntax, force_po, false); 476 477 exit (EXIT_SUCCESS); 478 } 479 480 481 static void 482 no_pass (int opt) 483 { 484 error (EXIT_SUCCESS, 0, 485 _("option '%c' cannot be used before 'J' or 'K' or 'T' or 'C' or 'X' has been specified"), 486 opt); 487 usage (EXIT_FAILURE); 488 } 489 490 491 /* Display usage information and exit. */ 492 static void 493 usage (int status) 494 { 495 if (status != EXIT_SUCCESS) 496 fprintf (stderr, _("Try `%s --help' for more information.\n"), 497 program_name); 498 else 499 { 500 printf (_("\ 501 Usage: %s [OPTION] [INPUTFILE]\n\ 502 "), program_name); 503 printf ("\n"); 504 /* xgettext: no-wrap */ 505 printf (_("\ 506 Extracts all messages of a translation catalog that match a given pattern\n\ 507 or belong to some given source files.\n\ 508 ")); 509 printf ("\n"); 510 printf (_("\ 511 Mandatory arguments to long options are mandatory for short options too.\n")); 512 printf ("\n"); 513 printf (_("\ 514 Input file location:\n")); 515 printf (_("\ 516 INPUTFILE input PO file\n")); 517 printf (_("\ 518 -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n")); 519 printf (_("\ 520 If no input file is given or if it is -, standard input is read.\n")); 521 printf ("\n"); 522 printf (_("\ 523 Output file location:\n")); 524 printf (_("\ 525 -o, --output-file=FILE write output to specified file\n")); 526 printf (_("\ 527 The results are written to standard output if no output file is specified\n\ 528 or if it is -.\n")); 529 printf ("\n"); 530 /* xgettext: no-wrap */ 531 printf (_("\ 532 Message selection:\n\ 533 [-N SOURCEFILE]... [-M DOMAINNAME]...\n\ 534 [-J MSGCTXT-PATTERN] [-K MSGID-PATTERN] [-T MSGSTR-PATTERN]\n\ 535 [-C COMMENT-PATTERN] [-X EXTRACTED-COMMENT-PATTERN]\n\ 536 A message is selected if it comes from one of the specified source files,\n\ 537 or if it comes from one of the specified domains,\n\ 538 or if -J is given and its context (msgctxt) matches MSGCTXT-PATTERN,\n\ 539 or if -K is given and its key (msgid or msgid_plural) matches MSGID-PATTERN,\n\ 540 or if -T is given and its translation (msgstr) matches MSGSTR-PATTERN,\n\ 541 or if -C is given and the translator's comment matches COMMENT-PATTERN,\n\ 542 or if -X is given and the extracted comment matches EXTRACTED-COMMENT-PATTERN.\n\ 543 \n\ 544 When more than one selection criterion is specified, the set of selected\n\ 545 messages is the union of the selected messages of each criterion.\n\ 546 \n\ 547 MSGCTXT-PATTERN or MSGID-PATTERN or MSGSTR-PATTERN or COMMENT-PATTERN or\n\ 548 EXTRACTED-COMMENT-PATTERN syntax:\n\ 549 [-E | -F] [-e PATTERN | -f FILE]...\n\ 550 PATTERNs are basic regular expressions by default, or extended regular\n\ 551 expressions if -E is given, or fixed strings if -F is given.\n\ 552 \n\ 553 -N, --location=SOURCEFILE select messages extracted from SOURCEFILE\n\ 554 -M, --domain=DOMAINNAME select messages belonging to domain DOMAINNAME\n\ 555 -J, --msgctxt start of patterns for the msgctxt\n\ 556 -K, --msgid start of patterns for the msgid\n\ 557 -T, --msgstr start of patterns for the msgstr\n\ 558 -C, --comment start of patterns for the translator's comment\n\ 559 -X, --extracted-comment start of patterns for the extracted comment\n\ 560 -E, --extended-regexp PATTERN is an extended regular expression\n\ 561 -F, --fixed-strings PATTERN is a set of newline-separated strings\n\ 562 -e, --regexp=PATTERN use PATTERN as a regular expression\n\ 563 -f, --file=FILE obtain PATTERN from FILE\n\ 564 -i, --ignore-case ignore case distinctions\n\ 565 -v, --invert-match output only the messages that do not match any\n\ 566 selection criterion\n\ 567 ")); 568 printf ("\n"); 569 printf (_("\ 570 Input file syntax:\n")); 571 printf (_("\ 572 -P, --properties-input input file is in Java .properties syntax\n")); 573 printf (_("\ 574 --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n")); 575 printf ("\n"); 576 printf (_("\ 577 Output details:\n")); 578 printf (_("\ 579 --no-escape do not use C escapes in output (default)\n")); 580 printf (_("\ 581 --escape use C escapes in output, no extended chars\n")); 582 printf (_("\ 583 --force-po write PO file even if empty\n")); 584 printf (_("\ 585 --indent indented output style\n")); 586 printf (_("\ 587 --no-location suppress '#: filename:line' lines\n")); 588 printf (_("\ 589 --add-location preserve '#: filename:line' lines (default)\n")); 590 printf (_("\ 591 --strict strict Uniforum output style\n")); 592 printf (_("\ 593 -p, --properties-output write out a Java .properties file\n")); 594 printf (_("\ 595 --stringtable-output write out a NeXTstep/GNUstep .strings file\n")); 596 printf (_("\ 597 -w, --width=NUMBER set output page width\n")); 598 printf (_("\ 599 --no-wrap do not break long message lines, longer than\n\ 600 the output page width, into several lines\n")); 601 printf (_("\ 602 --sort-output generate sorted output\n")); 603 printf (_("\ 604 --sort-by-file sort output by file location\n")); 605 printf ("\n"); 606 printf (_("\ 607 Informative output:\n")); 608 printf (_("\ 609 -h, --help display this help and exit\n")); 610 printf (_("\ 611 -V, --version output version information and exit\n")); 612 printf ("\n"); 613 fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"), 614 stdout); 615 } 616 617 exit (status); 618 } 619 620 621 /* Return 1 if FILENAME is contained in a list of filename patterns, 622 0 otherwise. */ 623 static bool 624 filename_list_match (const string_list_ty *slp, const char *filename) 625 { 626 size_t j; 627 628 for (j = 0; j < slp->nitems; ++j) 629 if (fnmatch (slp->item[j], filename, FNM_PATHNAME) == 0) 630 return true; 631 return false; 632 } 633 634 635 #ifdef EINTR 636 637 /* EINTR handling for close(). 638 These functions can return -1/EINTR even though we don't have any 639 signal handlers set up, namely when we get interrupted via SIGSTOP. */ 640 641 static inline int 642 nonintr_close (int fd) 643 { 644 int retval; 645 646 do 647 retval = close (fd); 648 while (retval < 0 && errno == EINTR); 649 650 return retval; 651 } 652 #define close nonintr_close 653 654 #endif 655 656 657 /* Process a string STR of size LEN bytes through grep, and return true 658 if it matches. */ 659 static bool 660 is_string_selected (int grep_pass, const char *str, size_t len) 661 { 662 const struct grep_task *gt = &grep_task[grep_pass]; 663 664 if (gt->pattern_count > 0) 665 { 666 size_t match_size; 667 size_t match_offset; 668 669 match_offset = 670 gt->matcher->execute (gt->compiled_patterns, str, len, 671 &match_size, false); 672 return (match_offset != (size_t) -1); 673 } 674 else 675 return 0; 676 } 677 678 679 /* Return true if a message matches, considering only the positive selection 680 criteria and ignoring --invert-match. */ 681 static bool 682 is_message_selected_no_invert (const message_ty *mp) 683 { 684 size_t i; 685 const char *msgstr; 686 size_t msgstr_len; 687 const char *p; 688 689 /* Test whether one of mp->filepos[] is selected. */ 690 for (i = 0; i < mp->filepos_count; i++) 691 if (filename_list_match (location_files, mp->filepos[i].file_name)) 692 return true; 693 694 /* Test msgctxt using the --msgctxt arguments. */ 695 if (mp->msgctxt != NULL 696 && is_string_selected (0, mp->msgctxt, strlen (mp->msgctxt))) 697 return true; 698 699 /* Test msgid and msgid_plural using the --msgid arguments. */ 700 if (is_string_selected (1, mp->msgid, strlen (mp->msgid))) 701 return true; 702 if (mp->msgid_plural != NULL 703 && is_string_selected (1, mp->msgid_plural, strlen (mp->msgid_plural))) 704 return true; 705 706 /* Test msgstr using the --msgstr arguments. */ 707 msgstr = mp->msgstr; 708 msgstr_len = mp->msgstr_len; 709 /* Process each NUL delimited substring separately. */ 710 for (p = msgstr; p < msgstr + msgstr_len; ) 711 { 712 size_t length = strlen (p); 713 714 if (is_string_selected (2, p, length)) 715 return true; 716 717 p += length + 1; 718 } 719 720 /* Test translator comments using the --comment arguments. */ 721 if (grep_task[3].pattern_count > 0 722 && mp->comment != NULL && mp->comment->nitems > 0) 723 { 724 size_t length; 725 char *total_comment; 726 char *q; 727 size_t j; 728 bool selected; 729 730 length = 0; 731 for (j = 0; j < mp->comment->nitems; j++) 732 length += strlen (mp->comment->item[j]) + 1; 733 total_comment = (char *) xallocsa (length); 734 735 q = total_comment; 736 for (j = 0; j < mp->comment->nitems; j++) 737 { 738 size_t l = strlen (mp->comment->item[j]); 739 740 memcpy (q, mp->comment->item[j], l); 741 q += l; 742 *q++ = '\n'; 743 } 744 if (q != total_comment + length) 745 abort (); 746 747 selected = is_string_selected (3, total_comment, length); 748 749 freesa (total_comment); 750 751 if (selected) 752 return true; 753 } 754 755 /* Test extracted comments using the --extracted-comment arguments. */ 756 if (grep_task[4].pattern_count > 0 757 && mp->comment_dot != NULL && mp->comment_dot->nitems > 0) 758 { 759 size_t length; 760 char *total_comment; 761 char *q; 762 size_t j; 763 bool selected; 764 765 length = 0; 766 for (j = 0; j < mp->comment_dot->nitems; j++) 767 length += strlen (mp->comment_dot->item[j]) + 1; 768 total_comment = (char *) xallocsa (length); 769 770 q = total_comment; 771 for (j = 0; j < mp->comment_dot->nitems; j++) 772 { 773 size_t l = strlen (mp->comment_dot->item[j]); 774 775 memcpy (q, mp->comment_dot->item[j], l); 776 q += l; 777 *q++ = '\n'; 778 } 779 if (q != total_comment + length) 780 abort (); 781 782 selected = is_string_selected (4, total_comment, length); 783 784 freesa (total_comment); 785 786 if (selected) 787 return true; 788 } 789 790 return false; 791 } 792 793 794 /* Return true if a message matches. */ 795 static bool 796 is_message_selected (const message_ty *mp) 797 { 798 bool result; 799 800 /* Always keep the header entry. */ 801 if (is_header (mp)) 802 return true; 803 804 result = is_message_selected_no_invert (mp); 805 806 if (invert_match) 807 return !result; 808 else 809 return result; 810 } 811 812 813 static void 814 process_message_list (const char *domain, message_list_ty *mlp) 815 { 816 if (string_list_member (domain_names, domain)) 817 /* Keep all the messages in the list. */ 818 ; 819 else 820 /* Keep only the selected messages. */ 821 message_list_remove_if_not (mlp, is_message_selected); 822 } 823 824 825 static msgdomain_list_ty * 826 process_msgdomain_list (msgdomain_list_ty *mdlp) 827 { 828 size_t k; 829 830 for (k = 0; k < mdlp->nitems; k++) 831 process_message_list (mdlp->item[k]->domain, mdlp->item[k]->messages); 832 833 return mdlp; 834 } 835