From: John Marino Date: Fri, 10 Oct 2014 22:51:31 +0000 (+0200) Subject: Merge branch 'vendor/GREP' X-Git-Tag: v4.1.0~66 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/5be44d7264347781e3ed476d25d623ecb3181508 Merge branch 'vendor/GREP' Conflicts: contrib/grep/src/main.c --- 5be44d7264347781e3ed476d25d623ecb3181508 diff --cc contrib/grep/src/grep.c index 1b2198fd35,7c0f8a86cb..d424f6b6df --- a/contrib/grep/src/grep.c +++ b/contrib/grep/src/grep.c @@@ -1,5 -1,1633 +1,1634 @@@ + /* grep.c - main driver file for grep. + Copyright (C) 1992, 1997-2002, 2004-2014 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 + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + 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., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + + /* Written July 1992 by Mike Haertel. */ + #include + #include + #include + #include + #include + #include + #include + #include + #include "system.h" + + #include "argmatch.h" + #include "c-ctype.h" + #include "closeout.h" + #include "colorize.h" + #include "error.h" + #include "exclude.h" + #include "exitfail.h" + #include "fcntl-safer.h" + #include "fts_.h" + #include "getopt.h" + #include "grep.h" + #include "intprops.h" + #include "progname.h" + #include "propername.h" + #include "quote.h" + #include "safe-read.h" #include "search.h" + #include "version-etc.h" + #include "xalloc.h" + #include "xstrtol.h" + + #define SEP_CHAR_SELECTED ':' + #define SEP_CHAR_REJECTED '-' + #define SEP_STR_GROUP "--" + + #define AUTHORS \ + proper_name ("Mike Haertel"), \ - _("others, see ") ++ _("others, see\n") + + /* When stdout is connected to a regular file, save its stat + information here, so that we can automatically skip it, thus + avoiding a potential (racy) infinite loop. */ + static struct stat out_stat; + + /* 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, suppress diagnostics for nonexistent or unreadable files. */ + static int suppress_errors; + + /* If nonzero, use color markers. */ + static int color_option; + + /* If nonzero, show only the part of a line matching the expression. */ + static int only_matching; + + /* If nonzero, make sure first content char in a line is on a tab stop. */ + static int align_tabs; + + /* The group separator used when context is requested. */ + static const char *group_separator = SEP_STR_GROUP; + + /* The context and logic for choosing default --color screen attributes + (foreground and background colors, etc.) are the following. + -- There are eight basic colors available, each with its own + nominal luminosity to the human eye and foreground/background + codes (black [0 %, 30/40], blue [11 %, 34/44], red [30 %, 31/41], + magenta [41 %, 35/45], green [59 %, 32/42], cyan [70 %, 36/46], + yellow [89 %, 33/43], and white [100 %, 37/47]). + -- Sometimes, white as a background is actually implemented using + a shade of light gray, so that a foreground white can be visible + on top of it (but most often not). + -- Sometimes, black as a foreground is actually implemented using + a shade of dark gray, so that it can be visible on top of a + background black (but most often not). + -- Sometimes, more colors are available, as extensions. + -- Other attributes can be selected/deselected (bold [1/22], + underline [4/24], standout/inverse [7/27], blink [5/25], and + invisible/hidden [8/28]). They are sometimes implemented by + using colors instead of what their names imply; e.g., bold is + often achieved by using brighter colors. In practice, only bold + is really available to us, underline sometimes being mapped by + the terminal to some strange color choice, and standout best + being left for use by downstream programs such as less(1). + -- We cannot assume that any of the extensions or special features + are available for the purpose of choosing defaults for everyone. + -- The most prevalent default terminal backgrounds are pure black + and pure white, and are not necessarily the same shades of + those as if they were selected explicitly with SGR sequences. + Some terminals use dark or light pictures as default background, + but those are covered over by an explicit selection of background + color with an SGR sequence; their users will appreciate their + background pictures not be covered like this, if possible. + -- Some uses of colors attributes is to make some output items + more understated (e.g., context lines); this cannot be achieved + by changing the background color. + -- For these reasons, the grep color defaults should strive not + to change the background color from its default, unless it's + for a short item that should be highlighted, not understated. + -- The grep foreground color defaults (without an explicitly set + background) should provide enough contrast to be readable on any + terminal with either a black (dark) or white (light) background. + This only leaves red, magenta, green, and cyan (and their bold + counterparts) and possibly bold blue. */ + /* The color strings used for matched text. + The user can overwrite them using the deprecated + environment variable GREP_COLOR or the new GREP_COLORS. */ + static const char *selected_match_color = "01;31"; /* bold red */ + static const char *context_match_color = "01;31"; /* bold red */ + + /* Other colors. Defaults look damn good. */ + static const char *filename_color = "35"; /* magenta */ + static const char *line_num_color = "32"; /* green */ + static const char *byte_num_color = "32"; /* green */ + static const char *sep_color = "36"; /* cyan */ + static const char *selected_line_color = ""; /* default color pair */ + static const char *context_line_color = ""; /* default color pair */ + + /* Select Graphic Rendition (SGR, "\33[...m") strings. */ + /* Also Erase in Line (EL) to Right ("\33[K") by default. */ + /* Why have EL to Right after SGR? + -- The behavior of line-wrapping when at the bottom of the + terminal screen and at the end of the current line is often + such that a new line is introduced, entirely cleared with + the current background color which may be different from the + default one (see the boolean back_color_erase terminfo(5) + capability), thus scrolling the display by one line. + The end of this new line will stay in this background color + even after reverting to the default background color with + "\33[m', unless it is explicitly cleared again with "\33[K" + (which is the behavior the user would instinctively expect + from the whole thing). There may be some unavoidable + background-color flicker at the end of this new line because + of this (when timing with the monitor's redraw is just right). + -- The behavior of HT (tab, "\t") is usually the same as that of + Cursor Forward Tabulation (CHT) with a default parameter + of 1 ("\33[I"), i.e., it performs pure movement to the next + tab stop, without any clearing of either content or screen + attributes (including background color); try + printf 'asdfqwerzxcv\rASDF\tZXCV\n' + in a bash(1) shell to demonstrate this. This is not what the + user would instinctively expect of HT (but is ok for CHT). + The instinctive behavior would include clearing the terminal + cells that are skipped over by HT with blank cells in the + current screen attributes, including background color; + the boolean dest_tabs_magic_smso terminfo(5) capability + indicates this saner behavior for HT, but only some rare + terminals have it (although it also indicates a special + glitch with standout mode in the Teleray terminal for which + it was initially introduced). The remedy is to add "\33K" + after each SGR sequence, be it START (to fix the behavior + of any HT after that before another SGR) or END (to fix the + behavior of an HT in default background color that would + follow a line-wrapping at the bottom of the screen in another + background color, and to complement doing it after START). + Piping grep's output through a pager such as less(1) avoids + any HT problems since the pager performs tab expansion. + + Generic disadvantages of this remedy are: + -- Some very rare terminals might support SGR but not EL (nobody + will use "grep --color" on a terminal that does not support + SGR in the first place). + -- Having these extra control sequences might somewhat complicate + the task of any program trying to parse "grep --color" + output in order to extract structuring information from it. + A specific disadvantage to doing it after SGR START is: + -- Even more possible background color flicker (when timing + with the monitor's redraw is just right), even when not at the + bottom of the screen. + There are no additional disadvantages specific to doing it after + SGR END. + + It would be impractical for GNU grep to become a full-fledged + terminal program linked against ncurses or the like, so it will + not detect terminfo(5) capabilities. */ + static const char *sgr_start = "\33[%sm\33[K"; + static const char *sgr_end = "\33[m\33[K"; + + /* SGR utility functions. */ + static void + pr_sgr_start (char const *s) + { + if (*s) + print_start_colorize (sgr_start, s); + } + static void + pr_sgr_end (char const *s) + { + if (*s) + print_end_colorize (sgr_end); + } + static void + pr_sgr_start_if (char const *s) + { + if (color_option) + pr_sgr_start (s); + } + static void + pr_sgr_end_if (char const *s) + { + if (color_option) + pr_sgr_end (s); + } + + struct color_cap + { + const char *name; + const char **var; + void (*fct) (void); + }; + + static void + color_cap_mt_fct (void) + { + /* Our caller just set selected_match_color. */ + context_match_color = selected_match_color; + } + + static void + color_cap_rv_fct (void) + { + /* By this point, it was 1 (or already -1). */ + color_option = -1; /* That's still != 0. */ + } + + static void + color_cap_ne_fct (void) + { + sgr_start = "\33[%sm"; + sgr_end = "\33[m"; + } + + /* For GREP_COLORS. */ + static const struct color_cap color_dict[] = + { + { "mt", &selected_match_color, color_cap_mt_fct }, /* both ms/mc */ + { "ms", &selected_match_color, NULL }, /* selected matched text */ + { "mc", &context_match_color, NULL }, /* context matched text */ + { "fn", &filename_color, NULL }, /* filename */ + { "ln", &line_num_color, NULL }, /* line number */ + { "bn", &byte_num_color, NULL }, /* byte (sic) offset */ + { "se", &sep_color, NULL }, /* separator */ + { "sl", &selected_line_color, NULL }, /* selected lines */ + { "cx", &context_line_color, NULL }, /* context lines */ + { "rv", NULL, color_cap_rv_fct }, /* -v reverses sl/cx */ + { "ne", NULL, color_cap_ne_fct }, /* no EL on SGR_* */ + { NULL, NULL, NULL } + }; + + static struct exclude *excluded_patterns; + static struct exclude *excluded_directory_patterns; + /* Short options. */ + static char const short_options[] = + "0123456789A:B:C:D:EFGHIPTUVX:abcd:e:f:hiLlm:noqRrsuvwxyZz"; + + /* Non-boolean long options that have no corresponding short equivalents. */ + enum + { + BINARY_FILES_OPTION = CHAR_MAX + 1, + COLOR_OPTION, + INCLUDE_OPTION, + EXCLUDE_OPTION, + EXCLUDE_FROM_OPTION, + LINE_BUFFERED_OPTION, + LABEL_OPTION, + EXCLUDE_DIRECTORY_OPTION, + GROUP_SEPARATOR_OPTION + }; + + /* Long options equivalences. */ + static struct option const long_options[] = + { + {"basic-regexp", no_argument, NULL, 'G'}, + {"extended-regexp", no_argument, NULL, 'E'}, + {"fixed-regexp", no_argument, NULL, 'F'}, + {"fixed-strings", no_argument, NULL, 'F'}, + {"perl-regexp", no_argument, NULL, 'P'}, + {"after-context", required_argument, NULL, 'A'}, + {"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'}, + {"exclude", required_argument, NULL, EXCLUDE_OPTION}, + {"exclude-from", required_argument, NULL, EXCLUDE_FROM_OPTION}, + {"exclude-dir", required_argument, NULL, EXCLUDE_DIRECTORY_OPTION}, + {"file", required_argument, NULL, 'f'}, + {"files-with-matches", no_argument, NULL, 'l'}, + {"files-without-match", no_argument, NULL, 'L'}, + {"group-separator", required_argument, NULL, GROUP_SEPARATOR_OPTION}, + {"help", no_argument, &show_help, 1}, + {"include", required_argument, NULL, INCLUDE_OPTION}, + {"ignore-case", no_argument, NULL, 'i'}, + {"initial-tab", no_argument, NULL, 'T'}, + {"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'}, + + {"no-filename", no_argument, NULL, 'h'}, + {"no-group-separator", no_argument, NULL, GROUP_SEPARATOR_OPTION}, + {"no-messages", no_argument, NULL, 's'}, + {"null", no_argument, NULL, 'Z'}, + {"null-data", no_argument, NULL, 'z'}, + {"only-matching", no_argument, NULL, 'o'}, + {"quiet", no_argument, NULL, 'q'}, + {"recursive", no_argument, NULL, 'r'}, + {"dereference-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} + }; + + /* Define flags declared in grep.h. */ + int match_icase; + int match_words; + int match_lines; + unsigned char eolbyte; + + static char const *matcher; + + /* For error messages. */ + /* The input file name, or (if standard input) "-" or a --label argument. */ + static char const *filename; + static size_t filename_prefix_len; + static int errseen; + static int write_error_seen; + + enum directories_type + { + READ_DIRECTORIES = 2, + RECURSE_DIRECTORIES, + SKIP_DIRECTORIES + }; + + /* How to handle directories. */ + static char const *const directories_args[] = + { + "read", "recurse", "skip", NULL + }; + static enum directories_type const directories_types[] = + { + READ_DIRECTORIES, RECURSE_DIRECTORIES, SKIP_DIRECTORIES + }; + ARGMATCH_VERIFY (directories_args, directories_types); + + static enum directories_type directories = READ_DIRECTORIES; + + enum { basic_fts_options = FTS_CWDFD | FTS_NOSTAT | FTS_TIGHT_CYCLE_CHECK }; + static int fts_options = basic_fts_options | FTS_COMFOLLOW | FTS_PHYSICAL; + + /* How to handle devices. */ + static enum + { + READ_COMMAND_LINE_DEVICES, + READ_DEVICES, + SKIP_DEVICES + } devices = READ_COMMAND_LINE_DEVICES; + + static int grepfile (int, char const *, int, int); + static int grepdesc (int, int); + + static void dos_binary (void); + static void dos_unix_byte_offsets (void); + static int undossify_input (char *, size_t); + + static int + is_device_mode (mode_t m) + { + return S_ISCHR (m) || S_ISBLK (m) || S_ISSOCK (m) || S_ISFIFO (m); + } + + /* Return nonzero if ST->st_size is defined. Assume the file is not a + symbolic link. */ + static int + usable_st_size (struct stat const *st) + { + return S_ISREG (st->st_mode) || S_TYPEISSHM (st) || S_TYPEISTMO (st); + } + + /* Functions we'll use to search. */ + typedef void (*compile_fp_t) (char const *, size_t); + typedef size_t (*execute_fp_t) (char const *, size_t, size_t *, char const *); + static compile_fp_t compile; + static execute_fp_t execute; + + /* Like error, but suppress the diagnostic if requested. */ + static void + suppressible_error (char const *mesg, int errnum) + { + if (! suppress_errors) + error (0, errnum, "%s", mesg); + errseen = 1; + } + + /* If there has already been a write error, don't bother closing + standard output, as that might elicit a duplicate diagnostic. */ + static void + clean_up_stdout (void) + { + if (! write_error_seen) + close_stdout (); + } + + /* Return 1 if a file is known to be binary for the purpose of 'grep'. + BUF, of size BUFSIZE, is the initial buffer read from the file with + descriptor FD and status ST. */ + static int + file_is_binary (char const *buf, size_t bufsize, int fd, struct stat const *st) + { + #ifndef SEEK_HOLE + enum { SEEK_HOLE = SEEK_END }; + #endif + + /* If -z, test only whether the initial buffer contains '\200'; + knowing about holes won't help. */ + if (! eolbyte) + return memchr (buf, '\200', bufsize) != 0; + + /* If the initial buffer contains a null byte, guess that the file + is binary. */ + if (memchr (buf, '\0', bufsize)) + return 1; + + /* If the file has holes, it must contain a null byte somewhere. */ + if (SEEK_HOLE != SEEK_END && usable_st_size (st)) + { + off_t cur = bufsize; + if (O_BINARY || fd == STDIN_FILENO) + { + cur = lseek (fd, 0, SEEK_CUR); + if (cur < 0) + return 0; + } + + /* Look for a hole after the current location. */ + off_t hole_start = lseek (fd, cur, SEEK_HOLE); + if (0 <= hole_start) + { + if (lseek (fd, cur, SEEK_SET) < 0) + suppressible_error (filename, errno); + if (hole_start < st->st_size) + return 1; + } + } + + /* Guess that the file does not contain binary data. */ + return 0; + } + + /* Convert STR to a nonnegative integer, storing the result in *OUT. + STR must be a valid context length argument; report an error if it + isn't. Silently ceiling *OUT at the maximum value, as that is + practically equivalent to infinity for grep's purposes. */ + static void + context_length_arg (char const *str, intmax_t *out) + { + switch (xstrtoimax (str, 0, 10, out, "")) + { + case LONGINT_OK: + case LONGINT_OVERFLOW: + if (0 <= *out) + break; + /* Fall through. */ + default: + error (EXIT_TROUBLE, 0, "%s: %s", str, + _("invalid context length argument")); + } + } + + /* Return nonzero if the file with NAME should be skipped. + If COMMAND_LINE is nonzero, it is a command-line argument. + If IS_DIR is nonzero, it is a directory. */ + static int + skipped_file (char const *name, int command_line, int is_dir) + { + return (is_dir + ? (directories == SKIP_DIRECTORIES + || (! (command_line && filename_prefix_len != 0) + && excluded_directory_patterns + && excluded_file_name (excluded_directory_patterns, name))) + : (excluded_patterns + && excluded_file_name (excluded_patterns, name))); + } + + /* Hairy buffering mechanism for grep. The intent is to keep + all reads aligned on a page boundary and multiples of the + page size, unless a read yields a partial page. */ + + static char *buffer; /* Base of buffer. */ + 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. */ + + /* 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))) + + /* 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, struct stat const *st) + { + 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 (S_ISREG (st->st_mode)) + { + if (fd != STDIN_FILENO) + bufoffset = 0; + else + { + bufoffset = lseek (fd, 0, SEEK_CUR); + if (bufoffset < 0) + { + suppressible_error (_("lseek failed"), errno); + return 0; + } + } + } + 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 zero if there's an error. */ + static int + fillbuf (size_t save, struct stat const *st) + { + ssize_t fillsize; + int cc = 1; + char *readbuf; + size_t readsize; + + /* Offset from start of buffer to start of old stuff + that we want to save. */ + 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; + + /* 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 (usable_st_size (st)) + { + off_t to_be_read = st->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; + } + + /* Add enough room so that the buffer is aligned and has room + for byte sentinels fore and aft. */ + 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; + + fillsize = safe_read (bufdesc, readbuf, readsize); + if (fillsize < 0) + fillsize = cc = 0; + bufoffset += fillsize; + fillsize = undossify_input (readbuf, 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. */ + + static int filename_mask; /* If zero, output nulls after filenames. */ + static int out_quiet; /* Suppress all normal output. */ + static bool out_invert; /* Print nonmatching stuff. */ + static int out_file; /* Print filenames. */ + static int out_line; /* Print line numbers. */ + static int out_byte; /* Print byte offsets. */ + static intmax_t out_before; /* Lines of leading context. */ + static intmax_t 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 intmax_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 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 uintmax_t totalnl; /* Total newline count before lastnl. */ + static intmax_t outleft; /* Maximum number of lines to be output. */ + static intmax_t 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. */ + + #include "dosbuf.c" + + /* 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 (EXIT_TROUBLE, 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++) + { + beg = memchr (beg, eolbyte, lim - beg); + if (!beg) + break; + newlines++; + } + totalnl = add_count (totalnl, newlines); + lastnl = lim; + } + + /* Print the current filename. */ + static void + print_filename (void) + { + pr_sgr_start_if (filename_color); + fputs (filename, stdout); + pr_sgr_end_if (filename_color); + } + + /* Print a character separator. */ + static void + print_sep (char sep) + { + pr_sgr_start_if (sep_color); + fputc (sep, stdout); + pr_sgr_end_if (sep_color); + } + + /* Print a line number or a byte offset. */ + static void + print_offset (uintmax_t pos, int min_width, const char *color) + { + /* 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; + + do + { + *--p = '0' + pos % 10; + --min_width; + } + while ((pos /= 10) != 0); + + /* Do this to maximize the probability of alignment across lines. */ + if (align_tabs) + while (--min_width >= 0) + *--p = ' '; + + pr_sgr_start_if (color); + fwrite (p, 1, buf + sizeof buf - p, stdout); + pr_sgr_end_if (color); + } + + /* Print a whole line head (filename, line, byte). */ + static void + print_line_head (char const *beg, char const *lim, int sep) + { + int pending_sep = 0; + + if (out_file) + { + print_filename (); + if (filename_mask) + pending_sep = 1; + else + fputc (0, stdout); + } + + if (out_line) + { + if (lastnl < lim) + { + nlscan (beg); + totalnl = add_count (totalnl, 1); + lastnl = lim; + } + if (pending_sep) + print_sep (sep); + print_offset (totalnl, 4, line_num_color); + pending_sep = 1; + } + + if (out_byte) + { + uintmax_t pos = add_count (totalcc, beg - bufbeg); + pos = dossified_pos (pos); + if (pending_sep) + print_sep (sep); + print_offset (pos, 6, byte_num_color); + pending_sep = 1; + } + + if (pending_sep) + { + /* This assumes sep is one column wide. + Try doing this any other way with Unicode + (and its combining and wide characters) + filenames and you're wasting your efforts. */ + if (align_tabs) + fputs ("\t\b", stdout); + + print_sep (sep); + } + } + + static const char * + print_line_middle (const char *beg, const char *lim, + const char *line_color, const char *match_color) + { + size_t match_size; + size_t match_offset; + const char *cur = beg; + const char *mid = NULL; + + while (cur < lim + && ((match_offset = execute (beg, lim - beg, &match_size, + beg + (cur - beg))) != (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) + { + /* Make minimal progress; there may be further non-empty matches. */ + /* XXX - Could really advance by one whole multi-octet character. */ + match_size = 1; + if (!mid) + mid = cur; + } + else + { + /* This function is called on a matching line only, + but is it selected or rejected/context? */ + if (only_matching) + print_line_head (b, lim, (out_invert ? SEP_CHAR_REJECTED + : SEP_CHAR_SELECTED)); + else + { + pr_sgr_start (line_color); + if (mid) + { + cur = mid; + mid = NULL; + } + fwrite (cur, sizeof (char), b - cur, stdout); + } + + pr_sgr_start_if (match_color); + fwrite (b, sizeof (char), match_size, stdout); + pr_sgr_end_if (match_color); + if (only_matching) + fputs ("\n", stdout); + } + cur = b + match_size; + } + + if (only_matching) + cur = lim; + else if (mid) + cur = mid; + + return cur; + } + + static const char * + print_line_tail (const char *beg, const char *lim, const char *line_color) + { + size_t eol_size; + size_t tail_size; + + eol_size = (lim > beg && lim[-1] == eolbyte); + eol_size += (lim - eol_size > beg && lim[-(1 + eol_size)] == '\r'); + tail_size = lim - eol_size - beg; + + if (tail_size > 0) + { + pr_sgr_start (line_color); + fwrite (beg, 1, tail_size, stdout); + beg += tail_size; + pr_sgr_end (line_color); + } + + return beg; + } + + static void + prline (char const *beg, char const *lim, int sep) + { + int matching; + const char *line_color; + const char *match_color; + + if (!only_matching) + print_line_head (beg, lim, sep); + + matching = (sep == SEP_CHAR_SELECTED) ^ out_invert; + + if (color_option) + { + line_color = (((sep == SEP_CHAR_SELECTED) + ^ (out_invert && (color_option < 0))) + ? selected_line_color : context_line_color); + match_color = (sep == SEP_CHAR_SELECTED + ? selected_match_color : context_match_color); + } + else + line_color = match_color = NULL; /* Shouldn't be used. */ + + if ((only_matching && matching) + || (color_option && (*line_color || *match_color))) + { + /* We already know that non-matching lines have no match (to colorize). */ + if (matching && (only_matching || *match_color)) + beg = print_line_middle (beg, lim, line_color, match_color); + + if (!only_matching && *line_color) + { + /* This code is exercised at least when grep is invoked like this: + echo k| GREP_COLORS='sl=01;32' src/grep k --color=always */ + beg = print_line_tail (beg, lim, line_color); + } + } + + if (!only_matching && lim > beg) + fwrite (beg, 1, lim - beg, stdout); + + if (ferror (stdout)) + { + write_error_seen = 1; + error (EXIT_TROUBLE, 0, _("write error")); + } + + lastout = lim; + + if (line_buffered) + fflush (stdout); + } + + /* 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 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 + 1 - lastout, + &match_size, NULL) == (size_t) -1) + == !out_invert)) + prline (lastout, nl + 1, SEP_CHAR_REJECTED); + else + pending = 0; + } + } + + /* Output the lines between BEG and LIM. Deal with context. */ + static void + prtext (char const *beg, char const *lim) + { + static bool used; /* Avoid printing SEP_STR_GROUP before any output. */ + char eol = eolbyte; + + if (!out_quiet && pending > 0) + prpending (beg); + + char const *p = beg; + + if (!out_quiet) + { + /* Deal with leading context. */ + char const *bp = lastout ? lastout : bufbeg; + intmax_t i; + for (i = 0; i < out_before; ++i) + if (p > bp) + do + --p; + while (p[-1] != eol); + + /* Print the group separator unless the output is adjacent to + the previous output in the file. */ + if ((0 <= out_before || 0 <= out_after) && used + && p != lastout && group_separator) + { + pr_sgr_start_if (sep_color); + fputs (group_separator, stdout); + pr_sgr_end_if (sep_color); + fputc ('\n', stdout); + } + + while (p < beg) + { + char const *nl = memchr (p, eol, beg - p); + nl++; + prline (p, nl, SEP_CHAR_REJECTED); + p = nl; + } + } + + intmax_t n; + if (out_invert) + { + /* One or more lines are output. */ + for (n = 0; p < lim && n < outleft; n++) + { + char const *nl = memchr (p, eol, lim - p); + nl++; + if (!out_quiet) + prline (p, nl, SEP_CHAR_SELECTED); + p = nl; + } + } + else + { + /* Just one line is output. */ + if (!out_quiet) + prline (beg, lim, SEP_CHAR_SELECTED); + n = 1; + p = lim; + } + + after_last_match = bufoffset - (buflim - p); + pending = out_quiet ? 0 : MAX (0, out_after); + used = true; + outleft -= n; + } + + /* Invoke the matcher, EXECUTE, on buffer BUF of SIZE bytes. If there + is no match, return (size_t) -1. Otherwise, set *MATCH_SIZE to the + length of the match and return the offset of the start of the match. */ + static size_t + do_execute (char const *buf, size_t size, size_t *match_size) + { + size_t result; + const char *line_next; + + /* With the current implementation, using --ignore-case with a multi-byte + character set is very inefficient when applied to a large buffer + containing many matches. We can avoid much of the wasted effort + by matching line-by-line. + + FIXME: this is just an ugly workaround, and it doesn't really + belong here. Also, PCRE is always using this same per-line + matching algorithm. Either we fix -i, or we should refactor + this code---for example, we could add another function pointer + to struct matcher to split the buffer passed to execute. It would + perform the memchr if line-by-line matching is necessary, or just + return buf + size otherwise. */ + if (! (execute == Fexecute || execute == Pexecute) + || MB_CUR_MAX == 1 || !match_icase) + return execute (buf, size, match_size, NULL); + + for (line_next = buf; line_next < buf + size; ) + { + const char *line_buf = line_next; + const char *line_end = memchr (line_buf, eolbyte, + (buf + size) - line_buf); + if (line_end == NULL) + line_next = line_end = buf + size; + else + line_next = line_end + 1; + + result = execute (line_buf, line_next - line_buf, match_size, NULL); + if (result != (size_t) -1) + return (line_buf - buf) + result; + } + + return (size_t) -1; + } + + /* Scan the specified portion of the buffer, matching lines (or + between matching lines if OUT_INVERT is true). Return a count of + lines printed. */ + static intmax_t + grepbuf (char const *beg, char const *lim) + { + intmax_t outleft0 = outleft; + char const *p; + char const *endp; + + for (p = beg; p < lim; p = endp) + { + size_t match_size; + size_t match_offset = do_execute (p, lim - p, &match_size); + if (match_offset == (size_t) -1) + { + if (!out_invert) + break; + match_offset = lim - p; + match_size = 0; + } + char const *b = p + match_offset; + endp = b + match_size; + /* Avoid matching the empty line at the end of the buffer. */ + if (!out_invert && b == lim) + break; + if (!out_invert || p < b) + { + char const *prbeg = out_invert ? p : b; + char const *prend = out_invert ? b : endp; + prtext (prbeg, prend); + if (!outleft || done_on_match) + { + if (exit_on_match) + exit (EXIT_SUCCESS); + break; + } + } + } + + return outleft0 - outleft; + } + + /* 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 intmax_t + grep (int fd, struct stat const *st) + { + intmax_t nlines, i; + int not_text; + size_t residue, save; + char oldc; + char *beg; + char *lim; + char eol = eolbyte; + + if (! reset (fd, st)) + return 0; + + totalcc = 0; + lastout = 0; + totalnl = 0; + outleft = max_count; + after_last_match = 0; + pending = 0; + + nlines = 0; + residue = 0; + save = 0; + + if (! fillbuf (save, st)) + { - suppressible_error (filename, errno); ++ if (errno != EINVAL) ++ suppressible_error (filename, errno); + return 0; + } + + not_text = (((binary_files == BINARY_BINARY_FILES && !out_quiet) + || binary_files == WITHOUT_MATCH_BINARY_FILES) + && file_is_binary (bufbeg, buflim - bufbeg, fd, st)); + 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; + + /* no more data to scan (eof) except for maybe a residue -> break */ + if (beg == buflim) + break; + + /* 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; + /* FIXME: use rawmemrchr if/when it exists, since we have ensured + that this use of memrchr is guaranteed never to return NULL. */ + lim = memrchr (beg - 1, eol, buflim - beg + 1); + ++lim; + 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)) + 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) + { + ++i; + do + --beg; + while (beg[-1] != eol); + } + + /* Detect whether leading context is adjacent to previous output. */ + if (beg != lastout) + lastout = 0; + + /* Handle some details and read more data to scan. */ + save = residue + lim - beg; + if (out_byte) + totalcc = add_count (totalcc, buflim - bufbeg - save); + if (out_line) + nlscan (beg); + if (! fillbuf (save, st)) + { + 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 + grepdirent (FTS *fts, FTSENT *ent, int command_line) + { + int follow, dirdesc; + struct stat *st = ent->fts_statp; + command_line &= ent->fts_level == FTS_ROOTLEVEL; + + if (ent->fts_info == FTS_DP) + { + if (directories == RECURSE_DIRECTORIES && command_line) + out_file &= ~ (2 * !no_filenames); + return 1; + } + + if (skipped_file (ent->fts_name, command_line, + (ent->fts_info == FTS_D || ent->fts_info == FTS_DC + || ent->fts_info == FTS_DNR))) + { + fts_set (fts, ent, FTS_SKIP); + return 1; + } + + filename = ent->fts_path + filename_prefix_len; + follow = (fts->fts_options & FTS_LOGICAL + || (fts->fts_options & FTS_COMFOLLOW && command_line)); + + switch (ent->fts_info) + { + case FTS_D: + if (directories == RECURSE_DIRECTORIES) + { + out_file |= 2 * !no_filenames; + return 1; + } + fts_set (fts, ent, FTS_SKIP); + break; + + case FTS_DC: + if (!suppress_errors) + error (0, 0, _("warning: %s: %s"), filename, + _("recursive directory loop")); + return 1; + + case FTS_DNR: + case FTS_ERR: + case FTS_NS: + suppressible_error (filename, ent->fts_errno); + return 1; + + case FTS_DEFAULT: + case FTS_NSOK: + if (devices == SKIP_DEVICES + || (devices == READ_COMMAND_LINE_DEVICES && !command_line)) + { + struct stat st1; + if (! st->st_mode) + { + /* The file type is not already known. Get the file status + before opening, since opening might have side effects + on a device. */ + int flag = follow ? 0 : AT_SYMLINK_NOFOLLOW; + if (fstatat (fts->fts_cwd_fd, ent->fts_accpath, &st1, flag) != 0) + { + suppressible_error (filename, errno); + return 1; + } + st = &st1; + } + if (is_device_mode (st->st_mode)) + return 1; + } + break; + + case FTS_F: + case FTS_SLNONE: + break; + + case FTS_SL: + case FTS_W: + return 1; + + default: + abort (); + } + + dirdesc = ((fts->fts_options & (FTS_NOCHDIR | FTS_CWDFD)) == FTS_CWDFD + ? fts->fts_cwd_fd + : AT_FDCWD); + return grepfile (dirdesc, ent->fts_accpath, follow, command_line); + } + + static int + grepfile (int dirdesc, char const *name, int follow, int command_line) + { + int desc = openat_safer (dirdesc, name, O_RDONLY | (follow ? 0 : O_NOFOLLOW)); + if (desc < 0) + { + if (follow || (errno != ELOOP && errno != EMLINK)) + suppressible_error (filename, errno); + return 1; + } + return grepdesc (desc, command_line); + } + + static int + grepdesc (int desc, int command_line) + { + intmax_t count; + int status = 1; + struct stat st; + + /* Get the file status, possibly for the second time. This catches + a race condition if the directory entry changes after the + directory entry is read and before the file is opened. For + example, normally DESC is a directory only at the top level, but + there is an exception if some other process substitutes a + directory for a non-directory while 'grep' is running. */ + if (fstat (desc, &st) != 0) + { + suppressible_error (filename, errno); + goto closeout; + } + + if (desc != STDIN_FILENO && command_line + && skipped_file (filename, 1, S_ISDIR (st.st_mode))) + goto closeout; + + if (desc != STDIN_FILENO + && directories == RECURSE_DIRECTORIES && S_ISDIR (st.st_mode)) + { + /* Traverse the directory starting with its full name, because + unfortunately fts provides no way to traverse the directory + starting from its file descriptor. */ + + FTS *fts; + FTSENT *ent; + int opts = fts_options & ~(command_line ? 0 : FTS_COMFOLLOW); + char *fts_arg[2]; + + /* Close DESC now, to conserve file descriptors if the race + condition occurs many times in a deep recursion. */ + if (close (desc) != 0) + suppressible_error (filename, errno); + + fts_arg[0] = (char *) filename; + fts_arg[1] = NULL; + fts = fts_open (fts_arg, opts, NULL); + + if (!fts) + xalloc_die (); + while ((ent = fts_read (fts))) + status &= grepdirent (fts, ent, command_line); + if (errno) + suppressible_error (filename, errno); + if (fts_close (fts) != 0) + suppressible_error (filename, errno); + return status; + } + if (desc != STDIN_FILENO + && ((directories == SKIP_DIRECTORIES && S_ISDIR (st.st_mode)) + || ((devices == SKIP_DEVICES + || (devices == READ_COMMAND_LINE_DEVICES && !command_line)) + && is_device_mode (st.st_mode)))) + goto closeout; + + /* If there is a regular file on stdout and the current file refers + to the same i-node, we have to report the problem and skip it. + Otherwise when matching lines from some other input reach the + disk before we open this file, we can end up reading and matching + those lines and appending them to the file from which we're reading. + Then we'd have what appears to be an infinite loop that'd terminate + only upon filling the output file system or reaching a quota. + However, there is no risk of an infinite loop if grep is generating + no output, i.e., with --silent, --quiet, -q. + Similarly, with any of these: + --max-count=N (-m) (for N >= 2) + --files-with-matches (-l) + --files-without-match (-L) + there is no risk of trouble. + For --max-count=1, grep stops after printing the first match, + so there is no risk of malfunction. But even --max-count=2, with + input==output, while there is no risk of infloop, there is a race + condition that could result in "alternate" output. */ + if (!out_quiet && list_files == 0 && 1 < max_count + && S_ISREG (out_stat.st_mode) && out_stat.st_ino + && SAME_INODE (st, out_stat)) + { + if (! suppress_errors) + error (0, 0, _("input file %s is also the output"), quote (filename)); + errseen = 1; + goto closeout; + } + + #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)) + SET_BINARY (desc); + #endif + + count = grep (desc, &st); + if (count < 0) + status = count + 2; + else + { + if (count_matches) + { + if (out_file) + { + print_filename (); + if (filename_mask) + print_sep (SEP_CHAR_SELECTED); + else + fputc (0, stdout); + } + printf ("%" PRIdMAX "\n", count); + } + + status = !count; + if (list_files == 1 - 2 * status) + { + print_filename (); + fputc ('\n' & filename_mask, stdout); + } + + if (desc == STDIN_FILENO) + { + off_t required_offset = outleft ? bufoffset : after_last_match; + if (required_offset != bufoffset + && lseek (desc, required_offset, SEEK_SET) < 0 + && S_ISREG (st.st_mode)) + suppressible_error (filename, errno); + } + } + + closeout: + if (desc != STDIN_FILENO && close (desc) != 0) + suppressible_error (filename, errno); + return status; + } + + static int + grep_command_line_arg (char const *arg) + { + if (STREQ (arg, "-")) + { + filename = label ? label : _("(standard input)"); + return grepdesc (STDIN_FILENO, 1); + } + else + { + filename = arg; + return grepfile (AT_FDCWD, arg, 1, 1); + } + } + + _Noreturn void usage (int); + 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")); + printf (_("PATTERN is, by default, a basic regular expression (BRE).\n")); + printf (_("\ + 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 (ERE)\n\ + -F, --fixed-strings PATTERN is a set of newline-separated fixed strings\n\ + -G, --basic-regexp PATTERN is a basic regular expression (BRE)\n\ + -P, --perl-regexp PATTERN is a Perl regular expression\n")); + /* -X is undocumented on purpose. */ + printf (_("\ + -e, --regexp=PATTERN use PATTERN for matching\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 display version information and exit\n\ + --help display this help text and exit\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 file name for each match\n\ + -h, --no-filename suppress the file name prefix on output\n\ + --label=LABEL use LABEL as the standard input file name prefix\n\ + ")); + printf (_("\ + -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\ + ")); + printf (_("\ + -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, --recursive like --directories=recurse\n\ + -R, --dereference-recursive likewise, but follow all symlinks\n\ + ")); + printf (_("\ + --include=FILE_PATTERN search only files that match FILE_PATTERN\n\ + --exclude=FILE_PATTERN skip files and directories matching FILE_PATTERN\n\ + --exclude-from=FILE skip files matching any file pattern from FILE\n\ + --exclude-dir=PATTERN directories that match PATTERN will be skipped.\n\ + ")); + printf (_("\ + -L, --files-without-match print only names of FILEs containing no match\n\ + -l, --files-with-matches print only names of FILEs containing matches\n\ + -c, --count print only a count of matching lines per FILE\n\ + -T, --initial-tab make tabs line up (if needed)\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\ + ")); + printf (_("\ + -NUM same as --context=NUM\n\ + --color[=WHEN],\n\ + --colour[=WHEN] use markers to highlight the matching strings;\n\ + WHEN is 'always', 'never', or 'auto'\n\ + -U, --binary do not strip CR characters at EOL (MSDOS/Windows)\n\ + -u, --unix-byte-offsets report offsets as if CRs were not there\n\ + (MSDOS/Windows)\n\ + \n")); + printf (_("\ + 'egrep' means 'grep -E'. 'fgrep' means 'grep -F'.\n\ + Direct invocation as either 'egrep' or 'fgrep' is deprecated.\n")); + printf (_("\ + When FILE is -, read standard input. With no FILE, read . if a command-line\n\ + -r is given, - otherwise. If fewer than two FILEs are given, assume -h.\n\ + Exit status is 0 if any line is selected, 1 otherwise;\n\ + if any error occurs and -q is not given, the exit status is 2.\n")); + printf (_("\nReport bugs to: %s\n"), PACKAGE_BUGREPORT); + printf (_("GNU Grep home page: <%s>\n"), + "http://www.gnu.org/software/grep/"); + fputs (_("General help using GNU software: \n"), + stdout); + + } + exit (status); + } + + /* Pattern compilers and matchers. */ static void Gcompile (char const *pattern, size_t size)