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