#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#if defined(HAVE_MMAP)
# include <sys/mman.h>
#endif
#if defined(HAVE_SETRLIMIT)
# include <sys/time.h>
# include <sys/resource.h>
#endif
#if defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H && defined HAVE_MBRTOWC
# define MBS_SUPPORT
# include <wchar.h>
# include <wctype.h>
#endif
#include <stdio.h>
#include "system.h"
#include "getopt.h"
#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 const *parent;
struct stat stat;
};
static struct stats stats_base;
static int show_help;
static int show_version;
static int suppress_errors;
static int mmap_option;
static int color_option;
static int only_matching;
static const char *grep_color = "01;31";
static struct exclude *excluded_patterns;
static struct exclude *included_patterns;
static char const short_options[] =
"0123456789A:B:C:D:EFGHIPUVX:abcd:e:f:hiKLlm:noqRrsuvwxyZz";
enum
{
BINARY_FILES_OPTION = CHAR_MAX + 1,
COLOR_OPTION,
INCLUDE_OPTION,
EXCLUDE_OPTION,
EXCLUDE_FROM_OPTION,
LINE_BUFFERED_OPTION,
LABEL_OPTION
};
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", 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'},
{"text", no_argument, NULL, 'a'},
{"binary", no_argument, NULL, 'U'},
{"unix-byte-offsets", no_argument, NULL, 'u'},
{"version", no_argument, NULL, 'V'},
{"with-filename", no_argument, NULL, 'H'},
{"word-regexp", no_argument, NULL, 'w'},
{0, 0, 0, 0}
};
int match_icase;
int match_words;
int match_lines;
unsigned char eolbyte;
char *program_name;
static char const *filename;
static int errseen;
static enum
{
READ_DIRECTORIES,
RECURSE_DIRECTORIES,
SKIP_DIRECTORIES
} directories = READ_DIRECTORIES;
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
static void (*compile) PARAMS ((char const *, size_t));
static size_t (*execute) PARAMS ((char const *, size_t, size_t *, int));
static void
suppressible_error (char const *mesg, int errnum)
{
if (! suppress_errors)
error (0, errnum, "%s", mesg);
errseen = 1;
}
static void
context_length_arg (char const *str, int *out)
{
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"));
}
}
static char *buffer;
static size_t bufalloc;
#define INITIAL_BUFSIZE 32768
static int bufdesc;
static char *bufbeg;
static char *buflim;
static size_t pagesize;
static off_t bufoffset;
static off_t after_last_match;
#if defined(HAVE_MMAP)
static int bufmapped;
static off_t initial_bufoffset;
#else
# define bufmapped 0
#endif
#define ALIGN_TO(val, alignment) \
((size_t) (val) % (alignment) == 0 \
? (val) \
: (val) + ((alignment) - (size_t) (val) % (alignment)))
static int
reset (int fd, char const *file, struct stats *stats)
{
if (! pagesize)
{
pagesize = getpagesize ();
if (pagesize == 0 || 2 * pagesize + 1 <= pagesize)
abort ();
bufalloc = ALIGN_TO (INITIAL_BUFSIZE, pagesize) + pagesize + 1;
buffer = xmalloc (bufalloc);
}
bufbeg = buflim = ALIGN_TO (buffer + 1, pagesize);
bufbeg[-1] = eolbyte;
bufdesc = fd;
if (fstat (fd, &stats->stat) != 0)
{
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)
bufoffset = 0;
else
{
bufoffset = lseek (fd, 0, SEEK_CUR);
if (bufoffset < 0)
{
error (0, errno, "lseek");
return 0;
}
}
#if defined(HAVE_MMAP)
initial_bufoffset = bufoffset;
bufmapped = mmap_option && bufoffset % pagesize == 0;
#endif
}
else
{
#if defined(HAVE_MMAP)
bufmapped = 0;
#endif
}
return 1;
}
static int
fillbuf (size_t save, struct stats const *stats)
{
size_t fillsize = 0;
int cc = 1;
char *readbuf;
size_t readsize;
size_t saved_offset = buflim - save - buffer;
if (pagesize <= buffer + bufalloc - buflim)
{
readbuf = buflim;
bufbeg = buflim - save;
}
else
{
size_t minsize = save + pagesize;
size_t newsize;
size_t newalloc;
char *newbuf;
for (newsize = bufalloc - pagesize - 1; newsize < minsize; newsize *= 2)
if (newsize * 2 < newsize || newsize * 2 + pagesize + 1 < newsize * 2)
xalloc_die ();
if (S_ISREG (stats->stat.st_mode))
{
off_t to_be_read = stats->stat.st_size - bufoffset;
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;
}
newalloc = newsize + pagesize + 1;
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)
{
free (buffer);
buffer = newbuf;
}
}
readsize = buffer + bufalloc - readbuf;
readsize -= readsize % pagesize;
#if defined(HAVE_MMAP)
if (bufmapped)
{
size_t mmapsize = readsize;
if (stats->stat.st_size - bufoffset < mmapsize)
{
mmapsize = stats->stat.st_size - bufoffset;
mmapsize -= mmapsize % pagesize;
}
if (mmapsize
&& (mmap ((caddr_t) readbuf, mmapsize,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED,
bufdesc, bufoffset)
!= (caddr_t) -1))
{
fillsize = mmapsize;
}
else
{
bufmapped = 0;
if (bufoffset != initial_bufoffset
&& lseek (bufdesc, bufoffset, SEEK_SET) < 0)
{
error (0, errno, "lseek");
cc = 0;
}
}
}
#endif
if (! fillsize)
{
ssize_t bytesread;
while ((bytesread = read (bufdesc, readbuf, readsize)) < 0
&& errno == EINTR)
continue;
if (bytesread < 0)
cc = 0;
else
fillsize = bytesread;
}
bufoffset += fillsize;
#if defined(HAVE_DOS_FILE_CONTENTS)
if (fillsize)
fillsize = undossify_input (readbuf, fillsize);
#endif
buflim = readbuf + fillsize;
return cc;
}
static enum
{
BINARY_BINARY_FILES,
TEXT_BINARY_FILES,
WITHOUT_MATCH_BINARY_FILES
} binary_files;
static int filename_mask;
static int out_quiet;
static int out_invert;
static int out_file;
static int out_line;
static int out_byte;
static int out_before;
static int out_after;
static int count_matches;
static int list_files;
static int no_filenames;
static off_t max_count;
static int line_buffered;
static char *label = NULL;
static uintmax_t totalcc;
static char const *lastnl;
static char const *lastout;
static uintmax_t totalnl;
static off_t outleft;
static int pending;
static int done_on_match;
static int exit_on_match;
#if defined(HAVE_DOS_FILE_CONTENTS)
# include "dosbuf.c"
#endif
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 const *lim)
{
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;
}
static void
print_offset_sep (uintmax_t pos, char sep)
{
char buf[sizeof pos * CHAR_BIT];
char *p = buf + sizeof buf - 1;
*p = sep;
do
*--p = '0' + pos % 10;
while ((pos /= 10) != 0);
fwrite (p, 1, buf + sizeof buf - p, stdout);
}
static void
prline (char const *beg, char const *lim, int sep)
{
if (out_file)
printf ("%s%c", filename, sep & filename_mask);
if (out_line)
{
nlscan (beg);
totalnl = add_count (totalnl, 1);
print_offset_sep (totalnl, sep);
lastnl = lim;
}
if (out_byte)
{
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)
{
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;
if (b == lim)
break;
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 (0, errno, _("writing output"));
lastout = lim;
if (line_buffered)
fflush (stdout);
}
static void
prpending (char const *lim)
{
if (!lastout)
lastout = bufbeg;
while (pending > 0 && lastout < lim)
{
char const *nl = memchr (lastout, eolbyte, lim - lastout);
size_t match_size;
--pending;
if (outleft
|| (((*execute) (lastout, nl - lastout, &match_size, 0) == (size_t) -1)
== !out_invert))
prline (lastout, nl + 1, '-');
else
pending = 0;
}
}
static void
prtext (char const *beg, char const *lim, int *nlinesp)
{
static int used;
char const *bp, *p;
char eol = eolbyte;
int i, n;
if (!out_quiet && pending > 0)
prpending (beg);
p = beg;
if (!out_quiet)
{
bp = lastout ? lastout : bufbeg;
for (i = 0; i < out_before; ++i)
if (p > bp)
do
--p;
while (p[-1] != eol);
if ((out_before || out_after) && used && p != lastout)
puts ("--");
while (p < beg)
{
char const *nl = memchr (p, eol, beg - p);
nl++;
prline (p, nl, '-');
p = nl;
}
}
if (nlinesp)
{
for (n = 0; p < lim && n < outleft; n++)
{
char const *nl = memchr (p, eol, lim - p);
nl++;
if (!out_quiet)
prline (p, nl, ':');
p = nl;
}
*nlinesp = n;
after_last_match = bufoffset - (buflim - p);
}
else
if (!out_quiet)
prline (beg, lim, ':');
pending = out_quiet ? 0 : out_after;
used = 1;
}
static int
grepbuf (char const *beg, char const *lim)
{
int nlines, n;
register char const *p;
size_t match_offset;
size_t match_size;
nlines = 0;
p = beg;
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;
if (b == lim)
break;
if (!out_invert)
{
prtext (b, endp, (int *) 0);
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;
}
if (out_invert && p < lim)
{
prtext (p, lim, &n);
nlines += n;
outleft -= n;
}
return nlines;
}
static int
grep (int fd, char const *file, struct stats *stats)
{
int nlines, i;
int not_text;
size_t residue, save;
char oldc;
char *beg;
char *lim;
char eol = eolbyte;
if (!reset (fd, file, stats))
return 0;
if (file && directories == RECURSE_DIRECTORIES
&& S_ISDIR (stats->stat.st_mode))
{
if (close (fd) != 0)
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;
residue = 0;
save = 0;
if (! fillbuf (save, stats))
{
if (! is_EISDIR (errno, file))
suppressible_error (filename, errno);
return 0;
}
not_text = (((binary_files == BINARY_BINARY_FILES && !out_quiet)
|| binary_files == WITHOUT_MATCH_BINARY_FILES)
&& memchr (bufbeg, eol ? '\0' : '\200', buflim - bufbeg));
if (not_text && binary_files == WITHOUT_MATCH_BINARY_FILES)
return 0;
done_on_match += not_text;
out_quiet += not_text;
for (;;)
{
lastnl = bufbeg;
if (lastout)
lastout = bufbeg;
beg = bufbeg + save;
if (beg == buflim)
break;
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)
{
if (outleft)
nlines += grepbuf (beg, lim);
if (pending)
prpending (lim);
if((!outleft && !pending) || (nlines && done_on_match && !out_invert))
goto finish_grep;
}
i = 0;
beg = lim;
while (i < out_before && beg > bufbeg && beg != lastout)
{
++i;
do
--beg;
while (beg[-1] != eol);
}
if (beg != lastout)
lastout = 0;
save = residue + lim - beg;
if (out_byte)
totalcc = add_count (totalcc, buflim - bufbeg - save);
if (out_line)
nlscan (beg);
if (! fillbuf (save, stats))
{
if (! is_EISDIR (errno, file))
suppressible_error (filename, errno);
goto finish_grep;
}
}
if (residue)
{
*buflim++ = eol;
if (outleft)
nlines += grepbuf (bufbeg + save - residue, buflim);
if (pending)
prpending (buflim);
}
finish_grep:
done_on_match -= not_text;
out_quiet -= not_text;
if ((not_text & ~out_quiet) && nlines != 0)
printf (_("Binary file %s matches\n"), filename);
return nlines;
}
static int
grepfile (char const *file, struct stats *stats)
{
int desc;
int count;
int status;
if (! file)
{
desc = 0;
filename = label ? label : _("(standard input)");
}
else
{
while ((desc = open (file, O_RDONLY)) < 0 && errno == EINTR)
continue;
if (desc < 0)
{
int e = errno;
if (is_EISDIR (e, file) && directories == RECURSE_DIRECTORIES)
{
if (stat (file, &stats->stat) != 0)
{
error (0, errno, "%s", file);
return 1;
}
return grepdir (file, stats);
}
if (!suppress_errors)
{
if (directories == SKIP_DIRECTORIES)
switch (e)
{
#if defined(EISDIR)
case EISDIR:
return 1;
#endif
case EACCES:
if (isdir (file))
return 1;
break;
}
}
suppressible_error (file, e);
return 1;
}
filename = file;
}
#if defined(SET_BINARY)
if (!isatty (desc))
SET_BINARY (desc);
#endif
count = grep (desc, file, stats);
if (count < 0)
status = count + 2;
else
{
if (count_matches)
{
if (out_file)
printf ("%s%c", filename, ':' & filename_mask);
printf ("%d\n", count);
}
status = !count;
if (list_files == 1 - 2 * status)
printf ("%s%c", filename, '\n' & filename_mask);
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 (0, errno, "%s", file);
break;
}
}
return status;
}
static int
grepdir (char const *dir, struct stats const *stats)
{
int status = 1;
struct stats const *ancestor;
char *name_space;
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;
}
name_space = savedir (dir, stats->stat.st_size, included_patterns,
excluded_patterns);
if (! name_space)
{
if (errno)
suppressible_error (dir, errno);
else
xalloc_die ();
}
else
{
size_t dirlen = strlen (dir);
int needs_slash = ! (dirlen == FILESYSTEM_PREFIX_LEN (dir)
|| IS_SLASH (dir[dirlen - 1]));
char *file = NULL;
char const *namep = name_space;
struct stats child;
child.parent = stats;
out_file += !no_filenames;
while (*namep)
{
size_t namelen = strlen (namep);
file = xrealloc (file, dirlen + 1 + namelen + 1);
strcpy (file, dir);
file[dirlen] = '/';
strcpy (file + dirlen + needs_slash, namep);
namep += namelen + 1;
status &= grepfile (file, &child);
}
out_file -= !no_filenames;
if (file)
free (file);
free (name_space);
}
return status;
}
static void
usage (int status)
{
if (status != 0)
{
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"), 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"), 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\
-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\
-i, --ignore-case ignore case distinctions\n\
-w, --word-regexp force PATTERN to match only whole words\n\
-x, --line-regexp force PATTERN to match only whole lines\n\
-z, --null-data a data line ends in 0 byte, not newline\n"));
printf (_("\
\n\
Miscellaneous:\n\
-s, --no-messages suppress error messages\n\
-v, --invert-match select non-matching lines\n\
-V, --version print version information and exit\n\
--help display this help and exit\n\
--mmap use memory-mapped input if possible\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\
-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\
-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\
-Z, --null print 0 byte after FILE name\n"));
printf (_("\
\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 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\
`egrep' means `grep -E'. `fgrep' means `grep -F'.\n\
With no FILE, or when FILE is -, read standard input. If less than\n\
two FILEs given, assume -h. Exit status is 0 if match, 1 if no match,\n\
and 2 if trouble.\n"));
printf (_("\nReport bugs to <bug-gnu-utils@gnu.org>.\n"));
}
exit (status);
}
static void
setmatcher (char const *m)
{
if (matcher && strcmp (matcher, m) != 0)
error (2, 0, _("conflicting matchers specified"));
matcher = m;
}
static int
install_matcher (char const *name)
{
int i;
#if defined(HAVE_SETRLIMIT)
struct rlimit rlim;
#endif
for (i = 0; matchers[i].compile; i++)
if (strcmp (name, matchers[i].name) == 0)
{
compile = matchers[i].compile;
execute = matchers[i].execute;
#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_STACK)
if (!getrlimit (RLIMIT_STACK, &rlim))
{
long newlim;
extern long int re_max_failures;
newlim = re_max_failures * 2 * 20 * sizeof (char *);
if (newlim > rlim.rlim_max)
{
newlim = rlim.rlim_max;
re_max_failures = newlim / (2 * 20 * sizeof (char *));
}
if (rlim.rlim_cur < newlim)
{
rlim.rlim_cur = newlim;
setrlimit (RLIMIT_STACK, &rlim);
}
}
#endif
return 1;
}
return 0;
}
static int
prepend_args (char const *options, char *buf, char **argv)
{
char const *o = options;
char *b = buf;
int n = 0;
for (;;)
{
while (ISSPACE ((unsigned char) *o))
o++;
if (!*o)
return n;
if (argv)
argv[n] = b;
n++;
do
if ((*b++ = *o++) == '\\' && *o)
b[-1] = *o++;
while (*o && ! ISSPACE ((unsigned char) *o));
*b++ = '\0';
}
}
static void
prepend_default_options (char const *options, int *pargc, char ***pargv)
{
if (options)
{
char *buf = xmalloc (strlen (options) + 1);
int prepended = prepend_args (options, buf, (char **) NULL);
int argc = *pargc;
char * const *argv = *pargv;
char **pp = (char **) xmalloc ((prepended + argc + 1) * sizeof *pp);
*pargc = prepended + argc;
*pargv = pp;
*pp++ = *argv++;
pp += prepend_args (options, buf, pp);
while ((*pp++ = *argv++))
continue;
}
}
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;
buf[0] = '\0';
while (opt = getopt_long (argc, argv, short_options, long_options, NULL),
'0' <= opt && opt <= '9')
{
p -= buf[0] == '0';
*p++ = opt;
if (p == buf + sizeof buf - 4)
{
strcpy (p, "...");
p += 3;
break;
}
}
if (p != buf)
{
*p = '\0';
context_length_arg (buf, default_context);
}
return opt;
}
int
main (int argc, char **argv)
{
char *keys;
size_t keycc, oldcc, keyalloc;
int with_filenames;
int opt, cc, status;
int default_context;
FILE *fp;
extern char *optarg;
extern int optind;
initialize_main (&argc, &argv);
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)
if (program_name)
{
char *p = program_name;
char *bslash = strrchr (argv[0], '\\');
if (bslash && bslash >= program_name)
program_name = bslash + 1;
else if (program_name == argv[0]
&& argv[0][0] && argv[0][1] == ':')
program_name = argv[0] + 2;
for ( ; *p; p++)
if (*p >= 'A' && *p <= 'Z')
*p += 'a' - 'A';
if ((p = strrchr (program_name, '.')) && strcmp (p, ".exe") == 0)
*p = '\0';
}
#endif
keys = NULL;
keycc = 0;
with_filenames = 0;
eolbyte = '\n';
filename_mask = ~0;
max_count = TYPE_MAXIMUM (off_t);
out_after = out_before = -1;
default_context = 0;
only_matching = 0;
#if defined(HAVE_SETLOCALE)
setlocale (LC_ALL, "");
#endif
#if defined(ENABLE_NLS)
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
#endif
atexit (close_stdout);
prepend_default_options (getenv ("GREP_OPTIONS"), &argc, &argv);
while ((opt = get_nondigit_option (argc, argv, &default_context)) != -1)
switch (opt)
{
case 'A':
context_length_arg (optarg, &out_after);
break;
case 'B':
context_length_arg (optarg, &out_before);
break;
case 'C':
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
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 defined(HAVE_DOS_FILE_CONTENTS)
dos_use_file_type = DOS_BINARY;
#endif
break;
case 'u':
#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':
count_matches = 1;
break;
case 'd':
if (strcmp (optarg, "read") == 0)
directories = READ_DIRECTORIES;
else if (strcmp (optarg, "skip") == 0)
directories = SKIP_DIRECTORIES;
else if (strcmp (optarg, "recurse") == 0)
directories = RECURSE_DIRECTORIES;
else
error (2, 0, _("unknown directories method"));
break;
case 'e':
cc = strlen (optarg);
keys = xrealloc (keys, keycc + cc + 1);
strcpy (&keys[keycc], optarg);
keycc += cc;
keys[keycc++] = '\n';
break;
case 'f':
fp = strcmp (optarg, "-") != 0 ? fopen (optarg, "r") : stdin;
if (!fp)
error (2, errno, "%s", optarg);
for (keyalloc = 1; keyalloc <= keycc + 1; keyalloc *= 2)
;
keys = xrealloc (keys, keyalloc);
oldcc = keycc;
while (!feof (fp)
&& (cc = fread (keys + keycc, 1, keyalloc - 1 - keycc, fp)) > 0)
{
keycc += cc;
if (keycc == keyalloc - 1)
keys = xrealloc (keys, keyalloc *= 2);
}
if (fp != stdin)
fclose(fp);
if (oldcc != keycc && keys[keycc - 1] != '\n')
keys[keycc++] = '\n';
break;
case 'h':
no_filenames = 1;
break;
case 'i':
case 'y':
match_icase = 1;
break;
case 'L':
list_files = -1;
break;
case 'l':
list_files = 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;
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':
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;
else if (strcmp (optarg, "text") == 0)
binary_files = TEXT_BINARY_FILES;
else if (strcmp (optarg, "without-match") == 0)
binary_files = WITHOUT_MATCH_BINARY_FILES;
else
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:
break;
default:
usage (2);
break;
}
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";
if (show_version)
{
printf (_("%s (GNU grep) %s\n"), matcher, VERSION);
printf ("\n");
printf (_("\
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"));
printf ("\n");
exit (0);
}
if (show_help)
usage (0);
if (keys)
{
if (keycc == 0)
{
out_invert ^= 1;
match_lines = match_words = 0;
}
else
--keycc;
}
else
if (optind < argc)
{
keys = argv[optind++];
keycc = strlen (keys);
}
else
usage (2);
if (!install_matcher (matcher) && !install_matcher ("default"))
abort ();
#ifdef MBS_SUPPORT
if (MB_CUR_MAX != 1 && match_icase)
{
wchar_t wc;
mbstate_t cur_state, prev_state;
int i, len = strlen(keys);
memset(&cur_state, 0, sizeof(mbstate_t));
for (i = 0; i <= len ;)
{
size_t mbclen;
mbclen = mbrtowc(&wc, keys + i, len - i, &cur_state);
if (mbclen == (size_t) -1 || mbclen == (size_t) -2 || mbclen == 0)
{
mbclen = 1;
}
else
{
if (iswupper((wint_t)wc))
{
wc = towlower((wint_t)wc);
wcrtomb(keys + i, wc, &cur_state);
}
}
i += mbclen;
}
}
#endif
(*compile)(keys, keycc);
if ((argc - optind > 1 && !no_filenames) || with_filenames)
out_file = 1;
#ifdef SET_BINARY
if (!isatty (1))
SET_BINARY (1);
#endif
if (max_count == 0)
exit (1);
if (optind < argc)
{
status = 1;
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);
}
while ( ++optind < argc);
}
else {
if (directories == RECURSE_DIRECTORIES) {
error (0, 0, _("warning: recursive search of stdin"));
}
status = grepfile ((char *) NULL, &stats_base);
}
exit (errseen ? 2 : status);
}