summaryrefslogtreecommitdiff
path: root/gnu/usr.bin/grep/grep.c
diff options
context:
space:
mode:
authorMichael Shalayeff <mickey@cvs.openbsd.org>2000-03-09 00:08:11 +0000
committerMichael Shalayeff <mickey@cvs.openbsd.org>2000-03-09 00:08:11 +0000
commit3f5956a6c8278d6b81c9304de4d6c2f09212c8d9 (patch)
tree9e2000b6473ce3c30201a5a51a096a2728d2239d /gnu/usr.bin/grep/grep.c
parent9da93fd65da2073262c1b5085492fbed52beb3b0 (diff)
new grep 2.4.1
whole bunch of bug fixes, mmap support (w/ --mmap) changed binary file grep behavior, but could be overwritten w/ -a millert@ ok
Diffstat (limited to 'gnu/usr.bin/grep/grep.c')
-rw-r--r--gnu/usr.bin/grep/grep.c1403
1 files changed, 999 insertions, 404 deletions
diff --git a/gnu/usr.bin/grep/grep.c b/gnu/usr.bin/grep/grep.c
index 37e6d19adfe..36c836274d3 100644
--- a/gnu/usr.bin/grep/grep.c
+++ b/gnu/usr.bin/grep/grep.c
@@ -1,5 +1,5 @@
/* grep.c - main driver file for grep.
- Copyright (C) 1992 Free Software Foundation, Inc.
+ Copyright 1992, 1997-1999, 2000 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,336 +13,468 @@
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
- Written July 1992 by Mike Haertel. */
+/* Written July 1992 by Mike Haertel. */
-#ifndef lint
-static char rcsid[] = "$Id: grep.c,v 1.2 1997/08/06 23:44:11 grr Exp $";
-#endif /* not lint */
-
-#include <errno.h>
-#include <stdio.h>
-
-#ifndef errno
-extern int errno;
+#ifdef HAVE_CONFIG_H
+# include <config.h>
#endif
-
-#ifdef STDC_HEADERS
-#include <stdlib.h>
-#else
#include <sys/types.h>
-extern char *malloc(), *realloc();
-extern void free();
-#endif
-
-#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
-#include <string.h>
-#ifdef NEED_MEMORY_H
-#include <memory.h>
-#endif
-#else
-#include <strings.h>
-#ifdef __STDC__
-extern void *memchr();
-#else
-extern char *memchr();
-#endif
-#define strrchr rindex
+#include <sys/stat.h>
+#if defined(HAVE_MMAP)
+# include <sys/mman.h>
#endif
-
-#ifdef HAVE_UNISTD_H
-#include <sys/types.h>
-#include <fcntl.h>
-#include <unistd.h>
-#else
-#define O_RDONLY 0
-extern int open(), read(), close();
+#if defined(HAVE_SETRLIMIT)
+# include <sys/time.h>
+# include <sys/resource.h>
#endif
-
-#include "getpagesize.h"
+#include <stdio.h>
+#include "system.h"
+#include "getopt.h"
#include "grep.h"
+#include "savedir.h"
#undef MAX
#define MAX(A,B) ((A) > (B) ? (A) : (B))
-/* Provide missing ANSI features if necessary. */
+struct stats
+{
+ struct stats *parent;
+ struct stat stat;
+};
-#ifndef HAVE_STRERROR
-extern int sys_nerr;
-extern char *sys_errlist[];
-#define strerror(E) ((E) < sys_nerr ? sys_errlist[(E)] : "bogus error number")
-#endif
+/* base of chain of stat buffers, used to detect directory loops */
+static struct stats stats_base;
-#ifndef HAVE_MEMCHR
-#ifdef __STDC__
-#define VOID void
-#else
-#define VOID char
-#endif
-VOID *
-memchr(vp, c, n)
- VOID *vp;
- int c;
- size_t n;
+/* if non-zero, display usage information and exit */
+static int show_help;
+
+/* If non-zero, print the version on standard output and exit. */
+static int show_version;
+
+/* If nonzero, use mmap if possible. */
+static int mmap_option;
+
+/* Short options. */
+static char const short_options[] =
+"0123456789A:B:C::EFGHIUVX:abcd:e:f:hiLlnqrsuvwxyZz";
+
+/* Non-boolean long options that have no corresponding short equivalents. */
+enum
{
- unsigned char *p;
+ BINARY_FILES_OPTION = CHAR_MAX + 1
+};
+
+/* Long options equivalences. */
+static struct option 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'},
+ {"count", no_argument, NULL, 'c'},
+ {"directories", required_argument, NULL, 'd'},
+ {"extended-regexp", no_argument, NULL, 'E'},
+ {"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},
+ {"ignore-case", no_argument, NULL, 'i'},
+ {"line-number", no_argument, NULL, 'n'},
+ {"line-regexp", no_argument, NULL, 'x'},
+ {"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'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"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}
+};
- for (p = (unsigned char *) vp; n--; ++p)
- if (*p == c)
- return (VOID *) p;
- return 0;
-}
-#endif
-
/* Define flags declared in grep.h. */
-char *matcher;
int match_icase;
int match_words;
int match_lines;
-
-/* Functions we'll use to search. */
-static void (*compile)();
-static char *(*execute)();
+unsigned char eolbyte;
/* For error messages. */
static char *prog;
-static char *filename;
+static char const *filename;
static int errseen;
+char const *matcher;
+
+/* How to handle directories. */
+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
+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 **));
+
/* Print a message and possibly an error string. Remember
that something awful happened. */
static void
-error(mesg, errnum)
-#ifdef __STDC__
- const
-#endif
- char *mesg;
- int errnum;
+error (const char *mesg, int errnum)
{
if (errnum)
- fprintf(stderr, "%s: %s: %s\n", prog, mesg, strerror(errnum));
+ fprintf (stderr, "%s: %s: %s\n", prog, mesg, strerror (errnum));
else
- fprintf(stderr, "%s: %s\n", prog, mesg);
+ fprintf (stderr, "%s: %s\n", prog, mesg);
errseen = 1;
}
-/* Like error(), but die horribly after printing. */
+/* Like error (), but die horribly after printing. */
void
-fatal(mesg, errnum)
-#ifdef __STDC__
- const
-#endif
- char *mesg;
- int errnum;
+fatal (const char *mesg, int errnum)
{
- error(mesg, errnum);
- exit(2);
+ error (mesg, errnum);
+ exit (2);
}
/* Interface to handle errors and fix library lossage. */
char *
-xmalloc(size)
- size_t size;
+xmalloc (size_t size)
{
char *result;
- result = malloc(size);
+ result = malloc (size);
if (size && !result)
- fatal("memory exhausted", 0);
+ fatal (_("memory exhausted"), 0);
return result;
}
/* Interface to handle errors and fix some library lossage. */
char *
-xrealloc(ptr, size)
- char *ptr;
- size_t size;
+xrealloc (char *ptr, size_t size)
{
char *result;
if (ptr)
- result = realloc(ptr, size);
+ result = realloc (ptr, size);
else
- result = malloc(size);
+ result = malloc (size);
if (size && !result)
- fatal("memory exhausted", 0);
+ fatal (_("memory exhausted"), 0);
return result;
}
-#if !defined(HAVE_VALLOC)
-#define valloc(x) malloc(x)
-#define vfree(x) free(x)
-#else
-#ifdef __STDC__
-extern void *valloc(size_t);
-#define vfree(x) ;
-#else
-extern char *valloc();
-#define vfree(x) ;
-#endif
-#endif
+/* 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)
+{
+ char const *p;
+ for (p = str; *p; p++)
+ if (*p < '0' || *p > '9')
+ return -1;
+
+ *out = atoi (optarg);
+ return 0;
+}
-#ifndef MULT
-#define MULT 5
-#endif
/* Hairy buffering mechanism for grep. The intent is to keep
all reads aligned on a page boundary and multiples of the
page size. */
+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 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. */
-#if defined(HAVE_WORKING_MMAP)
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
-static int bufmapped; /* True for ordinary files. */
-static struct stat bufstat; /* From fstat(). */
-static off_t bufoffset; /* What read() normally remembers. */
+#if defined(HAVE_MMAP)
+static int bufmapped; /* True if buffer is memory-mapped. */
+static off_t initial_bufoffset; /* Initial value of bufoffset. */
#endif
-/* Reset the buffer for a new file. Initialize
- on the first time through. */
-void
-reset(fd)
- int fd;
+/* Return VAL aligned to the next multiple of ALIGNMENT. VAL can be
+ an integer or a pointer. Both args must be free of side effects. */
+#define ALIGN_TO(val, alignment) \
+ ((size_t) (val) % (alignment) == 0 \
+ ? (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)
{
- static int initialized;
+ 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;
+}
- if (!initialized)
+/* 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
{
- initialized = 1;
+ size_t ubufsalloc;
+ pagesize = getpagesize ();
+ if (pagesize == 0)
+ abort ();
#ifndef BUFSALLOC
- bufsalloc = MAX(8192, getpagesize());
+ ubufsalloc = MAX (8192, pagesize);
#else
- bufsalloc = BUFSALLOC;
+ ubufsalloc = BUFSALLOC;
#endif
- bufalloc = MULT * bufsalloc;
+ 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... */
- buffer = valloc(bufalloc + 1);
- if (!buffer)
- fatal("memory exhausted", 0);
- bufbeg = buffer;
- buflim = buffer;
+ if (bufsalloc < ubufsalloc
+ || bufalloc / PREFERRED_SAVE_FACTOR != bufsalloc
+ || bufalloc + 1 < bufalloc
+ || ! (buffer = page_alloc (bufalloc + 1, &ubuffer)))
+ fatal (_("memory exhausted"), 0);
}
+
+ buflim = buffer;
bufdesc = fd;
-#if defined(HAVE_WORKING_MMAP)
- if (fstat(fd, &bufstat) < 0 || !S_ISREG(bufstat.st_mode))
- bufmapped = 0;
- else
+
+ if (fstat (fd, &stats->stat) != 0)
+ {
+ error ("fstat", errno);
+ return 0;
+ }
+ if (directories == SKIP_DIRECTORIES && S_ISDIR (stats->stat.st_mode))
+ return 0;
+ if (S_ISREG (stats->stat.st_mode))
{
- bufmapped = 1;
- bufoffset = lseek(fd, 0, 1);
+ if (file)
+ bufoffset = 0;
+ else
+ {
+ bufoffset = lseek (fd, 0, SEEK_CUR);
+ if (bufoffset < 0)
+ {
+ error ("lseek", errno);
+ return 0;
+ }
+ }
+#ifdef HAVE_MMAP
+ initial_bufoffset = bufoffset;
+ bufmapped = mmap_option && bufoffset % pagesize == 0;
+#endif
}
+ else
+ {
+#ifdef HAVE_MMAP
+ bufmapped = 0;
#endif
+ }
+ return 1;
}
/* Read new stuff into the buffer, saving the specified
amount of old stuff. When we're done, 'bufbeg' points
to the beginning of the buffer contents, and 'buflim'
- points just after the end. Return count of new stuff. */
+ points just after the end. Return zero if there's an error. */
static int
-fillbuf(save)
- size_t save;
+fillbuf (size_t save, struct stats *stats)
{
- char *nbuffer, *dp, *sp;
- int cc;
-#if defined(HAVE_WORKING_MMAP)
- caddr_t maddr;
-#endif
- static int pagesize;
+ size_t fillsize = 0;
+ int cc = 1;
+ size_t readsize;
- if (pagesize == 0 && (pagesize = getpagesize()) == 0)
- abort();
+ /* Offset from start of unaligned buffer to start of old stuff
+ that we want to save. */
+ size_t saved_offset = buflim - ubuffer - save;
- /* If the current line won't easily fit in the existing buffer
- allocate a MULT larger one. This can result in running out
- of memory on sparse files or those containing longs runs of
- <nul>'s or other non-<nl> characters */
-
- if (save > bufsalloc)
- {
- while (save > bufsalloc)
- bufsalloc *= 2;
- bufalloc = MULT * bufsalloc;
- nbuffer = valloc(bufalloc + 1);
- if (!nbuffer)
- fatal("memory exhausted", 0);
- }
- else
+ if (bufsalloc < save)
{
- nbuffer = buffer;
- buffer = NULL;
- }
+ size_t aligned_save = ALIGN_TO (save, pagesize);
+ size_t maxalloc = (size_t) -1;
+ size_t newalloc;
- sp = buflim - save;
- dp = nbuffer + bufsalloc - save;
- bufbeg = dp;
- while (save--)
- *dp++ = *sp++;
+ 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;
+ }
- /* We may have allocated a new, larger buffer. Since
- there is no portable vfree(), we may just have to forget
- about the old one. Sorry. */
- if (buffer != NULL)
- vfree(buffer);
- buffer = nbuffer;
+ /* 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;
+ }
-#if defined(HAVE_WORKING_MMAP)
- if (bufmapped && bufoffset % pagesize == 0
- && bufstat.st_size - bufoffset >= bufalloc - bufsalloc)
- {
- maddr = buffer + bufsalloc;
- maddr = mmap(maddr, bufalloc - bufsalloc, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_FIXED, bufdesc, bufoffset);
- if (maddr == (caddr_t) -1)
+ /* Grow the buffer size to be PREFERRED_SAVE_FACTOR times
+ bufsalloc.... */
+ newalloc = PREFERRED_SAVE_FACTOR * bufsalloc;
+ if (maxalloc < newalloc)
{
- fprintf(stderr, "%s: warning: %s: %s\n", filename,
- strerror(errno));
- goto tryread;
+ /* ... 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;
}
-#if 0
- /* You might thing this (or MADV_WILLNEED) would help,
- but it doesn't, at least not on a Sun running 4.1.
- In fact, it actually slows us down about 30%! */
- madvise(maddr, bufalloc - bufsalloc, MADV_SEQUENTIAL);
-#endif
- cc = bufalloc - bufsalloc;
- bufoffset += cc;
+
+ /* 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);
}
- else
+
+ bufbeg = buffer + bufsalloc - save;
+ memmove (bufbeg, ubuffer + saved_offset, save);
+ readsize = bufalloc - bufsalloc;
+
+#if defined(HAVE_MMAP)
+ if (bufmapped)
{
- tryread:
- /* We come here when we're not going to use mmap() any more.
- Note that we need to synchronize the file offset the
- first time through. */
- if (bufmapped)
+ size_t mmapsize = readsize;
+
+ /* Don't mmap past the end of the file; some hosts don't allow this.
+ Use `read' on the last page. */
+ if (stats->stat.st_size - bufoffset < mmapsize)
{
+ mmapsize = stats->stat.st_size - bufoffset;
+ mmapsize -= mmapsize % pagesize;
+ }
+
+ if (mmapsize
+ && (mmap ((caddr_t) (buffer + bufsalloc), mmapsize,
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED,
+ bufdesc, bufoffset)
+ != (caddr_t) -1))
+ {
+ /* Do not bother to use madvise with MADV_SEQUENTIAL or
+ MADV_WILLNEED on the mmapped memory. One might think it
+ would help, but it slows us down about 30% on SunOS 4.1. */
+ fillsize = mmapsize;
+ }
+ else
+ {
+ /* Stop using mmap on this file. Synchronize the file
+ offset. Do not warn about mmap failures. On some hosts
+ (e.g. Solaris 2.5) mmap can fail merely because some
+ other process has an advisory read lock on the file.
+ There's no point alarming the user about this misfeature. */
bufmapped = 0;
- lseek(bufdesc, bufoffset, 0);
+ if (bufoffset != initial_bufoffset
+ && lseek (bufdesc, bufoffset, SEEK_SET) < 0)
+ {
+ error ("lseek", errno);
+ cc = 0;
+ }
}
- cc = read(bufdesc, buffer + bufsalloc, bufalloc - bufsalloc);
}
-#else
- cc = read(bufdesc, buffer + bufsalloc, bufalloc - bufsalloc);
+#endif /*HAVE_MMAP*/
+
+ if (! fillsize)
+ {
+ ssize_t bytesread;
+ while ((bytesread = read (bufdesc, buffer + bufsalloc, readsize)) < 0
+ && errno == EINTR)
+ continue;
+ if (bytesread < 0)
+ cc = 0;
+ else
+ fillsize = bytesread;
+ }
+
+ bufoffset += fillsize;
+#if O_BINARY
+ if (fillsize)
+ fillsize = undossify_input (buffer + bufsalloc, fillsize);
#endif
- if (cc > 0)
- buflim = buffer + bufsalloc + cc;
- else
- buflim = buffer + bufsalloc;
+ buflim = buffer + bufsalloc + 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. */
+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. */
static int out_file; /* Print filenames. */
@@ -350,54 +482,79 @@ static int out_line; /* Print line numbers. */
static int out_byte; /* Print byte offsets. */
static int out_before; /* Lines of leading context. */
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. */
/* Internal variables to keep track of byte count, context, etc. */
-static size_t totalcc; /* Total character count before bufbeg. */
+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;
NULL if no character has been output
or if it's conceptually before bufbeg. */
-static size_t totalnl; /* Total newline count before lastnl. */
+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
+# include "dosbuf.c"
+#endif
static void
-nlscan(lim)
- char *lim;
+nlscan (char *lim)
{
char *beg;
+ for (beg = lastnl; (beg = memchr (beg, eolbyte, lim - beg)); beg++)
+ totalnl++;
+ lastnl = lim;
+}
+
+static void
+print_offset_sep (off_t pos, int sep)
+{
+ /* Do not rely on printf to print pos, since off_t may be longer than long,
+ and long long is not portable. */
+
+ char buf[sizeof pos * CHAR_BIT];
+ char *p = buf + sizeof buf - 1;
+ *p = sep;
+
+ do
+ *--p = '0' + pos % 10;
+ while ((pos /= 10) != 0);
- for (beg = lastnl; beg < lim; ++beg)
- if (*beg == '\n')
- ++totalnl;
- lastnl = beg;
+ fwrite (p, 1, buf + sizeof buf - p, stdout);
}
static void
-prline(beg, lim, sep)
- char *beg;
- char *lim;
- char sep;
+prline (char *beg, char *lim, int sep)
{
if (out_file)
- printf("%s%c", filename, sep);
+ printf ("%s%c", filename, sep & filename_mask);
if (out_line)
{
- nlscan(beg);
- printf("%d%c", ++totalnl, sep);
+ nlscan (beg);
+ print_offset_sep (++totalnl, sep);
lastnl = lim;
}
if (out_byte)
- printf("%lu%c", totalcc + (beg - bufbeg), sep);
- fwrite(beg, 1, lim - beg, stdout);
- if (ferror(stdout))
- error("writing output", errno);
+ {
+ off_t pos = totalcc + (beg - bufbeg);
+#if O_BINARY
+ pos = dossified_pos (pos);
+#endif
+ print_offset_sep (pos, sep);
+ }
+ fwrite (beg, 1, lim - beg, stdout);
+ if (ferror (stdout))
+ error (_("writing output"), errno);
lastout = lim;
}
/* Print pending lines of trailing context prior to LIM. */
static void
-prpending(lim)
- char *lim;
+prpending (char *lim)
{
char *nl;
@@ -406,28 +563,26 @@ prpending(lim)
while (pending > 0 && lastout < lim)
{
--pending;
- if ((nl = memchr(lastout, '\n', lim - lastout)) != 0)
+ if ((nl = memchr (lastout, eolbyte, lim - lastout)) != 0)
++nl;
else
nl = lim;
- prline(lastout, nl, '-');
+ prline (lastout, nl, '-');
}
}
/* 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. */
static void
-prtext(beg, lim, nlinesp)
- char *beg;
- char *lim;
- int *nlinesp;
+prtext (char *beg, char *lim, int *nlinesp)
{
static int used; /* avoid printing "--" before any output */
char *bp, *p, *nl;
+ char eol = eolbyte;
int i, n;
if (!out_quiet && pending > 0)
- prpending(beg);
+ prpending (beg);
p = beg;
@@ -440,17 +595,17 @@ prtext(beg, lim, nlinesp)
if (p > bp)
do
--p;
- while (p > bp && p[-1] != '\n');
+ while (p > bp && p[-1] != eol);
/* We only print the "--" separator if our output is
discontiguous from the last output in the file. */
if ((out_before || out_after) && used && p != lastout)
- puts("--");
+ puts ("--");
while (p < beg)
{
- nl = memchr(p, '\n', beg - p);
- prline(p, nl + 1, '-');
+ nl = memchr (p, eol, beg - p);
+ prline (p, nl + 1, '-');
p = nl + 1;
}
}
@@ -460,21 +615,21 @@ prtext(beg, lim, nlinesp)
/* Caller wants a line count. */
for (n = 0; p < lim; ++n)
{
- if ((nl = memchr(p, '\n', lim - p)) != 0)
+ if ((nl = memchr (p, eol, lim - p)) != 0)
++nl;
else
nl = lim;
if (!out_quiet)
- prline(p, nl, ':');
+ prline (p, nl, ':');
p = nl;
}
*nlinesp = n;
}
else
if (!out_quiet)
- prline(beg, lim, ':');
+ prline (beg, lim, ':');
- pending = out_after;
+ pending = out_quiet ? 0 : out_after;
used = 1;
}
@@ -482,51 +637,66 @@ prtext(beg, lim, nlinesp)
between matching lines if OUT_INVERT is true). Return a count of
lines printed. */
static int
-grepbuf(beg, lim)
- char *beg;
- char *lim;
+grepbuf (char *beg, char *lim)
{
int nlines, n;
register char *p, *b;
char *endp;
+ char eol = eolbyte;
nlines = 0;
p = beg;
while ((b = (*execute)(p, lim - p, &endp)) != 0)
{
/* Avoid matching the empty line at the end of the buffer. */
- if (b == lim && ((b > beg && b[-1] == '\n') || b == beg))
+ if (b == lim && ((b > beg && b[-1] == eol) || b == beg))
break;
if (!out_invert)
{
- prtext(b, endp, (int *) 0);
+ prtext (b, endp, (int *) 0);
nlines += 1;
+ if (done_on_match)
+ return nlines;
}
else if (p < b)
{
- prtext(p, b, &n);
+ prtext (p, b, &n);
nlines += n;
}
p = endp;
}
if (out_invert && p < lim)
{
- prtext(p, lim, &n);
+ prtext (p, lim, &n);
nlines += n;
}
return nlines;
}
-/* Search a given file. Return a count of lines printed. */
+/* Search a given file. Normally, return a count of lines printed;
+ but if the file is a directory and we search it recursively, then
+ return -2 if there was a match, and -1 otherwise. */
static int
-grep(fd)
- int fd;
+grep (int fd, char const *file, struct stats *stats)
{
int nlines, i;
+ int not_text;
size_t residue, save;
char *beg, *lim;
+ char eol = eolbyte;
+
+ if (!reset (fd, file, stats))
+ return 0;
- reset(fd);
+ if (file && directories == RECURSE_DIRECTORIES
+ && S_ISDIR (stats->stat.st_mode))
+ {
+ /* 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);
+ return grepdir (file, stats) - 2;
+ }
totalcc = 0;
lastout = 0;
@@ -537,27 +707,39 @@ grep(fd)
residue = 0;
save = 0;
+ if (! fillbuf (save, stats))
+ {
+ if (! (is_EISDIR (errno, file) && suppress_errors))
+ 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 (;;)
{
- if (fillbuf(save) < 0)
- {
- error(filename, errno);
- return nlines;
- }
lastnl = bufbeg;
if (lastout)
lastout = bufbeg;
if (buflim - bufbeg == save)
break;
beg = bufbeg + save - residue;
- for (lim = buflim; lim > beg && lim[-1] != '\n'; --lim)
+ for (lim = buflim; lim > beg && lim[-1] != eol; --lim)
;
residue = buflim - lim;
if (beg < lim)
{
- nlines += grepbuf(beg, lim);
+ nlines += grepbuf (beg, lim);
if (pending)
- prpending(lim);
+ prpending (lim);
+ if (nlines && done_on_match && !out_invert)
+ goto finish_grep;
}
i = 0;
beg = lim;
@@ -566,82 +748,440 @@ grep(fd)
++i;
do
--beg;
- while (beg > bufbeg && beg[-1] != '\n');
+ while (beg > bufbeg && beg[-1] != eol);
}
if (beg != lastout)
lastout = 0;
save = residue + lim - beg;
totalcc += buflim - bufbeg - save;
if (out_line)
- nlscan(beg);
+ nlscan (beg);
+ if (! fillbuf (save, stats))
+ {
+ if (! (is_EISDIR (errno, file) && suppress_errors))
+ error (filename, errno);
+ goto finish_grep;
+ }
}
if (residue)
{
- nlines += grepbuf(bufbeg + save - residue, buflim);
+ *buflim++ = eol;
+ nlines += grepbuf (bufbeg + save - residue, buflim);
if (pending)
- prpending(buflim);
+ 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 char version[] = "GNU grep version 2.0";
+static int
+grepfile (char const *file, struct stats *stats)
+{
+ int desc;
+ int count;
+ int status;
+
+ if (! file)
+ {
+ desc = 0;
+ filename = _("(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 (file, errno);
+ return 1;
+ }
+
+ return grepdir (file, stats);
+ }
+
+ if (!suppress_errors)
+ {
+ if (directories == SKIP_DIRECTORIES)
+ switch (e)
+ {
+#ifdef 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))
+ return 1;
+ break;
+ }
+
+ error (file, e);
+ }
+
+ return 1;
+ }
+
+ filename = file;
+ }
+
+#if O_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))
+ 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)
+ while (close (desc) != 0)
+ if (errno != EINTR)
+ {
+ error (file, errno);
+ break;
+ }
+ }
+
+ return status;
+}
+
+static int
+grepdir (char const *dir, struct stats *stats)
+{
+ int status = 1;
+ struct stats *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,
+ _("recursive directory loop"));
+ return 1;
+ }
+
+ name_space = savedir (dir, (unsigned) stats->stat.st_size);
+
+ if (! name_space)
+ {
+ if (errno)
+ {
+ if (!suppress_errors)
+ error (dir, errno);
+ }
+ else
+ fatal (_("Memory exhausted"), 0);
+ }
+ else
+ {
+ size_t dirlen = strlen (dir);
+ int needs_slash = ! (dirlen == FILESYSTEM_PREFIX_LEN (dir)
+ || IS_SLASH (dir[dirlen - 1]));
+ char *file = NULL;
+ char *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;
+}
-#define USAGE \
- "usage: %s [-[[AB] ]<num>] [-[CEFGVchilnqsvwx]] [-[ef]] <expr> [<files...>]\n"
+static void
+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);
+ }
+ else
+ {
+ printf (_("Usage: %s [OPTION]... PATTERN [FILE] ...\n"), prog);
+ 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);
+ 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"));
+ 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\
+ -b, --byte-offset print the byte offset with output lines\n\
+ -n, --line-number print line number with output lines\n\
+ -H, --with-filename print the filename for each match\n\
+ -h, --no-filename suppress the prefixing filename on output\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\
+ -r, --recursive equivalent to --directories=recurse.\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 (default 2) lines of output context\n\
+ unless overridden by -A or -B\n\
+ -NUM same as --context=NUM\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);
+}
+/* Set the matcher to M, reporting any conflicts. */
static void
-usage()
+setmatcher (char const *m)
{
- fprintf(stderr, USAGE, prog);
- exit(2);
+ if (matcher && strcmp (matcher, m) != 0)
+ fatal (_("conflicting matchers specified"), 0);
+ matcher = m;
}
/* Go through the matchers vector and look for the specified matcher.
If we find it, install it in compile and execute, and return 1. */
-int
-setmatcher(name)
- char *name;
+static int
+install_matcher (char const *name)
{
int i;
+#ifdef HAVE_SETRLIMIT
+ struct rlimit rlim;
+#endif
for (i = 0; matchers[i].name; ++i)
- if (strcmp(name, matchers[i].name) == 0)
+ if (strcmp (name, matchers[i].name) == 0)
{
compile = matchers[i].compile;
execute = matchers[i].execute;
+#if 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
+ more than 3MB-large stack.
+
+ The test for HAVE_SETRLIMIT should go into `configure'. */
+ if (!getrlimit (RLIMIT_STACK, &rlim))
+ {
+ long newlim;
+ extern long int re_max_failures; /* from regex.c */
+
+ /* Approximate the amount regex.c needs, plus some more. */
+ 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;
-}
+}
+
+/* Find the white-space-separated options specified by OPTIONS, and
+ using BUF to store copies of these options, set ARGV[0], ARGV[1],
+ etc. to the option copies. Return the number N of options found.
+ Do not set ARGV[N] to NULL. If ARGV is NULL, do not store ARGV[0]
+ etc. Backslash can be used to escape whitespace (and backslashes). */
+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';
+ }
+}
+
+/* Prepend the whitespace-separated options in OPTIONS to the argument
+ vector of a main program with argument count *PARGC and argument
+ vector *PARGV. */
+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;
+ }
+}
int
-main(argc, argv)
- int argc;
- char *argv[];
+main (int argc, char **argv)
{
char *keys;
size_t keycc, oldcc, keyalloc;
- int keyfound, count_matches, no_filenames, list_files, suppress_errors;
- int opt, cc, desc, count, status;
+ 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;
+ if (prog && strrchr (prog, '/'))
+ prog = strrchr (prog, '/') + 1;
+
+#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)
+ {
+ char *p = prog;
+ char *bslash = strrchr (argv[0], '\\');
+
+ if (bslash && bslash >= prog) /* for mixed forward/backslash case */
+ prog = bslash + 1;
+ else if (prog == argv[0]
+ && argv[0][0] && argv[0][1] == ':') /* "c:progname" */
+ prog = argv[0] + 2;
+
+ /* Collapse the letter-case, so `strcmp' could be used hence. */
+ for ( ; *p; p++)
+ if (*p >= 'A' && *p <= 'Z')
+ *p += 'a' - 'A';
+
+ /* Remove the .exe extension, if any. */
+ if ((p = strrchr (prog, '.')) && strcmp (p, ".exe") == 0)
+ *p = '\0';
+ }
+#endif
keys = NULL;
keycc = 0;
- keyfound = 0;
- count_matches = 0;
- no_filenames = 0;
- list_files = 0;
- suppress_errors = 0;
- matcher = NULL;
-
- while ((opt = getopt(argc, argv, "0123456789A:B:CEFGVX:bce:f:hiLlnoqsvwxy"))
- != EOF)
+ with_filenames = 0;
+ eolbyte = '\n';
+ filename_mask = ~0;
+
+ /* 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;
+
+
+/* Internationalization. */
+#if HAVE_SETLOCALE
+ setlocale (LC_ALL, "");
+#endif
+#if ENABLE_NLS
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+#endif
+
+ prepend_default_options (getenv ("GREP_OPTIONS"), &argc, &argv);
+
+ while ((opt = getopt_long (argc, argv, short_options, long_options, NULL))
+ != -1)
switch (opt)
{
case '0':
@@ -654,44 +1194,67 @@ main(argc, argv)
case '7':
case '8':
case '9':
- out_before = 10 * out_before + opt - '0';
- out_after = 10 * out_after + opt - '0';
+ digit_args_val = 10 * digit_args_val + opt - '0';
+ default_context = digit_args_val;
break;
case 'A':
- out_after = atoi(optarg);
- if (out_after < 0)
- usage();
+ if (optarg)
+ {
+ if (ck_atoi (optarg, &out_after))
+ fatal (_("invalid context length argument"), 0);
+ }
break;
case 'B':
- out_before = atoi(optarg);
- if (out_before < 0)
- usage();
+ if (optarg)
+ {
+ if (ck_atoi (optarg, &out_before))
+ fatal (_("invalid context length argument"), 0);
+ }
break;
case 'C':
- out_before = out_after = 2;
+ /* 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);
+ }
+ else
+ default_context = 2;
break;
case 'E':
- if (matcher && strcmp(matcher, "egrep") != 0)
- fatal("you may specify only one of -E, -F, or -G", 0);
- matcher = "posix-egrep";
+ setmatcher ("egrep");
break;
case 'F':
- if (matcher && strcmp(matcher, "fgrep") != 0)
- fatal("you may specify only one of -E, -F, or -G", 0);;
- matcher = "fgrep";
+ setmatcher ("fgrep");
break;
case 'G':
- if (matcher && strcmp(matcher, "grep") != 0)
- fatal("you may specify only one of -E, -F, or -G", 0);
- matcher = "grep";
+ setmatcher ("grep");
+ break;
+ case 'H':
+ with_filenames = 1;
+ break;
+ case 'I':
+ binary_files = WITHOUT_MATCH_BINARY_FILES;
+ break;
+ case 'U':
+#if O_BINARY
+ dos_use_file_type = DOS_BINARY;
+#endif
+ break;
+ case 'u':
+#if O_BINARY
+ dos_report_unix_offset = 1;
+#endif
break;
case 'V':
- fprintf(stderr, "%s\n", version);
+ show_version = 1;
break;
case 'X':
- if (matcher)
- fatal("matcher already specified", 0);
- matcher = optarg;
+ setmatcher (optarg);
+ break;
+ case 'a':
+ binary_files = TEXT_BINARY_FILES;
break;
case 'b':
out_byte = 1;
@@ -700,38 +1263,43 @@ main(argc, argv)
out_quiet = 1;
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
+ fatal (_("unknown directories method"), 0);
+ break;
case 'e':
- cc = strlen(optarg);
- keys = xrealloc(keys, keycc + cc + 1);
- if (keyfound)
- keys[keycc++] = '\n';
- strcpy(&keys[keycc], optarg);
+ cc = strlen (optarg);
+ keys = xrealloc (keys, keycc + cc + 1);
+ strcpy (&keys[keycc], optarg);
keycc += cc;
- keyfound = 1;
+ keys[keycc++] = '\n';
break;
case 'f':
- fp = strcmp(optarg, "-") != 0 ? fopen(optarg, "r") : stdin;
+ fp = strcmp (optarg, "-") != 0 ? fopen (optarg, "r") : stdin;
if (!fp)
- fatal(optarg, errno);
- for (keyalloc = 1; keyalloc <= keycc; keyalloc *= 2)
+ fatal (optarg, errno);
+ for (keyalloc = 1; keyalloc <= keycc + 1; keyalloc *= 2)
;
- keys = xrealloc(keys, keyalloc);
+ keys = xrealloc (keys, keyalloc);
oldcc = keycc;
- if (keyfound)
- keys[keycc++] = '\n';
- while (!feof(fp)
- && (cc = fread(keys + keycc, 1, keyalloc - keycc, fp)) > 0)
+ while (!feof (fp)
+ && (cc = fread (keys + keycc, 1, keyalloc - 1 - keycc, fp)) > 0)
{
keycc += cc;
- if (keycc == keyalloc)
- keys = xrealloc(keys, keyalloc *= 2);
+ if (keycc == keyalloc - 1)
+ keys = xrealloc (keys, keyalloc *= 2);
}
if (fp != stdin)
fclose(fp);
- /* Nuke the final newline to avoid matching a null string. */
- if (keycc - oldcc > 0 && keys[keycc - 1] == '\n')
- --keycc;
- keyfound = 1;
+ /* Append final newline if file ended in non-newline. */
+ if (oldcc != keycc && keys[keycc - 1] != '\n')
+ keys[keycc++] = '\n';
break;
case 'h':
no_filenames = 1;
@@ -745,20 +1313,23 @@ main(argc, argv)
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 'n':
out_line = 1;
break;
- case 'o':
- out_file = 1;
- break;
case 'q':
+ done_on_match = 1;
out_quiet = 1;
break;
+ case 'r':
+ directories = RECURSE_DIRECTORIES;
+ break;
case 's':
suppress_errors = 1;
break;
@@ -771,80 +1342,104 @@ main(argc, argv)
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
+ fatal (_("unknown binary-files type"), 0);
+ break;
+ case 0:
+ /* long options */
+ break;
default:
- usage();
+ usage (2);
break;
}
- if (!keyfound)
+ if (out_after < 0)
+ out_after = default_context;
+ if (out_before < 0)
+ out_before = default_context;
+
+ if (! matcher)
+ matcher = prog;
+
+ if (show_version)
+ {
+ printf (_("%s (GNU grep) %s\n"), matcher, VERSION);
+ printf ("\n");
+ printf (_("\
+Copyright 1988, 1992-1999, 2000 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)
+ /* No keys were specified (e.g. -f /dev/null). Match nothing. */
+ out_invert ^= 1;
+ else
+ /* Strip trailing newline. */
+ --keycc;
+ }
+ else
if (optind < argc)
{
keys = argv[optind++];
- keycc = strlen(keys);
+ keycc = strlen (keys);
}
else
- usage();
-
- if (!matcher)
- matcher = prog;
+ usage (2);
- if (!setmatcher(matcher) && !setmatcher("default"))
- abort();
+ if (!install_matcher (matcher) && !install_matcher ("default"))
+ abort ();
(*compile)(keys, keycc);
- if (argc - optind > 1 && !no_filenames)
+ if ((argc - optind > 1 && !no_filenames) || with_filenames)
out_file = 1;
- status = 1;
+#if O_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 (optind < argc)
- while (optind < argc)
- {
- desc = strcmp(argv[optind], "-") ? open(argv[optind], O_RDONLY) : 0;
- if (desc < 0)
- {
- if (!suppress_errors)
- error(argv[optind], errno);
- }
- else
- {
- filename = desc == 0 ? "(standard input)" : argv[optind];
- count = grep(desc);
- if (count_matches)
- {
- if (out_file)
- printf("%s:", filename);
- printf("%d\n", count);
- }
- if (count)
- {
- status = 0;
- if (list_files == 1)
- printf("%s\n", filename);
- }
- else if (list_files == -1)
- printf("%s\n", filename);
- }
- if (desc != 0)
- close(desc);
- ++optind;
- }
- else
{
- filename = "(standard input)";
- count = grep(0);
- if (count_matches)
- printf("%d\n", count);
- if (count)
+ status = 1;
+ do
{
- status = 0;
- if (list_files == 1)
- printf("(standard input)\n");
+ char *file = argv[optind];
+ status &= grepfile (strcmp (file, "-") == 0 ? (char *) NULL : file,
+ &stats_base);
}
- else if (list_files == -1)
- printf("(standard input)\n");
+ while ( ++optind < argc);
}
+ else
+ status = grepfile ((char *) NULL, &stats_base);
+
+ if (fclose (stdout) == EOF)
+ error (_("writing output"), errno);
- exit(errseen ? 2 : status);
+ exit (errseen ? 2 : status);
}