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
main(int argc,char ** argv)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
no_pass(int opt)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
usage(int status)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
filename_list_match(const string_list_ty * slp,const char * filename)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
nonintr_close(int fd)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
is_string_selected(int grep_pass,const char * str,size_t len)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
is_message_selected_no_invert(const message_ty * mp)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
is_message_selected(const message_ty * mp)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
process_message_list(const char * domain,message_list_ty * mlp)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 *
process_msgdomain_list(msgdomain_list_ty * mdlp)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