xref: /netbsd/external/gpl2/grep/dist/src/grep.c (revision 3f679887)
1*3f679887Swiz /*	$NetBSD: grep.c,v 1.5 2022/09/09 22:14:29 wiz Exp $	*/
2f7313695Schristos 
3f7313695Schristos /* grep.c - main driver file for grep.
4f7313695Schristos    Copyright 1992, 1997-1999, 2000 Free Software Foundation, Inc.
5f7313695Schristos 
6f7313695Schristos    This program is free software; you can redistribute it and/or modify
7f7313695Schristos    it under the terms of the GNU General Public License as published by
8f7313695Schristos    the Free Software Foundation; either version 2, or (at your option)
9f7313695Schristos    any later version.
10f7313695Schristos 
11f7313695Schristos    This program is distributed in the hope that it will be useful,
12f7313695Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
13f7313695Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14f7313695Schristos    GNU General Public License for more details.
15f7313695Schristos 
16f7313695Schristos    You should have received a copy of the GNU General Public License
17f7313695Schristos    along with this program; if not, write to the Free Software
18f7313695Schristos    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19f7313695Schristos    02111-1307, USA.  */
20f7313695Schristos 
21f7313695Schristos /* Written July 1992 by Mike Haertel.  */
22f7313695Schristos 
23f7313695Schristos #ifdef HAVE_CONFIG_H
24f7313695Schristos # include <config.h>
25f7313695Schristos #endif
26f7313695Schristos #include <sys/types.h>
27f7313695Schristos #include <sys/stat.h>
28f7313695Schristos #if defined(HAVE_MMAP)
29f7313695Schristos # include <sys/mman.h>
30f7313695Schristos #endif
31f7313695Schristos #if defined(HAVE_SETRLIMIT)
32f7313695Schristos # include <sys/time.h>
33f7313695Schristos # include <sys/resource.h>
34f7313695Schristos #endif
35f7313695Schristos #include <stdio.h>
36f7313695Schristos #include "system.h"
37f7313695Schristos #include "getopt.h"
38f7313695Schristos #include "getpagesize.h"
39f7313695Schristos #include "grep.h"
40f7313695Schristos #include "savedir.h"
41f7313695Schristos #include "xstrtol.h"
42f7313695Schristos #include "xalloc.h"
43f7313695Schristos #include "error.h"
44f7313695Schristos #include "exclude.h"
45f7313695Schristos #include "closeout.h"
46f7313695Schristos 
47f7313695Schristos #undef MAX
48f7313695Schristos #define MAX(A,B) ((A) > (B) ? (A) : (B))
49f7313695Schristos 
50f7313695Schristos struct stats
51f7313695Schristos {
52f7313695Schristos   struct stats const *parent;
53f7313695Schristos   struct stat stat;
54f7313695Schristos };
55f7313695Schristos 
56947ffbb0Schristos #include <limits.h>
57947ffbb0Schristos #define MAX_OFF_T (sizeof(off_t) == sizeof(char) ? INT_MAX : \
58947ffbb0Schristos                    (sizeof(off_t) == sizeof(short) ? SHRT_MAX : \
59947ffbb0Schristos                     (sizeof(off_t) == sizeof(int) ? INT_MAX : \
60947ffbb0Schristos                      (sizeof(off_t) == sizeof(long) ? LONG_MAX : \
61947ffbb0Schristos                       (sizeof(off_t) == sizeof(long long) ? LLONG_MAX : INTMAX_MAX)))))
62947ffbb0Schristos 
63f7313695Schristos /* base of chain of stat buffers, used to detect directory loops */
64f7313695Schristos static struct stats stats_base;
65f7313695Schristos 
66f7313695Schristos /* if non-zero, display usage information and exit */
67f7313695Schristos static int show_help;
68f7313695Schristos 
69f7313695Schristos /* If non-zero, print the version on standard output and exit.  */
70f7313695Schristos static int show_version;
71f7313695Schristos 
72f7313695Schristos /* If nonzero, suppress diagnostics for nonexistent or unreadable files.  */
73f7313695Schristos static int suppress_errors;
74f7313695Schristos 
75f7313695Schristos /* If nonzero, use mmap if possible.  */
76f7313695Schristos static int mmap_option;
77f7313695Schristos 
78f7313695Schristos /* If nonzero, use grep_color marker.  */
79f7313695Schristos static int color_option;
80f7313695Schristos 
81f7313695Schristos /* If nonzero, show only the part of a line matching the expression. */
82f7313695Schristos static int only_matching;
83f7313695Schristos 
84f7313695Schristos /* The color string used.  The user can overwrite it using the environment
85f7313695Schristos    variable GREP_COLOR.  The default is to print red.  */
86f7313695Schristos static const char *grep_color = "01;31";
87f7313695Schristos 
88f7313695Schristos static struct exclude *excluded_patterns;
89f7313695Schristos static struct exclude *included_patterns;
90f7313695Schristos /* Short options.  */
91f7313695Schristos static char const short_options[] =
92f7313695Schristos "0123456789A:B:C:D:EFGHIPUVX:abcd:e:f:hiKLlm:noqRrsuvwxyZz";
93f7313695Schristos 
94f7313695Schristos /* Non-boolean long options that have no corresponding short equivalents.  */
95f7313695Schristos enum
96f7313695Schristos {
97f7313695Schristos   BINARY_FILES_OPTION = CHAR_MAX + 1,
98f7313695Schristos   COLOR_OPTION,
99f7313695Schristos   INCLUDE_OPTION,
100f7313695Schristos   EXCLUDE_OPTION,
101f7313695Schristos   EXCLUDE_FROM_OPTION,
102f7313695Schristos   LINE_BUFFERED_OPTION,
103f7313695Schristos   LABEL_OPTION
104f7313695Schristos };
105f7313695Schristos 
106f7313695Schristos /* Long options equivalences. */
107f7313695Schristos static struct option const long_options[] =
108f7313695Schristos {
109f7313695Schristos   {"after-context", required_argument, NULL, 'A'},
110f7313695Schristos   {"basic-regexp", no_argument, NULL, 'G'},
111f7313695Schristos   {"before-context", required_argument, NULL, 'B'},
112f7313695Schristos   {"binary-files", required_argument, NULL, BINARY_FILES_OPTION},
113f7313695Schristos   {"byte-offset", no_argument, NULL, 'b'},
114f7313695Schristos   {"context", required_argument, NULL, 'C'},
115f7313695Schristos   {"color", optional_argument, NULL, COLOR_OPTION},
116f7313695Schristos   {"colour", optional_argument, NULL, COLOR_OPTION},
117f7313695Schristos   {"count", no_argument, NULL, 'c'},
118f7313695Schristos   {"devices", required_argument, NULL, 'D'},
119f7313695Schristos   {"directories", required_argument, NULL, 'd'},
120f7313695Schristos   {"extended-regexp", no_argument, NULL, 'E'},
121f7313695Schristos   {"exclude", required_argument, NULL, EXCLUDE_OPTION},
122f7313695Schristos   {"exclude-from", required_argument, NULL, EXCLUDE_FROM_OPTION},
123f7313695Schristos   {"file", required_argument, NULL, 'f'},
124f7313695Schristos   {"files-with-matches", no_argument, NULL, 'l'},
125f7313695Schristos   {"files-without-match", no_argument, NULL, 'L'},
126f7313695Schristos   {"fixed-regexp", no_argument, NULL, 'F'},
127f7313695Schristos   {"fixed-strings", no_argument, NULL, 'F'},
128f7313695Schristos   {"help", no_argument, &show_help, 1},
129f7313695Schristos   {"include", required_argument, NULL, INCLUDE_OPTION},
130f7313695Schristos   {"ignore-case", no_argument, NULL, 'i'},
131f7313695Schristos   {"label", required_argument, NULL, LABEL_OPTION},
132f7313695Schristos   {"line-buffered", no_argument, NULL, LINE_BUFFERED_OPTION},
133f7313695Schristos   {"line-number", no_argument, NULL, 'n'},
134f7313695Schristos   {"line-regexp", no_argument, NULL, 'x'},
135f7313695Schristos   {"max-count", required_argument, NULL, 'm'},
136f7313695Schristos   {"mmap", no_argument, &mmap_option, 1},
137f7313695Schristos   {"no-filename", no_argument, NULL, 'h'},
138f7313695Schristos   {"no-messages", no_argument, NULL, 's'},
139f7313695Schristos   {"null", no_argument, NULL, 'Z'},
140f7313695Schristos   {"null-data", no_argument, NULL, 'z'},
141f7313695Schristos   {"only-matching", no_argument, NULL, 'o'},
142f7313695Schristos   {"perl-regexp", no_argument, NULL, 'P'},
143f7313695Schristos   {"quiet", no_argument, NULL, 'q'},
144f7313695Schristos   {"recursive", no_argument, NULL, 'r'},
145f7313695Schristos   {"recursive", no_argument, NULL, 'R'},
146f7313695Schristos   {"regexp", required_argument, NULL, 'e'},
147f7313695Schristos   {"invert-match", no_argument, NULL, 'v'},
148f7313695Schristos   {"silent", no_argument, NULL, 'q'},
149f7313695Schristos   {"text", no_argument, NULL, 'a'},
150f7313695Schristos   {"binary", no_argument, NULL, 'U'},
151f7313695Schristos   {"unix-byte-offsets", no_argument, NULL, 'u'},
152f7313695Schristos   {"version", no_argument, NULL, 'V'},
153f7313695Schristos   {"with-filename", no_argument, NULL, 'H'},
154f7313695Schristos   {"word-regexp", no_argument, NULL, 'w'},
155f7313695Schristos   {0, 0, 0, 0}
156f7313695Schristos };
157f7313695Schristos 
158f7313695Schristos /* Define flags declared in grep.h. */
159f7313695Schristos int match_icase;
160f7313695Schristos int match_words;
161f7313695Schristos int match_lines;
162f7313695Schristos unsigned char eolbyte;
163f7313695Schristos 
164f7313695Schristos /* For error messages. */
165f7313695Schristos /* The name the program was run with, stripped of any leading path. */
166f7313695Schristos char *program_name;
167f7313695Schristos static char const *filename;
168f7313695Schristos static int errseen;
169f7313695Schristos 
170f7313695Schristos /* How to handle directories.  */
171f7313695Schristos static enum
172f7313695Schristos   {
173f7313695Schristos     READ_DIRECTORIES,
174f7313695Schristos     RECURSE_DIRECTORIES,
175f7313695Schristos     SKIP_DIRECTORIES
176f7313695Schristos   } directories = READ_DIRECTORIES;
177f7313695Schristos 
178f7313695Schristos /* How to handle devices. */
179f7313695Schristos static enum
180f7313695Schristos   {
181f7313695Schristos     READ_DEVICES,
182f7313695Schristos     SKIP_DEVICES
183f7313695Schristos   } devices = READ_DEVICES;
184f7313695Schristos 
185f7313695Schristos static int grepdir PARAMS ((char const *, struct stats const *));
186f7313695Schristos #if defined(HAVE_DOS_FILE_CONTENTS)
187f7313695Schristos static inline int undossify_input PARAMS ((register char *, size_t));
188f7313695Schristos #endif
189f7313695Schristos 
190f7313695Schristos /* Functions we'll use to search. */
191f7313695Schristos static void (*compile) PARAMS ((char const *, size_t));
192f7313695Schristos static size_t (*execute) PARAMS ((char const *, size_t, size_t *, int));
193f7313695Schristos 
194f7313695Schristos /* Like error, but suppress the diagnostic if requested.  */
195f7313695Schristos static void
suppressible_error(char const * mesg,int errnum)196f7313695Schristos suppressible_error (char const *mesg, int errnum)
197f7313695Schristos {
198f7313695Schristos   if (! suppress_errors)
199f7313695Schristos     error (0, errnum, "%s", mesg);
200f7313695Schristos   errseen = 1;
201f7313695Schristos }
202f7313695Schristos 
203f7313695Schristos /* Convert STR to a positive integer, storing the result in *OUT.
204f7313695Schristos    STR must be a valid context length argument; report an error if it
205f7313695Schristos    isn't.  */
206f7313695Schristos static void
context_length_arg(char const * str,int * out)207f7313695Schristos context_length_arg (char const *str, int *out)
208f7313695Schristos {
209f7313695Schristos   uintmax_t value;
210f7313695Schristos   if (! (xstrtoumax (str, 0, 10, &value, "") == LONGINT_OK
211f7313695Schristos 	 && 0 <= (*out = value)
212f7313695Schristos 	 && *out == value))
213f7313695Schristos     {
214947ffbb0Schristos       error (2, 0, "%s: %s", str, _("invalid context length argument"));
215f7313695Schristos     }
216f7313695Schristos }
217f7313695Schristos 
218f7313695Schristos 
219f7313695Schristos /* Hairy buffering mechanism for grep.  The intent is to keep
220f7313695Schristos    all reads aligned on a page boundary and multiples of the
221f7313695Schristos    page size, unless a read yields a partial page.  */
222f7313695Schristos 
223f7313695Schristos static char *buffer;		/* Base of buffer. */
224f7313695Schristos static size_t bufalloc;		/* Allocated buffer size, counting slop. */
225f7313695Schristos #define INITIAL_BUFSIZE 32768	/* Initial buffer size, not counting slop. */
226f7313695Schristos static int bufdesc;		/* File descriptor. */
227f7313695Schristos static char *bufbeg;		/* Beginning of user-visible stuff. */
228f7313695Schristos static char *buflim;		/* Limit of user-visible stuff. */
229f7313695Schristos static size_t pagesize;		/* alignment of memory pages */
230f7313695Schristos static off_t bufoffset;		/* Read offset; defined on regular files.  */
231f7313695Schristos static off_t after_last_match;	/* Pointer after last matching line that
232f7313695Schristos 				   would have been output if we were
233f7313695Schristos 				   outputting characters. */
234f7313695Schristos 
235f7313695Schristos #if defined(HAVE_MMAP)
236f7313695Schristos static int bufmapped;		/* True if buffer is memory-mapped.  */
237f7313695Schristos static off_t initial_bufoffset;	/* Initial value of bufoffset. */
238f7313695Schristos #else
239f7313695Schristos # define bufmapped 0
240f7313695Schristos #endif
241f7313695Schristos 
242f7313695Schristos /* Return VAL aligned to the next multiple of ALIGNMENT.  VAL can be
243f7313695Schristos    an integer or a pointer.  Both args must be free of side effects.  */
244f7313695Schristos #define ALIGN_TO(val, alignment) \
245f7313695Schristos   ((size_t) (val) % (alignment) == 0 \
246f7313695Schristos    ? (val) \
247f7313695Schristos    : (val) + ((alignment) - (size_t) (val) % (alignment)))
248f7313695Schristos 
249f7313695Schristos /* Reset the buffer for a new file, returning zero if we should skip it.
250f7313695Schristos    Initialize on the first time through. */
251f7313695Schristos static int
reset(int fd,char const * file,struct stats * stats)252f7313695Schristos reset (int fd, char const *file, struct stats *stats)
253f7313695Schristos {
254f7313695Schristos   if (! pagesize)
255f7313695Schristos     {
256f7313695Schristos       pagesize = getpagesize ();
257f7313695Schristos       if (pagesize == 0 || 2 * pagesize + 1 <= pagesize)
258f7313695Schristos 	abort ();
259f7313695Schristos       bufalloc = ALIGN_TO (INITIAL_BUFSIZE, pagesize) + pagesize + 1;
260f7313695Schristos       buffer = xmalloc (bufalloc);
261f7313695Schristos     }
262f7313695Schristos 
263f7313695Schristos   bufbeg = buflim = ALIGN_TO (buffer + 1, pagesize);
264f7313695Schristos   bufbeg[-1] = eolbyte;
265f7313695Schristos   bufdesc = fd;
266f7313695Schristos 
267f7313695Schristos   if (fstat (fd, &stats->stat) != 0)
268f7313695Schristos     {
269f7313695Schristos       error (0, errno, "fstat");
270f7313695Schristos       return 0;
271f7313695Schristos     }
272f7313695Schristos   if (directories == SKIP_DIRECTORIES && S_ISDIR (stats->stat.st_mode))
273f7313695Schristos     return 0;
2749a7f9996Schristos   if (devices == SKIP_DEVICES && (S_ISCHR(stats->stat.st_mode)
2759a7f9996Schristos   || S_ISBLK(stats->stat.st_mode)
2769a7f9996Schristos #ifdef S_ISSOCK
2779a7f9996Schristos   || S_ISSOCK(stats->stat.st_mode)
278f7313695Schristos #endif
2799a7f9996Schristos #ifdef S_ISFIFO
2809a7f9996Schristos   || S_ISFIFO(stats->stat.st_mode)
2819a7f9996Schristos #endif
2829a7f9996Schristos   ))
283f7313695Schristos     return 0;
284f7313695Schristos   if (S_ISREG (stats->stat.st_mode))
285f7313695Schristos     {
286f7313695Schristos       if (file)
287f7313695Schristos 	bufoffset = 0;
288f7313695Schristos       else
289f7313695Schristos 	{
290f7313695Schristos 	  bufoffset = lseek (fd, 0, SEEK_CUR);
291f7313695Schristos 	  if (bufoffset < 0)
292f7313695Schristos 	    {
293f7313695Schristos 	      error (0, errno, "lseek");
294f7313695Schristos 	      return 0;
295f7313695Schristos 	    }
296f7313695Schristos 	}
297f7313695Schristos #if defined(HAVE_MMAP)
298f7313695Schristos       initial_bufoffset = bufoffset;
299f7313695Schristos       bufmapped = mmap_option && bufoffset % pagesize == 0;
300f7313695Schristos #endif
301f7313695Schristos     }
302f7313695Schristos   else
303f7313695Schristos     {
304f7313695Schristos #if defined(HAVE_MMAP)
305f7313695Schristos       bufmapped = 0;
306f7313695Schristos #endif
307f7313695Schristos     }
308f7313695Schristos   return 1;
309f7313695Schristos }
310f7313695Schristos 
311f7313695Schristos /* Read new stuff into the buffer, saving the specified
312f7313695Schristos    amount of old stuff.  When we're done, 'bufbeg' points
313f7313695Schristos    to the beginning of the buffer contents, and 'buflim'
314f7313695Schristos    points just after the end.  Return zero if there's an error.  */
315f7313695Schristos static int
fillbuf(size_t save,struct stats const * stats)316f7313695Schristos fillbuf (size_t save, struct stats const *stats)
317f7313695Schristos {
318f7313695Schristos   size_t fillsize = 0;
319f7313695Schristos   int cc = 1;
320f7313695Schristos   char *readbuf;
321f7313695Schristos   size_t readsize;
322f7313695Schristos 
323f7313695Schristos   /* Offset from start of buffer to start of old stuff
324f7313695Schristos      that we want to save.  */
325f7313695Schristos   size_t saved_offset = buflim - save - buffer;
326f7313695Schristos 
327f7313695Schristos   if (pagesize <= buffer + bufalloc - buflim)
328f7313695Schristos     {
329f7313695Schristos       readbuf = buflim;
330f7313695Schristos       bufbeg = buflim - save;
331f7313695Schristos     }
332f7313695Schristos   else
333f7313695Schristos     {
334f7313695Schristos       size_t minsize = save + pagesize;
335f7313695Schristos       size_t newsize;
336f7313695Schristos       size_t newalloc;
337f7313695Schristos       char *newbuf;
338f7313695Schristos 
339f7313695Schristos       /* Grow newsize until it is at least as great as minsize.  */
340f7313695Schristos       for (newsize = bufalloc - pagesize - 1; newsize < minsize; newsize *= 2)
341f7313695Schristos 	if (newsize * 2 < newsize || newsize * 2 + pagesize + 1 < newsize * 2)
342f7313695Schristos 	  xalloc_die ();
343f7313695Schristos 
344f7313695Schristos       /* Try not to allocate more memory than the file size indicates,
345f7313695Schristos 	 as that might cause unnecessary memory exhaustion if the file
346f7313695Schristos 	 is large.  However, do not use the original file size as a
347f7313695Schristos 	 heuristic if we've already read past the file end, as most
348f7313695Schristos 	 likely the file is growing.  */
349f7313695Schristos       if (S_ISREG (stats->stat.st_mode))
350f7313695Schristos 	{
351f7313695Schristos 	  off_t to_be_read = stats->stat.st_size - bufoffset;
352f7313695Schristos 	  off_t maxsize_off = save + to_be_read;
353f7313695Schristos 	  if (0 <= to_be_read && to_be_read <= maxsize_off
354f7313695Schristos 	      && maxsize_off == (size_t) maxsize_off
355f7313695Schristos 	      && minsize <= (size_t) maxsize_off
356f7313695Schristos 	      && (size_t) maxsize_off < newsize)
357f7313695Schristos 	    newsize = maxsize_off;
358f7313695Schristos 	}
359f7313695Schristos 
360f7313695Schristos       /* Add enough room so that the buffer is aligned and has room
361f7313695Schristos 	 for byte sentinels fore and aft.  */
362f7313695Schristos       newalloc = newsize + pagesize + 1;
363f7313695Schristos 
364f7313695Schristos       newbuf = bufalloc < newalloc ? xmalloc (bufalloc = newalloc) : buffer;
365f7313695Schristos       readbuf = ALIGN_TO (newbuf + 1 + save, pagesize);
366f7313695Schristos       bufbeg = readbuf - save;
367f7313695Schristos       memmove (bufbeg, buffer + saved_offset, save);
368f7313695Schristos       bufbeg[-1] = eolbyte;
369f7313695Schristos       if (newbuf != buffer)
370f7313695Schristos 	{
371f7313695Schristos 	  free (buffer);
372f7313695Schristos 	  buffer = newbuf;
373f7313695Schristos 	}
374f7313695Schristos     }
375f7313695Schristos 
376f7313695Schristos   readsize = buffer + bufalloc - readbuf;
377f7313695Schristos   readsize -= readsize % pagesize;
378f7313695Schristos 
379f7313695Schristos #if defined(HAVE_MMAP)
380f7313695Schristos   if (bufmapped)
381f7313695Schristos     {
382f7313695Schristos       size_t mmapsize = readsize;
383f7313695Schristos 
384f7313695Schristos       /* Don't mmap past the end of the file; some hosts don't allow this.
385f7313695Schristos 	 Use `read' on the last page.  */
386f7313695Schristos       if (stats->stat.st_size - bufoffset < mmapsize)
387f7313695Schristos 	{
388f7313695Schristos 	  mmapsize = stats->stat.st_size - bufoffset;
389f7313695Schristos 	  mmapsize -= mmapsize % pagesize;
390f7313695Schristos 	}
391f7313695Schristos 
392f7313695Schristos       if (mmapsize
393f7313695Schristos 	  && (mmap ((caddr_t) readbuf, mmapsize,
394f7313695Schristos 		    PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED,
395f7313695Schristos 		    bufdesc, bufoffset)
396f7313695Schristos 	      != (caddr_t) -1))
397f7313695Schristos 	{
398f7313695Schristos 	  /* Do not bother to use madvise with MADV_SEQUENTIAL or
399f7313695Schristos 	     MADV_WILLNEED on the mmapped memory.  One might think it
400f7313695Schristos 	     would help, but it slows us down about 30% on SunOS 4.1.  */
401f7313695Schristos 	  fillsize = mmapsize;
402f7313695Schristos 	}
403f7313695Schristos       else
404f7313695Schristos 	{
405f7313695Schristos 	  /* Stop using mmap on this file.  Synchronize the file
406f7313695Schristos 	     offset.  Do not warn about mmap failures.  On some hosts
407f7313695Schristos 	     (e.g. Solaris 2.5) mmap can fail merely because some
408f7313695Schristos 	     other process has an advisory read lock on the file.
409f7313695Schristos 	     There's no point alarming the user about this misfeature.  */
410f7313695Schristos 	  bufmapped = 0;
411f7313695Schristos 	  if (bufoffset != initial_bufoffset
412f7313695Schristos 	      && lseek (bufdesc, bufoffset, SEEK_SET) < 0)
413f7313695Schristos 	    {
414f7313695Schristos 	      error (0, errno, "lseek");
415f7313695Schristos 	      cc = 0;
416f7313695Schristos 	    }
417f7313695Schristos 	}
418f7313695Schristos     }
419f7313695Schristos #endif /*HAVE_MMAP*/
420f7313695Schristos 
421f7313695Schristos   if (! fillsize)
422f7313695Schristos     {
423f7313695Schristos       ssize_t bytesread;
424f7313695Schristos       while ((bytesread = read (bufdesc, readbuf, readsize)) < 0
425f7313695Schristos 	     && errno == EINTR)
426f7313695Schristos 	continue;
427f7313695Schristos       if (bytesread < 0)
428f7313695Schristos 	cc = 0;
429f7313695Schristos       else
430f7313695Schristos 	fillsize = bytesread;
431f7313695Schristos     }
432f7313695Schristos 
433f7313695Schristos   bufoffset += fillsize;
434f7313695Schristos #if defined(HAVE_DOS_FILE_CONTENTS)
435f7313695Schristos   if (fillsize)
436f7313695Schristos     fillsize = undossify_input (readbuf, fillsize);
437f7313695Schristos #endif
438f7313695Schristos   buflim = readbuf + fillsize;
439f7313695Schristos   return cc;
440f7313695Schristos }
441f7313695Schristos 
442f7313695Schristos /* Flags controlling the style of output. */
443f7313695Schristos static enum
444f7313695Schristos {
445f7313695Schristos   BINARY_BINARY_FILES,
446f7313695Schristos   TEXT_BINARY_FILES,
447f7313695Schristos   WITHOUT_MATCH_BINARY_FILES
448f7313695Schristos } binary_files;		/* How to handle binary files.  */
449f7313695Schristos 
450f7313695Schristos static int filename_mask;	/* If zero, output nulls after filenames.  */
451f7313695Schristos static int out_quiet;		/* Suppress all normal output. */
452f7313695Schristos static int out_invert;		/* Print nonmatching stuff. */
453f7313695Schristos static int out_file;		/* Print filenames. */
454f7313695Schristos static int out_line;		/* Print line numbers. */
455f7313695Schristos static int out_byte;		/* Print byte offsets. */
456f7313695Schristos static int out_before;		/* Lines of leading context. */
457f7313695Schristos static int out_after;		/* Lines of trailing context. */
458f7313695Schristos static int count_matches;	/* Count matching lines.  */
459f7313695Schristos static int list_files;		/* List matching files.  */
460f7313695Schristos static int no_filenames;	/* Suppress file names.  */
461f7313695Schristos static off_t max_count;		/* Stop after outputting this many
462f7313695Schristos 				   lines from an input file.  */
463f7313695Schristos static int line_buffered;       /* If nonzero, use line buffering, i.e.
464f7313695Schristos 				   fflush everyline out.  */
465f7313695Schristos static char *label = NULL;      /* Fake filename for stdin */
466f7313695Schristos 
467f7313695Schristos 
468f7313695Schristos /* Internal variables to keep track of byte count, context, etc. */
469f7313695Schristos static uintmax_t totalcc;	/* Total character count before bufbeg. */
470f7313695Schristos static char const *lastnl;	/* Pointer after last newline counted. */
471f7313695Schristos static char const *lastout;	/* Pointer after last character output;
472f7313695Schristos 				   NULL if no character has been output
473f7313695Schristos 				   or if it's conceptually before bufbeg. */
474f7313695Schristos static uintmax_t totalnl;	/* Total newline count before lastnl. */
475f7313695Schristos static off_t outleft;		/* Maximum number of lines to be output.  */
476f7313695Schristos static int pending;		/* Pending lines of output.
477f7313695Schristos 				   Always kept 0 if out_quiet is true.  */
478f7313695Schristos static int done_on_match;	/* Stop scanning file on first match.  */
479f7313695Schristos static int exit_on_match;	/* Exit on first match.  */
480f7313695Schristos 
481f7313695Schristos #if defined(HAVE_DOS_FILE_CONTENTS)
482f7313695Schristos # include "dosbuf.c"
483f7313695Schristos #endif
484f7313695Schristos 
485f7313695Schristos /* Add two numbers that count input bytes or lines, and report an
486f7313695Schristos    error if the addition overflows.  */
487f7313695Schristos static uintmax_t
add_count(uintmax_t a,uintmax_t b)488f7313695Schristos add_count (uintmax_t a, uintmax_t b)
489f7313695Schristos {
490f7313695Schristos   uintmax_t sum = a + b;
491f7313695Schristos   if (sum < a)
492f7313695Schristos     error (2, 0, _("input is too large to count"));
493f7313695Schristos   return sum;
494f7313695Schristos }
495f7313695Schristos 
496f7313695Schristos static void
nlscan(char const * lim)497f7313695Schristos nlscan (char const *lim)
498f7313695Schristos {
499f7313695Schristos   size_t newlines = 0;
500f7313695Schristos   char const *beg;
501f7313695Schristos   for (beg = lastnl; beg != lim; beg = memchr (beg, eolbyte, lim - beg), beg++)
502f7313695Schristos     newlines++;
503f7313695Schristos   totalnl = add_count (totalnl, newlines);
504f7313695Schristos   lastnl = lim;
505f7313695Schristos }
506f7313695Schristos 
507f7313695Schristos /* Print a byte offset, followed by a character separator.  */
508f7313695Schristos static void
print_offset_sep(uintmax_t pos,char sep)509f7313695Schristos print_offset_sep (uintmax_t pos, char sep)
510f7313695Schristos {
511f7313695Schristos   /* Do not rely on printf to print pos, since uintmax_t may be longer
512f7313695Schristos      than long, and long long is not portable.  */
513f7313695Schristos 
514f7313695Schristos   char buf[sizeof pos * CHAR_BIT];
515f7313695Schristos   char *p = buf + sizeof buf - 1;
516f7313695Schristos   *p = sep;
517f7313695Schristos 
518f7313695Schristos   do
519f7313695Schristos     *--p = '0' + pos % 10;
520f7313695Schristos   while ((pos /= 10) != 0);
521f7313695Schristos 
522f7313695Schristos   fwrite (p, 1, buf + sizeof buf - p, stdout);
523f7313695Schristos }
524f7313695Schristos 
525f7313695Schristos static void
prline(char const * beg,char const * lim,int sep)526f7313695Schristos prline (char const *beg, char const *lim, int sep)
527f7313695Schristos {
528f7313695Schristos   if (out_file)
529f7313695Schristos     printf ("%s%c", filename, sep & filename_mask);
530f7313695Schristos   if (out_line)
531f7313695Schristos     {
532f7313695Schristos       nlscan (beg);
533f7313695Schristos       totalnl = add_count (totalnl, 1);
534f7313695Schristos       print_offset_sep (totalnl, sep);
535f7313695Schristos       lastnl = lim;
536f7313695Schristos     }
537f7313695Schristos   if (out_byte)
538f7313695Schristos     {
539f7313695Schristos       uintmax_t pos = add_count (totalcc, beg - bufbeg);
540f7313695Schristos #if defined(HAVE_DOS_FILE_CONTENTS)
541f7313695Schristos       pos = dossified_pos (pos);
542f7313695Schristos #endif
543f7313695Schristos       print_offset_sep (pos, sep);
544f7313695Schristos     }
545f7313695Schristos   if (only_matching)
546f7313695Schristos     {
547f7313695Schristos       size_t match_size;
548f7313695Schristos       size_t match_offset;
549f7313695Schristos       while ((match_offset = (*execute) (beg, lim - beg, &match_size, 1))
550f7313695Schristos 	  != (size_t) -1)
551f7313695Schristos         {
552f7313695Schristos 	  char const *b = beg + match_offset;
553f7313695Schristos 	  if (b == lim)
554f7313695Schristos 	    break;
555f7313695Schristos 	  if (match_size == 0)
556947ffbb0Schristos 	    {
557947ffbb0Schristos 	      beg++;
558947ffbb0Schristos 	      continue;
559947ffbb0Schristos 	    }
560f7313695Schristos 	  if(color_option)
561f7313695Schristos 	    printf("\33[%sm", grep_color);
562f7313695Schristos 	  fwrite(b, sizeof (char), match_size, stdout);
563f7313695Schristos 	  if(color_option)
564f7313695Schristos 	    fputs("\33[00m", stdout);
565f7313695Schristos 	  fputs("\n", stdout);
566f7313695Schristos 	  beg = b + match_size;
567f7313695Schristos         }
568f7313695Schristos       lastout = lim;
569f7313695Schristos       if(line_buffered)
570f7313695Schristos 	fflush(stdout);
571f7313695Schristos       return;
572f7313695Schristos     }
573f7313695Schristos   if (color_option)
574f7313695Schristos     {
575f7313695Schristos       size_t match_size;
576f7313695Schristos       size_t match_offset;
577f7313695Schristos       if(match_icase)
578f7313695Schristos         {
579f7313695Schristos 	  /* Yuck, this is tricky */
580f7313695Schristos           char *buf = (char*) xmalloc (lim - beg);
581f7313695Schristos 	  char *ibeg = buf;
582f7313695Schristos 	  char *ilim = ibeg + (lim - beg);
583f7313695Schristos 	  int i;
584f7313695Schristos 	  for (i = 0; i < lim - beg; i++)
585f7313695Schristos 	    ibeg[i] = tolower (beg[i]);
586f7313695Schristos 	  while ((match_offset = (*execute) (ibeg, ilim-ibeg, &match_size, 1))
587f7313695Schristos 		 != (size_t) -1)
588f7313695Schristos 	    {
589f7313695Schristos 	      char const *b = beg + match_offset;
590f7313695Schristos 	      if (b == lim)
591f7313695Schristos 		break;
592f7313695Schristos 	      fwrite (beg, sizeof (char), match_offset, stdout);
593f7313695Schristos 	      printf ("\33[%sm", grep_color);
594f7313695Schristos 	      fwrite (b, sizeof (char), match_size, stdout);
595f7313695Schristos 	      fputs ("\33[00m", stdout);
596f7313695Schristos 	      beg = b + match_size;
597f7313695Schristos 	      ibeg = ibeg + match_offset + match_size;
598f7313695Schristos 	    }
599f7313695Schristos 	  fwrite (beg, 1, lim - beg, stdout);
600f7313695Schristos 	  free (buf);
601f7313695Schristos 	  lastout = lim;
602f7313695Schristos 	  return;
603f7313695Schristos 	}
604f7313695Schristos       while (lim-beg && (match_offset = (*execute) (beg, lim - beg, &match_size, 1))
605f7313695Schristos 	     != (size_t) -1)
606f7313695Schristos 	{
607f7313695Schristos 	  char const *b = beg + match_offset;
608f7313695Schristos 	  /* Avoid matching the empty line at the end of the buffer. */
609f7313695Schristos 	  if (b == lim)
610f7313695Schristos 	    break;
611f7313695Schristos 	  /* Avoid hanging on grep --color "" foo */
612f7313695Schristos 	  if (match_size == 0)
613f7313695Schristos 	    break;
614f7313695Schristos 	  fwrite (beg, sizeof (char), match_offset, stdout);
615f7313695Schristos 	  printf ("\33[%sm", grep_color);
616f7313695Schristos 	  fwrite (b, sizeof (char), match_size, stdout);
617f7313695Schristos 	  fputs ("\33[00m", stdout);
618f7313695Schristos 	  beg = b + match_size;
619f7313695Schristos 	}
620f7313695Schristos     }
621f7313695Schristos   fwrite (beg, 1, lim - beg, stdout);
622f7313695Schristos   if (ferror (stdout))
623f7313695Schristos     error (0, errno, _("writing output"));
624f7313695Schristos   lastout = lim;
625f7313695Schristos   if (line_buffered)
626f7313695Schristos     fflush (stdout);
627f7313695Schristos }
628f7313695Schristos 
629f7313695Schristos /* Print pending lines of trailing context prior to LIM. Trailing context ends
630f7313695Schristos    at the next matching line when OUTLEFT is 0.  */
631f7313695Schristos static void
prpending(char const * lim)632f7313695Schristos prpending (char const *lim)
633f7313695Schristos {
634f7313695Schristos   if (!lastout)
635f7313695Schristos     lastout = bufbeg;
636f7313695Schristos   while (pending > 0 && lastout < lim)
637f7313695Schristos     {
638f7313695Schristos       char const *nl = memchr (lastout, eolbyte, lim - lastout);
639f7313695Schristos       size_t match_size;
640f7313695Schristos       --pending;
641f7313695Schristos       if (outleft
642f7313695Schristos 	  || (((*execute) (lastout, nl - lastout, &match_size, 0) == (size_t) -1)
643f7313695Schristos 	      == !out_invert))
644f7313695Schristos 	prline (lastout, nl + 1, '-');
645f7313695Schristos       else
646f7313695Schristos 	pending = 0;
647f7313695Schristos     }
648f7313695Schristos }
649f7313695Schristos 
650f7313695Schristos /* Print the lines between BEG and LIM.  Deal with context crap.
651f7313695Schristos    If NLINESP is non-null, store a count of lines between BEG and LIM.  */
652f7313695Schristos static void
prtext(char const * beg,char const * lim,int * nlinesp)653f7313695Schristos prtext (char const *beg, char const *lim, int *nlinesp)
654f7313695Schristos {
655f7313695Schristos   static int used;		/* avoid printing "--" before any output */
656f7313695Schristos   char const *bp, *p;
657f7313695Schristos   char eol = eolbyte;
658f7313695Schristos   int i, n;
659f7313695Schristos 
660f7313695Schristos   if (!out_quiet && pending > 0)
661f7313695Schristos     prpending (beg);
662f7313695Schristos 
663f7313695Schristos   p = beg;
664f7313695Schristos 
665f7313695Schristos   if (!out_quiet)
666f7313695Schristos     {
667f7313695Schristos       /* Deal with leading context crap. */
668f7313695Schristos 
669f7313695Schristos       bp = lastout ? lastout : bufbeg;
670f7313695Schristos       for (i = 0; i < out_before; ++i)
671f7313695Schristos 	if (p > bp)
672f7313695Schristos 	  do
673f7313695Schristos 	    --p;
674f7313695Schristos 	  while (p[-1] != eol);
675f7313695Schristos 
676f7313695Schristos       /* We only print the "--" separator if our output is
677f7313695Schristos 	 discontiguous from the last output in the file. */
678f7313695Schristos       if ((out_before || out_after) && used && p != lastout)
679f7313695Schristos 	puts ("--");
680f7313695Schristos 
681f7313695Schristos       while (p < beg)
682f7313695Schristos 	{
683f7313695Schristos 	  char const *nl = memchr (p, eol, beg - p);
684f7313695Schristos 	  nl++;
685f7313695Schristos 	  prline (p, nl, '-');
686f7313695Schristos 	  p = nl;
687f7313695Schristos 	}
688f7313695Schristos     }
689f7313695Schristos 
690f7313695Schristos   if (nlinesp)
691f7313695Schristos     {
692f7313695Schristos       /* Caller wants a line count. */
693f7313695Schristos       for (n = 0; p < lim && n < outleft; n++)
694f7313695Schristos 	{
695f7313695Schristos 	  char const *nl = memchr (p, eol, lim - p);
696f7313695Schristos 	  nl++;
697f7313695Schristos 	  if (!out_quiet)
698f7313695Schristos 	    prline (p, nl, ':');
699f7313695Schristos 	  p = nl;
700f7313695Schristos 	}
701f7313695Schristos       *nlinesp = n;
702f7313695Schristos 
703f7313695Schristos       /* relying on it that this function is never called when outleft = 0.  */
704f7313695Schristos       after_last_match = bufoffset - (buflim - p);
705f7313695Schristos     }
706f7313695Schristos   else
707f7313695Schristos     if (!out_quiet)
708f7313695Schristos       prline (beg, lim, ':');
709f7313695Schristos 
710f7313695Schristos   pending = out_quiet ? 0 : out_after;
711f7313695Schristos   used = 1;
712f7313695Schristos }
713f7313695Schristos 
714f7313695Schristos /* Scan the specified portion of the buffer, matching lines (or
715f7313695Schristos    between matching lines if OUT_INVERT is true).  Return a count of
716f7313695Schristos    lines printed. */
717f7313695Schristos static int
grepbuf(char const * beg,char const * lim)718f7313695Schristos grepbuf (char const *beg, char const *lim)
719f7313695Schristos {
720f7313695Schristos   int nlines, n;
721f7313695Schristos   register char const *p;
722f7313695Schristos   size_t match_offset;
723f7313695Schristos   size_t match_size;
724f7313695Schristos 
725f7313695Schristos   nlines = 0;
726f7313695Schristos   p = beg;
727f7313695Schristos   while ((match_offset = (*execute) (p, lim - p, &match_size, 0)) != (size_t) -1)
728f7313695Schristos     {
729f7313695Schristos       char const *b = p + match_offset;
730f7313695Schristos       char const *endp = b + match_size;
731f7313695Schristos       /* Avoid matching the empty line at the end of the buffer. */
732f7313695Schristos       if (b == lim)
733f7313695Schristos 	break;
734f7313695Schristos       if (!out_invert)
735f7313695Schristos 	{
736f7313695Schristos 	  prtext (b, endp, (int *) 0);
737f7313695Schristos 	  nlines++;
738f7313695Schristos           outleft--;
739f7313695Schristos 	  if (!outleft || done_on_match)
740f7313695Schristos 	    {
741f7313695Schristos 	      if (exit_on_match)
742f7313695Schristos 		exit (0);
743f7313695Schristos 	      after_last_match = bufoffset - (buflim - endp);
744f7313695Schristos 	      return nlines;
745f7313695Schristos 	    }
746f7313695Schristos 	}
747f7313695Schristos       else if (p < b)
748f7313695Schristos 	{
749f7313695Schristos 	  prtext (p, b, &n);
750f7313695Schristos 	  nlines += n;
751f7313695Schristos           outleft -= n;
752f7313695Schristos 	  if (!outleft)
753f7313695Schristos 	    return nlines;
754f7313695Schristos 	}
755f7313695Schristos       p = endp;
756f7313695Schristos     }
757f7313695Schristos   if (out_invert && p < lim)
758f7313695Schristos     {
759f7313695Schristos       prtext (p, lim, &n);
760f7313695Schristos       nlines += n;
761f7313695Schristos       outleft -= n;
762f7313695Schristos     }
763f7313695Schristos   return nlines;
764f7313695Schristos }
765f7313695Schristos 
766f7313695Schristos /* Search a given file.  Normally, return a count of lines printed;
767f7313695Schristos    but if the file is a directory and we search it recursively, then
768f7313695Schristos    return -2 if there was a match, and -1 otherwise.  */
769f7313695Schristos static int
grep(int fd,char const * file,struct stats * stats)770f7313695Schristos grep (int fd, char const *file, struct stats *stats)
771f7313695Schristos {
772f7313695Schristos   int nlines, i;
773f7313695Schristos   int not_text;
774f7313695Schristos   size_t residue, save;
775f7313695Schristos   char oldc;
776f7313695Schristos   char *beg;
777f7313695Schristos   char *lim;
778f7313695Schristos   char eol = eolbyte;
779f7313695Schristos 
780f7313695Schristos   if (!reset (fd, file, stats))
781f7313695Schristos     return 0;
782f7313695Schristos 
783f7313695Schristos   if (file && directories == RECURSE_DIRECTORIES
784f7313695Schristos       && S_ISDIR (stats->stat.st_mode))
785f7313695Schristos     {
786f7313695Schristos       /* Close fd now, so that we don't open a lot of file descriptors
787f7313695Schristos 	 when we recurse deeply.  */
788f7313695Schristos       if (close (fd) != 0)
789f7313695Schristos 	error (0, errno, "%s", file);
790f7313695Schristos       return grepdir (file, stats) - 2;
791f7313695Schristos     }
792f7313695Schristos 
793f7313695Schristos   totalcc = 0;
794f7313695Schristos   lastout = 0;
795f7313695Schristos   totalnl = 0;
796f7313695Schristos   outleft = max_count;
797f7313695Schristos   after_last_match = 0;
798f7313695Schristos   pending = 0;
799f7313695Schristos 
800f7313695Schristos   nlines = 0;
801f7313695Schristos   residue = 0;
802f7313695Schristos   save = 0;
803f7313695Schristos 
804f7313695Schristos   if (! fillbuf (save, stats))
805f7313695Schristos     {
806f7313695Schristos       if (! is_EISDIR (errno, file))
807f7313695Schristos 	suppressible_error (filename, errno);
808f7313695Schristos       return 0;
809f7313695Schristos     }
810f7313695Schristos 
811f7313695Schristos   not_text = (((binary_files == BINARY_BINARY_FILES && !out_quiet)
812f7313695Schristos 	       || binary_files == WITHOUT_MATCH_BINARY_FILES)
813f7313695Schristos 	      && memchr (bufbeg, eol ? '\0' : '\200', buflim - bufbeg));
814f7313695Schristos   if (not_text && binary_files == WITHOUT_MATCH_BINARY_FILES)
815f7313695Schristos     return 0;
816f7313695Schristos   done_on_match += not_text;
817f7313695Schristos   out_quiet += not_text;
818f7313695Schristos 
819f7313695Schristos   for (;;)
820f7313695Schristos     {
821f7313695Schristos       lastnl = bufbeg;
822f7313695Schristos       if (lastout)
823f7313695Schristos 	lastout = bufbeg;
824f7313695Schristos 
825f7313695Schristos       beg = bufbeg + save;
826f7313695Schristos 
827f7313695Schristos       /* no more data to scan (eof) except for maybe a residue -> break */
828f7313695Schristos       if (beg == buflim)
829f7313695Schristos 	break;
830f7313695Schristos 
831f7313695Schristos       /* Determine new residue (the length of an incomplete line at the end of
832f7313695Schristos          the buffer, 0 means there is no incomplete last line).  */
833f7313695Schristos       oldc = beg[-1];
834f7313695Schristos       beg[-1] = eol;
835f7313695Schristos       for (lim = buflim; lim[-1] != eol; lim--)
836f7313695Schristos 	continue;
837f7313695Schristos       beg[-1] = oldc;
838f7313695Schristos       if (lim == beg)
839f7313695Schristos 	lim = beg - residue;
840f7313695Schristos       beg -= residue;
841f7313695Schristos       residue = buflim - lim;
842f7313695Schristos 
843f7313695Schristos       if (beg < lim)
844f7313695Schristos 	{
845f7313695Schristos 	  if (outleft)
846f7313695Schristos 	    nlines += grepbuf (beg, lim);
847f7313695Schristos 	  if (pending)
848f7313695Schristos 	    prpending (lim);
849f7313695Schristos 	  if((!outleft && !pending) || (nlines && done_on_match && !out_invert))
850f7313695Schristos 	    goto finish_grep;
851f7313695Schristos 	}
852f7313695Schristos 
853f7313695Schristos       /* The last OUT_BEFORE lines at the end of the buffer will be needed as
854f7313695Schristos 	 leading context if there is a matching line at the begin of the
855f7313695Schristos 	 next data. Make beg point to their begin.  */
856f7313695Schristos       i = 0;
857f7313695Schristos       beg = lim;
858f7313695Schristos       while (i < out_before && beg > bufbeg && beg != lastout)
859f7313695Schristos 	{
860f7313695Schristos 	  ++i;
861f7313695Schristos 	  do
862f7313695Schristos 	    --beg;
863f7313695Schristos 	  while (beg[-1] != eol);
864f7313695Schristos 	}
865f7313695Schristos 
866f7313695Schristos       /* detect if leading context is discontinuous from last printed line.  */
867f7313695Schristos       if (beg != lastout)
868f7313695Schristos 	lastout = 0;
869f7313695Schristos 
870f7313695Schristos       /* Handle some details and read more data to scan.  */
871f7313695Schristos       save = residue + lim - beg;
872f7313695Schristos       if (out_byte)
873f7313695Schristos 	totalcc = add_count (totalcc, buflim - bufbeg - save);
874f7313695Schristos       if (out_line)
875f7313695Schristos 	nlscan (beg);
876f7313695Schristos       if (! fillbuf (save, stats))
877f7313695Schristos 	{
878f7313695Schristos 	  if (! is_EISDIR (errno, file))
879f7313695Schristos 	    suppressible_error (filename, errno);
880f7313695Schristos 	  goto finish_grep;
881f7313695Schristos 	}
882f7313695Schristos     }
883f7313695Schristos   if (residue)
884f7313695Schristos     {
885f7313695Schristos       *buflim++ = eol;
886f7313695Schristos       if (outleft)
887f7313695Schristos 	nlines += grepbuf (bufbeg + save - residue, buflim);
888f7313695Schristos       if (pending)
889f7313695Schristos         prpending (buflim);
890f7313695Schristos     }
891f7313695Schristos 
892f7313695Schristos  finish_grep:
893f7313695Schristos   done_on_match -= not_text;
894f7313695Schristos   out_quiet -= not_text;
895f7313695Schristos   if ((not_text & ~out_quiet) && nlines != 0)
896f7313695Schristos     printf (_("Binary file %s matches\n"), filename);
897f7313695Schristos   return nlines;
898f7313695Schristos }
899f7313695Schristos 
900f7313695Schristos static int
grepfile(char const * file,struct stats * stats)901f7313695Schristos grepfile (char const *file, struct stats *stats)
902f7313695Schristos {
903f7313695Schristos   int desc;
904f7313695Schristos   int count;
905f7313695Schristos   int status;
906f7313695Schristos 
907f7313695Schristos   if (! file)
908f7313695Schristos     {
909f7313695Schristos       desc = 0;
910f7313695Schristos       filename = label ? label : _("(standard input)");
911f7313695Schristos     }
912f7313695Schristos   else
913f7313695Schristos     {
914da44da66Schristos       while ((desc = open (file, O_RDONLY | O_NONBLOCK)) < 0 && errno == EINTR)
915f7313695Schristos 	continue;
916f7313695Schristos 
917da44da66Schristos       if (desc >= 0 && (status = fcntl (desc, F_GETFL, 0)) != -1)
918da44da66Schristos 	fcntl (desc, F_SETFL,  status & ~O_NONBLOCK);
919da44da66Schristos 
920f7313695Schristos       if (desc < 0)
921f7313695Schristos 	{
922f7313695Schristos 	  int e = errno;
923f7313695Schristos 
924f7313695Schristos 	  if (is_EISDIR (e, file) && directories == RECURSE_DIRECTORIES)
925f7313695Schristos 	    {
926f7313695Schristos 	      if (stat (file, &stats->stat) != 0)
927f7313695Schristos 		{
928f7313695Schristos 		  error (0, errno, "%s", file);
929f7313695Schristos 		  return 1;
930f7313695Schristos 		}
931f7313695Schristos 
932f7313695Schristos 	      return grepdir (file, stats);
933f7313695Schristos 	    }
934f7313695Schristos 
935f7313695Schristos 	  if (!suppress_errors)
936f7313695Schristos 	    {
937f7313695Schristos 	      if (directories == SKIP_DIRECTORIES)
938f7313695Schristos 		switch (e)
939f7313695Schristos 		  {
940f7313695Schristos #if defined(EISDIR)
941f7313695Schristos 		  case EISDIR:
942f7313695Schristos 		    return 1;
943f7313695Schristos #endif
944f7313695Schristos 		  case EACCES:
945f7313695Schristos 		    /* When skipping directories, don't worry about
946f7313695Schristos 		       directories that can't be opened.  */
947f7313695Schristos 		    if (isdir (file))
948f7313695Schristos 		      return 1;
949f7313695Schristos 		    break;
950f7313695Schristos 		  }
951f7313695Schristos 	    }
952f7313695Schristos 
953f7313695Schristos 	  suppressible_error (file, e);
954f7313695Schristos 	  return 1;
955f7313695Schristos 	}
956f7313695Schristos 
957f7313695Schristos       filename = file;
958f7313695Schristos     }
959f7313695Schristos 
960f7313695Schristos #if defined(SET_BINARY)
961f7313695Schristos   /* Set input to binary mode.  Pipes are simulated with files
962f7313695Schristos      on DOS, so this includes the case of "foo | grep bar".  */
963f7313695Schristos   if (!isatty (desc))
964f7313695Schristos     SET_BINARY (desc);
965f7313695Schristos #endif
966f7313695Schristos 
967f7313695Schristos   count = grep (desc, file, stats);
968f7313695Schristos   if (count < 0)
969f7313695Schristos     status = count + 2;
970f7313695Schristos   else
971f7313695Schristos     {
972f7313695Schristos       if (count_matches)
973f7313695Schristos 	{
974f7313695Schristos 	  if (out_file)
975f7313695Schristos 	    printf ("%s%c", filename, ':' & filename_mask);
976f7313695Schristos 	  printf ("%d\n", count);
977f7313695Schristos 	}
978f7313695Schristos 
979f7313695Schristos       status = !count;
980f7313695Schristos       if (list_files == 1 - 2 * status)
981f7313695Schristos 	printf ("%s%c", filename, '\n' & filename_mask);
982f7313695Schristos 
983f7313695Schristos       if (! file)
984f7313695Schristos 	{
985f7313695Schristos 	  off_t required_offset = outleft ? bufoffset : after_last_match;
986f7313695Schristos 	  if ((bufmapped || required_offset != bufoffset)
987f7313695Schristos 	      && lseek (desc, required_offset, SEEK_SET) < 0
988f7313695Schristos 	      && S_ISREG (stats->stat.st_mode))
989f7313695Schristos 	    error (0, errno, "%s", filename);
990f7313695Schristos 	}
991f7313695Schristos       else
992f7313695Schristos 	while (close (desc) != 0)
993f7313695Schristos 	  if (errno != EINTR)
994f7313695Schristos 	    {
995f7313695Schristos 	      error (0, errno, "%s", file);
996f7313695Schristos 	      break;
997f7313695Schristos 	    }
998f7313695Schristos     }
999f7313695Schristos 
1000f7313695Schristos   return status;
1001f7313695Schristos }
1002f7313695Schristos 
1003f7313695Schristos static int
grepdir(char const * dir,struct stats const * stats)1004f7313695Schristos grepdir (char const *dir, struct stats const *stats)
1005f7313695Schristos {
1006f7313695Schristos   int status = 1;
1007f7313695Schristos   struct stats const *ancestor;
1008f7313695Schristos   char *name_space;
1009f7313695Schristos 
1010f7313695Schristos   /* Mingw32 does not support st_ino.  No known working hosts use zero
1011f7313695Schristos      for st_ino, so assume that the Mingw32 bug applies if it's zero.  */
1012f7313695Schristos   if (stats->stat.st_ino)
1013f7313695Schristos     for (ancestor = stats;  (ancestor = ancestor->parent) != 0;  )
1014f7313695Schristos       if (ancestor->stat.st_ino == stats->stat.st_ino
1015f7313695Schristos 	  && ancestor->stat.st_dev == stats->stat.st_dev)
1016f7313695Schristos 	{
1017f7313695Schristos 	  if (!suppress_errors)
1018947ffbb0Schristos 	    error (0, 0, _("warning: %s: %s"), dir,
1019f7313695Schristos 		   _("recursive directory loop"));
1020f7313695Schristos 	  return 1;
1021f7313695Schristos 	}
1022f7313695Schristos 
1023f7313695Schristos   name_space = savedir (dir, stats->stat.st_size, included_patterns,
1024f7313695Schristos 			excluded_patterns);
1025f7313695Schristos 
1026f7313695Schristos   if (! name_space)
1027f7313695Schristos     {
1028f7313695Schristos       if (errno)
1029f7313695Schristos 	suppressible_error (dir, errno);
1030f7313695Schristos       else
1031f7313695Schristos 	xalloc_die ();
1032f7313695Schristos     }
1033f7313695Schristos   else
1034f7313695Schristos     {
1035f7313695Schristos       size_t dirlen = strlen (dir);
1036f7313695Schristos       int needs_slash = ! (dirlen == FILESYSTEM_PREFIX_LEN (dir)
1037f7313695Schristos 			   || IS_SLASH (dir[dirlen - 1]));
1038f7313695Schristos       char *file = NULL;
1039f7313695Schristos       char const *namep = name_space;
1040f7313695Schristos       struct stats child;
1041f7313695Schristos       child.parent = stats;
1042f7313695Schristos       out_file += !no_filenames;
1043f7313695Schristos       while (*namep)
1044f7313695Schristos 	{
1045f7313695Schristos 	  size_t namelen = strlen (namep);
1046f7313695Schristos 	  file = xrealloc (file, dirlen + 1 + namelen + 1);
1047f7313695Schristos 	  strcpy (file, dir);
1048f7313695Schristos 	  file[dirlen] = '/';
1049f7313695Schristos 	  strcpy (file + dirlen + needs_slash, namep);
1050f7313695Schristos 	  namep += namelen + 1;
1051f7313695Schristos 	  status &= grepfile (file, &child);
1052f7313695Schristos 	}
1053f7313695Schristos       out_file -= !no_filenames;
1054f7313695Schristos       if (file)
1055f7313695Schristos         free (file);
1056f7313695Schristos       free (name_space);
1057f7313695Schristos     }
1058f7313695Schristos 
1059f7313695Schristos   return status;
1060f7313695Schristos }
1061f7313695Schristos 
1062f7313695Schristos static void
usage(int status)1063f7313695Schristos usage (int status)
1064f7313695Schristos {
1065f7313695Schristos   if (status != 0)
1066f7313695Schristos     {
1067f7313695Schristos       fprintf (stderr, _("Usage: %s [OPTION]... PATTERN [FILE]...\n"),
1068f7313695Schristos 	       program_name);
1069f7313695Schristos       fprintf (stderr, _("Try `%s --help' for more information.\n"),
1070f7313695Schristos 	       program_name);
1071f7313695Schristos     }
1072f7313695Schristos   else
1073f7313695Schristos     {
1074f7313695Schristos       printf (_("Usage: %s [OPTION]... PATTERN [FILE] ...\n"), program_name);
1075f7313695Schristos       printf (_("\
1076f7313695Schristos Search for PATTERN in each FILE or standard input.\n\
1077f7313695Schristos Example: %s -i 'hello world' menu.h main.c\n\
1078f7313695Schristos \n\
1079f7313695Schristos Regexp selection and interpretation:\n"), program_name);
1080f7313695Schristos       printf (_("\
1081f7313695Schristos   -E, --extended-regexp     PATTERN is an extended regular expression\n\
1082f7313695Schristos   -F, --fixed-strings       PATTERN is a set of newline-separated strings\n\
1083947ffbb0Schristos   -G, --basic-regexp        PATTERN is a basic regular expression\n"));
1084947ffbb0Schristos #if HAVE_LIBPCRE
1085947ffbb0Schristos       printf (_("\
1086f7313695Schristos   -P, --perl-regexp         PATTERN is a Perl regular expression\n"));
1087947ffbb0Schristos #endif
1088f7313695Schristos       printf (_("\
1089f7313695Schristos   -e, --regexp=PATTERN      use PATTERN as a regular expression\n\
1090f7313695Schristos   -f, --file=FILE           obtain PATTERN from FILE\n\
1091f7313695Schristos   -i, --ignore-case         ignore case distinctions\n\
1092f7313695Schristos   -w, --word-regexp         force PATTERN to match only whole words\n\
1093f7313695Schristos   -x, --line-regexp         force PATTERN to match only whole lines\n\
1094f7313695Schristos   -z, --null-data           a data line ends in 0 byte, not newline\n"));
1095f7313695Schristos       printf (_("\
1096f7313695Schristos \n\
1097f7313695Schristos Miscellaneous:\n\
1098f7313695Schristos   -s, --no-messages         suppress error messages\n\
1099f7313695Schristos   -v, --invert-match        select non-matching lines\n\
1100f7313695Schristos   -V, --version             print version information and exit\n\
1101f7313695Schristos       --help                display this help and exit\n\
1102f7313695Schristos       --mmap                use memory-mapped input if possible\n"));
1103f7313695Schristos       printf (_("\
1104f7313695Schristos \n\
1105f7313695Schristos Output control:\n\
1106f7313695Schristos   -m, --max-count=NUM       stop after NUM matches\n\
1107f7313695Schristos   -b, --byte-offset         print the byte offset with output lines\n\
1108f7313695Schristos   -n, --line-number         print line number with output lines\n\
1109f7313695Schristos       --line-buffered       flush output on every line\n\
1110f7313695Schristos   -H, --with-filename       print the filename for each match\n\
1111f7313695Schristos   -h, --no-filename         suppress the prefixing filename on output\n\
1112f7313695Schristos       --label=LABEL         print LABEL as filename for standard input\n\
1113f7313695Schristos   -o, --only-matching       show only the part of a line matching PATTERN\n\
1114f7313695Schristos   -q, --quiet, --silent     suppress all normal output\n\
1115f7313695Schristos       --binary-files=TYPE   assume that binary files are TYPE\n\
1116f7313695Schristos                             TYPE is 'binary', 'text', or 'without-match'\n\
1117f7313695Schristos   -a, --text                equivalent to --binary-files=text\n\
1118f7313695Schristos   -I                        equivalent to --binary-files=without-match\n\
1119f7313695Schristos   -d, --directories=ACTION  how to handle directories\n\
1120f7313695Schristos                             ACTION is 'read', 'recurse', or 'skip'\n\
1121f7313695Schristos   -D, --devices=ACTION      how to handle devices, FIFOs and sockets\n\
1122f7313695Schristos                             ACTION is 'read' or 'skip'\n\
1123f7313695Schristos   -R, -r, --recursive       equivalent to --directories=recurse\n\
1124f7313695Schristos       --include=PATTERN     files that match PATTERN will be examined\n\
1125f7313695Schristos       --exclude=PATTERN     files that match PATTERN will be skipped.\n\
1126f7313695Schristos       --exclude-from=FILE   files that match PATTERN in FILE will be skipped.\n\
1127f7313695Schristos   -L, --files-without-match only print FILE names containing no match\n\
1128f7313695Schristos   -l, --files-with-matches  only print FILE names containing matches\n\
1129f7313695Schristos   -c, --count               only print a count of matching lines per FILE\n\
1130f7313695Schristos   -Z, --null                print 0 byte after FILE name\n"));
1131f7313695Schristos       printf (_("\
1132f7313695Schristos \n\
1133f7313695Schristos Context control:\n\
1134f7313695Schristos   -B, --before-context=NUM  print NUM lines of leading context\n\
1135f7313695Schristos   -A, --after-context=NUM   print NUM lines of trailing context\n\
1136f7313695Schristos   -C, --context=NUM         print NUM lines of output context\n\
1137f7313695Schristos   -NUM                      same as --context=NUM\n\
1138f7313695Schristos       --color[=WHEN],\n\
1139f7313695Schristos       --colour[=WHEN]       use markers to distinguish the matching string\n\
1140f7313695Schristos                             WHEN may be `always', `never' or `auto'.\n\
1141f7313695Schristos   -U, --binary              do not strip CR characters at EOL (MSDOS)\n\
1142f7313695Schristos   -u, --unix-byte-offsets   report offsets as if CRs were not there (MSDOS)\n\
1143f7313695Schristos \n\
1144f7313695Schristos `egrep' means `grep -E'.  `fgrep' means `grep -F'.\n\
1145f7313695Schristos With no FILE, or when FILE is -, read standard input.  If less than\n\
1146f7313695Schristos two FILEs given, assume -h.  Exit status is 0 if match, 1 if no match,\n\
1147f7313695Schristos and 2 if trouble.\n"));
1148f7313695Schristos       printf (_("\nReport bugs to <bug-grep@gnu.org>.\n"));
1149f7313695Schristos     }
1150f7313695Schristos   exit (status);
1151f7313695Schristos }
1152f7313695Schristos 
1153f7313695Schristos /* Set the matcher to M, reporting any conflicts.  */
1154f7313695Schristos static void
setmatcher(char const * m)1155f7313695Schristos setmatcher (char const *m)
1156f7313695Schristos {
1157f7313695Schristos   if (matcher && strcmp (matcher, m) != 0)
1158f7313695Schristos     error (2, 0, _("conflicting matchers specified"));
1159f7313695Schristos   matcher = m;
1160f7313695Schristos }
1161f7313695Schristos 
1162f7313695Schristos /* Go through the matchers vector and look for the specified matcher.
1163f7313695Schristos    If we find it, install it in compile and execute, and return 1.  */
1164f7313695Schristos static int
install_matcher(char const * name)1165f7313695Schristos install_matcher (char const *name)
1166f7313695Schristos {
1167f7313695Schristos   int i;
1168f7313695Schristos #if defined(HAVE_SETRLIMIT)
1169f7313695Schristos   struct rlimit rlim;
1170f7313695Schristos #endif
1171f7313695Schristos 
1172f7313695Schristos   for (i = 0; matchers[i].compile; i++)
1173f7313695Schristos     if (strcmp (name, matchers[i].name) == 0)
1174f7313695Schristos       {
1175f7313695Schristos 	compile = matchers[i].compile;
1176f7313695Schristos 	execute = matchers[i].execute;
1177f7313695Schristos #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_STACK)
1178f7313695Schristos 	/* I think every platform needs to do this, so that regex.c
1179f7313695Schristos 	   doesn't oveflow the stack.  The default value of
1180f7313695Schristos 	   `re_max_failures' is too large for some platforms: it needs
1181f7313695Schristos 	   more than 3MB-large stack.
1182f7313695Schristos 
1183f7313695Schristos 	   The test for HAVE_SETRLIMIT should go into `configure'.  */
1184f7313695Schristos 	if (!getrlimit (RLIMIT_STACK, &rlim))
1185f7313695Schristos 	  {
1186f7313695Schristos 	    long newlim;
1187f7313695Schristos 	    extern long int re_max_failures; /* from regex.c */
1188f7313695Schristos 
1189f7313695Schristos 	    /* Approximate the amount regex.c needs, plus some more.  */
1190f7313695Schristos 	    newlim = re_max_failures * 2 * 20 * sizeof (char *);
1191f7313695Schristos 	    if (newlim > rlim.rlim_max)
1192f7313695Schristos 	      {
1193f7313695Schristos 		newlim = rlim.rlim_max;
1194f7313695Schristos 		re_max_failures = newlim / (2 * 20 * sizeof (char *));
1195f7313695Schristos 	      }
1196f7313695Schristos 	    if (rlim.rlim_cur < newlim)
1197f7313695Schristos 	      {
1198f7313695Schristos 		rlim.rlim_cur = newlim;
1199f7313695Schristos 		setrlimit (RLIMIT_STACK, &rlim);
1200f7313695Schristos 	      }
1201f7313695Schristos 	  }
1202f7313695Schristos #endif
1203f7313695Schristos 	return 1;
1204f7313695Schristos       }
1205f7313695Schristos   return 0;
1206f7313695Schristos }
1207f7313695Schristos 
1208f7313695Schristos /* Find the white-space-separated options specified by OPTIONS, and
1209f7313695Schristos    using BUF to store copies of these options, set ARGV[0], ARGV[1],
1210f7313695Schristos    etc. to the option copies.  Return the number N of options found.
1211f7313695Schristos    Do not set ARGV[N] to NULL.  If ARGV is NULL, do not store ARGV[0]
1212f7313695Schristos    etc.  Backslash can be used to escape whitespace (and backslashes).  */
1213f7313695Schristos static int
prepend_args(char const * options,char * buf,char ** argv)1214f7313695Schristos prepend_args (char const *options, char *buf, char **argv)
1215f7313695Schristos {
1216f7313695Schristos   char const *o = options;
1217f7313695Schristos   char *b = buf;
1218f7313695Schristos   int n = 0;
1219f7313695Schristos 
1220f7313695Schristos   for (;;)
1221f7313695Schristos     {
1222f7313695Schristos       while (ISSPACE ((unsigned char) *o))
1223f7313695Schristos 	o++;
1224f7313695Schristos       if (!*o)
1225f7313695Schristos 	return n;
1226f7313695Schristos       if (argv)
1227f7313695Schristos 	argv[n] = b;
1228f7313695Schristos       n++;
1229f7313695Schristos 
1230f7313695Schristos       do
1231f7313695Schristos 	if ((*b++ = *o++) == '\\' && *o)
1232f7313695Schristos 	  b[-1] = *o++;
1233f7313695Schristos       while (*o && ! ISSPACE ((unsigned char) *o));
1234f7313695Schristos 
1235f7313695Schristos       *b++ = '\0';
1236f7313695Schristos     }
1237f7313695Schristos }
1238f7313695Schristos 
1239f7313695Schristos /* Prepend the whitespace-separated options in OPTIONS to the argument
1240f7313695Schristos    vector of a main program with argument count *PARGC and argument
1241f7313695Schristos    vector *PARGV.  */
1242f7313695Schristos static void
prepend_default_options(char const * options,int * pargc,char *** pargv)1243f7313695Schristos prepend_default_options (char const *options, int *pargc, char ***pargv)
1244f7313695Schristos {
1245f7313695Schristos   if (options)
1246f7313695Schristos     {
1247f7313695Schristos       char *buf = xmalloc (strlen (options) + 1);
1248f7313695Schristos       int prepended = prepend_args (options, buf, (char **) NULL);
1249f7313695Schristos       int argc = *pargc;
1250f7313695Schristos       char * const *argv = *pargv;
1251f7313695Schristos       char **pp = (char **) xmalloc ((prepended + argc + 1) * sizeof *pp);
1252f7313695Schristos       *pargc = prepended + argc;
1253f7313695Schristos       *pargv = pp;
1254f7313695Schristos       *pp++ = *argv++;
1255f7313695Schristos       pp += prepend_args (options, buf, pp);
1256f7313695Schristos       while ((*pp++ = *argv++))
1257f7313695Schristos 	continue;
1258f7313695Schristos     }
1259f7313695Schristos }
1260f7313695Schristos 
1261f7313695Schristos /* Get the next non-digit option from ARGC and ARGV.
1262f7313695Schristos    Return -1 if there are no more options.
1263f7313695Schristos    Process any digit options that were encountered on the way,
1264f7313695Schristos    and store the resulting integer into *DEFAULT_CONTEXT.  */
1265f7313695Schristos static int
get_nondigit_option(int argc,char * const * argv,int * default_context)1266f7313695Schristos get_nondigit_option (int argc, char *const *argv, int *default_context)
1267f7313695Schristos {
1268f7313695Schristos   int opt;
1269f7313695Schristos   char buf[sizeof (uintmax_t) * CHAR_BIT + 4];
1270f7313695Schristos   char *p = buf;
1271f7313695Schristos 
1272f7313695Schristos   /* Set buf[0] to anything but '0', for the leading-zero test below.  */
1273f7313695Schristos   buf[0] = '\0';
1274f7313695Schristos 
1275f7313695Schristos   while (opt = getopt_long (argc, argv, short_options, long_options, NULL),
1276f7313695Schristos 	 '0' <= opt && opt <= '9')
1277f7313695Schristos     {
1278f7313695Schristos       /* Suppress trivial leading zeros, to avoid incorrect
1279f7313695Schristos 	 diagnostic on strings like 00000000000.  */
1280f7313695Schristos       p -= buf[0] == '0';
1281f7313695Schristos 
1282f7313695Schristos       *p++ = opt;
1283f7313695Schristos       if (p == buf + sizeof buf - 4)
1284f7313695Schristos 	{
1285f7313695Schristos 	  /* Too many digits.  Append "..." to make context_length_arg
1286f7313695Schristos 	     complain about "X...", where X contains the digits seen
1287f7313695Schristos 	     so far.  */
1288f7313695Schristos 	  strcpy (p, "...");
1289f7313695Schristos 	  p += 3;
1290f7313695Schristos 	  break;
1291f7313695Schristos 	}
1292f7313695Schristos     }
1293f7313695Schristos   if (p != buf)
1294f7313695Schristos     {
1295f7313695Schristos       *p = '\0';
1296f7313695Schristos       context_length_arg (buf, default_context);
1297f7313695Schristos     }
1298f7313695Schristos 
1299f7313695Schristos   return opt;
1300f7313695Schristos }
1301f7313695Schristos 
1302f7313695Schristos int
main(int argc,char ** argv)1303f7313695Schristos main (int argc, char **argv)
1304f7313695Schristos {
1305f7313695Schristos   char *keys;
1306947ffbb0Schristos   size_t cc, keycc, oldcc, keyalloc;
1307f7313695Schristos   int with_filenames;
1308947ffbb0Schristos   int opt, status;
1309f7313695Schristos   int default_context;
1310f7313695Schristos   FILE *fp;
1311f7313695Schristos   extern char *optarg;
1312f7313695Schristos   extern int optind;
1313947ffbb0Schristos #ifdef __NetBSD__
1314947ffbb0Schristos   extern char *__progname;
1315947ffbb0Schristos #endif
1316f7313695Schristos 
1317f7313695Schristos   initialize_main (&argc, &argv);
1318f7313695Schristos   program_name = argv[0];
1319f7313695Schristos   if (program_name && strrchr (program_name, '/'))
1320f7313695Schristos     program_name = strrchr (program_name, '/') + 1;
1321f7313695Schristos 
1322f7313695Schristos   if (!strcmp(program_name, "egrep"))
1323f7313695Schristos     setmatcher ("egrep");
1324f7313695Schristos   if (!strcmp(program_name, "fgrep"))
1325f7313695Schristos     setmatcher ("fgrep");
1326f7313695Schristos 
1327f7313695Schristos #if defined(__MSDOS__) || defined(_WIN32)
1328f7313695Schristos   /* DOS and MS-Windows use backslashes as directory separators, and usually
1329f7313695Schristos      have an .exe suffix.  They also have case-insensitive filesystems.  */
1330f7313695Schristos   if (program_name)
1331f7313695Schristos     {
1332f7313695Schristos       char *p = program_name;
1333f7313695Schristos       char *bslash = strrchr (argv[0], '\\');
1334f7313695Schristos 
1335f7313695Schristos       if (bslash && bslash >= program_name) /* for mixed forward/backslash case */
1336f7313695Schristos 	program_name = bslash + 1;
1337f7313695Schristos       else if (program_name == argv[0]
1338f7313695Schristos 	       && argv[0][0] && argv[0][1] == ':') /* "c:progname" */
1339f7313695Schristos 	program_name = argv[0] + 2;
1340f7313695Schristos 
1341f7313695Schristos       /* Collapse the letter-case, so `strcmp' could be used hence.  */
1342f7313695Schristos       for ( ; *p; p++)
1343f7313695Schristos 	if (*p >= 'A' && *p <= 'Z')
1344f7313695Schristos 	  *p += 'a' - 'A';
1345f7313695Schristos 
1346f7313695Schristos       /* Remove the .exe extension, if any.  */
1347f7313695Schristos       if ((p = strrchr (program_name, '.')) && strcmp (p, ".exe") == 0)
1348f7313695Schristos 	*p = '\0';
1349f7313695Schristos     }
1350f7313695Schristos #endif
1351f7313695Schristos 
1352f7313695Schristos   keys = NULL;
1353f7313695Schristos   keycc = 0;
1354f7313695Schristos   with_filenames = 0;
1355f7313695Schristos   eolbyte = '\n';
1356f7313695Schristos   filename_mask = ~0;
1357f7313695Schristos 
1358947ffbb0Schristos   max_count = MAX_OFF_T;
1359f7313695Schristos 
1360f7313695Schristos   /* The value -1 means to use DEFAULT_CONTEXT. */
1361f7313695Schristos   out_after = out_before = -1;
1362f7313695Schristos   /* Default before/after context: chaged by -C/-NUM options */
1363f7313695Schristos   default_context = 0;
1364f7313695Schristos   /* Changed by -o option */
1365f7313695Schristos   only_matching = 0;
1366f7313695Schristos 
1367f7313695Schristos   /* Internationalization. */
1368f7313695Schristos #if defined(HAVE_SETLOCALE)
1369f7313695Schristos   setlocale (LC_ALL, "");
1370f7313695Schristos #endif
1371f7313695Schristos #if defined(ENABLE_NLS)
1372f7313695Schristos   bindtextdomain (PACKAGE, LOCALEDIR);
1373f7313695Schristos   textdomain (PACKAGE);
1374f7313695Schristos #endif
1375f7313695Schristos 
1376f7313695Schristos   atexit (close_stdout);
1377f7313695Schristos 
1378f7313695Schristos   prepend_default_options (getenv ("GREP_OPTIONS"), &argc, &argv);
1379f7313695Schristos 
1380f7313695Schristos   while ((opt = get_nondigit_option (argc, argv, &default_context)) != -1)
1381f7313695Schristos     switch (opt)
1382f7313695Schristos       {
1383f7313695Schristos       case 'A':
1384f7313695Schristos 	context_length_arg (optarg, &out_after);
1385f7313695Schristos 	break;
1386f7313695Schristos 
1387f7313695Schristos       case 'B':
1388f7313695Schristos 	context_length_arg (optarg, &out_before);
1389f7313695Schristos 	break;
1390f7313695Schristos 
1391f7313695Schristos       case 'C':
1392f7313695Schristos 	/* Set output match context, but let any explicit leading or
1393f7313695Schristos 	   trailing amount specified with -A or -B stand. */
1394f7313695Schristos 	context_length_arg (optarg, &default_context);
1395f7313695Schristos 	break;
1396f7313695Schristos 
1397f7313695Schristos       case 'D':
1398f7313695Schristos 	if (strcmp (optarg, "read") == 0)
1399f7313695Schristos 	  devices = READ_DEVICES;
1400f7313695Schristos 	else if (strcmp (optarg, "skip") == 0)
1401f7313695Schristos 	  devices = SKIP_DEVICES;
1402f7313695Schristos 	else
1403f7313695Schristos 	  error (2, 0, _("unknown devices method"));
1404f7313695Schristos 	break;
1405f7313695Schristos 
1406f7313695Schristos       case 'E':
1407f7313695Schristos 	setmatcher ("egrep");
1408f7313695Schristos 	break;
1409f7313695Schristos 
1410f7313695Schristos       case 'F':
1411f7313695Schristos 	setmatcher ("fgrep");
1412f7313695Schristos 	break;
1413f7313695Schristos 
1414f7313695Schristos       case 'P':
1415f7313695Schristos 	setmatcher ("perl");
1416f7313695Schristos 	break;
1417f7313695Schristos 
1418f7313695Schristos       case 'G':
1419f7313695Schristos 	setmatcher ("grep");
1420f7313695Schristos 	break;
1421f7313695Schristos 
1422f7313695Schristos       case 'H':
1423f7313695Schristos 	with_filenames = 1;
1424f7313695Schristos 	break;
1425f7313695Schristos 
1426f7313695Schristos       case 'I':
1427f7313695Schristos 	binary_files = WITHOUT_MATCH_BINARY_FILES;
1428f7313695Schristos 	break;
1429f7313695Schristos 
1430f7313695Schristos       case 'U':
1431f7313695Schristos #if defined(HAVE_DOS_FILE_CONTENTS)
1432f7313695Schristos 	dos_use_file_type = DOS_BINARY;
1433f7313695Schristos #endif
1434f7313695Schristos 	break;
1435f7313695Schristos 
1436f7313695Schristos       case 'u':
1437f7313695Schristos #if defined(HAVE_DOS_FILE_CONTENTS)
1438f7313695Schristos 	dos_report_unix_offset = 1;
1439f7313695Schristos #endif
1440f7313695Schristos 	break;
1441f7313695Schristos 
1442f7313695Schristos       case 'V':
1443f7313695Schristos 	show_version = 1;
1444f7313695Schristos 	break;
1445f7313695Schristos 
1446f7313695Schristos       case 'X':
1447f7313695Schristos 	setmatcher (optarg);
1448f7313695Schristos 	break;
1449f7313695Schristos 
1450f7313695Schristos       case 'a':
1451f7313695Schristos 	binary_files = TEXT_BINARY_FILES;
1452f7313695Schristos 	break;
1453f7313695Schristos 
1454f7313695Schristos       case 'b':
1455f7313695Schristos 	out_byte = 1;
1456f7313695Schristos 	break;
1457f7313695Schristos 
1458f7313695Schristos       case 'c':
1459f7313695Schristos 	count_matches = 1;
1460f7313695Schristos 	break;
1461f7313695Schristos 
1462f7313695Schristos       case 'd':
1463f7313695Schristos 	if (strcmp (optarg, "read") == 0)
1464f7313695Schristos 	  directories = READ_DIRECTORIES;
1465f7313695Schristos 	else if (strcmp (optarg, "skip") == 0)
1466f7313695Schristos 	  directories = SKIP_DIRECTORIES;
1467f7313695Schristos 	else if (strcmp (optarg, "recurse") == 0)
1468f7313695Schristos 	  directories = RECURSE_DIRECTORIES;
1469f7313695Schristos 	else
1470f7313695Schristos 	  error (2, 0, _("unknown directories method"));
1471f7313695Schristos 	break;
1472f7313695Schristos 
1473f7313695Schristos       case 'e':
1474f7313695Schristos 	cc = strlen (optarg);
1475f7313695Schristos 	keys = xrealloc (keys, keycc + cc + 1);
1476f7313695Schristos 	strcpy (&keys[keycc], optarg);
1477f7313695Schristos 	keycc += cc;
1478f7313695Schristos 	keys[keycc++] = '\n';
1479f7313695Schristos 	break;
1480f7313695Schristos 
1481f7313695Schristos       case 'f':
1482f7313695Schristos 	fp = strcmp (optarg, "-") != 0 ? fopen (optarg, "r") : stdin;
1483f7313695Schristos 	if (!fp)
1484f7313695Schristos 	  error (2, errno, "%s", optarg);
1485f7313695Schristos 	for (keyalloc = 1; keyalloc <= keycc + 1; keyalloc *= 2)
1486f7313695Schristos 	  ;
1487f7313695Schristos 	keys = xrealloc (keys, keyalloc);
1488f7313695Schristos 	oldcc = keycc;
1489f7313695Schristos 	while (!feof (fp)
1490f7313695Schristos 	       && (cc = fread (keys + keycc, 1, keyalloc - 1 - keycc, fp)) > 0)
1491f7313695Schristos 	  {
1492f7313695Schristos 	    keycc += cc;
1493f7313695Schristos 	    if (keycc == keyalloc - 1)
1494f7313695Schristos 	      keys = xrealloc (keys, keyalloc *= 2);
1495f7313695Schristos 	  }
1496f7313695Schristos 	if (fp != stdin)
1497f7313695Schristos 	  fclose(fp);
1498f7313695Schristos 	/* Append final newline if file ended in non-newline. */
1499f7313695Schristos 	if (oldcc != keycc && keys[keycc - 1] != '\n')
1500f7313695Schristos 	  keys[keycc++] = '\n';
1501f7313695Schristos 	break;
1502f7313695Schristos 
1503f7313695Schristos       case 'h':
1504f7313695Schristos 	no_filenames = 1;
1505f7313695Schristos 	break;
1506f7313695Schristos 
1507f7313695Schristos       case 'i':
1508f7313695Schristos       case 'y':			/* For old-timers . . . */
1509f7313695Schristos 	match_icase = 1;
1510f7313695Schristos 	break;
1511f7313695Schristos 
1512f7313695Schristos       case 'L':
1513f7313695Schristos 	/* Like -l, except list files that don't contain matches.
1514f7313695Schristos 	   Inspired by the same option in Hume's gre. */
1515f7313695Schristos 	list_files = -1;
1516f7313695Schristos 	break;
1517f7313695Schristos 
1518f7313695Schristos       case 'l':
1519f7313695Schristos 	list_files = 1;
1520f7313695Schristos 	break;
1521f7313695Schristos 
1522f7313695Schristos       case 'm':
1523f7313695Schristos 	{
1524f7313695Schristos 	  uintmax_t value;
1525f7313695Schristos 	  switch (xstrtoumax (optarg, 0, 10, &value, ""))
1526f7313695Schristos 	    {
1527f7313695Schristos 	    case LONGINT_OK:
1528f7313695Schristos 	      max_count = value;
1529f7313695Schristos 	      if (0 <= max_count && max_count == value)
1530f7313695Schristos 		break;
1531f7313695Schristos 	      /* Fall through.  */
1532f7313695Schristos 	    case LONGINT_OVERFLOW:
1533947ffbb0Schristos 	      max_count = MAX_OFF_T;
1534f7313695Schristos 	      break;
1535f7313695Schristos 
1536f7313695Schristos 	    default:
1537f7313695Schristos 	      error (2, 0, _("invalid max count"));
1538f7313695Schristos 	    }
1539f7313695Schristos 	}
1540f7313695Schristos 	break;
1541f7313695Schristos 
1542f7313695Schristos       case 'n':
1543f7313695Schristos 	out_line = 1;
1544f7313695Schristos 	break;
1545f7313695Schristos 
1546f7313695Schristos       case 'o':
1547f7313695Schristos 	only_matching = 1;
1548f7313695Schristos 	break;
1549f7313695Schristos 
1550f7313695Schristos       case 'q':
1551f7313695Schristos 	exit_on_match = 1;
1552f7313695Schristos 	close_stdout_set_status(0);
1553f7313695Schristos 	break;
1554f7313695Schristos 
1555f7313695Schristos       case 'R':
1556f7313695Schristos       case 'r':
1557f7313695Schristos 	directories = RECURSE_DIRECTORIES;
1558f7313695Schristos 	break;
1559f7313695Schristos 
1560f7313695Schristos       case 's':
1561f7313695Schristos 	suppress_errors = 1;
1562f7313695Schristos 	break;
1563f7313695Schristos 
1564f7313695Schristos       case 'v':
1565f7313695Schristos 	out_invert = 1;
1566f7313695Schristos 	break;
1567f7313695Schristos 
1568f7313695Schristos       case 'w':
1569f7313695Schristos 	match_words = 1;
1570f7313695Schristos 	break;
1571f7313695Schristos 
1572f7313695Schristos       case 'x':
1573f7313695Schristos 	match_lines = 1;
1574f7313695Schristos 	break;
1575f7313695Schristos 
1576f7313695Schristos       case 'Z':
1577f7313695Schristos 	filename_mask = 0;
1578f7313695Schristos 	break;
1579f7313695Schristos 
1580f7313695Schristos       case 'z':
1581f7313695Schristos 	eolbyte = '\0';
1582f7313695Schristos 	break;
1583f7313695Schristos 
1584f7313695Schristos       case BINARY_FILES_OPTION:
1585f7313695Schristos 	if (strcmp (optarg, "binary") == 0)
1586f7313695Schristos 	  binary_files = BINARY_BINARY_FILES;
1587f7313695Schristos 	else if (strcmp (optarg, "text") == 0)
1588f7313695Schristos 	  binary_files = TEXT_BINARY_FILES;
1589f7313695Schristos 	else if (strcmp (optarg, "without-match") == 0)
1590f7313695Schristos 	  binary_files = WITHOUT_MATCH_BINARY_FILES;
1591f7313695Schristos 	else
1592f7313695Schristos 	  error (2, 0, _("unknown binary-files type"));
1593f7313695Schristos 	break;
1594f7313695Schristos 
1595f7313695Schristos       case COLOR_OPTION:
1596f7313695Schristos         if(optarg) {
1597f7313695Schristos           if(!strcasecmp(optarg, "always") || !strcasecmp(optarg, "yes") ||
1598f7313695Schristos              !strcasecmp(optarg, "force"))
1599f7313695Schristos             color_option = 1;
1600f7313695Schristos           else if(!strcasecmp(optarg, "never") || !strcasecmp(optarg, "no") ||
1601f7313695Schristos                   !strcasecmp(optarg, "none"))
1602f7313695Schristos             color_option = 0;
1603f7313695Schristos           else if(!strcasecmp(optarg, "auto") || !strcasecmp(optarg, "tty") ||
1604f7313695Schristos                   !strcasecmp(optarg, "if-tty"))
1605f7313695Schristos             color_option = 2;
1606f7313695Schristos           else
1607f7313695Schristos             show_help = 1;
1608f7313695Schristos         } else
1609f7313695Schristos           color_option = 2;
1610f7313695Schristos         if(color_option == 2) {
1611f7313695Schristos           if(isatty(STDOUT_FILENO) && getenv("TERM") &&
1612f7313695Schristos 	     strcmp(getenv("TERM"), "dumb"))
1613f7313695Schristos                   color_option = 1;
1614f7313695Schristos           else
1615f7313695Schristos             color_option = 0;
1616f7313695Schristos         }
1617f7313695Schristos 	break;
1618f7313695Schristos 
1619f7313695Schristos       case EXCLUDE_OPTION:
1620f7313695Schristos 	if (!excluded_patterns)
1621f7313695Schristos 	  excluded_patterns = new_exclude ();
1622f7313695Schristos 	add_exclude (excluded_patterns, optarg);
1623f7313695Schristos 	break;
1624f7313695Schristos 
1625f7313695Schristos       case EXCLUDE_FROM_OPTION:
1626f7313695Schristos 	if (!excluded_patterns)
1627f7313695Schristos 	  excluded_patterns = new_exclude ();
1628f7313695Schristos         if (add_exclude_file (add_exclude, excluded_patterns, optarg, '\n')
1629f7313695Schristos 	    != 0)
1630f7313695Schristos           {
1631f7313695Schristos             error (2, errno, "%s", optarg);
1632f7313695Schristos           }
1633f7313695Schristos         break;
1634f7313695Schristos 
1635f7313695Schristos       case INCLUDE_OPTION:
1636f7313695Schristos 	if (!included_patterns)
1637f7313695Schristos 	  included_patterns = new_exclude ();
1638f7313695Schristos 	add_exclude (included_patterns, optarg);
1639f7313695Schristos 	break;
1640f7313695Schristos 
1641f7313695Schristos       case LINE_BUFFERED_OPTION:
1642f7313695Schristos 	line_buffered = 1;
1643f7313695Schristos 	break;
1644f7313695Schristos 
1645f7313695Schristos       case LABEL_OPTION:
1646f7313695Schristos 	label = optarg;
1647f7313695Schristos 	break;
1648f7313695Schristos 
1649f7313695Schristos       case 0:
1650f7313695Schristos 	/* long options */
1651f7313695Schristos 	break;
1652f7313695Schristos 
1653f7313695Schristos       default:
1654f7313695Schristos 	usage (2);
1655f7313695Schristos 	break;
1656f7313695Schristos 
1657f7313695Schristos       }
1658f7313695Schristos 
1659f7313695Schristos   /* POSIX.2 says that -q overrides -l, which in turn overrides the
1660f7313695Schristos      other output options.  */
1661f7313695Schristos   if (exit_on_match)
1662f7313695Schristos     list_files = 0;
1663f7313695Schristos   if (exit_on_match | list_files)
1664f7313695Schristos     {
1665f7313695Schristos       count_matches = 0;
1666f7313695Schristos       done_on_match = 1;
1667f7313695Schristos     }
1668f7313695Schristos   out_quiet = count_matches | done_on_match;
1669f7313695Schristos 
1670f7313695Schristos   if (out_after < 0)
1671f7313695Schristos     out_after = default_context;
1672f7313695Schristos   if (out_before < 0)
1673f7313695Schristos     out_before = default_context;
1674f7313695Schristos 
1675f7313695Schristos   if (color_option)
1676f7313695Schristos     {
1677f7313695Schristos       char *userval = getenv ("GREP_COLOR");
1678f7313695Schristos       if (userval != NULL && *userval != '\0')
1679f7313695Schristos 	grep_color = userval;
1680f7313695Schristos     }
1681f7313695Schristos 
1682f7313695Schristos   if (! matcher)
1683947ffbb0Schristos #ifdef __NetBSD__
1684947ffbb0Schristos     matcher = __progname;
1685947ffbb0Schristos #else
1686f7313695Schristos     matcher = "grep";
1687947ffbb0Schristos #endif
1688f7313695Schristos 
1689f7313695Schristos   if (show_version)
1690f7313695Schristos     {
1691f7313695Schristos       printf (_("%s (GNU grep) %s\n"), matcher, VERSION);
1692f7313695Schristos       printf ("\n");
1693f7313695Schristos       printf (_("\
1694f7313695Schristos Copyright 1988, 1992-1999, 2000, 2001 Free Software Foundation, Inc.\n"));
1695f7313695Schristos       printf (_("\
1696f7313695Schristos This is free software; see the source for copying conditions. There is NO\n\
1697f7313695Schristos warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"));
1698f7313695Schristos       printf ("\n");
1699f7313695Schristos       exit (0);
1700f7313695Schristos     }
1701f7313695Schristos 
1702f7313695Schristos   if (show_help)
1703f7313695Schristos     usage (0);
1704f7313695Schristos 
1705f7313695Schristos   if (keys)
1706f7313695Schristos     {
1707f7313695Schristos       if (keycc == 0)
1708f7313695Schristos 	{
1709f7313695Schristos 	  /* No keys were specified (e.g. -f /dev/null).  Match nothing.  */
1710f7313695Schristos 	  out_invert ^= 1;
1711f7313695Schristos 	  match_lines = match_words = 0;
1712f7313695Schristos 	}
1713f7313695Schristos       else
1714f7313695Schristos 	/* Strip trailing newline. */
1715f7313695Schristos         --keycc;
1716f7313695Schristos     }
1717f7313695Schristos   else
1718f7313695Schristos     if (optind < argc)
1719f7313695Schristos       {
1720f7313695Schristos 	keys = argv[optind++];
1721f7313695Schristos 	keycc = strlen (keys);
1722f7313695Schristos       }
1723f7313695Schristos     else
1724f7313695Schristos       usage (2);
1725f7313695Schristos 
1726f7313695Schristos   if (!install_matcher (matcher) && !install_matcher ("default"))
1727f7313695Schristos     abort ();
1728f7313695Schristos 
1729f7313695Schristos   (*compile)(keys, keycc);
1730f7313695Schristos 
1731f7313695Schristos   if ((argc - optind > 1 && !no_filenames) || with_filenames)
1732f7313695Schristos     out_file = 1;
1733f7313695Schristos 
1734f7313695Schristos #ifdef SET_BINARY
1735f7313695Schristos   /* Output is set to binary mode because we shouldn't convert
1736f7313695Schristos      NL to CR-LF pairs, especially when grepping binary files.  */
1737f7313695Schristos   if (!isatty (1))
1738f7313695Schristos     SET_BINARY (1);
1739f7313695Schristos #endif
1740f7313695Schristos 
1741f7313695Schristos   if (max_count == 0)
1742f7313695Schristos     exit (1);
1743f7313695Schristos 
1744f7313695Schristos   if (optind < argc)
1745f7313695Schristos     {
1746f7313695Schristos 	status = 1;
1747f7313695Schristos 	do
1748f7313695Schristos 	{
1749f7313695Schristos 	  char *file = argv[optind];
1750f7313695Schristos 	  if ((included_patterns || excluded_patterns)
1751f7313695Schristos 	      && !isdir (file))
1752f7313695Schristos 	    {
1753f7313695Schristos 	      if (included_patterns &&
1754f7313695Schristos 		  ! excluded_filename (included_patterns, file, 0))
1755f7313695Schristos 		continue;
1756f7313695Schristos 	      if (excluded_patterns &&
1757f7313695Schristos 		  excluded_filename (excluded_patterns, file, 0))
1758f7313695Schristos 		continue;
1759f7313695Schristos 	    }
1760f7313695Schristos 	  status &= grepfile (strcmp (file, "-") == 0 ? (char *) NULL : file,
1761f7313695Schristos 			      &stats_base);
1762f7313695Schristos 	}
1763f7313695Schristos 	while ( ++optind < argc);
1764f7313695Schristos     }
1765f7313695Schristos   else
1766947ffbb0Schristos     {
1767*3f679887Swiz       status = grepfile(directories == RECURSE_DIRECTORIES ? "." : (char *) NULL,
1768*3f679887Swiz 			&stats_base);
1769947ffbb0Schristos     }
1770f7313695Schristos 
1771f7313695Schristos   /* We register via atexit() to test stdout.  */
1772f7313695Schristos   exit (errseen ? 2 : status);
1773f7313695Schristos }
1774f7313695Schristos /* vim:set shiftwidth=2: */
1775