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