grep.c revision 131557
153451Speter/* grep.c - main driver file for grep.
2126435Sache   Copyright 1992, 1997-1999, 2000 Free Software Foundation, Inc.
353451Speter
453451Speter   This program is free software; you can redistribute it and/or modify
553451Speter   it under the terms of the GNU General Public License as published by
653451Speter   the Free Software Foundation; either version 2, or (at your option)
753451Speter   any later version.
853451Speter
953451Speter   This program is distributed in the hope that it will be useful,
1053451Speter   but WITHOUT ANY WARRANTY; without even the implied warranty of
1153451Speter   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1253451Speter   GNU General Public License for more details.
1353451Speter
1453451Speter   You should have received a copy of the GNU General Public License
1553451Speter   along with this program; if not, write to the Free Software
1653477Sobrien   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
1753477Sobrien   02111-1307, USA.  */
1853451Speter
1953477Sobrien/* Written July 1992 by Mike Haertel.  */
2053477Sobrien/* Builtin decompression 1997 by Wolfram Schneider <wosch@FreeBSD.org>.  */
2153451Speter
2253477Sobrien/* $FreeBSD: head/gnu/usr.bin/grep/grep.c 131557 2004-07-04 10:02:03Z tjr $ */
2353451Speter
2453477Sobrien#ifdef HAVE_CONFIG_H
2553477Sobrien# include <config.h>
2653451Speter#endif
2753451Speter#include <sys/types.h>
2853477Sobrien#include <sys/stat.h>
2953477Sobrien#if defined(HAVE_MMAP)
3053477Sobrien# include <sys/mman.h>
3153451Speter#endif
3253477Sobrien#if defined(HAVE_SETRLIMIT)
3353477Sobrien# include <sys/time.h>
3453477Sobrien# include <sys/resource.h>
3553451Speter#endif
3653477Sobrien#include <stdio.h>
3753477Sobrien#include "system.h"
3853477Sobrien#include "getopt.h"
3953451Speter#include "getpagesize.h"
4053451Speter#include "grep.h"
4153477Sobrien#include "savedir.h"
42131557Stjr#include "xstrtol.h"
43131557Stjr#include "xalloc.h"
44131557Stjr#include "error.h"
45131557Stjr#include "exclude.h"
46131557Stjr#include "closeout.h"
4753451Speter
4853451Speter#undef MAX
4953451Speter#define MAX(A,B) ((A) > (B) ? (A) : (B))
5053451Speter
5153477Sobrienstruct stats
5253477Sobrien{
53131557Stjr  struct stats const *parent;
5453477Sobrien  struct stat stat;
5553477Sobrien};
5653451Speter
5753477Sobrien/* base of chain of stat buffers, used to detect directory loops */
5853477Sobrienstatic struct stats stats_base;
5953451Speter
6053477Sobrien/* if non-zero, display usage information and exit */
6153477Sobrienstatic int show_help;
6253451Speter
6353477Sobrien/* If non-zero, print the version on standard output and exit.  */
6453477Sobrienstatic int show_version;
6553451Speter
66131557Stjr/* If nonzero, suppress diagnostics for nonexistent or unreadable files.  */
67131557Stjrstatic int suppress_errors;
68131557Stjr
6955379Sobrien/* If nonzero, use mmap if possible.  */
7055379Sobrienstatic int mmap_option;
7155379Sobrien
7255404Sru/* If zero, output nulls after filenames.  */
7355404Srustatic int filename_mask;
7455404Sru
75131557Stjr/* If nonzero, use grep_color marker.  */
76131557Stjrstatic int color_option;
77131557Stjr
78131557Stjr/* If nonzero, show only the part of a line matching the expression. */
79131557Stjrstatic int only_matching;
80131557Stjr
81131557Stjr/* The color string used.  The user can overwrite it using the environment
82131557Stjr   variable GREP_COLOR.  The default is to print red.  */
83131557Stjrstatic const char *grep_color = "01;31";
84131557Stjr
85131557Stjrstatic struct exclude *excluded_patterns;
86131557Stjrstatic struct exclude *included_patterns;
8755379Sobrien/* Short options.  */
8855379Sobrienstatic char const short_options[] =
89131557Stjr"0123456789A:B:C:D:EFGHIJPUVX:abcd:e:f:hiKLlm:noqRrsuvwxyZz";
9055379Sobrien
9156233Sru/* Non-boolean long options that have no corresponding short equivalents.  */
9256233Sruenum
9356233Sru{
94131557Stjr  BINARY_FILES_OPTION = CHAR_MAX + 1,
95131557Stjr  COLOR_OPTION,
96131557Stjr  INCLUDE_OPTION,
97131557Stjr  EXCLUDE_OPTION,
98131557Stjr  EXCLUDE_FROM_OPTION,
99131557Stjr  LINE_BUFFERED_OPTION,
100131557Stjr  LABEL_OPTION
10156233Sru};
10256233Sru
10353477Sobrien/* Long options equivalences. */
104131557Stjrstatic struct option const long_options[] =
10553477Sobrien{
10653477Sobrien  {"after-context", required_argument, NULL, 'A'},
10753477Sobrien  {"basic-regexp", no_argument, NULL, 'G'},
10853477Sobrien  {"before-context", required_argument, NULL, 'B'},
10956233Sru  {"binary-files", required_argument, NULL, BINARY_FILES_OPTION},
11053477Sobrien  {"byte-offset", no_argument, NULL, 'b'},
111131557Stjr  {"context", required_argument, NULL, 'C'},
112131557Stjr  {"color", optional_argument, NULL, COLOR_OPTION},
113131557Stjr  {"colour", optional_argument, NULL, COLOR_OPTION},
11453477Sobrien  {"count", no_argument, NULL, 'c'},
115131557Stjr  {"devices", required_argument, NULL, 'D'},
11653477Sobrien  {"directories", required_argument, NULL, 'd'},
11753477Sobrien  {"extended-regexp", no_argument, NULL, 'E'},
118131557Stjr  {"exclude", required_argument, NULL, EXCLUDE_OPTION},
119131557Stjr  {"exclude-from", required_argument, NULL, EXCLUDE_FROM_OPTION},
12053477Sobrien  {"file", required_argument, NULL, 'f'},
12153477Sobrien  {"files-with-matches", no_argument, NULL, 'l'},
12253477Sobrien  {"files-without-match", no_argument, NULL, 'L'},
12353477Sobrien  {"fixed-regexp", no_argument, NULL, 'F'},
12453477Sobrien  {"fixed-strings", no_argument, NULL, 'F'},
12553477Sobrien  {"help", no_argument, &show_help, 1},
126131557Stjr  {"include", required_argument, NULL, INCLUDE_OPTION},
12753477Sobrien  {"ignore-case", no_argument, NULL, 'i'},
128131557Stjr  {"label", required_argument, NULL, LABEL_OPTION},
129131557Stjr  {"line-buffered", no_argument, NULL, LINE_BUFFERED_OPTION},
13053477Sobrien  {"line-number", no_argument, NULL, 'n'},
13153477Sobrien  {"line-regexp", no_argument, NULL, 'x'},
132131557Stjr  {"max-count", required_argument, NULL, 'm'},
13355379Sobrien  {"mmap", no_argument, &mmap_option, 1},
13453477Sobrien  {"no-filename", no_argument, NULL, 'h'},
13553477Sobrien  {"no-messages", no_argument, NULL, 's'},
136103372Sobrien  {"bz2decompress", no_argument, NULL, 'J'},
13755379Sobrien#if HAVE_LIBZ > 0
13855404Sru  {"decompress", no_argument, NULL, 'Z'},
13955404Sru  {"null", no_argument, &filename_mask, 0},
14055379Sobrien#else
14155379Sobrien  {"null", no_argument, NULL, 'Z'},
14255379Sobrien#endif
14355379Sobrien  {"null-data", no_argument, NULL, 'z'},
144131557Stjr  {"only-matching", no_argument, NULL, 'o'},
145131557Stjr  {"perl-regexp", no_argument, NULL, 'P'},
14653477Sobrien  {"quiet", no_argument, NULL, 'q'},
14753477Sobrien  {"recursive", no_argument, NULL, 'r'},
148131557Stjr  {"recursive", no_argument, NULL, 'R'},
14953477Sobrien  {"regexp", required_argument, NULL, 'e'},
15055379Sobrien  {"invert-match", no_argument, NULL, 'v'},
15153477Sobrien  {"silent", no_argument, NULL, 'q'},
15253477Sobrien  {"text", no_argument, NULL, 'a'},
15353477Sobrien  {"binary", no_argument, NULL, 'U'},
15453477Sobrien  {"unix-byte-offsets", no_argument, NULL, 'u'},
15553477Sobrien  {"version", no_argument, NULL, 'V'},
15653477Sobrien  {"with-filename", no_argument, NULL, 'H'},
15753477Sobrien  {"word-regexp", no_argument, NULL, 'w'},
15853477Sobrien  {0, 0, 0, 0}
15953477Sobrien};
16053451Speter
16153451Speter/* Define flags declared in grep.h. */
16253451Speterint match_icase;
16353451Speterint match_words;
16453451Speterint match_lines;
16555379Sobrienunsigned char eolbyte;
16653451Speter
16753451Speter/* For error messages. */
168131557Stjr/* The name the program was run with, stripped of any leading path. */
169131557Stjrchar *program_name;
17053477Sobrienstatic char const *filename;
17153451Speterstatic int errseen;
17253451Speter
17353477Sobrien/* How to handle directories.  */
17453477Sobrienstatic enum
17553477Sobrien  {
17653477Sobrien    READ_DIRECTORIES,
17753477Sobrien    RECURSE_DIRECTORIES,
17853477Sobrien    SKIP_DIRECTORIES
179131557Stjr  } directories = READ_DIRECTORIES;
18053477Sobrien
181131557Stjr/* How to handle devices. */
182131557Stjrstatic enum
183131557Stjr  {
184131557Stjr    READ_DEVICES,
185131557Stjr    SKIP_DEVICES
186131557Stjr  } devices = READ_DEVICES;
187131557Stjr
188131557Stjrstatic int grepdir PARAMS ((char const *, struct stats const *));
189131557Stjr#if defined(HAVE_DOS_FILE_CONTENTS)
19053477Sobrienstatic inline int undossify_input PARAMS ((register char *, size_t));
19153477Sobrien#endif
19253477Sobrien
19353477Sobrien/* Functions we'll use to search. */
194131557Stjrstatic void (*compile) PARAMS ((char const *, size_t));
195131557Stjrstatic size_t (*execute) PARAMS ((char const *, size_t, size_t *, int));
19653477Sobrien
197131557Stjr/* Like error, but suppress the diagnostic if requested.  */
19853451Speterstatic void
199131557Stjrsuppressible_error (char const *mesg, int errnum)
20053451Speter{
201131557Stjr  if (! suppress_errors)
202131557Stjr    error (0, errnum, "%s", mesg);
20353451Speter  errseen = 1;
20453451Speter}
20553451Speter
20653477Sobrien/* Convert STR to a positive integer, storing the result in *OUT.
207131557Stjr   STR must be a valid context length argument; report an error if it
208131557Stjr   isn't.  */
209131557Stjrstatic void
210131557Stjrcontext_length_arg (char const *str, int *out)
21153477Sobrien{
212131557Stjr  uintmax_t value;
213131557Stjr  if (! (xstrtoumax (str, 0, 10, &value, "") == LONGINT_OK
214131557Stjr	 && 0 <= (*out = value)
215131557Stjr	 && *out == value))
216131557Stjr    {
217131557Stjr      error (2, 0, "%s: %s\n", str, _("invalid context length argument"));
218131557Stjr    }
21953477Sobrien}
22053477Sobrien
22153477Sobrien
22253451Speter/* Hairy buffering mechanism for grep.  The intent is to keep
22353451Speter   all reads aligned on a page boundary and multiples of the
224131557Stjr   page size, unless a read yields a partial page.  */
22553451Speter
22653451Speterstatic char *buffer;		/* Base of buffer. */
227131557Stjrstatic size_t bufalloc;		/* Allocated buffer size, counting slop. */
228131557Stjr#define INITIAL_BUFSIZE 32768	/* Initial buffer size, not counting slop. */
22953451Speterstatic int bufdesc;		/* File descriptor. */
23053451Speterstatic char *bufbeg;		/* Beginning of user-visible stuff. */
23153451Speterstatic char *buflim;		/* Limit of user-visible stuff. */
23253477Sobrienstatic size_t pagesize;		/* alignment of memory pages */
23355379Sobrienstatic off_t bufoffset;		/* Read offset; defined on regular files.  */
234131557Stjrstatic off_t after_last_match;	/* Pointer after last matching line that
235131557Stjr				   would have been output if we were
236131557Stjr				   outputting characters. */
23753451Speter
23853477Sobrien#if defined(HAVE_MMAP)
23955379Sobrienstatic int bufmapped;		/* True if buffer is memory-mapped.  */
24053477Sobrienstatic off_t initial_bufoffset;	/* Initial value of bufoffset. */
241131557Stjr#else
242131557Stjr# define bufmapped 0
24353451Speter#endif
24453451Speter
245103372Sobrien#include <bzlib.h>
246103372Sobrienstatic BZFILE* bzbufdesc;	/* libbz2 file handle. */
247103372Sobrienstatic int BZflag;		/* uncompress before searching. */
24853451Speter#if HAVE_LIBZ > 0
24953451Speter#include <zlib.h>
25053477Sobrienstatic gzFile gzbufdesc;	/* zlib file descriptor. */
25153477Sobrienstatic int Zflag;		/* uncompress before searching. */
25253451Speter#endif
25353451Speter
25453477Sobrien/* Return VAL aligned to the next multiple of ALIGNMENT.  VAL can be
25553477Sobrien   an integer or a pointer.  Both args must be free of side effects.  */
25653477Sobrien#define ALIGN_TO(val, alignment) \
25753477Sobrien  ((size_t) (val) % (alignment) == 0 \
25853477Sobrien   ? (val) \
25953477Sobrien   : (val) + ((alignment) - (size_t) (val) % (alignment)))
26053477Sobrien
26153477Sobrien/* Reset the buffer for a new file, returning zero if we should skip it.
26253477Sobrien   Initialize on the first time through. */
26353477Sobrienstatic int
26456920Srureset (int fd, char const *file, struct stats *stats)
26553451Speter{
266131557Stjr  if (! pagesize)
26753451Speter    {
26853477Sobrien      pagesize = getpagesize ();
269131557Stjr      if (pagesize == 0 || 2 * pagesize + 1 <= pagesize)
27053477Sobrien	abort ();
271131557Stjr      bufalloc = ALIGN_TO (INITIAL_BUFSIZE, pagesize) + pagesize + 1;
272131557Stjr      buffer = xmalloc (bufalloc);
27353451Speter    }
274103372Sobrien  if (BZflag)
275103372Sobrien    {
276103372Sobrien    bzbufdesc = BZ2_bzdopen(fd, "r");
277103372Sobrien    if (bzbufdesc == NULL)
278131557Stjr      error(2, 0, _("memory exhausted"));
279103372Sobrien    }
28053451Speter#if HAVE_LIBZ > 0
28155379Sobrien  if (Zflag)
28255379Sobrien    {
28353451Speter    gzbufdesc = gzdopen(fd, "r");
28453477Sobrien    if (gzbufdesc == NULL)
285131557Stjr      error(2, 0, _("memory exhausted"));
28655379Sobrien    }
28753451Speter#endif
28855379Sobrien
289131557Stjr  bufbeg = buflim = ALIGN_TO (buffer + 1, pagesize);
290131557Stjr  bufbeg[-1] = eolbyte;
29153477Sobrien  bufdesc = fd;
29253477Sobrien
29355379Sobrien  if (fstat (fd, &stats->stat) != 0)
29455379Sobrien    {
295131557Stjr      error (0, errno, "fstat");
29655379Sobrien      return 0;
29755379Sobrien    }
29853477Sobrien  if (directories == SKIP_DIRECTORIES && S_ISDIR (stats->stat.st_mode))
29953477Sobrien    return 0;
300131557Stjr#ifndef DJGPP
301131557Stjr  if (devices == SKIP_DEVICES && (S_ISCHR(stats->stat.st_mode) || S_ISBLK(stats->stat.st_mode) || S_ISSOCK(stats->stat.st_mode)))
302131557Stjr#else
303131557Stjr  if (devices == SKIP_DEVICES && (S_ISCHR(stats->stat.st_mode) || S_ISBLK(stats->stat.st_mode)))
304131557Stjr#endif
305131557Stjr    return 0;
30653477Sobrien  if (
307103372Sobrien      BZflag ||
30853451Speter#if HAVE_LIBZ > 0
30953477Sobrien      Zflag ||
31053451Speter#endif
31155379Sobrien      S_ISREG (stats->stat.st_mode))
31255379Sobrien    {
31355379Sobrien      if (file)
31455379Sobrien	bufoffset = 0;
31555379Sobrien      else
31655379Sobrien	{
31755379Sobrien	  bufoffset = lseek (fd, 0, SEEK_CUR);
31855379Sobrien	  if (bufoffset < 0)
31955379Sobrien	    {
320131557Stjr	      error (0, errno, "lseek");
32155379Sobrien	      return 0;
32255379Sobrien	    }
32355379Sobrien	}
324131557Stjr#if defined(HAVE_MMAP)
32555379Sobrien      initial_bufoffset = bufoffset;
32655379Sobrien      bufmapped = mmap_option && bufoffset % pagesize == 0;
32755379Sobrien#endif
32855379Sobrien    }
32953451Speter  else
33053451Speter    {
331131557Stjr#if defined(HAVE_MMAP)
33255379Sobrien      bufmapped = 0;
33355379Sobrien#endif
33453451Speter    }
33553477Sobrien  return 1;
33653451Speter}
33753451Speter
33853451Speter/* Read new stuff into the buffer, saving the specified
33953451Speter   amount of old stuff.  When we're done, 'bufbeg' points
34053451Speter   to the beginning of the buffer contents, and 'buflim'
34155379Sobrien   points just after the end.  Return zero if there's an error.  */
34253451Speterstatic int
343131557Stjrfillbuf (size_t save, struct stats const *stats)
34453451Speter{
34555379Sobrien  size_t fillsize = 0;
34655379Sobrien  int cc = 1;
347131557Stjr  char *readbuf;
34855379Sobrien  size_t readsize;
34953451Speter
350131557Stjr  /* Offset from start of buffer to start of old stuff
35155379Sobrien     that we want to save.  */
352131557Stjr  size_t saved_offset = buflim - save - buffer;
35355379Sobrien
354131557Stjr  if (pagesize <= buffer + bufalloc - buflim)
35553451Speter    {
356131557Stjr      readbuf = buflim;
357131557Stjr      bufbeg = buflim - save;
358131557Stjr    }
359131557Stjr  else
360131557Stjr    {
361131557Stjr      size_t minsize = save + pagesize;
362131557Stjr      size_t newsize;
36355379Sobrien      size_t newalloc;
364131557Stjr      char *newbuf;
36553477Sobrien
366131557Stjr      /* Grow newsize until it is at least as great as minsize.  */
367131557Stjr      for (newsize = bufalloc - pagesize - 1; newsize < minsize; newsize *= 2)
368131557Stjr	if (newsize * 2 < newsize || newsize * 2 + pagesize + 1 < newsize * 2)
369131557Stjr	  xalloc_die ();
370131557Stjr
371131557Stjr      /* Try not to allocate more memory than the file size indicates,
372131557Stjr	 as that might cause unnecessary memory exhaustion if the file
373131557Stjr	 is large.  However, do not use the original file size as a
374131557Stjr	 heuristic if we've already read past the file end, as most
375131557Stjr	 likely the file is growing.  */
37655379Sobrien      if (S_ISREG (stats->stat.st_mode))
37755379Sobrien	{
37855379Sobrien	  off_t to_be_read = stats->stat.st_size - bufoffset;
379131557Stjr	  off_t maxsize_off = save + to_be_read;
380131557Stjr	  if (0 <= to_be_read && to_be_read <= maxsize_off
381131557Stjr	      && maxsize_off == (size_t) maxsize_off
382131557Stjr	      && minsize <= (size_t) maxsize_off
383131557Stjr	      && (size_t) maxsize_off < newsize)
384131557Stjr	    newsize = maxsize_off;
38555379Sobrien	}
38655379Sobrien
387131557Stjr      /* Add enough room so that the buffer is aligned and has room
388131557Stjr	 for byte sentinels fore and aft.  */
389131557Stjr      newalloc = newsize + pagesize + 1;
39055379Sobrien
391131557Stjr      newbuf = bufalloc < newalloc ? xmalloc (bufalloc = newalloc) : buffer;
392131557Stjr      readbuf = ALIGN_TO (newbuf + 1 + save, pagesize);
393131557Stjr      bufbeg = readbuf - save;
394131557Stjr      memmove (bufbeg, buffer + saved_offset, save);
395131557Stjr      bufbeg[-1] = eolbyte;
396131557Stjr      if (newbuf != buffer)
39755379Sobrien	{
398131557Stjr	  free (buffer);
399131557Stjr	  buffer = newbuf;
40055379Sobrien	}
40153451Speter    }
40253451Speter
403131557Stjr  readsize = buffer + bufalloc - readbuf;
404131557Stjr  readsize -= readsize % pagesize;
40555379Sobrien
40653477Sobrien#if defined(HAVE_MMAP)
40755379Sobrien  if (bufmapped)
40853451Speter    {
40955379Sobrien      size_t mmapsize = readsize;
41055379Sobrien
41155379Sobrien      /* Don't mmap past the end of the file; some hosts don't allow this.
41255379Sobrien	 Use `read' on the last page.  */
41355379Sobrien      if (stats->stat.st_size - bufoffset < mmapsize)
41453451Speter	{
41555379Sobrien	  mmapsize = stats->stat.st_size - bufoffset;
41655379Sobrien	  mmapsize -= mmapsize % pagesize;
41753451Speter	}
41855379Sobrien
41955379Sobrien      if (mmapsize
420131557Stjr	  && (mmap ((caddr_t) readbuf, mmapsize,
42155379Sobrien		    PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED,
42255379Sobrien		    bufdesc, bufoffset)
42355379Sobrien	      != (caddr_t) -1))
42453451Speter	{
42555379Sobrien	  /* Do not bother to use madvise with MADV_SEQUENTIAL or
42655379Sobrien	     MADV_WILLNEED on the mmapped memory.  One might think it
42755379Sobrien	     would help, but it slows us down about 30% on SunOS 4.1.  */
42855379Sobrien	  fillsize = mmapsize;
42955379Sobrien	}
43055379Sobrien      else
43155379Sobrien	{
43255379Sobrien	  /* Stop using mmap on this file.  Synchronize the file
43355379Sobrien	     offset.  Do not warn about mmap failures.  On some hosts
43455379Sobrien	     (e.g. Solaris 2.5) mmap can fail merely because some
43555379Sobrien	     other process has an advisory read lock on the file.
43655379Sobrien	     There's no point alarming the user about this misfeature.  */
43753451Speter	  bufmapped = 0;
43855379Sobrien	  if (bufoffset != initial_bufoffset
43955379Sobrien	      && lseek (bufdesc, bufoffset, SEEK_SET) < 0)
44055379Sobrien	    {
441131557Stjr	      error (0, errno, "lseek");
44255379Sobrien	      cc = 0;
44355379Sobrien	    }
44453451Speter	}
44555379Sobrien    }
44655379Sobrien#endif /*HAVE_MMAP*/
44755379Sobrien
44855379Sobrien  if (! fillsize)
44955379Sobrien    {
45055379Sobrien      ssize_t bytesread;
45155379Sobrien      do
452104555Sobrien	if (BZflag && bzbufdesc)
453103372Sobrien	  {
454104555Sobrien	    int bzerr;
455131557Stjr	    bytesread = BZ2_bzRead (&bzerr, bzbufdesc, readbuf, readsize);
456104555Sobrien
457104555Sobrien	    switch (bzerr)
458104555Sobrien	      {
459104555Sobrien	      case BZ_OK:
460104555Sobrien	      case BZ_STREAM_END:
461104555Sobrien		/* ok */
462104555Sobrien		break;
463104555Sobrien	      case BZ_DATA_ERROR_MAGIC:
464104555Sobrien		BZ2_bzReadClose (&bzerr, bzbufdesc); bzbufdesc = NULL;
465104555Sobrien		lseek (bufdesc, 0, SEEK_SET);
466131557Stjr		bytesread = read (bufdesc, readbuf, readsize);
467104555Sobrien		break;
468104555Sobrien	      default:
469104555Sobrien		bytesread = 0;
470104555Sobrien		break;
471104555Sobrien	      }
472103372Sobrien	  }
473103372Sobrien	else
47453451Speter#if HAVE_LIBZ > 0
47555379Sobrien	if (Zflag)
476131557Stjr	  bytesread = gzread (gzbufdesc, readbuf, readsize);
47755379Sobrien	else
47855379Sobrien#endif
479131557Stjr	  bytesread = read (bufdesc, readbuf, readsize);
48055379Sobrien      while (bytesread < 0 && errno == EINTR);
48155379Sobrien      if (bytesread < 0)
48255379Sobrien	cc = 0;
48353477Sobrien      else
48455379Sobrien	fillsize = bytesread;
48553451Speter    }
48655379Sobrien
48755379Sobrien  bufoffset += fillsize;
488131557Stjr#if defined(HAVE_DOS_FILE_CONTENTS)
48955379Sobrien  if (fillsize)
490131557Stjr    fillsize = undossify_input (readbuf, fillsize);
49153451Speter#endif
492131557Stjr  buflim = readbuf + fillsize;
49353451Speter  return cc;
49453451Speter}
49553451Speter
49653451Speter/* Flags controlling the style of output. */
49756233Srustatic enum
498131557Stjr{
499131557Stjr  BINARY_BINARY_FILES,
500131557Stjr  TEXT_BINARY_FILES,
501131557Stjr  WITHOUT_MATCH_BINARY_FILES
502131557Stjr} binary_files;		/* How to handle binary files.  */
503131557Stjr
504131557Stjrstatic int filename_mask;	/* If zero, output nulls after filenames.  */
50553451Speterstatic int out_quiet;		/* Suppress all normal output. */
50653451Speterstatic int out_invert;		/* Print nonmatching stuff. */
50753451Speterstatic int out_file;		/* Print filenames. */
50853451Speterstatic int out_line;		/* Print line numbers. */
50953451Speterstatic int out_byte;		/* Print byte offsets. */
51053451Speterstatic int out_before;		/* Lines of leading context. */
51153451Speterstatic int out_after;		/* Lines of trailing context. */
51253477Sobrienstatic int count_matches;	/* Count matching lines.  */
51353477Sobrienstatic int list_files;		/* List matching files.  */
51453477Sobrienstatic int no_filenames;	/* Suppress file names.  */
515131557Stjrstatic off_t max_count;		/* Stop after outputting this many
516131557Stjr				   lines from an input file.  */
517131557Stjrstatic int line_buffered;       /* If nonzero, use line buffering, i.e.
518131557Stjr				   fflush everyline out.  */
519131557Stjrstatic char *label = NULL;      /* Fake filename for stdin */
52053451Speter
521131557Stjr
52253451Speter/* Internal variables to keep track of byte count, context, etc. */
523131557Stjrstatic uintmax_t totalcc;	/* Total character count before bufbeg. */
524131557Stjrstatic char const *lastnl;	/* Pointer after last newline counted. */
525131557Stjrstatic char const *lastout;	/* Pointer after last character output;
52653451Speter				   NULL if no character has been output
52753451Speter				   or if it's conceptually before bufbeg. */
528131557Stjrstatic uintmax_t totalnl;	/* Total newline count before lastnl. */
529131557Stjrstatic off_t outleft;		/* Maximum number of lines to be output.  */
530131557Stjrstatic int pending;		/* Pending lines of output.
531131557Stjr				   Always kept 0 if out_quiet is true.  */
532131557Stjrstatic int done_on_match;	/* Stop scanning file on first match.  */
533131557Stjrstatic int exit_on_match;	/* Exit on first match.  */
53453451Speter
535131557Stjr#if defined(HAVE_DOS_FILE_CONTENTS)
53653477Sobrien# include "dosbuf.c"
53753477Sobrien#endif
53853477Sobrien
539131557Stjr/* Add two numbers that count input bytes or lines, and report an
540131557Stjr   error if the addition overflows.  */
541131557Stjrstatic uintmax_t
542131557Stjradd_count (uintmax_t a, uintmax_t b)
543131557Stjr{
544131557Stjr  uintmax_t sum = a + b;
545131557Stjr  if (sum < a)
546131557Stjr    error (2, 0, _("input is too large to count"));
547131557Stjr  return sum;
548131557Stjr}
549131557Stjr
55053451Speterstatic void
551131557Stjrnlscan (char const *lim)
55253451Speter{
553131557Stjr  size_t newlines = 0;
554131557Stjr  char const *beg;
555131557Stjr  for (beg = lastnl; beg != lim; beg = memchr (beg, eolbyte, lim - beg), beg++)
556131557Stjr    newlines++;
557131557Stjr  totalnl = add_count (totalnl, newlines);
55855379Sobrien  lastnl = lim;
55953451Speter}
56053451Speter
561131557Stjr/* Print a byte offset, followed by a character separator.  */
56253451Speterstatic void
563131557Stjrprint_offset_sep (uintmax_t pos, char sep)
56453477Sobrien{
565131557Stjr  /* Do not rely on printf to print pos, since uintmax_t may be longer
566131557Stjr     than long, and long long is not portable.  */
56753477Sobrien
56853477Sobrien  char buf[sizeof pos * CHAR_BIT];
56953477Sobrien  char *p = buf + sizeof buf - 1;
57053477Sobrien  *p = sep;
57153477Sobrien
57253477Sobrien  do
57353477Sobrien    *--p = '0' + pos % 10;
57453477Sobrien  while ((pos /= 10) != 0);
57553477Sobrien
57653477Sobrien  fwrite (p, 1, buf + sizeof buf - p, stdout);
57753477Sobrien}
57853477Sobrien
57953477Sobrienstatic void
580131557Stjrprline (char const *beg, char const *lim, int sep)
58153451Speter{
58253451Speter  if (out_file)
58355379Sobrien    printf ("%s%c", filename, sep & filename_mask);
58453451Speter  if (out_line)
58553451Speter    {
58653477Sobrien      nlscan (beg);
587131557Stjr      totalnl = add_count (totalnl, 1);
588131557Stjr      print_offset_sep (totalnl, sep);
58953451Speter      lastnl = lim;
59053451Speter    }
59153451Speter  if (out_byte)
59253477Sobrien    {
593131557Stjr      uintmax_t pos = add_count (totalcc, beg - bufbeg);
594131557Stjr#if defined(HAVE_DOS_FILE_CONTENTS)
59553477Sobrien      pos = dossified_pos (pos);
59653477Sobrien#endif
59753477Sobrien      print_offset_sep (pos, sep);
59853477Sobrien    }
599131557Stjr  if (only_matching)
600131557Stjr    {
601131557Stjr      size_t match_size;
602131557Stjr      size_t match_offset;
603131557Stjr      while ((match_offset = (*execute) (beg, lim - beg, &match_size, 1))
604131557Stjr	  != (size_t) -1)
605131557Stjr        {
606131557Stjr	  char const *b = beg + match_offset;
607131557Stjr	  if (b == lim)
608131557Stjr	    break;
609131557Stjr	  if (match_size == 0)
610131557Stjr	    break;
611131557Stjr	  if(color_option)
612131557Stjr	    printf("\33[%sm", grep_color);
613131557Stjr	  fwrite(b, sizeof (char), match_size, stdout);
614131557Stjr	  if(color_option)
615131557Stjr	    fputs("\33[00m", stdout);
616131557Stjr	  fputs("\n", stdout);
617131557Stjr	  beg = b + match_size;
618131557Stjr        }
619131557Stjr      lastout = lim;
620131557Stjr      if(line_buffered)
621131557Stjr	fflush(stdout);
622131557Stjr      return;
623131557Stjr    }
624131557Stjr  if (color_option)
625131557Stjr    {
626131557Stjr      size_t match_size;
627131557Stjr      size_t match_offset;
628131557Stjr      if(match_icase)
629131557Stjr        {
630131557Stjr	  /* Yuck, this is tricky */
631131557Stjr          char *buf = (char*) xmalloc (lim - beg);
632131557Stjr	  char *ibeg = buf;
633131557Stjr	  char *ilim = ibeg + (lim - beg);
634131557Stjr	  int i;
635131557Stjr	  for (i = 0; i < lim - beg; i++)
636131557Stjr	    ibeg[i] = tolower (beg[i]);
637131557Stjr	  while ((match_offset = (*execute) (ibeg, ilim-ibeg, &match_size, 1))
638131557Stjr		 != (size_t) -1)
639131557Stjr	    {
640131557Stjr	      char const *b = beg + match_offset;
641131557Stjr	      if (b == lim)
642131557Stjr		break;
643131557Stjr	      fwrite (beg, sizeof (char), match_offset, stdout);
644131557Stjr	      printf ("\33[%sm", grep_color);
645131557Stjr	      fwrite (b, sizeof (char), match_size, stdout);
646131557Stjr	      fputs ("\33[00m", stdout);
647131557Stjr	      beg = b + match_size;
648131557Stjr	      ibeg = ibeg + match_offset + match_size;
649131557Stjr	    }
650131557Stjr	  fwrite (beg, 1, lim - beg, stdout);
651131557Stjr	  free (buf);
652131557Stjr	  lastout = lim;
653131557Stjr	  return;
654131557Stjr	}
655131557Stjr      while (lim-beg && (match_offset = (*execute) (beg, lim - beg, &match_size, 1))
656131557Stjr	     != (size_t) -1)
657131557Stjr	{
658131557Stjr	  char const *b = beg + match_offset;
659131557Stjr	  /* Avoid matching the empty line at the end of the buffer. */
660131557Stjr	  if (b == lim)
661131557Stjr	    break;
662131557Stjr	  /* Avoid hanging on grep --color "" foo */
663131557Stjr	  if (match_size == 0)
664131557Stjr	    break;
665131557Stjr	  fwrite (beg, sizeof (char), match_offset, stdout);
666131557Stjr	  printf ("\33[%sm", grep_color);
667131557Stjr	  fwrite (b, sizeof (char), match_size, stdout);
668131557Stjr	  fputs ("\33[00m", stdout);
669131557Stjr	  beg = b + match_size;
670131557Stjr	}
671131557Stjr    }
67253477Sobrien  fwrite (beg, 1, lim - beg, stdout);
67353477Sobrien  if (ferror (stdout))
674131557Stjr    error (0, errno, _("writing output"));
67553451Speter  lastout = lim;
676131557Stjr  if (line_buffered)
677131557Stjr    fflush (stdout);
67853451Speter}
67953451Speter
680131557Stjr/* Print pending lines of trailing context prior to LIM. Trailing context ends
681131557Stjr   at the next matching line when OUTLEFT is 0.  */
68253451Speterstatic void
683131557Stjrprpending (char const *lim)
68453451Speter{
68553451Speter  if (!lastout)
68653451Speter    lastout = bufbeg;
68753451Speter  while (pending > 0 && lastout < lim)
68853451Speter    {
689131557Stjr      char const *nl = memchr (lastout, eolbyte, lim - lastout);
690131557Stjr      size_t match_size;
69153451Speter      --pending;
692131557Stjr      if (outleft
693131557Stjr	  || (((*execute) (lastout, nl - lastout, &match_size, 0) == (size_t) -1)
694131557Stjr	      == !out_invert))
695131557Stjr	prline (lastout, nl + 1, '-');
69653451Speter      else
697131557Stjr	pending = 0;
69853451Speter    }
69953451Speter}
70053451Speter
70153451Speter/* Print the lines between BEG and LIM.  Deal with context crap.
702131557Stjr   If NLINESP is non-null, store a count of lines between BEG and LIM.  */
70353451Speterstatic void
704131557Stjrprtext (char const *beg, char const *lim, int *nlinesp)
70553451Speter{
70653451Speter  static int used;		/* avoid printing "--" before any output */
707131557Stjr  char const *bp, *p;
70855379Sobrien  char eol = eolbyte;
70953451Speter  int i, n;
71053451Speter
71153451Speter  if (!out_quiet && pending > 0)
71253477Sobrien    prpending (beg);
71353451Speter
71453451Speter  p = beg;
71553451Speter
71653451Speter  if (!out_quiet)
71753451Speter    {
71853451Speter      /* Deal with leading context crap. */
71953451Speter
72053451Speter      bp = lastout ? lastout : bufbeg;
72153451Speter      for (i = 0; i < out_before; ++i)
72253451Speter	if (p > bp)
72353451Speter	  do
72453451Speter	    --p;
725131557Stjr	  while (p[-1] != eol);
72653451Speter
72753451Speter      /* We only print the "--" separator if our output is
72853451Speter	 discontiguous from the last output in the file. */
72953451Speter      if ((out_before || out_after) && used && p != lastout)
73053477Sobrien	puts ("--");
73153451Speter
73253451Speter      while (p < beg)
73353451Speter	{
734131557Stjr	  char const *nl = memchr (p, eol, beg - p);
735131557Stjr	  nl++;
736131557Stjr	  prline (p, nl, '-');
737131557Stjr	  p = nl;
73853451Speter	}
73953451Speter    }
74053451Speter
74153451Speter  if (nlinesp)
74253451Speter    {
74353451Speter      /* Caller wants a line count. */
744131557Stjr      for (n = 0; p < lim && n < outleft; n++)
74553451Speter	{
746131557Stjr	  char const *nl = memchr (p, eol, lim - p);
747131557Stjr	  nl++;
74853451Speter	  if (!out_quiet)
74953477Sobrien	    prline (p, nl, ':');
75053451Speter	  p = nl;
75153451Speter	}
75253451Speter      *nlinesp = n;
753131557Stjr
754131557Stjr      /* relying on it that this function is never called when outleft = 0.  */
755131557Stjr      after_last_match = bufoffset - (buflim - p);
75653451Speter    }
75753451Speter  else
75853451Speter    if (!out_quiet)
75953477Sobrien      prline (beg, lim, ':');
76053451Speter
76155379Sobrien  pending = out_quiet ? 0 : out_after;
76253451Speter  used = 1;
76353451Speter}
76453451Speter
76553451Speter/* Scan the specified portion of the buffer, matching lines (or
76653451Speter   between matching lines if OUT_INVERT is true).  Return a count of
76753451Speter   lines printed. */
76853451Speterstatic int
769131557Stjrgrepbuf (char const *beg, char const *lim)
77053451Speter{
77153451Speter  int nlines, n;
772131557Stjr  register char const *p;
773131557Stjr  size_t match_offset;
774131557Stjr  size_t match_size;
77553451Speter
77653451Speter  nlines = 0;
77753451Speter  p = beg;
778131557Stjr  while ((match_offset = (*execute) (p, lim - p, &match_size, 0)) != (size_t) -1)
77953451Speter    {
780131557Stjr      char const *b = p + match_offset;
781131557Stjr      char const *endp = b + match_size;
78253451Speter      /* Avoid matching the empty line at the end of the buffer. */
783131557Stjr      if (b == lim)
78453451Speter	break;
78553451Speter      if (!out_invert)
78653451Speter	{
78753477Sobrien	  prtext (b, endp, (int *) 0);
788131557Stjr	  nlines++;
789131557Stjr          outleft--;
790131557Stjr	  if (!outleft || done_on_match)
791131557Stjr	    {
792131557Stjr	      if (exit_on_match)
793131557Stjr		exit (0);
794131557Stjr	      after_last_match = bufoffset - (buflim - endp);
795131557Stjr	      return nlines;
796131557Stjr	    }
79753451Speter	}
79853451Speter      else if (p < b)
79953451Speter	{
80053477Sobrien	  prtext (p, b, &n);
80153451Speter	  nlines += n;
802131557Stjr          outleft -= n;
803131557Stjr	  if (!outleft)
804131557Stjr	    return nlines;
80553451Speter	}
80653451Speter      p = endp;
80753451Speter    }
80853451Speter  if (out_invert && p < lim)
80953451Speter    {
81053477Sobrien      prtext (p, lim, &n);
81153451Speter      nlines += n;
812131557Stjr      outleft -= n;
81353451Speter    }
81453451Speter  return nlines;
81553451Speter}
81653451Speter
81753477Sobrien/* Search a given file.  Normally, return a count of lines printed;
81853477Sobrien   but if the file is a directory and we search it recursively, then
81953477Sobrien   return -2 if there was a match, and -1 otherwise.  */
82053451Speterstatic int
82156920Srugrep (int fd, char const *file, struct stats *stats)
82253451Speter{
82353451Speter  int nlines, i;
82453477Sobrien  int not_text;
82553451Speter  size_t residue, save;
826131557Stjr  char oldc;
827131557Stjr  char *beg;
828131557Stjr  char *lim;
82955379Sobrien  char eol = eolbyte;
83053451Speter
83153477Sobrien  if (!reset (fd, file, stats))
83253477Sobrien    return 0;
83353451Speter
83453477Sobrien  if (file && directories == RECURSE_DIRECTORIES
83553477Sobrien      && S_ISDIR (stats->stat.st_mode))
83653477Sobrien    {
83753477Sobrien      /* Close fd now, so that we don't open a lot of file descriptors
83853477Sobrien	 when we recurse deeply.  */
839104555Sobrien      if (BZflag && bzbufdesc)
840103372Sobrien	BZ2_bzclose(bzbufdesc);
841103372Sobrien      else
84253477Sobrien#if HAVE_LIBZ > 0
84353477Sobrien      if (Zflag)
84453477Sobrien	gzclose(gzbufdesc);
84553477Sobrien      else
84653477Sobrien#endif
84753477Sobrien      if (close (fd) != 0)
848131557Stjr	error (0, errno, "%s", file);
84953477Sobrien      return grepdir (file, stats) - 2;
85053477Sobrien    }
85153477Sobrien
85253451Speter  totalcc = 0;
85353451Speter  lastout = 0;
85453451Speter  totalnl = 0;
855131557Stjr  outleft = max_count;
856131557Stjr  after_last_match = 0;
85753451Speter  pending = 0;
85853451Speter
85953451Speter  nlines = 0;
86053451Speter  residue = 0;
86153451Speter  save = 0;
86253451Speter
86355379Sobrien  if (! fillbuf (save, stats))
86453451Speter    {
865131557Stjr      if (! is_EISDIR (errno, file))
866131557Stjr	suppressible_error (filename, errno);
86756233Sru      return 0;
86853477Sobrien    }
86953451Speter
87056233Sru  not_text = (((binary_files == BINARY_BINARY_FILES && !out_quiet)
87156233Sru	       || binary_files == WITHOUT_MATCH_BINARY_FILES)
87255379Sobrien	      && memchr (bufbeg, eol ? '\0' : '\200', buflim - bufbeg));
87356233Sru  if (not_text && binary_files == WITHOUT_MATCH_BINARY_FILES)
87456233Sru    return 0;
87553477Sobrien  done_on_match += not_text;
87653477Sobrien  out_quiet += not_text;
87753451Speter
87853477Sobrien  for (;;)
87953477Sobrien    {
88053451Speter      lastnl = bufbeg;
88153451Speter      if (lastout)
88253451Speter	lastout = bufbeg;
883131557Stjr
884131557Stjr      beg = bufbeg + save;
885131557Stjr
886131557Stjr      /* no more data to scan (eof) except for maybe a residue -> break */
887131557Stjr      if (beg == buflim)
88853451Speter	break;
889131557Stjr
890131557Stjr      /* Determine new residue (the length of an incomplete line at the end of
891131557Stjr         the buffer, 0 means there is no incomplete last line).  */
892131557Stjr      oldc = beg[-1];
893131557Stjr      beg[-1] = eol;
894131557Stjr      for (lim = buflim; lim[-1] != eol; lim--)
895131557Stjr	continue;
896131557Stjr      beg[-1] = oldc;
897131557Stjr      if (lim == beg)
898131557Stjr	lim = beg - residue;
899131557Stjr      beg -= residue;
90053451Speter      residue = buflim - lim;
901131557Stjr
90253451Speter      if (beg < lim)
90353451Speter	{
904131557Stjr	  if (outleft)
905131557Stjr	    nlines += grepbuf (beg, lim);
90653451Speter	  if (pending)
90753477Sobrien	    prpending (lim);
908131557Stjr	  if((!outleft && !pending) || (nlines && done_on_match && !out_invert))
90953477Sobrien	    goto finish_grep;
91053451Speter	}
911131557Stjr
912131557Stjr      /* The last OUT_BEFORE lines at the end of the buffer will be needed as
913131557Stjr	 leading context if there is a matching line at the begin of the
914131557Stjr	 next data. Make beg point to their begin.  */
91553451Speter      i = 0;
91653451Speter      beg = lim;
91753451Speter      while (i < out_before && beg > bufbeg && beg != lastout)
91853451Speter	{
91953451Speter	  ++i;
92053451Speter	  do
92153451Speter	    --beg;
922131557Stjr	  while (beg[-1] != eol);
92353451Speter	}
924131557Stjr
925131557Stjr      /* detect if leading context is discontinuous from last printed line.  */
92653451Speter      if (beg != lastout)
92753451Speter	lastout = 0;
928131557Stjr
929131557Stjr      /* Handle some details and read more data to scan.  */
93053451Speter      save = residue + lim - beg;
931131557Stjr      if (out_byte)
932131557Stjr	totalcc = add_count (totalcc, buflim - bufbeg - save);
93353451Speter      if (out_line)
93453477Sobrien	nlscan (beg);
93555379Sobrien      if (! fillbuf (save, stats))
93653477Sobrien	{
937131557Stjr	  if (! is_EISDIR (errno, file))
938131557Stjr	    suppressible_error (filename, errno);
93953477Sobrien	  goto finish_grep;
94053477Sobrien	}
94153451Speter    }
94253451Speter  if (residue)
94353451Speter    {
94456920Sru      *buflim++ = eol;
945131557Stjr      if (outleft)
946131557Stjr	nlines += grepbuf (bufbeg + save - residue, buflim);
94753451Speter      if (pending)
948131557Stjr        prpending (buflim);
94953451Speter    }
95053477Sobrien
95153477Sobrien finish_grep:
95253477Sobrien  done_on_match -= not_text;
95353477Sobrien  out_quiet -= not_text;
95453477Sobrien  if ((not_text & ~out_quiet) && nlines != 0)
95553477Sobrien    printf (_("Binary file %s matches\n"), filename);
95653451Speter  return nlines;
95753451Speter}
95853451Speter
95953477Sobrienstatic int
96056920Srugrepfile (char const *file, struct stats *stats)
96153477Sobrien{
96253477Sobrien  int desc;
96353477Sobrien  int count;
96453477Sobrien  int status;
96553477Sobrien
96653477Sobrien  if (! file)
96753477Sobrien    {
96853477Sobrien      desc = 0;
969131557Stjr      filename = label ? label : _("(standard input)");
97053477Sobrien    }
97153477Sobrien  else
97253477Sobrien    {
97355379Sobrien      while ((desc = open (file, O_RDONLY)) < 0 && errno == EINTR)
97455379Sobrien	continue;
97553477Sobrien
97653477Sobrien      if (desc < 0)
97753477Sobrien	{
97853477Sobrien	  int e = errno;
979131557Stjr
98053477Sobrien	  if (is_EISDIR (e, file) && directories == RECURSE_DIRECTORIES)
98153477Sobrien	    {
98253477Sobrien	      if (stat (file, &stats->stat) != 0)
98353477Sobrien		{
984131557Stjr		  error (0, errno, "%s", file);
98553477Sobrien		  return 1;
98653477Sobrien		}
98753477Sobrien
98853477Sobrien	      return grepdir (file, stats);
98953477Sobrien	    }
990131557Stjr
99153477Sobrien	  if (!suppress_errors)
99253477Sobrien	    {
99353477Sobrien	      if (directories == SKIP_DIRECTORIES)
99453477Sobrien		switch (e)
99553477Sobrien		  {
996131557Stjr#if defined(EISDIR)
99753477Sobrien		  case EISDIR:
99853477Sobrien		    return 1;
99953451Speter#endif
100053477Sobrien		  case EACCES:
100153477Sobrien		    /* When skipping directories, don't worry about
100253477Sobrien		       directories that can't be opened.  */
1003131557Stjr		    if (isdir (file))
100453477Sobrien		      return 1;
100553477Sobrien		    break;
100653477Sobrien		  }
100753477Sobrien	    }
100853477Sobrien
1009131557Stjr	  suppressible_error (file, e);
101053477Sobrien	  return 1;
101153477Sobrien	}
101253477Sobrien
101353477Sobrien      filename = file;
101453477Sobrien    }
101553477Sobrien
1016131557Stjr#if defined(SET_BINARY)
101753477Sobrien  /* Set input to binary mode.  Pipes are simulated with files
101853477Sobrien     on DOS, so this includes the case of "foo | grep bar".  */
101953477Sobrien  if (!isatty (desc))
102053477Sobrien    SET_BINARY (desc);
102153477Sobrien#endif
102253477Sobrien
102353477Sobrien  count = grep (desc, file, stats);
102453477Sobrien  if (count < 0)
102553477Sobrien    status = count + 2;
102653477Sobrien  else
102753477Sobrien    {
102853477Sobrien      if (count_matches)
102953477Sobrien	{
103053477Sobrien	  if (out_file)
103155379Sobrien	    printf ("%s%c", filename, ':' & filename_mask);
103253477Sobrien	  printf ("%d\n", count);
103353477Sobrien	}
103453477Sobrien
103555379Sobrien      status = !count;
103655379Sobrien      if (list_files == 1 - 2 * status)
103755379Sobrien	printf ("%s%c", filename, '\n' & filename_mask);
103853477Sobrien
1039104555Sobrien      if (BZflag && bzbufdesc)
1040103372Sobrien	BZ2_bzclose(bzbufdesc);
1041103372Sobrien      else
104253451Speter#if HAVE_LIBZ > 0
104353477Sobrien      if (Zflag)
104453477Sobrien	gzclose(gzbufdesc);
104553477Sobrien      else
104653451Speter#endif
1047131557Stjr      if (! file)
1048131557Stjr	{
1049131557Stjr	  off_t required_offset = outleft ? bufoffset : after_last_match;
1050131557Stjr	  if ((bufmapped || required_offset != bufoffset)
1051131557Stjr	      && lseek (desc, required_offset, SEEK_SET) < 0
1052131557Stjr	      && S_ISREG (stats->stat.st_mode))
1053131557Stjr	    error (0, errno, "%s", filename);
1054131557Stjr	}
1055131557Stjr      else
105655379Sobrien	while (close (desc) != 0)
105755379Sobrien	  if (errno != EINTR)
105855379Sobrien	    {
1059131557Stjr	      error (0, errno, "%s", file);
106055379Sobrien	      break;
106155379Sobrien	    }
106253477Sobrien    }
106353451Speter
106453477Sobrien  return status;
106553477Sobrien}
106653477Sobrien
106753477Sobrienstatic int
1068131557Stjrgrepdir (char const *dir, struct stats const *stats)
106953477Sobrien{
107053477Sobrien  int status = 1;
1071131557Stjr  struct stats const *ancestor;
107253477Sobrien  char *name_space;
107353477Sobrien
1074131557Stjr  /* Mingw32 does not support st_ino.  No known working hosts use zero
1075131557Stjr     for st_ino, so assume that the Mingw32 bug applies if it's zero.  */
1076131557Stjr  if (stats->stat.st_ino)
1077131557Stjr    for (ancestor = stats;  (ancestor = ancestor->parent) != 0;  )
1078131557Stjr      if (ancestor->stat.st_ino == stats->stat.st_ino
1079131557Stjr	  && ancestor->stat.st_dev == stats->stat.st_dev)
1080131557Stjr	{
1081131557Stjr	  if (!suppress_errors)
1082131557Stjr	    error (0, 0, _("warning: %s: %s\n"), dir,
108353477Sobrien		   _("recursive directory loop"));
1084131557Stjr	  return 1;
1085131557Stjr	}
108653477Sobrien
1087131557Stjr  name_space = savedir (dir, stats->stat.st_size, included_patterns,
1088131557Stjr			excluded_patterns);
108953477Sobrien
109053477Sobrien  if (! name_space)
109153477Sobrien    {
109253477Sobrien      if (errno)
1093131557Stjr	suppressible_error (dir, errno);
109453477Sobrien      else
1095131557Stjr	xalloc_die ();
109653477Sobrien    }
109753477Sobrien  else
109853477Sobrien    {
109953477Sobrien      size_t dirlen = strlen (dir);
110053477Sobrien      int needs_slash = ! (dirlen == FILESYSTEM_PREFIX_LEN (dir)
110153477Sobrien			   || IS_SLASH (dir[dirlen - 1]));
110253477Sobrien      char *file = NULL;
1103131557Stjr      char const *namep = name_space;
110453477Sobrien      struct stats child;
110553477Sobrien      child.parent = stats;
110653477Sobrien      out_file += !no_filenames;
110753477Sobrien      while (*namep)
110853477Sobrien	{
110953477Sobrien	  size_t namelen = strlen (namep);
111053477Sobrien	  file = xrealloc (file, dirlen + 1 + namelen + 1);
111153477Sobrien	  strcpy (file, dir);
111253477Sobrien	  file[dirlen] = '/';
111353477Sobrien	  strcpy (file + dirlen + needs_slash, namep);
111453477Sobrien	  namep += namelen + 1;
111553477Sobrien	  status &= grepfile (file, &child);
111653477Sobrien	}
111753477Sobrien      out_file -= !no_filenames;
111853477Sobrien      if (file)
111953477Sobrien        free (file);
112053477Sobrien      free (name_space);
112153477Sobrien    }
112253477Sobrien
112353477Sobrien  return status;
112453477Sobrien}
112553477Sobrien
112653451Speterstatic void
112756920Sruusage (int status)
112853451Speter{
112953477Sobrien  if (status != 0)
113053477Sobrien    {
1131131557Stjr      fprintf (stderr, _("Usage: %s [OPTION]... PATTERN [FILE]...\n"),
1132131557Stjr	       program_name);
1133131557Stjr      fprintf (stderr, _("Try `%s --help' for more information.\n"),
1134131557Stjr	       program_name);
113553477Sobrien    }
113653477Sobrien  else
113753477Sobrien    {
1138131557Stjr      printf (_("Usage: %s [OPTION]... PATTERN [FILE] ...\n"), program_name);
113953477Sobrien      printf (_("\
114053477SobrienSearch for PATTERN in each FILE or standard input.\n\
114156920SruExample: %s -i 'hello world' menu.h main.c\n\
114253477Sobrien\n\
1143131557StjrRegexp selection and interpretation:\n"), program_name);
114455379Sobrien      printf (_("\
114553477Sobrien  -E, --extended-regexp     PATTERN is an extended regular expression\n\
114655379Sobrien  -F, --fixed-strings       PATTERN is a set of newline-separated strings\n\
1147131557Stjr  -G, --basic-regexp        PATTERN is a basic regular expression\n\
1148131557Stjr  -P, --perl-regexp         PATTERN is a Perl regular expression\n"));
114955379Sobrien      printf (_("\
115053477Sobrien  -e, --regexp=PATTERN      use PATTERN as a regular expression\n\
115153477Sobrien  -f, --file=FILE           obtain PATTERN from FILE\n\
115253477Sobrien  -i, --ignore-case         ignore case distinctions\n\
115353477Sobrien  -w, --word-regexp         force PATTERN to match only whole words\n\
115455379Sobrien  -x, --line-regexp         force PATTERN to match only whole lines\n\
115555379Sobrien  -z, --null-data           a data line ends in 0 byte, not newline\n"));
115653477Sobrien      printf (_("\
115753477Sobrien\n\
115853477SobrienMiscellaneous:\n\
115953477Sobrien  -s, --no-messages         suppress error messages\n\
116055379Sobrien  -v, --invert-match        select non-matching lines\n\
116153477Sobrien  -V, --version             print version information and exit\n\
116255379Sobrien      --help                display this help and exit\n\
1163103372Sobrien  -J, --bz2decompress       decompress bzip2'ed input before searching\n\
116453477Sobrien  -Z, --decompress          decompress input before searching (HAVE_LIBZ=1)\n\
116555379Sobrien      --mmap                use memory-mapped input if possible\n"));
116653477Sobrien      printf (_("\
116753477Sobrien\n\
116853477SobrienOutput control:\n\
1169131557Stjr  -m, --max-count=NUM       stop after NUM matches\n\
117053477Sobrien  -b, --byte-offset         print the byte offset with output lines\n\
117153477Sobrien  -n, --line-number         print line number with output lines\n\
1172131557Stjr      --line-buffered       flush output on every line\n\
117353477Sobrien  -H, --with-filename       print the filename for each match\n\
117453477Sobrien  -h, --no-filename         suppress the prefixing filename on output\n\
1175131557Stjr      --label=LABEL         print LABEL as filename for standard input\n\
1176131557Stjr  -o, --only-matching       show only the part of a line matching PATTERN\n\
117753477Sobrien  -q, --quiet, --silent     suppress all normal output\n\
117856233Sru      --binary-files=TYPE   assume that binary files are TYPE\n\
1179131557Stjr                            TYPE is 'binary', 'text', or 'without-match'\n\
118056920Sru  -a, --text                equivalent to --binary-files=text\n\
118156920Sru  -I                        equivalent to --binary-files=without-match\n\
118253477Sobrien  -d, --directories=ACTION  how to handle directories\n\
1183131557Stjr                            ACTION is 'read', 'recurse', or 'skip'\n\
1184131557Stjr  -D, --devices=ACTION      how to handle devices, FIFOs and sockets\n\
1185131557Stjr                            ACTION is 'read' or 'skip'\n\
1186131557Stjr  -R, -r, --recursive       equivalent to --directories=recurse\n\
1187131557Stjr      --include=PATTERN     files that match PATTERN will be examined\n\
1188131557Stjr      --exclude=PATTERN     files that match PATTERN will be skipped.\n\
1189131557Stjr      --exclude-from=FILE   files that match PATTERN in FILE will be skipped.\n\
119053477Sobrien  -L, --files-without-match only print FILE names containing no match\n\
119153477Sobrien  -l, --files-with-matches  only print FILE names containing matches\n\
119255379Sobrien  -c, --count               only print a count of matching lines per FILE\n\
119355379Sobrien      --null                print 0 byte after FILE name\n"));
119453477Sobrien      printf (_("\
119553477Sobrien\n\
119653477SobrienContext control:\n\
119753477Sobrien  -B, --before-context=NUM  print NUM lines of leading context\n\
119853477Sobrien  -A, --after-context=NUM   print NUM lines of trailing context\n\
1199131557Stjr  -C, --context=NUM         print NUM lines of output context\n\
120053477Sobrien  -NUM                      same as --context=NUM\n\
1201131557Stjr      --color[=WHEN],\n\
1202131557Stjr      --colour[=WHEN]       use markers to distinguish the matching string\n\
1203131557Stjr                            WHEN may be `always', `never' or `auto'.\n\
120453477Sobrien  -U, --binary              do not strip CR characters at EOL (MSDOS)\n\
120553477Sobrien  -u, --unix-byte-offsets   report offsets as if CRs were not there (MSDOS)\n\
120653477Sobrien\n\
120755379Sobrien`egrep' means `grep -E'.  `fgrep' means `grep -F'.\n\
120855379SobrienWith no FILE, or when FILE is -, read standard input.  If less than\n\
120955379Sobrientwo FILEs given, assume -h.  Exit status is 0 if match, 1 if no match,\n\
121055379Sobrienand 2 if trouble.\n"));
121153477Sobrien      printf (_("\nReport bugs to <bug-gnu-utils@gnu.org>.\n"));
121253477Sobrien    }
121353477Sobrien  exit (status);
121453451Speter}
121553451Speter
121655379Sobrien/* Set the matcher to M, reporting any conflicts.  */
121755379Sobrienstatic void
121856920Srusetmatcher (char const *m)
121955379Sobrien{
122055379Sobrien  if (matcher && strcmp (matcher, m) != 0)
1221131557Stjr    error (2, 0, _("conflicting matchers specified"));
122255379Sobrien  matcher = m;
122355379Sobrien}
122455379Sobrien
122553451Speter/* Go through the matchers vector and look for the specified matcher.
122653451Speter   If we find it, install it in compile and execute, and return 1.  */
122753477Sobrienstatic int
122856920Sruinstall_matcher (char const *name)
122953451Speter{
123053451Speter  int i;
1231131557Stjr#if defined(HAVE_SETRLIMIT)
123253477Sobrien  struct rlimit rlim;
123353477Sobrien#endif
123453451Speter
1235131557Stjr  for (i = 0; matchers[i].compile; i++)
123653477Sobrien    if (strcmp (name, matchers[i].name) == 0)
123753451Speter      {
123853451Speter	compile = matchers[i].compile;
123953451Speter	execute = matchers[i].execute;
1240131557Stjr#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_STACK)
124153477Sobrien	/* I think every platform needs to do this, so that regex.c
124253477Sobrien	   doesn't oveflow the stack.  The default value of
124353477Sobrien	   `re_max_failures' is too large for some platforms: it needs
124453477Sobrien	   more than 3MB-large stack.
124553477Sobrien
124653477Sobrien	   The test for HAVE_SETRLIMIT should go into `configure'.  */
124753477Sobrien	if (!getrlimit (RLIMIT_STACK, &rlim))
124853477Sobrien	  {
124953477Sobrien	    long newlim;
125053477Sobrien	    extern long int re_max_failures; /* from regex.c */
125153477Sobrien
125253477Sobrien	    /* Approximate the amount regex.c needs, plus some more.  */
125353477Sobrien	    newlim = re_max_failures * 2 * 20 * sizeof (char *);
125453477Sobrien	    if (newlim > rlim.rlim_max)
125553477Sobrien	      {
125653477Sobrien		newlim = rlim.rlim_max;
125753477Sobrien		re_max_failures = newlim / (2 * 20 * sizeof (char *));
125853477Sobrien	      }
125953477Sobrien	    if (rlim.rlim_cur < newlim)
1260131557Stjr	      {
1261131557Stjr		rlim.rlim_cur = newlim;
1262131557Stjr		setrlimit (RLIMIT_STACK, &rlim);
1263131557Stjr	      }
126453477Sobrien	  }
126553477Sobrien#endif
126653451Speter	return 1;
126753451Speter      }
126853451Speter  return 0;
126953451Speter}
127053451Speter
127153705Sobrien/* Find the white-space-separated options specified by OPTIONS, and
127253705Sobrien   using BUF to store copies of these options, set ARGV[0], ARGV[1],
127353705Sobrien   etc. to the option copies.  Return the number N of options found.
127453705Sobrien   Do not set ARGV[N] to NULL.  If ARGV is NULL, do not store ARGV[0]
127553705Sobrien   etc.  Backslash can be used to escape whitespace (and backslashes).  */
127653705Sobrienstatic int
127756920Sruprepend_args (char const *options, char *buf, char **argv)
127853705Sobrien{
127953705Sobrien  char const *o = options;
128053705Sobrien  char *b = buf;
128153705Sobrien  int n = 0;
128253705Sobrien
128353705Sobrien  for (;;)
128453705Sobrien    {
128553705Sobrien      while (ISSPACE ((unsigned char) *o))
128653705Sobrien	o++;
128753705Sobrien      if (!*o)
128853705Sobrien	return n;
128953705Sobrien      if (argv)
129053705Sobrien	argv[n] = b;
129153705Sobrien      n++;
129253705Sobrien
129353705Sobrien      do
129453705Sobrien	if ((*b++ = *o++) == '\\' && *o)
129553705Sobrien	  b[-1] = *o++;
129653705Sobrien      while (*o && ! ISSPACE ((unsigned char) *o));
129753705Sobrien
129853705Sobrien      *b++ = '\0';
129953705Sobrien    }
130053705Sobrien}
130153705Sobrien
130253705Sobrien/* Prepend the whitespace-separated options in OPTIONS to the argument
130353705Sobrien   vector of a main program with argument count *PARGC and argument
130453705Sobrien   vector *PARGV.  */
130553705Sobrienstatic void
130656920Sruprepend_default_options (char const *options, int *pargc, char ***pargv)
130753705Sobrien{
130853705Sobrien  if (options)
130953705Sobrien    {
131053705Sobrien      char *buf = xmalloc (strlen (options) + 1);
131153705Sobrien      int prepended = prepend_args (options, buf, (char **) NULL);
131253705Sobrien      int argc = *pargc;
131353705Sobrien      char * const *argv = *pargv;
131453705Sobrien      char **pp = (char **) xmalloc ((prepended + argc + 1) * sizeof *pp);
131553705Sobrien      *pargc = prepended + argc;
131653705Sobrien      *pargv = pp;
131753705Sobrien      *pp++ = *argv++;
131853705Sobrien      pp += prepend_args (options, buf, pp);
131953705Sobrien      while ((*pp++ = *argv++))
132053705Sobrien	continue;
132153705Sobrien    }
132253705Sobrien}
132353705Sobrien
1324131557Stjr/* Get the next non-digit option from ARGC and ARGV.
1325131557Stjr   Return -1 if there are no more options.
1326131557Stjr   Process any digit options that were encountered on the way,
1327131557Stjr   and store the resulting integer into *DEFAULT_CONTEXT.  */
1328131557Stjrstatic int
1329131557Stjrget_nondigit_option (int argc, char *const *argv, int *default_context)
1330131557Stjr{
1331131557Stjr  int opt;
1332131557Stjr  char buf[sizeof (uintmax_t) * CHAR_BIT + 4];
1333131557Stjr  char *p = buf;
1334131557Stjr
1335131557Stjr  /* Set buf[0] to anything but '0', for the leading-zero test below.  */
1336131557Stjr  buf[0] = '\0';
1337131557Stjr
1338131557Stjr  while (opt = getopt_long (argc, argv, short_options, long_options, NULL),
1339131557Stjr	 '0' <= opt && opt <= '9')
1340131557Stjr    {
1341131557Stjr      /* Suppress trivial leading zeros, to avoid incorrect
1342131557Stjr	 diagnostic on strings like 00000000000.  */
1343131557Stjr      p -= buf[0] == '0';
1344131557Stjr
1345131557Stjr      *p++ = opt;
1346131557Stjr      if (p == buf + sizeof buf - 4)
1347131557Stjr	{
1348131557Stjr	  /* Too many digits.  Append "..." to make context_length_arg
1349131557Stjr	     complain about "X...", where X contains the digits seen
1350131557Stjr	     so far.  */
1351131557Stjr	  strcpy (p, "...");
1352131557Stjr	  p += 3;
1353131557Stjr	  break;
1354131557Stjr	}
1355131557Stjr    }
1356131557Stjr  if (p != buf)
1357131557Stjr    {
1358131557Stjr      *p = '\0';
1359131557Stjr      context_length_arg (buf, default_context);
1360131557Stjr    }
1361131557Stjr
1362131557Stjr  return opt;
1363131557Stjr}
1364131557Stjr
136553451Speterint
136656920Srumain (int argc, char **argv)
136753451Speter{
136853451Speter  char *keys;
136953451Speter  size_t keycc, oldcc, keyalloc;
137053477Sobrien  int with_filenames;
137153477Sobrien  int opt, cc, status;
137256920Sru  int default_context;
137353451Speter  FILE *fp;
137453451Speter  extern char *optarg;
137553451Speter  extern int optind;
137653477Sobrien
137753477Sobrien  initialize_main (&argc, &argv);
1378131557Stjr  program_name = argv[0];
1379131557Stjr  if (program_name && strrchr (program_name, '/'))
1380131557Stjr    program_name = strrchr (program_name, '/') + 1;
138153477Sobrien
138253477Sobrien#if HAVE_LIBZ > 0
1383131557Stjr  if (program_name[0] == 'z') {
138453477Sobrien    Zflag = 1;
1385131557Stjr    ++program_name;
138653477Sobrien  }
138753451Speter#endif
1388131557Stjr  if (program_name[0] == 'b') {
1389103372Sobrien    BZflag = 1;
1390131557Stjr    ++program_name;
1391103372Sobrien  }
139253451Speter
139353477Sobrien#if defined(__MSDOS__) || defined(_WIN32)
139453477Sobrien  /* DOS and MS-Windows use backslashes as directory separators, and usually
139553477Sobrien     have an .exe suffix.  They also have case-insensitive filesystems.  */
1396131557Stjr  if (program_name)
139753477Sobrien    {
1398131557Stjr      char *p = program_name;
139953477Sobrien      char *bslash = strrchr (argv[0], '\\');
140053477Sobrien
1401131557Stjr      if (bslash && bslash >= program_name) /* for mixed forward/backslash case */
1402131557Stjr	program_name = bslash + 1;
1403131557Stjr      else if (program_name == argv[0]
140453477Sobrien	       && argv[0][0] && argv[0][1] == ':') /* "c:progname" */
1405131557Stjr	program_name = argv[0] + 2;
140653477Sobrien
140753477Sobrien      /* Collapse the letter-case, so `strcmp' could be used hence.  */
140853477Sobrien      for ( ; *p; p++)
140953477Sobrien	if (*p >= 'A' && *p <= 'Z')
141053477Sobrien	  *p += 'a' - 'A';
141153477Sobrien
141253477Sobrien      /* Remove the .exe extension, if any.  */
1413131557Stjr      if ((p = strrchr (program_name, '.')) && strcmp (p, ".exe") == 0)
141453477Sobrien	*p = '\0';
141553477Sobrien    }
141653451Speter#endif
141753451Speter
141853451Speter  keys = NULL;
141953451Speter  keycc = 0;
142053477Sobrien  with_filenames = 0;
142155379Sobrien  eolbyte = '\n';
142255379Sobrien  filename_mask = ~0;
142353477Sobrien
1424131557Stjr  max_count = TYPE_MAXIMUM (off_t);
1425131557Stjr
142653477Sobrien  /* The value -1 means to use DEFAULT_CONTEXT. */
142753477Sobrien  out_after = out_before = -1;
142853477Sobrien  /* Default before/after context: chaged by -C/-NUM options */
142953477Sobrien  default_context = 0;
1430131557Stjr  /* Changed by -o option */
1431131557Stjr  only_matching = 0;
143253477Sobrien
1433131557Stjr  /* Internationalization. */
1434131557Stjr#if defined(HAVE_SETLOCALE)
143553477Sobrien  setlocale (LC_ALL, "");
143653451Speter#endif
1437131557Stjr#if defined(ENABLE_NLS)
143853477Sobrien  bindtextdomain (PACKAGE, LOCALEDIR);
143953477Sobrien  textdomain (PACKAGE);
144053451Speter#endif
144153451Speter
1442131557Stjr  atexit (close_stdout);
1443131557Stjr
144453705Sobrien  prepend_default_options (getenv ("GREP_OPTIONS"), &argc, &argv);
144553705Sobrien
1446131557Stjr  while ((opt = get_nondigit_option (argc, argv, &default_context)) != -1)
144753451Speter    switch (opt)
144853451Speter      {
144953451Speter      case 'A':
1450131557Stjr	context_length_arg (optarg, &out_after);
145153451Speter	break;
1452131557Stjr
145353451Speter      case 'B':
1454131557Stjr	context_length_arg (optarg, &out_before);
145553451Speter	break;
1456131557Stjr
145753451Speter      case 'C':
145853477Sobrien	/* Set output match context, but let any explicit leading or
145953477Sobrien	   trailing amount specified with -A or -B stand. */
1460131557Stjr	context_length_arg (optarg, &default_context);
1461131557Stjr	break;
1462131557Stjr
1463131557Stjr      case 'D':
1464131557Stjr	if (strcmp (optarg, "read") == 0)
1465131557Stjr	  devices = READ_DEVICES;
1466131557Stjr	else if (strcmp (optarg, "skip") == 0)
1467131557Stjr	  devices = SKIP_DEVICES;
146853477Sobrien	else
1469131557Stjr	  error (2, 0, _("unknown devices method"));
147053451Speter	break;
1471131557Stjr
147253451Speter      case 'E':
147355379Sobrien	setmatcher ("egrep");
147453451Speter	break;
1475131557Stjr
147653451Speter      case 'F':
147755379Sobrien	setmatcher ("fgrep");
147853451Speter	break;
1479131557Stjr
1480131557Stjr      case 'P':
1481131557Stjr	setmatcher ("perl");
1482131557Stjr	break;
1483131557Stjr
148453451Speter      case 'G':
148555379Sobrien	setmatcher ("grep");
148653451Speter	break;
1487131557Stjr
148853477Sobrien      case 'H':
148953477Sobrien	with_filenames = 1;
149053477Sobrien	break;
1491131557Stjr
149256920Sru      case 'I':
149356920Sru	binary_files = WITHOUT_MATCH_BINARY_FILES;
149456920Sru	break;
1495103372Sobrien      case 'J':
1496104555Sobrien	if (Zflag)
1497104555Sobrien	  {
1498104555Sobrien	    printf (_("Cannot mix -Z and -J.\n"));
1499104555Sobrien	    usage (2);
1500104555Sobrien	  }
1501103372Sobrien	BZflag = 1;
1502103372Sobrien	break;
1503131557Stjr
150455379Sobrien      case 'U':
1505131557Stjr#if defined(HAVE_DOS_FILE_CONTENTS)
150653477Sobrien	dos_use_file_type = DOS_BINARY;
150755379Sobrien#endif
150853477Sobrien	break;
1509131557Stjr
151053477Sobrien      case 'u':
1511131557Stjr#if defined(HAVE_DOS_FILE_CONTENTS)
151253477Sobrien	dos_report_unix_offset = 1;
151355379Sobrien#endif
151453477Sobrien	break;
1515131557Stjr
151653451Speter      case 'V':
151753477Sobrien	show_version = 1;
151853451Speter	break;
1519131557Stjr
152053451Speter      case 'X':
152155379Sobrien	setmatcher (optarg);
152253451Speter	break;
1523131557Stjr
152453451Speter      case 'a':
152556233Sru	binary_files = TEXT_BINARY_FILES;
152653451Speter	break;
1527131557Stjr
152853451Speter      case 'b':
152953451Speter	out_byte = 1;
153053451Speter	break;
1531131557Stjr
153253451Speter      case 'c':
153353451Speter	count_matches = 1;
153453451Speter	break;
1535131557Stjr
153653477Sobrien      case 'd':
153753477Sobrien	if (strcmp (optarg, "read") == 0)
153853477Sobrien	  directories = READ_DIRECTORIES;
153953477Sobrien	else if (strcmp (optarg, "skip") == 0)
154053477Sobrien	  directories = SKIP_DIRECTORIES;
154153477Sobrien	else if (strcmp (optarg, "recurse") == 0)
154253477Sobrien	  directories = RECURSE_DIRECTORIES;
154353477Sobrien	else
1544131557Stjr	  error (2, 0, _("unknown directories method"));
154553477Sobrien	break;
1546131557Stjr
154753451Speter      case 'e':
154853477Sobrien	cc = strlen (optarg);
154953477Sobrien	keys = xrealloc (keys, keycc + cc + 1);
155053477Sobrien	strcpy (&keys[keycc], optarg);
155153451Speter	keycc += cc;
155253477Sobrien	keys[keycc++] = '\n';
155353451Speter	break;
1554131557Stjr
155553451Speter      case 'f':
155653477Sobrien	fp = strcmp (optarg, "-") != 0 ? fopen (optarg, "r") : stdin;
155753451Speter	if (!fp)
1558131557Stjr	  error (2, errno, "%s", optarg);
155953477Sobrien	for (keyalloc = 1; keyalloc <= keycc + 1; keyalloc *= 2)
156053451Speter	  ;
156153477Sobrien	keys = xrealloc (keys, keyalloc);
156253451Speter	oldcc = keycc;
156353477Sobrien	while (!feof (fp)
156453477Sobrien	       && (cc = fread (keys + keycc, 1, keyalloc - 1 - keycc, fp)) > 0)
156553451Speter	  {
156653451Speter	    keycc += cc;
156753477Sobrien	    if (keycc == keyalloc - 1)
156853477Sobrien	      keys = xrealloc (keys, keyalloc *= 2);
156953451Speter	  }
157053451Speter	if (fp != stdin)
157153451Speter	  fclose(fp);
157253477Sobrien	/* Append final newline if file ended in non-newline. */
157353477Sobrien	if (oldcc != keycc && keys[keycc - 1] != '\n')
157453477Sobrien	  keys[keycc++] = '\n';
157553451Speter	break;
1576131557Stjr
157753451Speter      case 'h':
157853451Speter	no_filenames = 1;
157953451Speter	break;
1580131557Stjr
158153451Speter      case 'i':
158253451Speter      case 'y':			/* For old-timers . . . */
158353451Speter	match_icase = 1;
158453451Speter	break;
1585131557Stjr
158653451Speter      case 'L':
158753451Speter	/* Like -l, except list files that don't contain matches.
158853451Speter	   Inspired by the same option in Hume's gre. */
158953451Speter	list_files = -1;
159053451Speter	break;
1591131557Stjr
159253451Speter      case 'l':
159353451Speter	list_files = 1;
159453451Speter	break;
1595131557Stjr
1596131557Stjr      case 'm':
1597131557Stjr	{
1598131557Stjr	  uintmax_t value;
1599131557Stjr	  switch (xstrtoumax (optarg, 0, 10, &value, ""))
1600131557Stjr	    {
1601131557Stjr	    case LONGINT_OK:
1602131557Stjr	      max_count = value;
1603131557Stjr	      if (0 <= max_count && max_count == value)
1604131557Stjr		break;
1605131557Stjr	      /* Fall through.  */
1606131557Stjr	    case LONGINT_OVERFLOW:
1607131557Stjr	      max_count = TYPE_MAXIMUM (off_t);
1608131557Stjr	      break;
1609131557Stjr
1610131557Stjr	    default:
1611131557Stjr	      error (2, 0, _("invalid max count"));
1612131557Stjr	    }
1613131557Stjr	}
1614131557Stjr	break;
1615131557Stjr
161653451Speter      case 'n':
161753451Speter	out_line = 1;
161853451Speter	break;
1619131557Stjr
1620131557Stjr      case 'o':
1621131557Stjr	only_matching = 1;
1622131557Stjr	break;
1623131557Stjr
162453451Speter      case 'q':
1625131557Stjr	exit_on_match = 1;
1626131557Stjr	close_stdout_set_status(0);
162753451Speter	break;
1628131557Stjr
162953478Sobrien      case 'R':
163053477Sobrien      case 'r':
163153477Sobrien	directories = RECURSE_DIRECTORIES;
163253477Sobrien	break;
1633131557Stjr
163453451Speter      case 's':
163553451Speter	suppress_errors = 1;
163653451Speter	break;
1637131557Stjr
163853451Speter      case 'v':
163953451Speter	out_invert = 1;
164053451Speter	break;
1641131557Stjr
164253451Speter      case 'w':
164353451Speter	match_words = 1;
164453451Speter	break;
1645131557Stjr
164653451Speter      case 'x':
164753451Speter	match_lines = 1;
164853451Speter	break;
1649131557Stjr
165055379Sobrien      case 'Z':
165155379Sobrien#if HAVE_LIBZ > 0
1652104555Sobrien	if (BZflag)
1653104555Sobrien	  {
1654104555Sobrien	    printf (_("Cannot mix -J and -Z.\n"));
1655104555Sobrien	    usage (2);
1656104555Sobrien	  }
165755379Sobrien	Zflag = 1;
165855379Sobrien#else
165955379Sobrien	filename_mask = 0;
166055379Sobrien#endif
166155379Sobrien	break;
1662131557Stjr
166355379Sobrien      case 'z':
166455379Sobrien	eolbyte = '\0';
166555379Sobrien	break;
1666131557Stjr
166756233Sru      case BINARY_FILES_OPTION:
166856233Sru	if (strcmp (optarg, "binary") == 0)
166956233Sru	  binary_files = BINARY_BINARY_FILES;
167056233Sru	else if (strcmp (optarg, "text") == 0)
167156233Sru	  binary_files = TEXT_BINARY_FILES;
167256233Sru	else if (strcmp (optarg, "without-match") == 0)
167356233Sru	  binary_files = WITHOUT_MATCH_BINARY_FILES;
167456233Sru	else
1675131557Stjr	  error (2, 0, _("unknown binary-files type"));
167656233Sru	break;
1677131557Stjr
1678131557Stjr      case COLOR_OPTION:
1679131557Stjr        if(optarg) {
1680131557Stjr          if(!strcasecmp(optarg, "always") || !strcasecmp(optarg, "yes") ||
1681131557Stjr             !strcasecmp(optarg, "force"))
1682131557Stjr            color_option = 1;
1683131557Stjr          else if(!strcasecmp(optarg, "never") || !strcasecmp(optarg, "no") ||
1684131557Stjr                  !strcasecmp(optarg, "none"))
1685131557Stjr            color_option = 0;
1686131557Stjr          else if(!strcasecmp(optarg, "auto") || !strcasecmp(optarg, "tty") ||
1687131557Stjr                  !strcasecmp(optarg, "if-tty"))
1688131557Stjr            color_option = 2;
1689131557Stjr          else
1690131557Stjr            show_help = 1;
1691131557Stjr        } else
1692131557Stjr          color_option = 2;
1693131557Stjr        if(color_option == 2) {
1694131557Stjr          if(isatty(STDOUT_FILENO) && getenv("TERM") &&
1695131557Stjr	     strcmp(getenv("TERM"), "dumb"))
1696131557Stjr                  color_option = 1;
1697131557Stjr          else
1698131557Stjr            color_option = 0;
1699131557Stjr        }
1700131557Stjr	break;
1701131557Stjr
1702131557Stjr      case EXCLUDE_OPTION:
1703131557Stjr	if (!excluded_patterns)
1704131557Stjr	  excluded_patterns = new_exclude ();
1705131557Stjr	add_exclude (excluded_patterns, optarg);
1706131557Stjr	break;
1707131557Stjr
1708131557Stjr      case EXCLUDE_FROM_OPTION:
1709131557Stjr	if (!excluded_patterns)
1710131557Stjr	  excluded_patterns = new_exclude ();
1711131557Stjr        if (add_exclude_file (add_exclude, excluded_patterns, optarg, '\n')
1712131557Stjr	    != 0)
1713131557Stjr          {
1714131557Stjr            error (2, errno, "%s", optarg);
1715131557Stjr          }
1716131557Stjr        break;
1717131557Stjr
1718131557Stjr      case INCLUDE_OPTION:
1719131557Stjr	if (!included_patterns)
1720131557Stjr	  included_patterns = new_exclude ();
1721131557Stjr	add_exclude (included_patterns, optarg);
1722131557Stjr	break;
1723131557Stjr
1724131557Stjr      case LINE_BUFFERED_OPTION:
1725131557Stjr	line_buffered = 1;
1726131557Stjr	break;
1727131557Stjr
1728131557Stjr      case LABEL_OPTION:
1729131557Stjr	label = optarg;
1730131557Stjr	break;
1731131557Stjr
173253477Sobrien      case 0:
173353477Sobrien	/* long options */
173453477Sobrien	break;
1735131557Stjr
173653451Speter      default:
173753477Sobrien	usage (2);
173853451Speter	break;
1739131557Stjr
174053451Speter      }
174153451Speter
1742131557Stjr  /* POSIX.2 says that -q overrides -l, which in turn overrides the
1743131557Stjr     other output options.  */
1744131557Stjr  if (exit_on_match)
1745131557Stjr    list_files = 0;
1746131557Stjr  if (exit_on_match | list_files)
1747131557Stjr    {
1748131557Stjr      count_matches = 0;
1749131557Stjr      done_on_match = 1;
1750131557Stjr    }
1751131557Stjr  out_quiet = count_matches | done_on_match;
1752131557Stjr
175353477Sobrien  if (out_after < 0)
175453477Sobrien    out_after = default_context;
175553477Sobrien  if (out_before < 0)
175653477Sobrien    out_before = default_context;
175753477Sobrien
1758131557Stjr  if (color_option)
1759131557Stjr    {
1760131557Stjr      char *userval = getenv ("GREP_COLOR");
1761131557Stjr      if (userval != NULL && *userval != '\0')
1762131557Stjr	grep_color = userval;
1763131557Stjr    }
1764131557Stjr
176555379Sobrien  if (! matcher)
1766131557Stjr    matcher = program_name;
176755379Sobrien
176853477Sobrien  if (show_version)
176953477Sobrien    {
177055379Sobrien      printf (_("%s (GNU grep) %s\n"), matcher, VERSION);
177153477Sobrien      printf ("\n");
177253477Sobrien      printf (_("\
1773131557StjrCopyright 1988, 1992-1999, 2000, 2001 Free Software Foundation, Inc.\n"));
177453477Sobrien      printf (_("\
177553477SobrienThis is free software; see the source for copying conditions. There is NO\n\
177653477Sobrienwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"));
177753477Sobrien      printf ("\n");
177853477Sobrien      exit (0);
177953477Sobrien    }
178053477Sobrien
178153477Sobrien  if (show_help)
178253477Sobrien    usage (0);
178353477Sobrien
178453477Sobrien  if (keys)
178553477Sobrien    {
178653477Sobrien      if (keycc == 0)
1787131557Stjr	{
1788131557Stjr	  /* No keys were specified (e.g. -f /dev/null).  Match nothing.  */
1789131557Stjr	  out_invert ^= 1;
1790131557Stjr	  match_lines = match_words = 0;
1791131557Stjr	}
179253477Sobrien      else
179353477Sobrien	/* Strip trailing newline. */
179453477Sobrien        --keycc;
179553477Sobrien    }
179653477Sobrien  else
179753451Speter    if (optind < argc)
179853451Speter      {
179953451Speter	keys = argv[optind++];
180053477Sobrien	keycc = strlen (keys);
180153451Speter      }
180253451Speter    else
180353477Sobrien      usage (2);
180453451Speter
180555379Sobrien  if (!install_matcher (matcher) && !install_matcher ("default"))
180653477Sobrien    abort ();
180753451Speter
180853451Speter  (*compile)(keys, keycc);
180953451Speter
181053477Sobrien  if ((argc - optind > 1 && !no_filenames) || with_filenames)
181153451Speter    out_file = 1;
181253451Speter
1813131557Stjr#ifdef SET_BINARY
181453477Sobrien  /* Output is set to binary mode because we shouldn't convert
181553477Sobrien     NL to CR-LF pairs, especially when grepping binary files.  */
181653477Sobrien  if (!isatty (1))
181753477Sobrien    SET_BINARY (1);
181853451Speter#endif
181953451Speter
1820131557Stjr  if (max_count == 0)
1821131557Stjr    exit (1);
182253451Speter
182353451Speter  if (optind < argc)
182453451Speter    {
182553477Sobrien	status = 1;
182653477Sobrien	do
182753451Speter	{
182853477Sobrien	  char *file = argv[optind];
1829131557Stjr	  if ((included_patterns || excluded_patterns)
1830131557Stjr	      && !isdir (file))
1831131557Stjr	    {
1832131557Stjr	      if (included_patterns &&
1833131557Stjr		  ! excluded_filename (included_patterns, file, 0))
1834131557Stjr		continue;
1835131557Stjr	      if (excluded_patterns &&
1836131557Stjr		  excluded_filename (excluded_patterns, file, 0))
1837131557Stjr		continue;
1838131557Stjr	    }
183953477Sobrien	  status &= grepfile (strcmp (file, "-") == 0 ? (char *) NULL : file,
184053477Sobrien			      &stats_base);
184153451Speter	}
184253477Sobrien	while ( ++optind < argc);
184353451Speter    }
184453477Sobrien  else
184553477Sobrien    status = grepfile ((char *) NULL, &stats_base);
184653451Speter
1847131557Stjr  /* We register via atexit() to test stdout.  */
184853477Sobrien  exit (errseen ? 2 : status);
184953451Speter}
1850131557Stjr/* vim:set shiftwidth=2: */
1851