aboutsummaryrefslogtreecommitdiff
path: root/gnu/usr.bin/grep/grep.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin/grep/grep.c')
-rw-r--r--gnu/usr.bin/grep/grep.c1038
1 files changed, 668 insertions, 370 deletions
diff --git a/gnu/usr.bin/grep/grep.c b/gnu/usr.bin/grep/grep.c
index 0966a29c82e1..638663454f22 100644
--- a/gnu/usr.bin/grep/grep.c
+++ b/gnu/usr.bin/grep/grep.c
@@ -36,13 +36,18 @@
#include "getpagesize.h"
#include "grep.h"
#include "savedir.h"
+#include "xstrtol.h"
+#include "xalloc.h"
+#include "error.h"
+#include "exclude.h"
+#include "closeout.h"
#undef MAX
#define MAX(A,B) ((A) > (B) ? (A) : (B))
struct stats
{
- struct stats *parent;
+ struct stats const *parent;
struct stat stat;
};
@@ -55,47 +60,80 @@ static int show_help;
/* If non-zero, print the version on standard output and exit. */
static int show_version;
+/* If nonzero, suppress diagnostics for nonexistent or unreadable files. */
+static int suppress_errors;
+
/* If nonzero, use mmap if possible. */
static int mmap_option;
+/* If nonzero, use grep_color marker. */
+static int color_option;
+
+/* If nonzero, show only the part of a line matching the expression. */
+static int only_matching;
+
+/* The color string used. The user can overwrite it using the environment
+ variable GREP_COLOR. The default is to print red. */
+static const char *grep_color = "01;31";
+
+static struct exclude *excluded_patterns;
+static struct exclude *included_patterns;
/* Short options. */
static char const short_options[] =
-"0123456789A:B:C::EFGHIUVX:abcd:e:f:hiLlnqrsuvwxyZz";
+"0123456789A:B:C:D:EFGHIPUVX:abcd:e:f:hiKLlm:noqRrsuvwxyZz";
/* Non-boolean long options that have no corresponding short equivalents. */
enum
{
- BINARY_FILES_OPTION = CHAR_MAX + 1
+ BINARY_FILES_OPTION = CHAR_MAX + 1,
+ COLOR_OPTION,
+ INCLUDE_OPTION,
+ EXCLUDE_OPTION,
+ EXCLUDE_FROM_OPTION,
+ LINE_BUFFERED_OPTION,
+ LABEL_OPTION
};
/* Long options equivalences. */
-static struct option long_options[] =
+static struct option const long_options[] =
{
{"after-context", required_argument, NULL, 'A'},
{"basic-regexp", no_argument, NULL, 'G'},
{"before-context", required_argument, NULL, 'B'},
{"binary-files", required_argument, NULL, BINARY_FILES_OPTION},
{"byte-offset", no_argument, NULL, 'b'},
- {"context", optional_argument, NULL, 'C'},
+ {"context", required_argument, NULL, 'C'},
+ {"color", optional_argument, NULL, COLOR_OPTION},
+ {"colour", optional_argument, NULL, COLOR_OPTION},
{"count", no_argument, NULL, 'c'},
+ {"devices", required_argument, NULL, 'D'},
{"directories", required_argument, NULL, 'd'},
{"extended-regexp", no_argument, NULL, 'E'},
+ {"exclude", required_argument, NULL, EXCLUDE_OPTION},
+ {"exclude-from", required_argument, NULL, EXCLUDE_FROM_OPTION},
{"file", required_argument, NULL, 'f'},
{"files-with-matches", no_argument, NULL, 'l'},
{"files-without-match", no_argument, NULL, 'L'},
{"fixed-regexp", no_argument, NULL, 'F'},
{"fixed-strings", no_argument, NULL, 'F'},
{"help", no_argument, &show_help, 1},
+ {"include", required_argument, NULL, INCLUDE_OPTION},
{"ignore-case", no_argument, NULL, 'i'},
+ {"label", required_argument, NULL, LABEL_OPTION},
+ {"line-buffered", no_argument, NULL, LINE_BUFFERED_OPTION},
{"line-number", no_argument, NULL, 'n'},
{"line-regexp", no_argument, NULL, 'x'},
+ {"max-count", required_argument, NULL, 'm'},
{"mmap", no_argument, &mmap_option, 1},
{"no-filename", no_argument, NULL, 'h'},
{"no-messages", no_argument, NULL, 's'},
{"null", no_argument, NULL, 'Z'},
{"null-data", no_argument, NULL, 'z'},
+ {"only-matching", no_argument, NULL, 'o'},
+ {"perl-regexp", no_argument, NULL, 'P'},
{"quiet", no_argument, NULL, 'q'},
{"recursive", no_argument, NULL, 'r'},
+ {"recursive", no_argument, NULL, 'R'},
{"regexp", required_argument, NULL, 'e'},
{"invert-match", no_argument, NULL, 'v'},
{"silent", no_argument, NULL, 'q'},
@@ -115,7 +153,8 @@ int match_lines;
unsigned char eolbyte;
/* For error messages. */
-static char *prog;
+/* The name the program was run with, stripped of any leading path. */
+char *program_name;
static char const *filename;
static int errseen;
@@ -125,115 +164,70 @@ static enum
READ_DIRECTORIES,
RECURSE_DIRECTORIES,
SKIP_DIRECTORIES
- } directories;
-
-static int ck_atoi PARAMS ((char const *, int *));
-static void usage PARAMS ((int)) __attribute__((noreturn));
-static void error PARAMS ((const char *, int));
-static void setmatcher PARAMS ((char const *));
-static int install_matcher PARAMS ((char const *));
-static int prepend_args PARAMS ((char const *, char *, char **));
-static void prepend_default_options PARAMS ((char const *, int *, char ***));
-static char *page_alloc PARAMS ((size_t, char **));
-static int reset PARAMS ((int, char const *, struct stats *));
-static int fillbuf PARAMS ((size_t, struct stats *));
-static int grepbuf PARAMS ((char *, char *));
-static void prtext PARAMS ((char *, char *, int *));
-static void prpending PARAMS ((char *));
-static void prline PARAMS ((char *, char *, int));
-static void print_offset_sep PARAMS ((off_t, int));
-static void nlscan PARAMS ((char *));
-static int grep PARAMS ((int, char const *, struct stats *));
-static int grepdir PARAMS ((char const *, struct stats *));
-static int grepfile PARAMS ((char const *, struct stats *));
-#if O_BINARY
+ } directories = READ_DIRECTORIES;
+
+/* How to handle devices. */
+static enum
+ {
+ READ_DEVICES,
+ SKIP_DEVICES
+ } devices = READ_DEVICES;
+
+static int grepdir PARAMS ((char const *, struct stats const *));
+#if defined(HAVE_DOS_FILE_CONTENTS)
static inline int undossify_input PARAMS ((register char *, size_t));
#endif
/* Functions we'll use to search. */
-static void (*compile) PARAMS ((char *, size_t));
-static char *(*execute) PARAMS ((char *, size_t, char **));
+static void (*compile) PARAMS ((char const *, size_t));
+static size_t (*execute) PARAMS ((char const *, size_t, size_t *, int));
-/* Print a message and possibly an error string. Remember
- that something awful happened. */
+/* Like error, but suppress the diagnostic if requested. */
static void
-error (const char *mesg, int errnum)
+suppressible_error (char const *mesg, int errnum)
{
- if (errnum)
- fprintf (stderr, "%s: %s: %s\n", prog, mesg, strerror (errnum));
- else
- fprintf (stderr, "%s: %s\n", prog, mesg);
+ if (! suppress_errors)
+ error (0, errnum, "%s", mesg);
errseen = 1;
}
-/* Like error (), but die horribly after printing. */
-void
-fatal (const char *mesg, int errnum)
-{
- error (mesg, errnum);
- exit (2);
-}
-
-/* Interface to handle errors and fix library lossage. */
-char *
-xmalloc (size_t size)
-{
- char *result;
-
- result = malloc (size);
- if (size && !result)
- fatal (_("memory exhausted"), 0);
- return result;
-}
-
-/* Interface to handle errors and fix some library lossage. */
-char *
-xrealloc (char *ptr, size_t size)
-{
- char *result;
-
- if (ptr)
- result = realloc (ptr, size);
- else
- result = malloc (size);
- if (size && !result)
- fatal (_("memory exhausted"), 0);
- return result;
-}
-
/* Convert STR to a positive integer, storing the result in *OUT.
- If STR is not a valid integer, return -1 (otherwise 0). */
-static int
-ck_atoi (char const *str, int *out)
+ STR must be a valid context length argument; report an error if it
+ isn't. */
+static void
+context_length_arg (char const *str, int *out)
{
- char const *p;
- for (p = str; *p; p++)
- if (*p < '0' || *p > '9')
- return -1;
-
- *out = atoi (optarg);
- return 0;
+ uintmax_t value;
+ if (! (xstrtoumax (str, 0, 10, &value, "") == LONGINT_OK
+ && 0 <= (*out = value)
+ && *out == value))
+ {
+ error (2, 0, "%s: %s\n", str, _("invalid context length argument"));
+ }
}
/* Hairy buffering mechanism for grep. The intent is to keep
all reads aligned on a page boundary and multiples of the
- page size. */
+ page size, unless a read yields a partial page. */
-static char *ubuffer; /* Unaligned base of buffer. */
static char *buffer; /* Base of buffer. */
-static size_t bufsalloc; /* Allocated size of buffer save region. */
-static size_t bufalloc; /* Total buffer size. */
-#define PREFERRED_SAVE_FACTOR 5 /* Preferred value of bufalloc / bufsalloc. */
+static size_t bufalloc; /* Allocated buffer size, counting slop. */
+#define INITIAL_BUFSIZE 32768 /* Initial buffer size, not counting slop. */
static int bufdesc; /* File descriptor. */
static char *bufbeg; /* Beginning of user-visible stuff. */
static char *buflim; /* Limit of user-visible stuff. */
static size_t pagesize; /* alignment of memory pages */
static off_t bufoffset; /* Read offset; defined on regular files. */
+static off_t after_last_match; /* Pointer after last matching line that
+ would have been output if we were
+ outputting characters. */
#if defined(HAVE_MMAP)
static int bufmapped; /* True if buffer is memory-mapped. */
static off_t initial_bufoffset; /* Initial value of bufoffset. */
+#else
+# define bufmapped 0
#endif
/* Return VAL aligned to the next multiple of ALIGNMENT. VAL can be
@@ -243,66 +237,37 @@ static off_t initial_bufoffset; /* Initial value of bufoffset. */
? (val) \
: (val) + ((alignment) - (size_t) (val) % (alignment)))
-/* Return the address of a page-aligned buffer of size SIZE,
- reallocating it from *UP. Set *UP to the newly allocated (but
- possibly unaligned) buffer used to build the aligned buffer. To
- free the buffer, free (*UP). */
-static char *
-page_alloc (size_t size, char **up)
-{
- size_t asize = size + pagesize - 1;
- if (size <= asize)
- {
- char *p = *up ? realloc (*up, asize) : malloc (asize);
- if (p)
- {
- *up = p;
- return ALIGN_TO (p, pagesize);
- }
- }
- return NULL;
-}
-
/* Reset the buffer for a new file, returning zero if we should skip it.
Initialize on the first time through. */
static int
reset (int fd, char const *file, struct stats *stats)
{
- if (pagesize)
- bufsalloc = ALIGN_TO (bufalloc / PREFERRED_SAVE_FACTOR, pagesize);
- else
+ if (! pagesize)
{
- size_t ubufsalloc;
pagesize = getpagesize ();
- if (pagesize == 0)
+ if (pagesize == 0 || 2 * pagesize + 1 <= pagesize)
abort ();
-#ifndef BUFSALLOC
- ubufsalloc = MAX (8192, pagesize);
-#else
- ubufsalloc = BUFSALLOC;
-#endif
- bufsalloc = ALIGN_TO (ubufsalloc, pagesize);
- bufalloc = PREFERRED_SAVE_FACTOR * bufsalloc;
- /* The 1 byte of overflow is a kludge for dfaexec(), which
- inserts a sentinel newline at the end of the buffer
- being searched. There's gotta be a better way... */
- if (bufsalloc < ubufsalloc
- || bufalloc / PREFERRED_SAVE_FACTOR != bufsalloc
- || bufalloc + 1 < bufalloc
- || ! (buffer = page_alloc (bufalloc + 1, &ubuffer)))
- fatal (_("memory exhausted"), 0);
+ bufalloc = ALIGN_TO (INITIAL_BUFSIZE, pagesize) + pagesize + 1;
+ buffer = xmalloc (bufalloc);
}
- buflim = buffer;
+ bufbeg = buflim = ALIGN_TO (buffer + 1, pagesize);
+ bufbeg[-1] = eolbyte;
bufdesc = fd;
if (fstat (fd, &stats->stat) != 0)
{
- error ("fstat", errno);
+ error (0, errno, "fstat");
return 0;
}
if (directories == SKIP_DIRECTORIES && S_ISDIR (stats->stat.st_mode))
return 0;
+#ifndef DJGPP
+ if (devices == SKIP_DEVICES && (S_ISCHR(stats->stat.st_mode) || S_ISBLK(stats->stat.st_mode) || S_ISSOCK(stats->stat.st_mode)))
+#else
+ if (devices == SKIP_DEVICES && (S_ISCHR(stats->stat.st_mode) || S_ISBLK(stats->stat.st_mode)))
+#endif
+ return 0;
if (S_ISREG (stats->stat.st_mode))
{
if (file)
@@ -312,18 +277,18 @@ reset (int fd, char const *file, struct stats *stats)
bufoffset = lseek (fd, 0, SEEK_CUR);
if (bufoffset < 0)
{
- error ("lseek", errno);
+ error (0, errno, "lseek");
return 0;
}
}
-#ifdef HAVE_MMAP
+#if defined(HAVE_MMAP)
initial_bufoffset = bufoffset;
bufmapped = mmap_option && bufoffset % pagesize == 0;
#endif
}
else
{
-#ifdef HAVE_MMAP
+#if defined(HAVE_MMAP)
bufmapped = 0;
#endif
}
@@ -335,73 +300,68 @@ reset (int fd, char const *file, struct stats *stats)
to the beginning of the buffer contents, and 'buflim'
points just after the end. Return zero if there's an error. */
static int
-fillbuf (size_t save, struct stats *stats)
+fillbuf (size_t save, struct stats const *stats)
{
size_t fillsize = 0;
int cc = 1;
+ char *readbuf;
size_t readsize;
- /* Offset from start of unaligned buffer to start of old stuff
+ /* Offset from start of buffer to start of old stuff
that we want to save. */
- size_t saved_offset = buflim - ubuffer - save;
+ size_t saved_offset = buflim - save - buffer;
- if (bufsalloc < save)
+ if (pagesize <= buffer + bufalloc - buflim)
+ {
+ readbuf = buflim;
+ bufbeg = buflim - save;
+ }
+ else
{
- size_t aligned_save = ALIGN_TO (save, pagesize);
- size_t maxalloc = (size_t) -1;
+ size_t minsize = save + pagesize;
+ size_t newsize;
size_t newalloc;
-
+ char *newbuf;
+
+ /* Grow newsize until it is at least as great as minsize. */
+ for (newsize = bufalloc - pagesize - 1; newsize < minsize; newsize *= 2)
+ if (newsize * 2 < newsize || newsize * 2 + pagesize + 1 < newsize * 2)
+ xalloc_die ();
+
+ /* Try not to allocate more memory than the file size indicates,
+ as that might cause unnecessary memory exhaustion if the file
+ is large. However, do not use the original file size as a
+ heuristic if we've already read past the file end, as most
+ likely the file is growing. */
if (S_ISREG (stats->stat.st_mode))
{
- /* Calculate an upper bound on how much memory we should allocate.
- We can't use ALIGN_TO here, since off_t might be longer than
- size_t. Watch out for arithmetic overflow. */
off_t to_be_read = stats->stat.st_size - bufoffset;
- size_t slop = to_be_read % pagesize;
- off_t aligned_to_be_read = to_be_read + (slop ? pagesize - slop : 0);
- off_t maxalloc_off = aligned_save + aligned_to_be_read;
- if (0 <= maxalloc_off && maxalloc_off == (size_t) maxalloc_off)
- maxalloc = maxalloc_off;
+ off_t maxsize_off = save + to_be_read;
+ if (0 <= to_be_read && to_be_read <= maxsize_off
+ && maxsize_off == (size_t) maxsize_off
+ && minsize <= (size_t) maxsize_off
+ && (size_t) maxsize_off < newsize)
+ newsize = maxsize_off;
}
- /* Grow bufsalloc until it is at least as great as `save'; but
- if there is an overflow, just grow it to the next page boundary. */
- while (bufsalloc < save)
- if (bufsalloc < bufsalloc * 2)
- bufsalloc *= 2;
- else
- {
- bufsalloc = aligned_save;
- break;
- }
+ /* Add enough room so that the buffer is aligned and has room
+ for byte sentinels fore and aft. */
+ newalloc = newsize + pagesize + 1;
- /* Grow the buffer size to be PREFERRED_SAVE_FACTOR times
- bufsalloc.... */
- newalloc = PREFERRED_SAVE_FACTOR * bufsalloc;
- if (maxalloc < newalloc)
+ newbuf = bufalloc < newalloc ? xmalloc (bufalloc = newalloc) : buffer;
+ readbuf = ALIGN_TO (newbuf + 1 + save, pagesize);
+ bufbeg = readbuf - save;
+ memmove (bufbeg, buffer + saved_offset, save);
+ bufbeg[-1] = eolbyte;
+ if (newbuf != buffer)
{
- /* ... except don't grow it more than a pagesize past the
- file size, as that might cause unnecessary memory
- exhaustion if the file is large. */
- newalloc = maxalloc;
- bufsalloc = aligned_save;
+ free (buffer);
+ buffer = newbuf;
}
-
- /* Check that the above calculations made progress, which might
- not occur if there is arithmetic overflow. If there's no
- progress, or if the new buffer size is larger than the old
- and buffer reallocation fails, report memory exhaustion. */
- if (bufsalloc < save || newalloc < save
- || (newalloc == save && newalloc != maxalloc)
- || (bufalloc < newalloc
- && ! (buffer
- = page_alloc ((bufalloc = newalloc) + 1, &ubuffer))))
- fatal (_("memory exhausted"), 0);
}
- bufbeg = buffer + bufsalloc - save;
- memmove (bufbeg, ubuffer + saved_offset, save);
- readsize = bufalloc - bufsalloc;
+ readsize = buffer + bufalloc - readbuf;
+ readsize -= readsize % pagesize;
#if defined(HAVE_MMAP)
if (bufmapped)
@@ -417,7 +377,7 @@ fillbuf (size_t save, struct stats *stats)
}
if (mmapsize
- && (mmap ((caddr_t) (buffer + bufsalloc), mmapsize,
+ && (mmap ((caddr_t) readbuf, mmapsize,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED,
bufdesc, bufoffset)
!= (caddr_t) -1))
@@ -438,7 +398,7 @@ fillbuf (size_t save, struct stats *stats)
if (bufoffset != initial_bufoffset
&& lseek (bufdesc, bufoffset, SEEK_SET) < 0)
{
- error ("lseek", errno);
+ error (0, errno, "lseek");
cc = 0;
}
}
@@ -448,7 +408,7 @@ fillbuf (size_t save, struct stats *stats)
if (! fillsize)
{
ssize_t bytesread;
- while ((bytesread = read (bufdesc, buffer + bufsalloc, readsize)) < 0
+ while ((bytesread = read (bufdesc, readbuf, readsize)) < 0
&& errno == EINTR)
continue;
if (bytesread < 0)
@@ -458,21 +418,22 @@ fillbuf (size_t save, struct stats *stats)
}
bufoffset += fillsize;
-#if O_BINARY
+#if defined(HAVE_DOS_FILE_CONTENTS)
if (fillsize)
- fillsize = undossify_input (buffer + bufsalloc, fillsize);
+ fillsize = undossify_input (readbuf, fillsize);
#endif
- buflim = buffer + bufsalloc + fillsize;
+ buflim = readbuf + fillsize;
return cc;
}
/* Flags controlling the style of output. */
static enum
- {
- BINARY_BINARY_FILES,
- TEXT_BINARY_FILES,
- WITHOUT_MATCH_BINARY_FILES
- } binary_files; /* How to handle binary files. */
+{
+ BINARY_BINARY_FILES,
+ TEXT_BINARY_FILES,
+ WITHOUT_MATCH_BINARY_FILES
+} binary_files; /* How to handle binary files. */
+
static int filename_mask; /* If zero, output nulls after filenames. */
static int out_quiet; /* Suppress all normal output. */
static int out_invert; /* Print nonmatching stuff. */
@@ -484,36 +445,58 @@ static int out_after; /* Lines of trailing context. */
static int count_matches; /* Count matching lines. */
static int list_files; /* List matching files. */
static int no_filenames; /* Suppress file names. */
-static int suppress_errors; /* Suppress diagnostics. */
+static off_t max_count; /* Stop after outputting this many
+ lines from an input file. */
+static int line_buffered; /* If nonzero, use line buffering, i.e.
+ fflush everyline out. */
+static char *label = NULL; /* Fake filename for stdin */
+
/* Internal variables to keep track of byte count, context, etc. */
-static off_t totalcc; /* Total character count before bufbeg. */
-static char *lastnl; /* Pointer after last newline counted. */
-static char *lastout; /* Pointer after last character output;
+static uintmax_t totalcc; /* Total character count before bufbeg. */
+static char const *lastnl; /* Pointer after last newline counted. */
+static char const *lastout; /* Pointer after last character output;
NULL if no character has been output
or if it's conceptually before bufbeg. */
-static off_t totalnl; /* Total newline count before lastnl. */
-static int pending; /* Pending lines of output. */
-static int done_on_match; /* Stop scanning file on first match */
-
-#if O_BINARY
+static uintmax_t totalnl; /* Total newline count before lastnl. */
+static off_t outleft; /* Maximum number of lines to be output. */
+static int pending; /* Pending lines of output.
+ Always kept 0 if out_quiet is true. */
+static int done_on_match; /* Stop scanning file on first match. */
+static int exit_on_match; /* Exit on first match. */
+
+#if defined(HAVE_DOS_FILE_CONTENTS)
# include "dosbuf.c"
#endif
+/* Add two numbers that count input bytes or lines, and report an
+ error if the addition overflows. */
+static uintmax_t
+add_count (uintmax_t a, uintmax_t b)
+{
+ uintmax_t sum = a + b;
+ if (sum < a)
+ error (2, 0, _("input is too large to count"));
+ return sum;
+}
+
static void
-nlscan (char *lim)
+nlscan (char const *lim)
{
- char *beg;
- for (beg = lastnl; (beg = memchr (beg, eolbyte, lim - beg)); beg++)
- totalnl++;
+ size_t newlines = 0;
+ char const *beg;
+ for (beg = lastnl; beg != lim; beg = memchr (beg, eolbyte, lim - beg), beg++)
+ newlines++;
+ totalnl = add_count (totalnl, newlines);
lastnl = lim;
}
+/* Print a byte offset, followed by a character separator. */
static void
-print_offset_sep (off_t pos, int sep)
+print_offset_sep (uintmax_t pos, char sep)
{
- /* Do not rely on printf to print pos, since off_t may be longer than long,
- and long long is not portable. */
+ /* Do not rely on printf to print pos, since uintmax_t may be longer
+ than long, and long long is not portable. */
char buf[sizeof pos * CHAR_BIT];
char *p = buf + sizeof buf - 1;
@@ -527,56 +510,134 @@ print_offset_sep (off_t pos, int sep)
}
static void
-prline (char *beg, char *lim, int sep)
+prline (char const *beg, char const *lim, int sep)
{
if (out_file)
printf ("%s%c", filename, sep & filename_mask);
if (out_line)
{
nlscan (beg);
- print_offset_sep (++totalnl, sep);
+ totalnl = add_count (totalnl, 1);
+ print_offset_sep (totalnl, sep);
lastnl = lim;
}
if (out_byte)
{
- off_t pos = totalcc + (beg - bufbeg);
-#if O_BINARY
+ uintmax_t pos = add_count (totalcc, beg - bufbeg);
+#if defined(HAVE_DOS_FILE_CONTENTS)
pos = dossified_pos (pos);
#endif
print_offset_sep (pos, sep);
}
+ if (only_matching)
+ {
+ size_t match_size;
+ size_t match_offset;
+ while ((match_offset = (*execute) (beg, lim - beg, &match_size, 1))
+ != (size_t) -1)
+ {
+ char const *b = beg + match_offset;
+ if (b == lim)
+ break;
+ if (match_size == 0)
+ break;
+ if(color_option)
+ printf("\33[%sm", grep_color);
+ fwrite(b, sizeof (char), match_size, stdout);
+ if(color_option)
+ fputs("\33[00m", stdout);
+ fputs("\n", stdout);
+ beg = b + match_size;
+ }
+ lastout = lim;
+ if(line_buffered)
+ fflush(stdout);
+ return;
+ }
+ if (color_option)
+ {
+ size_t match_size;
+ size_t match_offset;
+ if(match_icase)
+ {
+ /* Yuck, this is tricky */
+ char *buf = (char*) xmalloc (lim - beg);
+ char *ibeg = buf;
+ char *ilim = ibeg + (lim - beg);
+ int i;
+ for (i = 0; i < lim - beg; i++)
+ ibeg[i] = tolower (beg[i]);
+ while ((match_offset = (*execute) (ibeg, ilim-ibeg, &match_size, 1))
+ != (size_t) -1)
+ {
+ char const *b = beg + match_offset;
+ if (b == lim)
+ break;
+ fwrite (beg, sizeof (char), match_offset, stdout);
+ printf ("\33[%sm", grep_color);
+ fwrite (b, sizeof (char), match_size, stdout);
+ fputs ("\33[00m", stdout);
+ beg = b + match_size;
+ ibeg = ibeg + match_offset + match_size;
+ }
+ fwrite (beg, 1, lim - beg, stdout);
+ free (buf);
+ lastout = lim;
+ return;
+ }
+ while (lim-beg && (match_offset = (*execute) (beg, lim - beg, &match_size, 1))
+ != (size_t) -1)
+ {
+ char const *b = beg + match_offset;
+ /* Avoid matching the empty line at the end of the buffer. */
+ if (b == lim)
+ break;
+ /* Avoid hanging on grep --color "" foo */
+ if (match_size == 0)
+ break;
+ fwrite (beg, sizeof (char), match_offset, stdout);
+ printf ("\33[%sm", grep_color);
+ fwrite (b, sizeof (char), match_size, stdout);
+ fputs ("\33[00m", stdout);
+ beg = b + match_size;
+ }
+ }
fwrite (beg, 1, lim - beg, stdout);
if (ferror (stdout))
- error (_("writing output"), errno);
+ error (0, errno, _("writing output"));
lastout = lim;
+ if (line_buffered)
+ fflush (stdout);
}
-/* Print pending lines of trailing context prior to LIM. */
+/* Print pending lines of trailing context prior to LIM. Trailing context ends
+ at the next matching line when OUTLEFT is 0. */
static void
-prpending (char *lim)
+prpending (char const *lim)
{
- char *nl;
-
if (!lastout)
lastout = bufbeg;
while (pending > 0 && lastout < lim)
{
+ char const *nl = memchr (lastout, eolbyte, lim - lastout);
+ size_t match_size;
--pending;
- if ((nl = memchr (lastout, eolbyte, lim - lastout)) != 0)
- ++nl;
+ if (outleft
+ || (((*execute) (lastout, nl - lastout, &match_size, 0) == (size_t) -1)
+ == !out_invert))
+ prline (lastout, nl + 1, '-');
else
- nl = lim;
- prline (lastout, nl, '-');
+ pending = 0;
}
}
/* Print the lines between BEG and LIM. Deal with context crap.
- If NLINESP is non-null, store a count of lines between BEG and LIM. */
+ If NLINESP is non-null, store a count of lines between BEG and LIM. */
static void
-prtext (char *beg, char *lim, int *nlinesp)
+prtext (char const *beg, char const *lim, int *nlinesp)
{
static int used; /* avoid printing "--" before any output */
- char *bp, *p, *nl;
+ char const *bp, *p;
char eol = eolbyte;
int i, n;
@@ -594,7 +655,7 @@ prtext (char *beg, char *lim, int *nlinesp)
if (p > bp)
do
--p;
- while (p > bp && p[-1] != eol);
+ while (p[-1] != eol);
/* We only print the "--" separator if our output is
discontiguous from the last output in the file. */
@@ -603,26 +664,28 @@ prtext (char *beg, char *lim, int *nlinesp)
while (p < beg)
{
- nl = memchr (p, eol, beg - p);
- prline (p, nl + 1, '-');
- p = nl + 1;
+ char const *nl = memchr (p, eol, beg - p);
+ nl++;
+ prline (p, nl, '-');
+ p = nl;
}
}
if (nlinesp)
{
/* Caller wants a line count. */
- for (n = 0; p < lim; ++n)
+ for (n = 0; p < lim && n < outleft; n++)
{
- if ((nl = memchr (p, eol, lim - p)) != 0)
- ++nl;
- else
- nl = lim;
+ char const *nl = memchr (p, eol, lim - p);
+ nl++;
if (!out_quiet)
prline (p, nl, ':');
p = nl;
}
*nlinesp = n;
+
+ /* relying on it that this function is never called when outleft = 0. */
+ after_last_match = bufoffset - (buflim - p);
}
else
if (!out_quiet)
@@ -636,31 +699,42 @@ prtext (char *beg, char *lim, int *nlinesp)
between matching lines if OUT_INVERT is true). Return a count of
lines printed. */
static int
-grepbuf (char *beg, char *lim)
+grepbuf (char const *beg, char const *lim)
{
int nlines, n;
- register char *p, *b;
- char *endp;
- char eol = eolbyte;
+ register char const *p;
+ size_t match_offset;
+ size_t match_size;
nlines = 0;
p = beg;
- while ((b = (*execute)(p, lim - p, &endp)) != 0)
+ while ((match_offset = (*execute) (p, lim - p, &match_size, 0)) != (size_t) -1)
{
+ char const *b = p + match_offset;
+ char const *endp = b + match_size;
/* Avoid matching the empty line at the end of the buffer. */
- if (b == lim && ((b > beg && b[-1] == eol) || b == beg))
+ if (b == lim)
break;
if (!out_invert)
{
prtext (b, endp, (int *) 0);
- nlines += 1;
- if (done_on_match)
- return nlines;
+ nlines++;
+ outleft--;
+ if (!outleft || done_on_match)
+ {
+ if (exit_on_match)
+ exit (0);
+ after_last_match = bufoffset - (buflim - endp);
+ return nlines;
+ }
}
else if (p < b)
{
prtext (p, b, &n);
nlines += n;
+ outleft -= n;
+ if (!outleft)
+ return nlines;
}
p = endp;
}
@@ -668,6 +742,7 @@ grepbuf (char *beg, char *lim)
{
prtext (p, lim, &n);
nlines += n;
+ outleft -= n;
}
return nlines;
}
@@ -681,7 +756,9 @@ grep (int fd, char const *file, struct stats *stats)
int nlines, i;
int not_text;
size_t residue, save;
- char *beg, *lim;
+ char oldc;
+ char *beg;
+ char *lim;
char eol = eolbyte;
if (!reset (fd, file, stats))
@@ -693,13 +770,15 @@ grep (int fd, char const *file, struct stats *stats)
/* Close fd now, so that we don't open a lot of file descriptors
when we recurse deeply. */
if (close (fd) != 0)
- error (file, errno);
+ error (0, errno, "%s", file);
return grepdir (file, stats) - 2;
}
totalcc = 0;
lastout = 0;
totalnl = 0;
+ outleft = max_count;
+ after_last_match = 0;
pending = 0;
nlines = 0;
@@ -708,8 +787,8 @@ grep (int fd, char const *file, struct stats *stats)
if (! fillbuf (save, stats))
{
- if (! (is_EISDIR (errno, file) && suppress_errors))
- error (filename, errno);
+ if (! is_EISDIR (errno, file))
+ suppressible_error (filename, errno);
return 0;
}
@@ -726,20 +805,38 @@ grep (int fd, char const *file, struct stats *stats)
lastnl = bufbeg;
if (lastout)
lastout = bufbeg;
- if (buflim - bufbeg == save)
+
+ beg = bufbeg + save;
+
+ /* no more data to scan (eof) except for maybe a residue -> break */
+ if (beg == buflim)
break;
- beg = bufbeg + save - residue;
- for (lim = buflim; lim > beg && lim[-1] != eol; --lim)
- ;
+
+ /* Determine new residue (the length of an incomplete line at the end of
+ the buffer, 0 means there is no incomplete last line). */
+ oldc = beg[-1];
+ beg[-1] = eol;
+ for (lim = buflim; lim[-1] != eol; lim--)
+ continue;
+ beg[-1] = oldc;
+ if (lim == beg)
+ lim = beg - residue;
+ beg -= residue;
residue = buflim - lim;
+
if (beg < lim)
{
- nlines += grepbuf (beg, lim);
+ if (outleft)
+ nlines += grepbuf (beg, lim);
if (pending)
prpending (lim);
- if (nlines && done_on_match && !out_invert)
+ if((!outleft && !pending) || (nlines && done_on_match && !out_invert))
goto finish_grep;
}
+
+ /* The last OUT_BEFORE lines at the end of the buffer will be needed as
+ leading context if there is a matching line at the begin of the
+ next data. Make beg point to their begin. */
i = 0;
beg = lim;
while (i < out_before && beg > bufbeg && beg != lastout)
@@ -747,27 +844,33 @@ grep (int fd, char const *file, struct stats *stats)
++i;
do
--beg;
- while (beg > bufbeg && beg[-1] != eol);
+ while (beg[-1] != eol);
}
+
+ /* detect if leading context is discontinuous from last printed line. */
if (beg != lastout)
lastout = 0;
+
+ /* Handle some details and read more data to scan. */
save = residue + lim - beg;
- totalcc += buflim - bufbeg - save;
+ if (out_byte)
+ totalcc = add_count (totalcc, buflim - bufbeg - save);
if (out_line)
nlscan (beg);
if (! fillbuf (save, stats))
{
- if (! (is_EISDIR (errno, file) && suppress_errors))
- error (filename, errno);
+ if (! is_EISDIR (errno, file))
+ suppressible_error (filename, errno);
goto finish_grep;
}
}
if (residue)
{
*buflim++ = eol;
- nlines += grepbuf (bufbeg + save - residue, buflim);
+ if (outleft)
+ nlines += grepbuf (bufbeg + save - residue, buflim);
if (pending)
- prpending (buflim);
+ prpending (buflim);
}
finish_grep:
@@ -788,7 +891,7 @@ grepfile (char const *file, struct stats *stats)
if (! file)
{
desc = 0;
- filename = _("(standard input)");
+ filename = label ? label : _("(standard input)");
}
else
{
@@ -798,46 +901,44 @@ grepfile (char const *file, struct stats *stats)
if (desc < 0)
{
int e = errno;
-
+
if (is_EISDIR (e, file) && directories == RECURSE_DIRECTORIES)
{
if (stat (file, &stats->stat) != 0)
{
- error (file, errno);
+ error (0, errno, "%s", file);
return 1;
}
return grepdir (file, stats);
}
-
+
if (!suppress_errors)
{
if (directories == SKIP_DIRECTORIES)
switch (e)
{
-#ifdef EISDIR
+#if defined(EISDIR)
case EISDIR:
return 1;
#endif
case EACCES:
/* When skipping directories, don't worry about
directories that can't be opened. */
- if (stat (file, &stats->stat) == 0
- && S_ISDIR (stats->stat.st_mode))
+ if (isdir (file))
return 1;
break;
}
-
- error (file, e);
}
+ suppressible_error (file, e);
return 1;
}
filename = file;
}
-#if O_BINARY
+#if defined(SET_BINARY)
/* Set input to binary mode. Pipes are simulated with files
on DOS, so this includes the case of "foo | grep bar". */
if (!isatty (desc))
@@ -860,11 +961,19 @@ grepfile (char const *file, struct stats *stats)
if (list_files == 1 - 2 * status)
printf ("%s%c", filename, '\n' & filename_mask);
- if (file)
+ if (! file)
+ {
+ off_t required_offset = outleft ? bufoffset : after_last_match;
+ if ((bufmapped || required_offset != bufoffset)
+ && lseek (desc, required_offset, SEEK_SET) < 0
+ && S_ISREG (stats->stat.st_mode))
+ error (0, errno, "%s", filename);
+ }
+ else
while (close (desc) != 0)
if (errno != EINTR)
{
- error (file, errno);
+ error (0, errno, "%s", file);
break;
}
}
@@ -873,33 +982,34 @@ grepfile (char const *file, struct stats *stats)
}
static int
-grepdir (char const *dir, struct stats *stats)
+grepdir (char const *dir, struct stats const *stats)
{
int status = 1;
- struct stats *ancestor;
+ struct stats const *ancestor;
char *name_space;
- for (ancestor = stats; (ancestor = ancestor->parent) != 0; )
- if (ancestor->stat.st_ino == stats->stat.st_ino
- && ancestor->stat.st_dev == stats->stat.st_dev)
- {
- if (!suppress_errors)
- fprintf (stderr, _("%s: warning: %s: %s\n"), prog, dir,
+ /* Mingw32 does not support st_ino. No known working hosts use zero
+ for st_ino, so assume that the Mingw32 bug applies if it's zero. */
+ if (stats->stat.st_ino)
+ for (ancestor = stats; (ancestor = ancestor->parent) != 0; )
+ if (ancestor->stat.st_ino == stats->stat.st_ino
+ && ancestor->stat.st_dev == stats->stat.st_dev)
+ {
+ if (!suppress_errors)
+ error (0, 0, _("warning: %s: %s\n"), dir,
_("recursive directory loop"));
- return 1;
- }
+ return 1;
+ }
- name_space = savedir (dir, (unsigned) stats->stat.st_size);
+ name_space = savedir (dir, stats->stat.st_size, included_patterns,
+ excluded_patterns);
if (! name_space)
{
if (errno)
- {
- if (!suppress_errors)
- error (dir, errno);
- }
+ suppressible_error (dir, errno);
else
- fatal (_("Memory exhausted"), 0);
+ xalloc_die ();
}
else
{
@@ -907,7 +1017,7 @@ grepdir (char const *dir, struct stats *stats)
int needs_slash = ! (dirlen == FILESYSTEM_PREFIX_LEN (dir)
|| IS_SLASH (dir[dirlen - 1]));
char *file = NULL;
- char *namep = name_space;
+ char const *namep = name_space;
struct stats child;
child.parent = stats;
out_file += !no_filenames;
@@ -935,21 +1045,24 @@ usage (int status)
{
if (status != 0)
{
- fprintf (stderr, _("Usage: %s [OPTION]... PATTERN [FILE]...\n"), prog);
- fprintf (stderr, _("Try `%s --help' for more information.\n"), prog);
+ fprintf (stderr, _("Usage: %s [OPTION]... PATTERN [FILE]...\n"),
+ program_name);
+ fprintf (stderr, _("Try `%s --help' for more information.\n"),
+ program_name);
}
else
{
- printf (_("Usage: %s [OPTION]... PATTERN [FILE] ...\n"), prog);
+ printf (_("Usage: %s [OPTION]... PATTERN [FILE] ...\n"), program_name);
printf (_("\
Search for PATTERN in each FILE or standard input.\n\
Example: %s -i 'hello world' menu.h main.c\n\
\n\
-Regexp selection and interpretation:\n"), prog);
+Regexp selection and interpretation:\n"), program_name);
printf (_("\
-E, --extended-regexp PATTERN is an extended regular expression\n\
-F, --fixed-strings PATTERN is a set of newline-separated strings\n\
- -G, --basic-regexp PATTERN is a basic regular expression\n"));
+ -G, --basic-regexp PATTERN is a basic regular expression\n\
+ -P, --perl-regexp PATTERN is a Perl regular expression\n"));
printf (_("\
-e, --regexp=PATTERN use PATTERN as a regular expression\n\
-f, --file=FILE obtain PATTERN from FILE\n\
@@ -968,18 +1081,27 @@ Miscellaneous:\n\
printf (_("\
\n\
Output control:\n\
+ -m, --max-count=NUM stop after NUM matches\n\
-b, --byte-offset print the byte offset with output lines\n\
-n, --line-number print line number with output lines\n\
+ --line-buffered flush output on every line\n\
-H, --with-filename print the filename for each match\n\
-h, --no-filename suppress the prefixing filename on output\n\
+ --label=LABEL print LABEL as filename for standard input\n\
+ -o, --only-matching show only the part of a line matching PATTERN\n\
-q, --quiet, --silent suppress all normal output\n\
--binary-files=TYPE assume that binary files are TYPE\n\
- TYPE is 'binary', 'text', or 'without-match'.\n\
+ TYPE is 'binary', 'text', or 'without-match'\n\
-a, --text equivalent to --binary-files=text\n\
-I equivalent to --binary-files=without-match\n\
-d, --directories=ACTION how to handle directories\n\
- ACTION is 'read', 'recurse', or 'skip'.\n\
- -r, --recursive equivalent to --directories=recurse.\n\
+ ACTION is 'read', 'recurse', or 'skip'\n\
+ -D, --devices=ACTION how to handle devices, FIFOs and sockets\n\
+ ACTION is 'read' or 'skip'\n\
+ -R, -r, --recursive equivalent to --directories=recurse\n\
+ --include=PATTERN files that match PATTERN will be examined\n\
+ --exclude=PATTERN files that match PATTERN will be skipped.\n\
+ --exclude-from=FILE files that match PATTERN in FILE will be skipped.\n\
-L, --files-without-match only print FILE names containing no match\n\
-l, --files-with-matches only print FILE names containing matches\n\
-c, --count only print a count of matching lines per FILE\n\
@@ -989,9 +1111,11 @@ Output control:\n\
Context control:\n\
-B, --before-context=NUM print NUM lines of leading context\n\
-A, --after-context=NUM print NUM lines of trailing context\n\
- -C, --context[=NUM] print NUM (default 2) lines of output context\n\
- unless overridden by -A or -B\n\
+ -C, --context=NUM print NUM lines of output context\n\
-NUM same as --context=NUM\n\
+ --color[=WHEN],\n\
+ --colour[=WHEN] use markers to distinguish the matching string\n\
+ WHEN may be `always', `never' or `auto'.\n\
-U, --binary do not strip CR characters at EOL (MSDOS)\n\
-u, --unix-byte-offsets report offsets as if CRs were not there (MSDOS)\n\
\n\
@@ -1009,7 +1133,7 @@ static void
setmatcher (char const *m)
{
if (matcher && strcmp (matcher, m) != 0)
- fatal (_("conflicting matchers specified"), 0);
+ error (2, 0, _("conflicting matchers specified"));
matcher = m;
}
@@ -1019,16 +1143,16 @@ static int
install_matcher (char const *name)
{
int i;
-#ifdef HAVE_SETRLIMIT
+#if defined(HAVE_SETRLIMIT)
struct rlimit rlim;
#endif
- for (i = 0; matchers[i].name; ++i)
+ for (i = 0; matchers[i].compile; i++)
if (strcmp (name, matchers[i].name) == 0)
{
compile = matchers[i].compile;
execute = matchers[i].execute;
-#if HAVE_SETRLIMIT && defined(RLIMIT_STACK)
+#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_STACK)
/* I think every platform needs to do this, so that regex.c
doesn't oveflow the stack. The default value of
`re_max_failures' is too large for some platforms: it needs
@@ -1048,9 +1172,10 @@ install_matcher (char const *name)
re_max_failures = newlim / (2 * 20 * sizeof (char *));
}
if (rlim.rlim_cur < newlim)
- rlim.rlim_cur = newlim;
-
- setrlimit (RLIMIT_STACK, &rlim);
+ {
+ rlim.rlim_cur = newlim;
+ setrlimit (RLIMIT_STACK, &rlim);
+ }
}
#endif
return 1;
@@ -1111,6 +1236,47 @@ prepend_default_options (char const *options, int *pargc, char ***pargv)
}
}
+/* Get the next non-digit option from ARGC and ARGV.
+ Return -1 if there are no more options.
+ Process any digit options that were encountered on the way,
+ and store the resulting integer into *DEFAULT_CONTEXT. */
+static int
+get_nondigit_option (int argc, char *const *argv, int *default_context)
+{
+ int opt;
+ char buf[sizeof (uintmax_t) * CHAR_BIT + 4];
+ char *p = buf;
+
+ /* Set buf[0] to anything but '0', for the leading-zero test below. */
+ buf[0] = '\0';
+
+ while (opt = getopt_long (argc, argv, short_options, long_options, NULL),
+ '0' <= opt && opt <= '9')
+ {
+ /* Suppress trivial leading zeros, to avoid incorrect
+ diagnostic on strings like 00000000000. */
+ p -= buf[0] == '0';
+
+ *p++ = opt;
+ if (p == buf + sizeof buf - 4)
+ {
+ /* Too many digits. Append "..." to make context_length_arg
+ complain about "X...", where X contains the digits seen
+ so far. */
+ strcpy (p, "...");
+ p += 3;
+ break;
+ }
+ }
+ if (p != buf)
+ {
+ *p = '\0';
+ context_length_arg (buf, default_context);
+ }
+
+ return opt;
+}
+
int
main (int argc, char **argv)
{
@@ -1119,29 +1285,33 @@ main (int argc, char **argv)
int with_filenames;
int opt, cc, status;
int default_context;
- unsigned digit_args_val;
FILE *fp;
extern char *optarg;
extern int optind;
initialize_main (&argc, &argv);
- prog = argv[0];
- if (prog && strrchr (prog, '/'))
- prog = strrchr (prog, '/') + 1;
+ program_name = argv[0];
+ if (program_name && strrchr (program_name, '/'))
+ program_name = strrchr (program_name, '/') + 1;
+
+ if (!strcmp(program_name, "egrep"))
+ setmatcher ("egrep");
+ if (!strcmp(program_name, "fgrep"))
+ setmatcher ("fgrep");
#if defined(__MSDOS__) || defined(_WIN32)
/* DOS and MS-Windows use backslashes as directory separators, and usually
have an .exe suffix. They also have case-insensitive filesystems. */
- if (prog)
+ if (program_name)
{
- char *p = prog;
+ char *p = program_name;
char *bslash = strrchr (argv[0], '\\');
- if (bslash && bslash >= prog) /* for mixed forward/backslash case */
- prog = bslash + 1;
- else if (prog == argv[0]
+ if (bslash && bslash >= program_name) /* for mixed forward/backslash case */
+ program_name = bslash + 1;
+ else if (program_name == argv[0]
&& argv[0][0] && argv[0][1] == ':') /* "c:progname" */
- prog = argv[0] + 2;
+ program_name = argv[0] + 2;
/* Collapse the letter-case, so `strcmp' could be used hence. */
for ( ; *p; p++)
@@ -1149,7 +1319,7 @@ main (int argc, char **argv)
*p += 'a' - 'A';
/* Remove the .exe extension, if any. */
- if ((p = strrchr (prog, '.')) && strcmp (p, ".exe") == 0)
+ if ((p = strrchr (program_name, '.')) && strcmp (p, ".exe") == 0)
*p = '\0';
}
#endif
@@ -1160,108 +1330,110 @@ main (int argc, char **argv)
eolbyte = '\n';
filename_mask = ~0;
+ max_count = TYPE_MAXIMUM (off_t);
+
/* The value -1 means to use DEFAULT_CONTEXT. */
out_after = out_before = -1;
/* Default before/after context: chaged by -C/-NUM options */
default_context = 0;
- /* Accumulated value of individual digits in a -NUM option */
- digit_args_val = 0;
-
+ /* Changed by -o option */
+ only_matching = 0;
-/* Internationalization. */
-#if HAVE_SETLOCALE
+ /* Internationalization. */
+#if defined(HAVE_SETLOCALE)
setlocale (LC_ALL, "");
#endif
-#if ENABLE_NLS
+#if defined(ENABLE_NLS)
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
#endif
+ atexit (close_stdout);
+
prepend_default_options (getenv ("GREP_OPTIONS"), &argc, &argv);
- while ((opt = getopt_long (argc, argv, short_options, long_options, NULL))
- != -1)
+ while ((opt = get_nondigit_option (argc, argv, &default_context)) != -1)
switch (opt)
{
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- digit_args_val = 10 * digit_args_val + opt - '0';
- default_context = digit_args_val;
- break;
case 'A':
- if (optarg)
- {
- if (ck_atoi (optarg, &out_after))
- fatal (_("invalid context length argument"), 0);
- }
+ context_length_arg (optarg, &out_after);
break;
+
case 'B':
- if (optarg)
- {
- if (ck_atoi (optarg, &out_before))
- fatal (_("invalid context length argument"), 0);
- }
+ context_length_arg (optarg, &out_before);
break;
+
case 'C':
/* Set output match context, but let any explicit leading or
trailing amount specified with -A or -B stand. */
- if (optarg)
- {
- if (ck_atoi (optarg, &default_context))
- fatal (_("invalid context length argument"), 0);
- }
+ context_length_arg (optarg, &default_context);
+ break;
+
+ case 'D':
+ if (strcmp (optarg, "read") == 0)
+ devices = READ_DEVICES;
+ else if (strcmp (optarg, "skip") == 0)
+ devices = SKIP_DEVICES;
else
- default_context = 2;
+ error (2, 0, _("unknown devices method"));
break;
+
case 'E':
setmatcher ("egrep");
break;
+
case 'F':
setmatcher ("fgrep");
break;
+
+ case 'P':
+ setmatcher ("perl");
+ break;
+
case 'G':
setmatcher ("grep");
break;
+
case 'H':
with_filenames = 1;
break;
+
case 'I':
binary_files = WITHOUT_MATCH_BINARY_FILES;
break;
+
case 'U':
-#if O_BINARY
+#if defined(HAVE_DOS_FILE_CONTENTS)
dos_use_file_type = DOS_BINARY;
#endif
break;
+
case 'u':
-#if O_BINARY
+#if defined(HAVE_DOS_FILE_CONTENTS)
dos_report_unix_offset = 1;
#endif
break;
+
case 'V':
show_version = 1;
break;
+
case 'X':
setmatcher (optarg);
break;
+
case 'a':
binary_files = TEXT_BINARY_FILES;
break;
+
case 'b':
out_byte = 1;
break;
+
case 'c':
- out_quiet = 1;
count_matches = 1;
break;
+
case 'd':
if (strcmp (optarg, "read") == 0)
directories = READ_DIRECTORIES;
@@ -1270,8 +1442,9 @@ main (int argc, char **argv)
else if (strcmp (optarg, "recurse") == 0)
directories = RECURSE_DIRECTORIES;
else
- fatal (_("unknown directories method"), 0);
+ error (2, 0, _("unknown directories method"));
break;
+
case 'e':
cc = strlen (optarg);
keys = xrealloc (keys, keycc + cc + 1);
@@ -1279,10 +1452,11 @@ main (int argc, char **argv)
keycc += cc;
keys[keycc++] = '\n';
break;
+
case 'f':
fp = strcmp (optarg, "-") != 0 ? fopen (optarg, "r") : stdin;
if (!fp)
- fatal (optarg, errno);
+ error (2, errno, "%s", optarg);
for (keyalloc = 1; keyalloc <= keycc + 1; keyalloc *= 2)
;
keys = xrealloc (keys, keyalloc);
@@ -1300,53 +1474,88 @@ main (int argc, char **argv)
if (oldcc != keycc && keys[keycc - 1] != '\n')
keys[keycc++] = '\n';
break;
+
case 'h':
no_filenames = 1;
break;
+
case 'i':
case 'y': /* For old-timers . . . */
match_icase = 1;
break;
+
case 'L':
/* Like -l, except list files that don't contain matches.
Inspired by the same option in Hume's gre. */
- out_quiet = 1;
list_files = -1;
- done_on_match = 1;
break;
+
case 'l':
- out_quiet = 1;
list_files = 1;
- done_on_match = 1;
break;
+
+ case 'm':
+ {
+ uintmax_t value;
+ switch (xstrtoumax (optarg, 0, 10, &value, ""))
+ {
+ case LONGINT_OK:
+ max_count = value;
+ if (0 <= max_count && max_count == value)
+ break;
+ /* Fall through. */
+ case LONGINT_OVERFLOW:
+ max_count = TYPE_MAXIMUM (off_t);
+ break;
+
+ default:
+ error (2, 0, _("invalid max count"));
+ }
+ }
+ break;
+
case 'n':
out_line = 1;
break;
+
+ case 'o':
+ only_matching = 1;
+ break;
+
case 'q':
- done_on_match = 1;
- out_quiet = 1;
+ exit_on_match = 1;
+ close_stdout_set_status(0);
break;
+
+ case 'R':
case 'r':
directories = RECURSE_DIRECTORIES;
break;
+
case 's':
suppress_errors = 1;
break;
+
case 'v':
out_invert = 1;
break;
+
case 'w':
match_words = 1;
break;
+
case 'x':
match_lines = 1;
break;
+
case 'Z':
filename_mask = 0;
break;
+
case 'z':
eolbyte = '\0';
break;
+
case BINARY_FILES_OPTION:
if (strcmp (optarg, "binary") == 0)
binary_files = BINARY_BINARY_FILES;
@@ -1355,21 +1564,96 @@ main (int argc, char **argv)
else if (strcmp (optarg, "without-match") == 0)
binary_files = WITHOUT_MATCH_BINARY_FILES;
else
- fatal (_("unknown binary-files type"), 0);
+ error (2, 0, _("unknown binary-files type"));
+ break;
+
+ case COLOR_OPTION:
+ if(optarg) {
+ if(!strcasecmp(optarg, "always") || !strcasecmp(optarg, "yes") ||
+ !strcasecmp(optarg, "force"))
+ color_option = 1;
+ else if(!strcasecmp(optarg, "never") || !strcasecmp(optarg, "no") ||
+ !strcasecmp(optarg, "none"))
+ color_option = 0;
+ else if(!strcasecmp(optarg, "auto") || !strcasecmp(optarg, "tty") ||
+ !strcasecmp(optarg, "if-tty"))
+ color_option = 2;
+ else
+ show_help = 1;
+ } else
+ color_option = 2;
+ if(color_option == 2) {
+ if(isatty(STDOUT_FILENO) && getenv("TERM") &&
+ strcmp(getenv("TERM"), "dumb"))
+ color_option = 1;
+ else
+ color_option = 0;
+ }
break;
+
+ case EXCLUDE_OPTION:
+ if (!excluded_patterns)
+ excluded_patterns = new_exclude ();
+ add_exclude (excluded_patterns, optarg);
+ break;
+
+ case EXCLUDE_FROM_OPTION:
+ if (!excluded_patterns)
+ excluded_patterns = new_exclude ();
+ if (add_exclude_file (add_exclude, excluded_patterns, optarg, '\n')
+ != 0)
+ {
+ error (2, errno, "%s", optarg);
+ }
+ break;
+
+ case INCLUDE_OPTION:
+ if (!included_patterns)
+ included_patterns = new_exclude ();
+ add_exclude (included_patterns, optarg);
+ break;
+
+ case LINE_BUFFERED_OPTION:
+ line_buffered = 1;
+ break;
+
+ case LABEL_OPTION:
+ label = optarg;
+ break;
+
case 0:
/* long options */
break;
+
default:
usage (2);
break;
+
}
+ /* POSIX.2 says that -q overrides -l, which in turn overrides the
+ other output options. */
+ if (exit_on_match)
+ list_files = 0;
+ if (exit_on_match | list_files)
+ {
+ count_matches = 0;
+ done_on_match = 1;
+ }
+ out_quiet = count_matches | done_on_match;
+
if (out_after < 0)
out_after = default_context;
if (out_before < 0)
out_before = default_context;
+ if (color_option)
+ {
+ char *userval = getenv ("GREP_COLOR");
+ if (userval != NULL && *userval != '\0')
+ grep_color = userval;
+ }
+
if (! matcher)
matcher = "grep";
@@ -1378,7 +1662,7 @@ main (int argc, char **argv)
printf (_("%s (GNU grep) %s\n"), matcher, VERSION);
printf ("\n");
printf (_("\
-Copyright 1988, 1992-1999, 2000 Free Software Foundation, Inc.\n"));
+Copyright 1988, 1992-1999, 2000, 2001 Free Software Foundation, Inc.\n"));
printf (_("\
This is free software; see the source for copying conditions. There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"));
@@ -1392,8 +1676,11 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"))
if (keys)
{
if (keycc == 0)
- /* No keys were specified (e.g. -f /dev/null). Match nothing. */
- out_invert ^= 1;
+ {
+ /* No keys were specified (e.g. -f /dev/null). Match nothing. */
+ out_invert ^= 1;
+ match_lines = match_words = 0;
+ }
else
/* Strip trailing newline. */
--keycc;
@@ -1415,13 +1702,15 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"))
if ((argc - optind > 1 && !no_filenames) || with_filenames)
out_file = 1;
-#if O_BINARY
+#ifdef SET_BINARY
/* Output is set to binary mode because we shouldn't convert
NL to CR-LF pairs, especially when grepping binary files. */
if (!isatty (1))
SET_BINARY (1);
#endif
+ if (max_count == 0)
+ exit (1);
if (optind < argc)
{
@@ -1429,6 +1718,16 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"))
do
{
char *file = argv[optind];
+ if ((included_patterns || excluded_patterns)
+ && !isdir (file))
+ {
+ if (included_patterns &&
+ ! excluded_filename (included_patterns, file, 0))
+ continue;
+ if (excluded_patterns &&
+ excluded_filename (excluded_patterns, file, 0))
+ continue;
+ }
status &= grepfile (strcmp (file, "-") == 0 ? (char *) NULL : file,
&stats_base);
}
@@ -1437,8 +1736,7 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"))
else
status = grepfile ((char *) NULL, &stats_base);
- if (fclose (stdout) == EOF)
- error (_("writing output"), errno);
-
+ /* We register via atexit() to test stdout. */
exit (errseen ? 2 : status);
}
+/* vim:set shiftwidth=2: */