agrep.c revision 1.2
1/*
2  agrep.c - Approximate grep
3
4  This software is released under a BSD-style license.
5  See the file LICENSE for details and copyright.
6
7*/
8
9#ifdef HAVE_CONFIG_H
10#include <config.h>
11#endif /* HAVE_CONFIG_H */
12#include <stdio.h>
13#include <stdlib.h>
14#include <locale.h>
15#include <string.h>
16#include <sys/types.h>
17#include <sys/stat.h>
18#include <fcntl.h>
19#include <errno.h>
20#include <assert.h>
21#include <limits.h>
22#include <unistd.h>
23#ifdef HAVE_GETOPT_H
24#include <getopt.h>
25#endif /* HAVE_GETOPT_H */
26#include "regex.h"
27
28#ifdef HAVE_GETTEXT
29#include <libintl.h>
30#else
31#define gettext(s) s
32#define bindtextdomain(p, d)
33#define textdomain(p)
34#endif
35
36#define _(String) gettext(String)
37
38#undef MAX
39#undef MIN
40#define MAX(a, b) (((a) >= (b)) ? (a) : (b))
41#define MIN(a, b) (((a) <= (b)) ? (a) : (b))
42
43/* Short options. */
44static char const short_options[] =
45"cd:e:hiklnqsvwyBD:E:HI:MS:V0123456789-:";
46
47static int show_help;
48char *program_name;
49
50#ifdef HAVE_GETOPT_LONG
51/* Long options that have no corresponding short equivalents. */
52enum {
53  COLOR_OPTION = CHAR_MAX + 1,
54  SHOW_POSITION_OPTION
55};
56
57/* Long option equivalences. */
58static struct option const long_options[] =
59{
60  {"best-match", no_argument, NULL, 'B'},
61  {"color", no_argument, NULL, COLOR_OPTION},
62  {"colour", no_argument, NULL, COLOR_OPTION},
63  {"count", no_argument, NULL, 'c'},
64  {"delete-cost", required_argument, NULL, 'D'},
65  {"delimiter", no_argument, NULL, 'd'},
66  {"delimiter-after", no_argument, NULL, 'M'},
67  {"files-with-matches", no_argument, NULL, 'l'},
68  {"help", no_argument, &show_help, 1},
69  {"ignore-case", no_argument, NULL, 'i'},
70  {"insert-cost", required_argument, NULL, 'I'},
71  {"invert-match", no_argument, NULL, 'v'},
72  {"line-number", no_argument, NULL, 'n'},
73  {"literal", no_argument, NULL, 'k'},
74  {"max-errors", required_argument, NULL, 'E'},
75  {"no-filename", no_argument, NULL, 'h'},
76  {"nothing", no_argument, NULL, 'y'},
77  {"quiet", no_argument, NULL, 'q'},
78  {"record-number", no_argument, NULL, 'n'},
79  {"regexp", required_argument, NULL, 'e'},
80  {"show-cost", no_argument, NULL, 's'},
81  {"show-position", no_argument, NULL, SHOW_POSITION_OPTION},
82  {"silent", no_argument, NULL, 'q'},
83  {"substitute-cost", required_argument, NULL, 'S'},
84  {"version", no_argument, NULL, 'V'},
85  {"with-filename", no_argument, NULL, 'H'},
86  {"word-regexp", no_argument, NULL, 'w'},
87  {0, 0, 0, 0}
88};
89#endif /* HAVE_GETOPT_LONG */
90
91__dead static void
92tre_agrep_usage(int status)
93{
94  if (status != 0)
95    {
96      fprintf(stderr, _("Usage: %s [OPTION]... PATTERN [FILE]...\n"),
97	      program_name);
98      fprintf(stderr, _("Try `%s --help' for more information.\n"),
99              program_name);
100    }
101  else
102    {
103      printf(_("Usage: %s [OPTION]... PATTERN [FILE]...\n"), program_name);
104      printf(_("\
105Searches for approximate matches of PATTERN in each FILE or standard input.\n\
106Example: `%s -2 optimize foo.txt' outputs all lines in file `foo.txt' that\n\
107match \"optimize\" within two errors.  E.g. lines which contain \"optimise\",\n\
108\"optmise\", and \"opitmize\" all match.\n"), program_name);
109      printf("\n");
110      printf(_("\
111Regexp selection and interpretation:\n\
112  -e, --regexp=PATTERN	    use PATTERN as a regular expression\n\
113  -i, --ignore-case	    ignore case distinctions\n\
114  -k, --literal		    PATTERN is a literal string\n\
115  -w, --word-regexp	    force PATTERN to match only whole words\n\
116\n\
117Approximate matching settings:\n\
118  -D, --delete-cost=NUM	    set cost of missing characters\n\
119  -I, --insert-cost=NUM	    set cost of extra characters\n\
120  -S, --substitute-cost=NUM set cost of wrong characters\n\
121  -E, --max-errors=NUM	    select records that have at most NUM errors\n\
122  -#			    select records that have at most # errors (# is a\n\
123			    digit between 0 and 9)\n\
124\n\
125Miscellaneous:\n\
126  -d, --delimiter=PATTERN   set the record delimiter regular expression\n\
127  -v, --invert-match	    select non-matching records\n\
128  -V, --version		    print version information and exit\n\
129  -y, --nothing		    does nothing (for compatibility with the non-free\n\
130			    agrep program)\n\
131      --help		    display this help and exit\n\
132\n\
133Output control:\n\
134  -B, --best-match	    only output records with least errors\n\
135  -c, --count		    only print a count of matching records per FILE\n\
136  -h, --no-filename	    suppress the prefixing filename on output\n\
137  -H, --with-filename	    print the filename for each match\n\
138  -l, --files-with-matches  only print FILE names containing matches\n\
139  -M, --delimiter-after     print record delimiter after record if -d is used\n\
140  -n, --record-number	    print record number with output\n\
141      --line-number         same as -n\n\
142  -q, --quiet, --silent	    suppress all normal output\n\
143  -s, --show-cost	    print match cost with output\n\
144      --colour, --color     use markers to distinguish the matching \
145strings\n\
146      --show-position       prefix each output record with start and end\n\
147                            position of the first match within the record\n"));
148      printf("\n");
149      printf(_("\
150With no FILE, or when FILE is -, reads standard input.  If less than two\n\
151FILEs are given, -h is assumed.  Exit status is 0 if a match is found, 1 for\n\
152no match, and 2 if there were errors.  If -E or -# is not specified, only\n\
153exact matches are selected.\n"));
154      printf("\n");
155      printf(_("\
156PATTERN is a POSIX extended regular expression (ERE) with the TRE extensions.\n\
157See tre(7) for a complete description.\n"));
158      printf("\n");
159      printf(_("Report bugs to: "));
160      printf("%s.\n", PACKAGE_BUGREPORT);
161    }
162  exit(status);
163}
164
165static regex_t preg;	  /* Compiled pattern to search for. */
166static regex_t delim;	  /* Compiled record delimiter pattern. */
167
168#define INITIAL_BUF_SIZE 10240	/* Initial size of the buffer. */
169static char *buf;	   /* Buffer for scanning text. */
170static int buf_size;	   /* Current size of the buffer. */
171static int data_len;	   /* Amount of data in the buffer. */
172static char *record;	   /* Start of current record. */
173static char *next_record;  /* Start of next record. */
174static int record_len;	   /* Length of current record. */
175static int delim_len;      /* Length of delimiter before record. */
176static int next_delim_len; /* Length of delimiter after record. */
177static int delim_after = 1;/* If true, print the delimiter after the record. */
178static int at_eof;
179static int have_matches;   /* If true, matches have been found. */
180
181static int invert_match;   /* Show only non-matching records. */
182static int print_filename; /* Output filename. */
183static int print_recnum;   /* Output record number. */
184static int print_cost;	   /* Output match cost. */
185static int count_matches;  /* Count matching records. */
186static int list_files;	   /* List matching files. */
187static int color_option;   /* Highlight matches. */
188static int print_position;  /* Show start and end offsets for matches. */
189
190static int best_match;	     /* Output only best matches. */
191static int best_cost;	     /* Best match cost found so far. */
192static int be_silent;	     /* Never output anything */
193
194static regaparams_t match_params;
195
196/* The color string used with the --color option.  If set, the
197   environment variable GREP_COLOR overrides this default value. */
198static const char *highlight = "01;31";
199
200/* Sets `record' to the next complete record from file `fd', and `record_len'
201   to the length of the record.	 Returns 1 when there are no more records,
202   0 otherwise. */
203static inline int
204tre_agrep_get_next_record(int fd, const char *filename)
205{
206  if (at_eof)
207    return 1;
208
209  while (1)
210    {
211      int errcode;
212      regmatch_t pmatch[1];
213
214      if (next_record == NULL)
215	{
216	  int r;
217	  int read_size = buf_size - data_len;
218
219	  if (read_size <= 0)
220	    {
221	      /* The buffer is full and no record delimiter found yet,
222		 we need to grow the buffer.  We double the size to
223		 avoid rescanning the data too many times when the
224		 records are very large. */
225	      buf_size *= 2;
226	      buf = realloc(buf, buf_size);
227	      if (buf == NULL)
228		{
229		  fprintf(stderr, "%s: %s\n", program_name, _("Out of memory"));
230		  exit(2);
231		}
232	      read_size = buf_size - data_len;
233	    }
234
235	  r = read(fd, buf + data_len, read_size);
236	  if (r < 0)
237	    {
238	      /* Read error. */
239	      char *err;
240	      if (errno == EINTR)
241		continue;
242	      err = strerror(errno);
243	      fprintf(stderr, "%s: ", program_name);
244	      fprintf(stderr, _("Error reading from %s: %s\n"), filename, err);
245	      return 1;
246	    }
247
248	  if (r == 0)
249	    {
250	      /* End of file.  Return the last record. */
251	      record = buf;
252	      record_len = data_len;
253	      at_eof = 1;
254	      /* The empty string after a trailing delimiter is not considered
255		 to be a record. */
256	      if (record_len == 0)
257		return 1;
258	      return 0;
259	    }
260	  data_len += r;
261	  next_record = buf;
262	}
263
264      /* Find the next record delimiter. */
265      errcode = tre_regnexec(&delim, next_record, data_len - (next_record - buf),
266			 1, pmatch, 0);
267
268
269      switch (errcode)
270	{
271	case REG_OK:
272	  /* Record delimiter found, now we know how long the current
273	     record is. */
274	  record = next_record;
275	  record_len = pmatch[0].rm_so;
276	  delim_len = next_delim_len;
277
278	  next_delim_len = pmatch[0].rm_eo - pmatch[0].rm_so;
279	  next_record = next_record + pmatch[0].rm_eo;
280	  return 0;
281	  break;
282
283	case REG_NOMATCH:
284	  if (next_record == buf)
285	    {
286	      next_record = NULL;
287	      continue;
288	    }
289
290	  /* Move the data to start of the buffer and read more
291	     data. */
292	  memmove(buf, next_record, buf + data_len - next_record);
293	  data_len = buf + data_len - next_record;
294	  next_record = NULL;
295	  continue;
296	  break;
297
298	case REG_ESPACE:
299	  fprintf(stderr, "%s: %s\n", program_name, _("Out of memory"));
300	  exit(2);
301	  break;
302
303	default:
304	  assert(0);
305	  break;
306	}
307    }
308}
309
310
311static int
312tre_agrep_handle_file(const char *filename)
313{
314  int fd;
315  int count = 0;
316  int recnum = 0;
317
318  /* Allocate the initial buffer. */
319  if (buf == NULL)
320    {
321      buf = malloc(INITIAL_BUF_SIZE);
322      if (buf == NULL)
323	{
324	  fprintf(stderr, "%s: %s\n", program_name, _("Out of memory"));
325	  exit(2);
326	}
327      buf_size = INITIAL_BUF_SIZE;
328    }
329
330  /* Reset read buffer state. */
331  next_record = NULL;
332  data_len = 0;
333
334  if (!filename || strcmp(filename, "-") == 0)
335    {
336      if (best_match)
337	{
338	  fprintf(stderr, "%s: %s\n", program_name,
339		  _("Cannot use -B when reading from standard input."));
340	  return 2;
341	}
342      fd = 0;
343      filename = _("(standard input)");
344    }
345  else
346    {
347      fd = open(filename, O_RDONLY);
348    }
349
350  if (fd < 0)
351    {
352      fprintf(stderr, "%s: %s: %s\n", program_name, filename, strerror(errno));
353      return 1;
354    }
355
356
357  /* Go through all records and output the matching ones, or the non-matching
358     ones if `invert_match' is true. */
359  at_eof = 0;
360  while (!tre_agrep_get_next_record(fd, filename))
361    {
362      int errcode;
363      regamatch_t match;
364      regmatch_t pmatch[1];
365      recnum++;
366      memset(&match, 0, sizeof(match));
367      if (best_match)
368	match_params.max_cost = best_cost;
369      if (color_option || print_position)
370	{
371	  match.pmatch = pmatch;
372	  match.nmatch = 1;
373	}
374
375      /* Stop searching for better matches if an exact match is found. */
376      if (best_match == 1 && best_cost == 0)
377	break;
378
379      /* See if the record matches. */
380      errcode = tre_reganexec(&preg, record, record_len, &match, match_params, 0);
381      if ((!invert_match && errcode == REG_OK)
382	  || (invert_match && errcode == REG_NOMATCH))
383	{
384	  if (be_silent)
385	    exit(0);
386
387	  count++;
388	  have_matches = 1;
389	  if (best_match)
390	    {
391	      if (best_match == 1)
392		{
393		  /* First best match pass. */
394		  if (match.cost < best_cost)
395		    best_cost = match.cost;
396		  continue;
397		}
398	      /* Second best match pass. */
399	      if (match.cost > best_cost)
400		continue;
401	    }
402
403	  if (list_files)
404	    {
405	      printf("%s\n", filename);
406	      break;
407	    }
408	  else if (!count_matches)
409	    {
410	      if (print_filename)
411		printf("%s:", filename);
412	      if (print_recnum)
413		printf("%d:", recnum);
414	      if (print_cost)
415		printf("%d:", match.cost);
416	      if (print_position)
417		printf("%d-%d:",
418		       invert_match ? 0 : (int)pmatch[0].rm_so,
419		       invert_match ? record_len : (int)pmatch[0].rm_eo);
420
421	      /* Adjust record boundaries so we print the delimiter
422		 before or after the record. */
423	      if (delim_after)
424		{
425		  record_len += next_delim_len;
426		}
427	      else
428		{
429		  record -= delim_len;
430		  record_len += delim_len;
431		  pmatch[0].rm_so += delim_len;
432		  pmatch[0].rm_eo += delim_len;
433		}
434
435	      if (color_option && !invert_match)
436		{
437		  printf("%.*s", (int)pmatch[0].rm_so, record);
438		  printf("\33[%sm", highlight);
439		  printf("%.*s", (int)(pmatch[0].rm_eo - pmatch[0].rm_so),
440			 record + pmatch[0].rm_so);
441		  fputs("\33[00m", stdout);
442		  printf("%.*s", (int)(record_len - pmatch[0].rm_eo),
443			 record + pmatch[0].rm_eo);
444		}
445	      else
446		{
447		  printf("%.*s", record_len, record);
448		}
449	    }
450	}
451    }
452
453  if (count_matches && !best_match && !be_silent)
454    {
455      if (print_filename)
456	printf("%s:", filename);
457      printf("%d\n", count);
458    }
459
460  if (fd)
461    close(fd);
462
463  return 0;
464}
465
466
467
468int
469main(int argc, char **argv)
470{
471  int c, errcode;
472  int comp_flags = REG_EXTENDED;
473  char *tmp_str;
474  char *regexp = NULL;
475  const char *delim_regexp = "\n";
476  int word_regexp = 0;
477  int literal_string = 0;
478  int max_cost_set = 0;
479
480  setlocale (LC_ALL, "");
481  bindtextdomain (PACKAGE, LOCALEDIR);
482  textdomain (PACKAGE);
483
484  /* Get the program name without the path (for error messages etc). */
485  program_name = argv[0];
486  if (program_name)
487    {
488      tmp_str = strrchr(program_name, '/');
489      if (tmp_str)
490	program_name = tmp_str + 1;
491    }
492
493  /* Defaults. */
494  print_filename = -1;
495  print_cost = 0;
496  be_silent = 0;
497  tre_regaparams_default(&match_params);
498  match_params.max_cost = 0;
499
500  /* Parse command line options. */
501  while (1)
502    {
503#ifdef HAVE_GETOPT_LONG
504      c = getopt_long(argc, argv, short_options, long_options, NULL);
505#else /* !HAVE_GETOPT_LONG */
506      c = getopt(argc, argv, short_options);
507#endif /* !HAVE_GETOPT_LONG */
508      if (c == -1)
509	break;
510
511      switch (c)
512	{
513	case 'c':
514	  /* Count number of matching records. */
515	  count_matches = 1;
516	  break;
517	case 'd':
518	  /* Set record delimiter regexp. */
519	  delim_regexp = optarg;
520	  if (delim_after == 1)
521	    delim_after = 0;
522	  break;
523	case 'e':
524	  /* Regexp to use. */
525	  regexp = optarg;
526	  break;
527	case 'h':
528	  /* Don't prefix filename on output if there are multiple files. */
529	  print_filename = 0;
530	  break;
531	case 'i':
532	  /* Ignore case. */
533	  comp_flags |= REG_ICASE;
534	  break;
535	case 'k':
536	  /* The pattern is a literal string. */
537	  literal_string = 1;
538	  break;
539	case 'l':
540	  /* Only print files that contain matches. */
541	  list_files = 1;
542	  break;
543	case 'n':
544	  /* Print record number of matching record. */
545	  print_recnum = 1;
546	  break;
547	case 'q':
548	  be_silent = 1;
549	  break;
550	case 's':
551	  /* Print match cost of matching record. */
552	  print_cost = 1;
553	  break;
554	case 'v':
555	  /* Select non-matching records. */
556	  invert_match = 1;
557	  break;
558	case 'w':
559	  /* Match only whole words. */
560	  word_regexp = 1;
561	  break;
562	case 'y':
563	  /* Compatibility option, does nothing. */
564	  break;
565	case 'B':
566	  /* Select only the records which have the best match. */
567	  best_match = 1;
568	  break;
569	case 'D':
570	  /* Set the cost of a deletion. */
571	  match_params.cost_del = atoi(optarg);
572	  break;
573	case 'E':
574	  /* Set the maximum number of errors allowed for a record to match. */
575	  match_params.max_cost = atoi(optarg);
576	  max_cost_set = 1;
577	  break;
578	case 'H':
579	  /* Always print filename prefix on output. */
580	  print_filename = 1;
581	  break;
582	case 'I':
583	  /* Set the cost of an insertion. */
584	  match_params.cost_ins = atoi(optarg);
585	  break;
586	case 'M':
587	  /* Print delimiters after matches instead of before. */
588	  delim_after = 2;
589	  break;
590	case 'S':
591	  /* Set the cost of a substitution. */
592	  match_params.cost_subst = atoi(optarg);
593	  break;
594	case 'V':
595	  {
596	    /* Print version string and exit. */
597	    char *version;
598	    tre_config(TRE_CONFIG_VERSION, &version);
599	    printf("%s (TRE agrep) %s\n\n", program_name, version);
600	    printf(_("\
601Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>.\n"));
602	    printf("\n");
603	    exit(0);
604	    break;
605	  }
606	case '?':
607	  /* Ambiguous match or extraneous parameter. */
608	  break;
609
610	case '-':
611	  /* Emulate some long options on systems which don't
612	     have getopt_long. */
613	  if (strcmp(optarg, "color") == 0
614	      || strcmp(optarg, "colour") == 0)
615	    color_option = 1;
616	  else if (strcmp(optarg, "show-position") == 0)
617	    print_position = 1;
618	  else if (strcmp(optarg, "help") == 0)
619	    show_help = 1;
620	  else
621	    {
622	      fprintf(stderr, _("%s: invalid option --%s\n"),
623		      program_name, optarg);
624	      exit(2);
625	    }
626	  break;
627
628#ifdef HAVE_GETOPT_LONG
629	case COLOR_OPTION:
630	  color_option = 1;
631	  break;
632	case SHOW_POSITION_OPTION:
633	  print_position = 1;
634	  break;
635#endif /* HAVE_GETOPT_LONG */
636	case 0:
637	  /* Long options without corresponding short options. */
638	  break;
639
640	default:
641	  if (c >= '0' && c <= '9')
642	    match_params.max_cost = c - '0';
643	  else
644	    tre_agrep_usage(2);
645	  max_cost_set = 1;
646	  break;
647	}
648    }
649
650  if (show_help)
651    tre_agrep_usage(0);
652
653  if (color_option)
654    {
655      char *user_highlight = getenv("GREP_COLOR");
656      if (user_highlight && *user_highlight != '\0')
657	highlight = user_highlight;
658    }
659
660  /* Get the pattern. */
661  if (regexp == NULL)
662    {
663      if (optind >= argc)
664	tre_agrep_usage(2);
665      regexp = argv[optind++];
666    }
667
668  /* If -k is specified, make the regexp literal.  This uses
669     the \Q and \E extensions.	If the string already contains
670     occurrences of \E, we need to handle them separately.  This is a
671     pain, but can't really be avoided if we want to create a regexp
672     which works together with -w (see below). */
673  if (literal_string)
674    {
675      char *next_pos = regexp;
676      char *new_re, *new_re_end;
677      int n = 0;
678      int len;
679
680      next_pos = regexp;
681      while (next_pos)
682	{
683	  next_pos = strstr(next_pos, "\\E");
684	  if (next_pos)
685	    {
686	      n++;
687	      next_pos += 2;
688	    }
689	}
690
691      len = strlen(regexp);
692      new_re = malloc(len + 5 + n * 7);
693      if (!new_re)
694	{
695	  fprintf(stderr, "%s: %s\n", program_name, _("Out of memory"));
696	  return 2;
697	}
698
699      next_pos = regexp;
700      new_re_end = new_re;
701      strcpy(new_re_end, "\\Q");
702      new_re_end += 2;
703      while (next_pos)
704	{
705	  char *start = next_pos;
706	  next_pos = strstr(next_pos, "\\E");
707	  if (next_pos)
708	    {
709	      strncpy(new_re_end, start, next_pos - start);
710	      new_re_end += next_pos - start;
711	      strcpy(new_re_end, "\\E\\\\E\\Q");
712	      new_re_end += 7;
713	      next_pos += 2;
714	    }
715	  else
716	    {
717	      strcpy(new_re_end, start);
718	      new_re_end += strlen(start);
719	    }
720	}
721      strcpy(new_re_end, "\\E");
722      regexp = new_re;
723    }
724
725  /* If -w is specified, prepend beginning-of-word and end-of-word
726     assertions to the regexp before compiling. */
727  if (word_regexp)
728    {
729      char *tmp = regexp;
730      int len = strlen(tmp);
731      regexp = malloc(len + 7);
732      if (regexp == NULL)
733	{
734	  fprintf(stderr, "%s: %s\n", program_name, _("Out of memory"));
735	  return 2;
736	}
737      strcpy(regexp, "\\<(");
738      strcpy(regexp + 3, tmp);
739      strcpy(regexp + len + 3, ")\\>");
740    }
741
742  /* Compile the pattern. */
743  errcode = tre_regcomp(&preg, regexp, comp_flags);
744  if (errcode)
745    {
746      char errbuf[256];
747      tre_regerror(errcode, &preg, errbuf, sizeof(errbuf));
748      fprintf(stderr, "%s: %s: %s\n",
749	      program_name, _("Error in search pattern"), errbuf);
750      return 2;
751    }
752
753  /* Compile the record delimiter pattern. */
754  errcode = tre_regcomp(&delim, delim_regexp, REG_EXTENDED | REG_NEWLINE);
755  if (errcode)
756    {
757      char errbuf[256];
758      tre_regerror(errcode, &preg, errbuf, sizeof(errbuf));
759      fprintf(stderr, "%s: %s: %s\n",
760	      program_name, _("Error in record delimiter pattern"), errbuf);
761      return 2;
762    }
763
764  if (tre_regexec(&delim, "", 0, NULL, 0) == REG_OK)
765    {
766      fprintf(stderr, "%s: %s\n", program_name,
767	      _("Record delimiter pattern must not match an empty string"));
768      return 2;
769    }
770
771  /* The rest of the arguments are file(s) to match. */
772
773  /* If -h or -H were not specified, print filenames if there are more
774     than one files specified. */
775  if (print_filename == -1)
776    {
777      if (argc - optind <= 1)
778	print_filename = 0;
779      else
780	print_filename = 1;
781    }
782
783  if (optind >= argc)
784    {
785      /* There are no files specified, read from stdin. */
786      tre_agrep_handle_file(NULL);
787    }
788  else if (best_match)
789    {
790      int first_ind = optind;
791
792      /* Best match mode.  Set up the limits first. */
793      if (!max_cost_set)
794	match_params.max_cost = INT_MAX;
795      best_cost = INT_MAX;
796
797      /* Scan all files once without outputting anything, searching
798	 for the best matches. */
799      while (optind < argc)
800	tre_agrep_handle_file(argv[optind++]);
801
802      /* If there were no matches, bail out now. */
803      if (best_cost == INT_MAX)
804	return 1;
805
806      /* Otherwise, rescan the files with max_cost set to the cost
807	 of the best match found previously, this time outputting
808	 the matches. */
809      match_params.max_cost = best_cost;
810      best_match = 2;
811      optind = first_ind;
812      while (optind < argc)
813	tre_agrep_handle_file(argv[optind++]);
814    }
815  else
816    {
817      /* Normal mode. */
818      while (optind < argc)
819	tre_agrep_handle_file(argv[optind++]);
820    }
821
822  return have_matches == 0;
823}
824