|
Packit |
709fb3 |
/* grep.c - main driver file for grep.
|
|
Packit |
709fb3 |
Copyright (C) 1992, 1997-2002, 2004-2017 Free Software Foundation, Inc.
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
This program is free software; you can redistribute it and/or modify
|
|
Packit |
709fb3 |
it under the terms of the GNU General Public License as published by
|
|
Packit |
709fb3 |
the Free Software Foundation; either version 3, or (at your option)
|
|
Packit |
709fb3 |
any later version.
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
This program is distributed in the hope that it will be useful,
|
|
Packit |
709fb3 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
709fb3 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
709fb3 |
GNU General Public License for more details.
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
You should have received a copy of the GNU General Public License
|
|
Packit |
709fb3 |
along with this program; if not, write to the Free Software
|
|
Packit |
709fb3 |
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
|
|
Packit |
709fb3 |
02110-1301, USA. */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Written July 1992 by Mike Haertel. */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
#include <config.h>
|
|
Packit |
709fb3 |
#include <sys/types.h>
|
|
Packit |
709fb3 |
#include <sys/stat.h>
|
|
Packit |
709fb3 |
#include <wchar.h>
|
|
Packit |
709fb3 |
#include <fcntl.h>
|
|
Packit |
709fb3 |
#include <inttypes.h>
|
|
Packit |
709fb3 |
#include <stdarg.h>
|
|
Packit |
709fb3 |
#include <stdio.h>
|
|
Packit |
709fb3 |
#include "system.h"
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
#include "argmatch.h"
|
|
Packit |
709fb3 |
#include "c-ctype.h"
|
|
Packit |
709fb3 |
#include "closeout.h"
|
|
Packit |
709fb3 |
#include "colorize.h"
|
|
Packit |
709fb3 |
#include "die.h"
|
|
Packit |
709fb3 |
#include "error.h"
|
|
Packit |
709fb3 |
#include "exclude.h"
|
|
Packit |
709fb3 |
#include "exitfail.h"
|
|
Packit |
709fb3 |
#include "fcntl-safer.h"
|
|
Packit |
709fb3 |
#include "fts_.h"
|
|
Packit |
709fb3 |
#include "getopt.h"
|
|
Packit |
709fb3 |
#include "getprogname.h"
|
|
Packit |
709fb3 |
#include "grep.h"
|
|
Packit |
709fb3 |
#include "intprops.h"
|
|
Packit |
709fb3 |
#include "propername.h"
|
|
Packit |
709fb3 |
#include "quote.h"
|
|
Packit |
709fb3 |
#include "safe-read.h"
|
|
Packit |
709fb3 |
#include "search.h"
|
|
Packit |
709fb3 |
#include "version-etc.h"
|
|
Packit |
709fb3 |
#include "xalloc.h"
|
|
Packit |
709fb3 |
#include "xbinary-io.h"
|
|
Packit |
709fb3 |
#include "xstrtol.h"
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
enum { SEP_CHAR_SELECTED = ':' };
|
|
Packit |
709fb3 |
enum { SEP_CHAR_REJECTED = '-' };
|
|
Packit |
709fb3 |
static char const SEP_STR_GROUP[] = "--";
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
#define AUTHORS \
|
|
Packit |
709fb3 |
proper_name ("Mike Haertel"), \
|
|
Packit |
709fb3 |
_("others, see <http://git.sv.gnu.org/cgit/grep.git/tree/AUTHORS>")
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* When stdout is connected to a regular file, save its stat
|
|
Packit |
709fb3 |
information here, so that we can automatically skip it, thus
|
|
Packit |
709fb3 |
avoiding a potential (racy) infinite loop. */
|
|
Packit |
709fb3 |
static struct stat out_stat;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* if non-zero, display usage information and exit */
|
|
Packit |
709fb3 |
static int show_help;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Print the version on standard output and exit. */
|
|
Packit |
709fb3 |
static bool show_version;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Suppress diagnostics for nonexistent or unreadable files. */
|
|
Packit |
709fb3 |
static bool suppress_errors;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* If nonzero, use color markers. */
|
|
Packit |
709fb3 |
static int color_option;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Show only the part of a line matching the expression. */
|
|
Packit |
709fb3 |
static bool only_matching;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* If nonzero, make sure first content char in a line is on a tab stop. */
|
|
Packit |
709fb3 |
static bool align_tabs;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Print width of line numbers and byte offsets. Nonzero if ALIGN_TABS. */
|
|
Packit |
709fb3 |
static int offset_width;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* See below */
|
|
Packit |
709fb3 |
struct FL_pair
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
char const *filename;
|
|
Packit |
709fb3 |
size_t lineno;
|
|
Packit |
709fb3 |
};
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* A list of lineno,filename pairs corresponding to -f FILENAME
|
|
Packit |
709fb3 |
arguments. Since we store the concatenation of all patterns in
|
|
Packit |
709fb3 |
a single array, KEYS, be they from the command line via "-e PAT"
|
|
Packit |
709fb3 |
or read from one or more -f-specified FILENAMES. Given this
|
|
Packit |
709fb3 |
invocation, grep -f <(seq 5) -f <(seq 2) -f <(seq 3) FILE, there
|
|
Packit |
709fb3 |
will be three entries in LF_PAIR: {1, x} {6, y} {8, z}, where
|
|
Packit |
709fb3 |
x, y and z are just place-holders for shell-generated names. */
|
|
Packit |
709fb3 |
static struct FL_pair *fl_pair;
|
|
Packit |
709fb3 |
static size_t n_fl_pair_slots;
|
|
Packit |
709fb3 |
/* Count not only -f-specified files, but also individual -e operands
|
|
Packit |
709fb3 |
and any command-line argument that serves as a regular expression. */
|
|
Packit |
709fb3 |
static size_t n_pattern_files;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* The number of patterns seen so far.
|
|
Packit |
709fb3 |
It is advanced by fl_add and, when needed, used in pattern_file_name
|
|
Packit |
709fb3 |
to derive a file-relative line number. */
|
|
Packit |
709fb3 |
static size_t n_patterns;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Return the number of newline bytes in BUF with size SIZE. */
|
|
Packit |
709fb3 |
static size_t _GL_ATTRIBUTE_PURE
|
|
Packit |
709fb3 |
count_nl_bytes (char const *buf, size_t size)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
char const *p = buf;
|
|
Packit |
709fb3 |
char const *end_p = buf + size;
|
|
Packit |
709fb3 |
size_t n = 0;
|
|
Packit |
709fb3 |
while ((p = memchr (p, '\n', end_p - p)))
|
|
Packit |
709fb3 |
p++, n++;
|
|
Packit |
709fb3 |
return n;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Append a FILENAME,line-number pair to FL_PAIR, and update
|
|
Packit |
709fb3 |
pattern-related counts from the contents of BUF with SIZE bytes. */
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
fl_add (char const *buf, size_t size, char const *filename)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (n_fl_pair_slots <= n_pattern_files)
|
|
Packit |
709fb3 |
fl_pair = x2nrealloc (fl_pair, &n_fl_pair_slots, sizeof *fl_pair);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
fl_pair[n_pattern_files].lineno = n_patterns + 1;
|
|
Packit |
709fb3 |
fl_pair[n_pattern_files].filename = filename;
|
|
Packit |
709fb3 |
n_pattern_files++;
|
|
Packit |
709fb3 |
n_patterns += count_nl_bytes (buf, size);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Map the line number, LINENO, of one of the input patterns to the
|
|
Packit |
709fb3 |
name of the file from which it came. If it was read from stdin
|
|
Packit |
709fb3 |
or if it was specified on the command line, return "-". */
|
|
Packit |
709fb3 |
char const * _GL_ATTRIBUTE_PURE
|
|
Packit |
709fb3 |
pattern_file_name (size_t lineno, size_t *new_lineno)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
size_t i;
|
|
Packit |
709fb3 |
for (i = 1; i < n_pattern_files; i++)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (lineno < fl_pair[i].lineno)
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
*new_lineno = lineno - fl_pair[i - 1].lineno + 1;
|
|
Packit |
709fb3 |
return fl_pair[i - 1].filename;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
#if HAVE_ASAN
|
|
Packit |
709fb3 |
/* Record the starting address and length of the sole poisoned region,
|
|
Packit |
709fb3 |
so that we can unpoison it later, just before each following read. */
|
|
Packit |
709fb3 |
static void const *poison_buf;
|
|
Packit |
709fb3 |
static size_t poison_len;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
clear_asan_poison (void)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (poison_buf)
|
|
Packit |
709fb3 |
__asan_unpoison_memory_region (poison_buf, poison_len);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
asan_poison (void const *addr, size_t size)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
poison_buf = addr;
|
|
Packit |
709fb3 |
poison_len = size;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
__asan_poison_memory_region (poison_buf, poison_len);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
#else
|
|
Packit |
709fb3 |
static void clear_asan_poison (void) { }
|
|
Packit |
709fb3 |
static void asan_poison (void const volatile *addr, size_t size) { }
|
|
Packit |
709fb3 |
#endif
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* The group separator used when context is requested. */
|
|
Packit |
709fb3 |
static const char *group_separator = SEP_STR_GROUP;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* The context and logic for choosing default --color screen attributes
|
|
Packit |
709fb3 |
(foreground and background colors, etc.) are the following.
|
|
Packit |
709fb3 |
-- There are eight basic colors available, each with its own
|
|
Packit |
709fb3 |
nominal luminosity to the human eye and foreground/background
|
|
Packit |
709fb3 |
codes (black [0 %, 30/40], blue [11 %, 34/44], red [30 %, 31/41],
|
|
Packit |
709fb3 |
magenta [41 %, 35/45], green [59 %, 32/42], cyan [70 %, 36/46],
|
|
Packit |
709fb3 |
yellow [89 %, 33/43], and white [100 %, 37/47]).
|
|
Packit |
709fb3 |
-- Sometimes, white as a background is actually implemented using
|
|
Packit |
709fb3 |
a shade of light gray, so that a foreground white can be visible
|
|
Packit |
709fb3 |
on top of it (but most often not).
|
|
Packit |
709fb3 |
-- Sometimes, black as a foreground is actually implemented using
|
|
Packit |
709fb3 |
a shade of dark gray, so that it can be visible on top of a
|
|
Packit |
709fb3 |
background black (but most often not).
|
|
Packit |
709fb3 |
-- Sometimes, more colors are available, as extensions.
|
|
Packit |
709fb3 |
-- Other attributes can be selected/deselected (bold [1/22],
|
|
Packit |
709fb3 |
underline [4/24], standout/inverse [7/27], blink [5/25], and
|
|
Packit |
709fb3 |
invisible/hidden [8/28]). They are sometimes implemented by
|
|
Packit |
709fb3 |
using colors instead of what their names imply; e.g., bold is
|
|
Packit |
709fb3 |
often achieved by using brighter colors. In practice, only bold
|
|
Packit |
709fb3 |
is really available to us, underline sometimes being mapped by
|
|
Packit |
709fb3 |
the terminal to some strange color choice, and standout best
|
|
Packit |
709fb3 |
being left for use by downstream programs such as less(1).
|
|
Packit |
709fb3 |
-- We cannot assume that any of the extensions or special features
|
|
Packit |
709fb3 |
are available for the purpose of choosing defaults for everyone.
|
|
Packit |
709fb3 |
-- The most prevalent default terminal backgrounds are pure black
|
|
Packit |
709fb3 |
and pure white, and are not necessarily the same shades of
|
|
Packit |
709fb3 |
those as if they were selected explicitly with SGR sequences.
|
|
Packit |
709fb3 |
Some terminals use dark or light pictures as default background,
|
|
Packit |
709fb3 |
but those are covered over by an explicit selection of background
|
|
Packit |
709fb3 |
color with an SGR sequence; their users will appreciate their
|
|
Packit |
709fb3 |
background pictures not be covered like this, if possible.
|
|
Packit |
709fb3 |
-- Some uses of colors attributes is to make some output items
|
|
Packit |
709fb3 |
more understated (e.g., context lines); this cannot be achieved
|
|
Packit |
709fb3 |
by changing the background color.
|
|
Packit |
709fb3 |
-- For these reasons, the grep color defaults should strive not
|
|
Packit |
709fb3 |
to change the background color from its default, unless it's
|
|
Packit |
709fb3 |
for a short item that should be highlighted, not understated.
|
|
Packit |
709fb3 |
-- The grep foreground color defaults (without an explicitly set
|
|
Packit |
709fb3 |
background) should provide enough contrast to be readable on any
|
|
Packit |
709fb3 |
terminal with either a black (dark) or white (light) background.
|
|
Packit |
709fb3 |
This only leaves red, magenta, green, and cyan (and their bold
|
|
Packit |
709fb3 |
counterparts) and possibly bold blue. */
|
|
Packit |
709fb3 |
/* The color strings used for matched text.
|
|
Packit |
709fb3 |
The user can overwrite them using the deprecated
|
|
Packit |
709fb3 |
environment variable GREP_COLOR or the new GREP_COLORS. */
|
|
Packit |
709fb3 |
static const char *selected_match_color = "01;31"; /* bold red */
|
|
Packit |
709fb3 |
static const char *context_match_color = "01;31"; /* bold red */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Other colors. Defaults look damn good. */
|
|
Packit |
709fb3 |
static const char *filename_color = "35"; /* magenta */
|
|
Packit |
709fb3 |
static const char *line_num_color = "32"; /* green */
|
|
Packit |
709fb3 |
static const char *byte_num_color = "32"; /* green */
|
|
Packit |
709fb3 |
static const char *sep_color = "36"; /* cyan */
|
|
Packit |
709fb3 |
static const char *selected_line_color = ""; /* default color pair */
|
|
Packit |
709fb3 |
static const char *context_line_color = ""; /* default color pair */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Select Graphic Rendition (SGR, "\33[...m") strings. */
|
|
Packit |
709fb3 |
/* Also Erase in Line (EL) to Right ("\33[K") by default. */
|
|
Packit |
709fb3 |
/* Why have EL to Right after SGR?
|
|
Packit |
709fb3 |
-- The behavior of line-wrapping when at the bottom of the
|
|
Packit |
709fb3 |
terminal screen and at the end of the current line is often
|
|
Packit |
709fb3 |
such that a new line is introduced, entirely cleared with
|
|
Packit |
709fb3 |
the current background color which may be different from the
|
|
Packit |
709fb3 |
default one (see the boolean back_color_erase terminfo(5)
|
|
Packit |
709fb3 |
capability), thus scrolling the display by one line.
|
|
Packit |
709fb3 |
The end of this new line will stay in this background color
|
|
Packit |
709fb3 |
even after reverting to the default background color with
|
|
Packit |
709fb3 |
"\33[m', unless it is explicitly cleared again with "\33[K"
|
|
Packit |
709fb3 |
(which is the behavior the user would instinctively expect
|
|
Packit |
709fb3 |
from the whole thing). There may be some unavoidable
|
|
Packit |
709fb3 |
background-color flicker at the end of this new line because
|
|
Packit |
709fb3 |
of this (when timing with the monitor's redraw is just right).
|
|
Packit |
709fb3 |
-- The behavior of HT (tab, "\t") is usually the same as that of
|
|
Packit |
709fb3 |
Cursor Forward Tabulation (CHT) with a default parameter
|
|
Packit |
709fb3 |
of 1 ("\33[I"), i.e., it performs pure movement to the next
|
|
Packit |
709fb3 |
tab stop, without any clearing of either content or screen
|
|
Packit |
709fb3 |
attributes (including background color); try
|
|
Packit |
709fb3 |
printf 'asdfqwerzxcv\rASDF\tZXCV\n'
|
|
Packit |
709fb3 |
in a bash(1) shell to demonstrate this. This is not what the
|
|
Packit |
709fb3 |
user would instinctively expect of HT (but is ok for CHT).
|
|
Packit |
709fb3 |
The instinctive behavior would include clearing the terminal
|
|
Packit |
709fb3 |
cells that are skipped over by HT with blank cells in the
|
|
Packit |
709fb3 |
current screen attributes, including background color;
|
|
Packit |
709fb3 |
the boolean dest_tabs_magic_smso terminfo(5) capability
|
|
Packit |
709fb3 |
indicates this saner behavior for HT, but only some rare
|
|
Packit |
709fb3 |
terminals have it (although it also indicates a special
|
|
Packit |
709fb3 |
glitch with standout mode in the Teleray terminal for which
|
|
Packit |
709fb3 |
it was initially introduced). The remedy is to add "\33K"
|
|
Packit |
709fb3 |
after each SGR sequence, be it START (to fix the behavior
|
|
Packit |
709fb3 |
of any HT after that before another SGR) or END (to fix the
|
|
Packit |
709fb3 |
behavior of an HT in default background color that would
|
|
Packit |
709fb3 |
follow a line-wrapping at the bottom of the screen in another
|
|
Packit |
709fb3 |
background color, and to complement doing it after START).
|
|
Packit |
709fb3 |
Piping grep's output through a pager such as less(1) avoids
|
|
Packit |
709fb3 |
any HT problems since the pager performs tab expansion.
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
Generic disadvantages of this remedy are:
|
|
Packit |
709fb3 |
-- Some very rare terminals might support SGR but not EL (nobody
|
|
Packit |
709fb3 |
will use "grep --color" on a terminal that does not support
|
|
Packit |
709fb3 |
SGR in the first place).
|
|
Packit |
709fb3 |
-- Having these extra control sequences might somewhat complicate
|
|
Packit |
709fb3 |
the task of any program trying to parse "grep --color"
|
|
Packit |
709fb3 |
output in order to extract structuring information from it.
|
|
Packit |
709fb3 |
A specific disadvantage to doing it after SGR START is:
|
|
Packit |
709fb3 |
-- Even more possible background color flicker (when timing
|
|
Packit |
709fb3 |
with the monitor's redraw is just right), even when not at the
|
|
Packit |
709fb3 |
bottom of the screen.
|
|
Packit |
709fb3 |
There are no additional disadvantages specific to doing it after
|
|
Packit |
709fb3 |
SGR END.
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
It would be impractical for GNU grep to become a full-fledged
|
|
Packit |
709fb3 |
terminal program linked against ncurses or the like, so it will
|
|
Packit |
709fb3 |
not detect terminfo(5) capabilities. */
|
|
Packit |
709fb3 |
static const char *sgr_start = "\33[%sm\33[K";
|
|
Packit |
709fb3 |
static const char *sgr_end = "\33[m\33[K";
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* SGR utility functions. */
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
pr_sgr_start (char const *s)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (*s)
|
|
Packit |
709fb3 |
print_start_colorize (sgr_start, s);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
pr_sgr_end (char const *s)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (*s)
|
|
Packit |
709fb3 |
print_end_colorize (sgr_end);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
pr_sgr_start_if (char const *s)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (color_option)
|
|
Packit |
709fb3 |
pr_sgr_start (s);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
pr_sgr_end_if (char const *s)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (color_option)
|
|
Packit |
709fb3 |
pr_sgr_end (s);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
struct color_cap
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
const char *name;
|
|
Packit |
709fb3 |
const char **var;
|
|
Packit |
709fb3 |
void (*fct) (void);
|
|
Packit |
709fb3 |
};
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
color_cap_mt_fct (void)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* Our caller just set selected_match_color. */
|
|
Packit |
709fb3 |
context_match_color = selected_match_color;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
color_cap_rv_fct (void)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* By this point, it was 1 (or already -1). */
|
|
Packit |
709fb3 |
color_option = -1; /* That's still != 0. */
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
color_cap_ne_fct (void)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
sgr_start = "\33[%sm";
|
|
Packit |
709fb3 |
sgr_end = "\33[m";
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* For GREP_COLORS. */
|
|
Packit |
709fb3 |
static const struct color_cap color_dict[] =
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
{ "mt", &selected_match_color, color_cap_mt_fct }, /* both ms/mc */
|
|
Packit |
709fb3 |
{ "ms", &selected_match_color, NULL }, /* selected matched text */
|
|
Packit |
709fb3 |
{ "mc", &context_match_color, NULL }, /* context matched text */
|
|
Packit |
709fb3 |
{ "fn", &filename_color, NULL }, /* filename */
|
|
Packit |
709fb3 |
{ "ln", &line_num_color, NULL }, /* line number */
|
|
Packit |
709fb3 |
{ "bn", &byte_num_color, NULL }, /* byte (sic) offset */
|
|
Packit |
709fb3 |
{ "se", &sep_color, NULL }, /* separator */
|
|
Packit |
709fb3 |
{ "sl", &selected_line_color, NULL }, /* selected lines */
|
|
Packit |
709fb3 |
{ "cx", &context_line_color, NULL }, /* context lines */
|
|
Packit |
709fb3 |
{ "rv", NULL, color_cap_rv_fct }, /* -v reverses sl/cx */
|
|
Packit |
709fb3 |
{ "ne", NULL, color_cap_ne_fct }, /* no EL on SGR_* */
|
|
Packit |
709fb3 |
{ NULL, NULL, NULL }
|
|
Packit |
709fb3 |
};
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Saved errno value from failed output functions on stdout. */
|
|
Packit |
709fb3 |
static int stdout_errno;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
putchar_errno (int c)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (putchar (c) < 0)
|
|
Packit |
709fb3 |
stdout_errno = errno;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
fputs_errno (char const *s)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (fputs (s, stdout) < 0)
|
|
Packit |
709fb3 |
stdout_errno = errno;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static void _GL_ATTRIBUTE_FORMAT_PRINTF (1, 2)
|
|
Packit |
709fb3 |
printf_errno (char const *format, ...)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
va_list ap;
|
|
Packit |
709fb3 |
va_start (ap, format);
|
|
Packit |
709fb3 |
if (vfprintf (stdout, format, ap) < 0)
|
|
Packit |
709fb3 |
stdout_errno = errno;
|
|
Packit |
709fb3 |
va_end (ap);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
fwrite_errno (void const *ptr, size_t size, size_t nmemb)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (fwrite (ptr, size, nmemb, stdout) != nmemb)
|
|
Packit |
709fb3 |
stdout_errno = errno;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
fflush_errno (void)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (fflush (stdout) != 0)
|
|
Packit |
709fb3 |
stdout_errno = errno;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static struct exclude *excluded_patterns[2];
|
|
Packit |
709fb3 |
static struct exclude *excluded_directory_patterns[2];
|
|
Packit |
709fb3 |
/* Short options. */
|
|
Packit |
709fb3 |
static char const short_options[] =
|
|
Packit |
709fb3 |
"0123456789A:B:C:D:EFGHIPTUVX:abcd:e:f:hiLlm:noqRrsuvwxyZz";
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Non-boolean long options that have no corresponding short equivalents. */
|
|
Packit |
709fb3 |
enum
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
BINARY_FILES_OPTION = CHAR_MAX + 1,
|
|
Packit |
709fb3 |
COLOR_OPTION,
|
|
Packit |
709fb3 |
EXCLUDE_DIRECTORY_OPTION,
|
|
Packit |
709fb3 |
EXCLUDE_OPTION,
|
|
Packit |
709fb3 |
EXCLUDE_FROM_OPTION,
|
|
Packit |
709fb3 |
GROUP_SEPARATOR_OPTION,
|
|
Packit |
709fb3 |
INCLUDE_OPTION,
|
|
Packit |
709fb3 |
LINE_BUFFERED_OPTION,
|
|
Packit |
709fb3 |
LABEL_OPTION
|
|
Packit |
709fb3 |
};
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Long options equivalences. */
|
|
Packit |
709fb3 |
static struct option const long_options[] =
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
{"basic-regexp", no_argument, NULL, 'G'},
|
|
Packit |
709fb3 |
{"extended-regexp", no_argument, NULL, 'E'},
|
|
Packit |
709fb3 |
{"fixed-regexp", no_argument, NULL, 'F'},
|
|
Packit |
709fb3 |
{"fixed-strings", no_argument, NULL, 'F'},
|
|
Packit |
709fb3 |
{"perl-regexp", no_argument, NULL, 'P'},
|
|
Packit |
709fb3 |
{"after-context", required_argument, NULL, 'A'},
|
|
Packit |
709fb3 |
{"before-context", required_argument, NULL, 'B'},
|
|
Packit |
709fb3 |
{"binary-files", required_argument, NULL, BINARY_FILES_OPTION},
|
|
Packit |
709fb3 |
{"byte-offset", no_argument, NULL, 'b'},
|
|
Packit |
709fb3 |
{"context", required_argument, NULL, 'C'},
|
|
Packit |
709fb3 |
{"color", optional_argument, NULL, COLOR_OPTION},
|
|
Packit |
709fb3 |
{"colour", optional_argument, NULL, COLOR_OPTION},
|
|
Packit |
709fb3 |
{"count", no_argument, NULL, 'c'},
|
|
Packit |
709fb3 |
{"devices", required_argument, NULL, 'D'},
|
|
Packit |
709fb3 |
{"directories", required_argument, NULL, 'd'},
|
|
Packit |
709fb3 |
{"exclude", required_argument, NULL, EXCLUDE_OPTION},
|
|
Packit |
709fb3 |
{"exclude-from", required_argument, NULL, EXCLUDE_FROM_OPTION},
|
|
Packit |
709fb3 |
{"exclude-dir", required_argument, NULL, EXCLUDE_DIRECTORY_OPTION},
|
|
Packit |
709fb3 |
{"file", required_argument, NULL, 'f'},
|
|
Packit |
709fb3 |
{"files-with-matches", no_argument, NULL, 'l'},
|
|
Packit |
709fb3 |
{"files-without-match", no_argument, NULL, 'L'},
|
|
Packit |
709fb3 |
{"group-separator", required_argument, NULL, GROUP_SEPARATOR_OPTION},
|
|
Packit |
709fb3 |
{"help", no_argument, &show_help, 1},
|
|
Packit |
709fb3 |
{"include", required_argument, NULL, INCLUDE_OPTION},
|
|
Packit |
709fb3 |
{"ignore-case", no_argument, NULL, 'i'},
|
|
Packit |
709fb3 |
{"initial-tab", no_argument, NULL, 'T'},
|
|
Packit |
709fb3 |
{"label", required_argument, NULL, LABEL_OPTION},
|
|
Packit |
709fb3 |
{"line-buffered", no_argument, NULL, LINE_BUFFERED_OPTION},
|
|
Packit |
709fb3 |
{"line-number", no_argument, NULL, 'n'},
|
|
Packit |
709fb3 |
{"line-regexp", no_argument, NULL, 'x'},
|
|
Packit |
709fb3 |
{"max-count", required_argument, NULL, 'm'},
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
{"no-filename", no_argument, NULL, 'h'},
|
|
Packit |
709fb3 |
{"no-group-separator", no_argument, NULL, GROUP_SEPARATOR_OPTION},
|
|
Packit |
709fb3 |
{"no-messages", no_argument, NULL, 's'},
|
|
Packit |
709fb3 |
{"null", no_argument, NULL, 'Z'},
|
|
Packit |
709fb3 |
{"null-data", no_argument, NULL, 'z'},
|
|
Packit |
709fb3 |
{"only-matching", no_argument, NULL, 'o'},
|
|
Packit |
709fb3 |
{"quiet", no_argument, NULL, 'q'},
|
|
Packit |
709fb3 |
{"recursive", no_argument, NULL, 'r'},
|
|
Packit |
709fb3 |
{"dereference-recursive", no_argument, NULL, 'R'},
|
|
Packit |
709fb3 |
{"regexp", required_argument, NULL, 'e'},
|
|
Packit |
709fb3 |
{"invert-match", no_argument, NULL, 'v'},
|
|
Packit |
709fb3 |
{"silent", no_argument, NULL, 'q'},
|
|
Packit |
709fb3 |
{"text", no_argument, NULL, 'a'},
|
|
Packit |
709fb3 |
{"binary", no_argument, NULL, 'U'},
|
|
Packit |
709fb3 |
{"unix-byte-offsets", no_argument, NULL, 'u'},
|
|
Packit |
709fb3 |
{"version", no_argument, NULL, 'V'},
|
|
Packit |
709fb3 |
{"with-filename", no_argument, NULL, 'H'},
|
|
Packit |
709fb3 |
{"word-regexp", no_argument, NULL, 'w'},
|
|
Packit |
709fb3 |
{0, 0, 0, 0}
|
|
Packit |
709fb3 |
};
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Define flags declared in grep.h. */
|
|
Packit |
709fb3 |
bool match_icase;
|
|
Packit |
709fb3 |
bool match_words;
|
|
Packit |
709fb3 |
bool match_lines;
|
|
Packit |
709fb3 |
char eolbyte;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* For error messages. */
|
|
Packit |
709fb3 |
/* The input file name, or (if standard input) null or a --label argument. */
|
|
Packit |
709fb3 |
static char const *filename;
|
|
Packit |
709fb3 |
/* Omit leading "./" from file names in diagnostics. */
|
|
Packit |
709fb3 |
static bool omit_dot_slash;
|
|
Packit |
709fb3 |
static bool errseen;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* True if output from the current input file has been suppressed
|
|
Packit |
709fb3 |
because an output line had an encoding error. */
|
|
Packit |
709fb3 |
static bool encoding_error_output;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
enum directories_type
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
READ_DIRECTORIES = 2,
|
|
Packit |
709fb3 |
RECURSE_DIRECTORIES,
|
|
Packit |
709fb3 |
SKIP_DIRECTORIES
|
|
Packit |
709fb3 |
};
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* How to handle directories. */
|
|
Packit |
709fb3 |
static char const *const directories_args[] =
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
"read", "recurse", "skip", NULL
|
|
Packit |
709fb3 |
};
|
|
Packit |
709fb3 |
static enum directories_type const directories_types[] =
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
READ_DIRECTORIES, RECURSE_DIRECTORIES, SKIP_DIRECTORIES
|
|
Packit |
709fb3 |
};
|
|
Packit |
709fb3 |
ARGMATCH_VERIFY (directories_args, directories_types);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static enum directories_type directories = READ_DIRECTORIES;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
enum { basic_fts_options = FTS_CWDFD | FTS_NOSTAT | FTS_TIGHT_CYCLE_CHECK };
|
|
Packit |
709fb3 |
static int fts_options = basic_fts_options | FTS_COMFOLLOW | FTS_PHYSICAL;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* How to handle devices. */
|
|
Packit |
709fb3 |
static enum
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
READ_COMMAND_LINE_DEVICES,
|
|
Packit |
709fb3 |
READ_DEVICES,
|
|
Packit |
709fb3 |
SKIP_DEVICES
|
|
Packit |
709fb3 |
} devices = READ_COMMAND_LINE_DEVICES;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static bool grepfile (int, char const *, bool, bool);
|
|
Packit |
709fb3 |
static bool grepdesc (int, bool);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
is_device_mode (mode_t m)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
return S_ISCHR (m) || S_ISBLK (m) || S_ISSOCK (m) || S_ISFIFO (m);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
skip_devices (bool command_line)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
return (devices == SKIP_DEVICES
|
|
Packit |
709fb3 |
|| ((devices == READ_COMMAND_LINE_DEVICES) & !command_line));
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Return if ST->st_size is defined. Assume the file is not a
|
|
Packit |
709fb3 |
symbolic link. */
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
usable_st_size (struct stat const *st)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
return S_ISREG (st->st_mode) || S_TYPEISSHM (st) || S_TYPEISTMO (st);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Lame substitutes for SEEK_DATA and SEEK_HOLE on platforms lacking them.
|
|
Packit |
709fb3 |
Do not rely on these finding data or holes if they equal SEEK_SET. */
|
|
Packit |
709fb3 |
#ifndef SEEK_DATA
|
|
Packit |
709fb3 |
enum { SEEK_DATA = SEEK_SET };
|
|
Packit |
709fb3 |
#endif
|
|
Packit |
709fb3 |
#ifndef SEEK_HOLE
|
|
Packit |
709fb3 |
enum { SEEK_HOLE = SEEK_SET };
|
|
Packit |
709fb3 |
#endif
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* True if lseek with SEEK_CUR or SEEK_DATA failed on the current input. */
|
|
Packit |
709fb3 |
static bool seek_failed;
|
|
Packit |
709fb3 |
static bool seek_data_failed;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Functions we'll use to search. */
|
|
Packit |
709fb3 |
typedef void *(*compile_fp_t) (char *, size_t, reg_syntax_t);
|
|
Packit |
709fb3 |
typedef size_t (*execute_fp_t) (void *, char const *, size_t, size_t *,
|
|
Packit |
709fb3 |
char const *);
|
|
Packit |
709fb3 |
static execute_fp_t execute;
|
|
Packit |
709fb3 |
static void *compiled_pattern;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static char const *
|
|
Packit |
709fb3 |
input_filename (void)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (!filename)
|
|
Packit |
709fb3 |
filename = _("(standard input)");
|
|
Packit |
709fb3 |
return filename;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Unless requested, diagnose an error about the input file. */
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
suppressible_error (int errnum)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (! suppress_errors)
|
|
Packit |
709fb3 |
error (0, errnum, "%s", input_filename ());
|
|
Packit |
709fb3 |
errseen = true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* If there has already been a write error, don't bother closing
|
|
Packit |
709fb3 |
standard output, as that might elicit a duplicate diagnostic. */
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
clean_up_stdout (void)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (! stdout_errno)
|
|
Packit |
709fb3 |
close_stdout ();
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* A cast to TYPE of VAL. Use this when TYPE is a pointer type, VAL
|
|
Packit |
709fb3 |
is properly aligned for TYPE, and 'gcc -Wcast-align' cannot infer
|
|
Packit |
709fb3 |
the alignment and would otherwise complain about the cast. */
|
|
Packit |
709fb3 |
#if 4 < __GNUC__ + (6 <= __GNUC_MINOR__)
|
|
Packit |
709fb3 |
# define CAST_ALIGNED(type, val) \
|
|
Packit |
709fb3 |
({ __typeof__ (val) val_ = val; \
|
|
Packit |
709fb3 |
_Pragma ("GCC diagnostic push") \
|
|
Packit |
709fb3 |
_Pragma ("GCC diagnostic ignored \"-Wcast-align\"") \
|
|
Packit |
709fb3 |
(type) val_; \
|
|
Packit |
709fb3 |
_Pragma ("GCC diagnostic pop") \
|
|
Packit |
709fb3 |
})
|
|
Packit |
709fb3 |
#else
|
|
Packit |
709fb3 |
# define CAST_ALIGNED(type, val) ((type) (val))
|
|
Packit |
709fb3 |
#endif
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* An unsigned type suitable for fast matching. */
|
|
Packit |
709fb3 |
typedef uintmax_t uword;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
struct localeinfo localeinfo;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* A mask to test for unibyte characters, with the pattern repeated to
|
|
Packit |
709fb3 |
fill a uword. For a multibyte character encoding where
|
|
Packit |
709fb3 |
all bytes are unibyte characters, this is 0. For UTF-8, this is
|
|
Packit |
709fb3 |
0x808080.... For encodings where unibyte characters have no discerned
|
|
Packit |
709fb3 |
pattern, this is all 1s. The unsigned char C is a unibyte
|
|
Packit |
709fb3 |
character if C & UNIBYTE_MASK is zero. If the uword W is the
|
|
Packit |
709fb3 |
concatenation of bytes, the bytes are all unibyte characters
|
|
Packit |
709fb3 |
if W & UNIBYTE_MASK is zero. */
|
|
Packit |
709fb3 |
static uword unibyte_mask;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
initialize_unibyte_mask (void)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* For each encoding error I that MASK does not already match,
|
|
Packit |
709fb3 |
accumulate I's most significant 1 bit by ORing it into MASK.
|
|
Packit |
709fb3 |
Although any 1 bit of I could be used, in practice high-order
|
|
Packit |
709fb3 |
bits work better. */
|
|
Packit |
709fb3 |
unsigned char mask = 0;
|
|
Packit |
709fb3 |
int ms1b = 1;
|
|
Packit |
709fb3 |
for (int i = 1; i <= UCHAR_MAX; i++)
|
|
Packit |
709fb3 |
if ((localeinfo.sbclen[i] != 1) & ! (mask & i))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
while (ms1b * 2 <= i)
|
|
Packit |
709fb3 |
ms1b *= 2;
|
|
Packit |
709fb3 |
mask |= ms1b;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Now MASK will detect any encoding-error byte, although it may
|
|
Packit |
709fb3 |
cry wolf and it may not be optimal. Build a uword-length mask by
|
|
Packit |
709fb3 |
repeating MASK. */
|
|
Packit |
709fb3 |
uword uword_max = -1;
|
|
Packit |
709fb3 |
unibyte_mask = uword_max / UCHAR_MAX * mask;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Skip the easy bytes in a buffer that is guaranteed to have a sentinel
|
|
Packit |
709fb3 |
that is not easy, and return a pointer to the first non-easy byte.
|
|
Packit |
709fb3 |
The easy bytes all have UNIBYTE_MASK off. */
|
|
Packit |
709fb3 |
static char const * _GL_ATTRIBUTE_PURE
|
|
Packit |
709fb3 |
skip_easy_bytes (char const *buf)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* Search a byte at a time until the pointer is aligned, then a
|
|
Packit |
709fb3 |
uword at a time until a match is found, then a byte at a time to
|
|
Packit |
709fb3 |
identify the exact byte. The uword search may go slightly past
|
|
Packit |
709fb3 |
the buffer end, but that's benign. */
|
|
Packit |
709fb3 |
char const *p;
|
|
Packit |
709fb3 |
uword const *s;
|
|
Packit |
709fb3 |
for (p = buf; (uintptr_t) p % sizeof (uword) != 0; p++)
|
|
Packit |
709fb3 |
if (to_uchar (*p) & unibyte_mask)
|
|
Packit |
709fb3 |
return p;
|
|
Packit |
709fb3 |
for (s = CAST_ALIGNED (uword const *, p); ! (*s & unibyte_mask); s++)
|
|
Packit |
709fb3 |
continue;
|
|
Packit |
709fb3 |
for (p = (char const *) s; ! (to_uchar (*p) & unibyte_mask); p++)
|
|
Packit |
709fb3 |
continue;
|
|
Packit |
709fb3 |
return p;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Return true if BUF, of size SIZE, has an encoding error.
|
|
Packit |
709fb3 |
BUF must be followed by at least sizeof (uword) bytes,
|
|
Packit |
709fb3 |
the first of which may be modified. */
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
buf_has_encoding_errors (char *buf, size_t size)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (! unibyte_mask)
|
|
Packit |
709fb3 |
return false;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
mbstate_t mbs = { 0 };
|
|
Packit |
709fb3 |
size_t clen;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
buf[size] = -1;
|
|
Packit |
709fb3 |
for (char const *p = buf; (p = skip_easy_bytes (p)) < buf + size; p += clen)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
clen = mbrlen (p, buf + size - p, &mbs);
|
|
Packit |
709fb3 |
if ((size_t) -2 <= clen)
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
return false;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Return true if BUF, of size SIZE, has a null byte.
|
|
Packit |
709fb3 |
BUF must be followed by at least one byte,
|
|
Packit |
709fb3 |
which may be arbitrarily written to or read from. */
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
buf_has_nulls (char *buf, size_t size)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
buf[size] = 0;
|
|
Packit |
709fb3 |
return strlen (buf) != size;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Return true if a file is known to contain null bytes.
|
|
Packit |
709fb3 |
SIZE bytes have already been read from the file
|
|
Packit |
709fb3 |
with descriptor FD and status ST. */
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
file_must_have_nulls (size_t size, int fd, struct stat const *st)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* If the file has holes, it must contain a null byte somewhere. */
|
|
Packit |
709fb3 |
if (SEEK_HOLE != SEEK_SET && !seek_failed
|
|
Packit |
709fb3 |
&& usable_st_size (st) && size < st->st_size)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
off_t cur = size;
|
|
Packit |
709fb3 |
if (O_BINARY || fd == STDIN_FILENO)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
cur = lseek (fd, 0, SEEK_CUR);
|
|
Packit |
709fb3 |
if (cur < 0)
|
|
Packit |
709fb3 |
return false;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Look for a hole after the current location. */
|
|
Packit |
709fb3 |
off_t hole_start = lseek (fd, cur, SEEK_HOLE);
|
|
Packit |
709fb3 |
if (0 <= hole_start)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (lseek (fd, cur, SEEK_SET) < 0)
|
|
Packit |
709fb3 |
suppressible_error (errno);
|
|
Packit |
709fb3 |
if (hole_start < st->st_size)
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
return false;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Convert STR to a nonnegative integer, storing the result in *OUT.
|
|
Packit |
709fb3 |
STR must be a valid context length argument; report an error if it
|
|
Packit |
709fb3 |
isn't. Silently ceiling *OUT at the maximum value, as that is
|
|
Packit |
709fb3 |
practically equivalent to infinity for grep's purposes. */
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
context_length_arg (char const *str, intmax_t *out)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
switch (xstrtoimax (str, 0, 10, out, ""))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
case LONGINT_OK:
|
|
Packit |
709fb3 |
case LONGINT_OVERFLOW:
|
|
Packit |
709fb3 |
if (0 <= *out)
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
FALLTHROUGH;
|
|
Packit |
709fb3 |
default:
|
|
Packit |
709fb3 |
die (EXIT_TROUBLE, 0, "%s: %s", str,
|
|
Packit |
709fb3 |
_("invalid context length argument"));
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Return the add_exclude options suitable for excluding a file name.
|
|
Packit |
709fb3 |
If COMMAND_LINE, it is a command-line file name. */
|
|
Packit |
709fb3 |
static int
|
|
Packit |
709fb3 |
exclude_options (bool command_line)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
return EXCLUDE_WILDCARDS | (command_line ? 0 : EXCLUDE_ANCHORED);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Return true if the file with NAME should be skipped.
|
|
Packit |
709fb3 |
If COMMAND_LINE, it is a command-line argument.
|
|
Packit |
709fb3 |
If IS_DIR, it is a directory. */
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
skipped_file (char const *name, bool command_line, bool is_dir)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
struct exclude **pats;
|
|
Packit |
709fb3 |
if (! is_dir)
|
|
Packit |
709fb3 |
pats = excluded_patterns;
|
|
Packit |
709fb3 |
else if (directories == SKIP_DIRECTORIES)
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
else if (command_line && omit_dot_slash)
|
|
Packit |
709fb3 |
return false;
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
pats = excluded_directory_patterns;
|
|
Packit |
709fb3 |
return pats[command_line] && excluded_file_name (pats[command_line], name);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Hairy buffering mechanism for grep. The intent is to keep
|
|
Packit |
709fb3 |
all reads aligned on a page boundary and multiples of the
|
|
Packit |
709fb3 |
page size, unless a read yields a partial page. */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static char *buffer; /* Base of buffer. */
|
|
Packit |
709fb3 |
static size_t bufalloc; /* Allocated buffer size, counting slop. */
|
|
Packit |
709fb3 |
enum { INITIAL_BUFSIZE = 32768 }; /* Initial buffer size, not counting slop. */
|
|
Packit |
709fb3 |
static int bufdesc; /* File descriptor. */
|
|
Packit |
709fb3 |
static char *bufbeg; /* Beginning of user-visible stuff. */
|
|
Packit |
709fb3 |
static char *buflim; /* Limit of user-visible stuff. */
|
|
Packit |
709fb3 |
static size_t pagesize; /* alignment of memory pages */
|
|
Packit |
709fb3 |
static off_t bufoffset; /* Read offset. */
|
|
Packit |
709fb3 |
static off_t after_last_match; /* Pointer after last matching line that
|
|
Packit |
709fb3 |
would have been output if we were
|
|
Packit |
709fb3 |
outputting characters. */
|
|
Packit |
709fb3 |
static bool skip_nuls; /* Skip '\0' in data. */
|
|
Packit |
709fb3 |
static bool skip_empty_lines; /* Skip empty lines in data. */
|
|
Packit |
709fb3 |
static uintmax_t totalnl; /* Total newline count before lastnl. */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Return VAL aligned to the next multiple of ALIGNMENT. VAL can be
|
|
Packit |
709fb3 |
an integer or a pointer. Both args must be free of side effects. */
|
|
Packit |
709fb3 |
#define ALIGN_TO(val, alignment) \
|
|
Packit |
709fb3 |
((size_t) (val) % (alignment) == 0 \
|
|
Packit |
709fb3 |
? (val) \
|
|
Packit |
709fb3 |
: (val) + ((alignment) - (size_t) (val) % (alignment)))
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Add two numbers that count input bytes or lines, and report an
|
|
Packit |
709fb3 |
error if the addition overflows. */
|
|
Packit |
709fb3 |
static uintmax_t
|
|
Packit |
709fb3 |
add_count (uintmax_t a, uintmax_t b)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
uintmax_t sum = a + b;
|
|
Packit |
709fb3 |
if (sum < a)
|
|
Packit |
709fb3 |
die (EXIT_TROUBLE, 0, _("input is too large to count"));
|
|
Packit |
709fb3 |
return sum;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Return true if BUF (of size SIZE) is all zeros. */
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
all_zeros (char const *buf, size_t size)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
for (char const *p = buf; p < buf + size; p++)
|
|
Packit |
709fb3 |
if (*p)
|
|
Packit |
709fb3 |
return false;
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Reset the buffer for a new file, returning false if we should skip it.
|
|
Packit |
709fb3 |
Initialize on the first time through. */
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
reset (int fd, struct stat const *st)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
bufbeg = buflim = ALIGN_TO (buffer + 1, pagesize);
|
|
Packit |
709fb3 |
bufbeg[-1] = eolbyte;
|
|
Packit |
709fb3 |
bufdesc = fd;
|
|
Packit |
709fb3 |
bufoffset = fd == STDIN_FILENO ? lseek (fd, 0, SEEK_CUR) : 0;
|
|
Packit |
709fb3 |
seek_failed = bufoffset < 0;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Assume SEEK_DATA fails if SEEK_CUR does. */
|
|
Packit |
709fb3 |
seek_data_failed = seek_failed;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (seek_failed)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (errno != ESPIPE)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
suppressible_error (errno);
|
|
Packit |
709fb3 |
return false;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
bufoffset = 0;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Read new stuff into the buffer, saving the specified
|
|
Packit |
709fb3 |
amount of old stuff. When we're done, 'bufbeg' points
|
|
Packit |
709fb3 |
to the beginning of the buffer contents, and 'buflim'
|
|
Packit |
709fb3 |
points just after the end. Return false if there's an error. */
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
fillbuf (size_t save, struct stat const *st)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
size_t fillsize;
|
|
Packit |
709fb3 |
bool cc = true;
|
|
Packit |
709fb3 |
char *readbuf;
|
|
Packit |
709fb3 |
size_t readsize;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Offset from start of buffer to start of old stuff
|
|
Packit |
709fb3 |
that we want to save. */
|
|
Packit |
709fb3 |
size_t saved_offset = buflim - save - buffer;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (pagesize <= buffer + bufalloc - sizeof (uword) - buflim)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
readbuf = buflim;
|
|
Packit |
709fb3 |
bufbeg = buflim - save;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
size_t minsize = save + pagesize;
|
|
Packit |
709fb3 |
size_t newsize;
|
|
Packit |
709fb3 |
size_t newalloc;
|
|
Packit |
709fb3 |
char *newbuf;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Grow newsize until it is at least as great as minsize. */
|
|
Packit |
709fb3 |
for (newsize = bufalloc - pagesize - sizeof (uword);
|
|
Packit |
709fb3 |
newsize < minsize;
|
|
Packit |
709fb3 |
newsize *= 2)
|
|
Packit |
709fb3 |
if ((SIZE_MAX - pagesize - sizeof (uword)) / 2 < newsize)
|
|
Packit |
709fb3 |
xalloc_die ();
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Try not to allocate more memory than the file size indicates,
|
|
Packit |
709fb3 |
as that might cause unnecessary memory exhaustion if the file
|
|
Packit |
709fb3 |
is large. However, do not use the original file size as a
|
|
Packit |
709fb3 |
heuristic if we've already read past the file end, as most
|
|
Packit |
709fb3 |
likely the file is growing. */
|
|
Packit |
709fb3 |
if (usable_st_size (st))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
off_t to_be_read = st->st_size - bufoffset;
|
|
Packit |
709fb3 |
off_t maxsize_off = save + to_be_read;
|
|
Packit |
709fb3 |
if (0 <= to_be_read && to_be_read <= maxsize_off
|
|
Packit |
709fb3 |
&& maxsize_off == (size_t) maxsize_off
|
|
Packit |
709fb3 |
&& minsize <= (size_t) maxsize_off
|
|
Packit |
709fb3 |
&& (size_t) maxsize_off < newsize)
|
|
Packit |
709fb3 |
newsize = maxsize_off;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Add enough room so that the buffer is aligned and has room
|
|
Packit |
709fb3 |
for byte sentinels fore and aft, and so that a uword can
|
|
Packit |
709fb3 |
be read aft. */
|
|
Packit |
709fb3 |
newalloc = newsize + pagesize + sizeof (uword);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
newbuf = bufalloc < newalloc ? xmalloc (bufalloc = newalloc) : buffer;
|
|
Packit |
709fb3 |
readbuf = ALIGN_TO (newbuf + 1 + save, pagesize);
|
|
Packit |
709fb3 |
bufbeg = readbuf - save;
|
|
Packit |
709fb3 |
memmove (bufbeg, buffer + saved_offset, save);
|
|
Packit |
709fb3 |
bufbeg[-1] = eolbyte;
|
|
Packit |
709fb3 |
if (newbuf != buffer)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
free (buffer);
|
|
Packit |
709fb3 |
buffer = newbuf;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
clear_asan_poison ();
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
readsize = buffer + bufalloc - sizeof (uword) - readbuf;
|
|
Packit |
709fb3 |
readsize -= readsize % pagesize;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
while (true)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
fillsize = safe_read (bufdesc, readbuf, readsize);
|
|
Packit |
709fb3 |
if (fillsize == SAFE_READ_ERROR)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
fillsize = 0;
|
|
Packit |
709fb3 |
cc = false;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
bufoffset += fillsize;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (((fillsize == 0) | !skip_nuls) || !all_zeros (readbuf, fillsize))
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
totalnl = add_count (totalnl, fillsize);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (SEEK_DATA != SEEK_SET && !seek_data_failed)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* Solaris SEEK_DATA fails with errno == ENXIO in a hole at EOF. */
|
|
Packit |
709fb3 |
off_t data_start = lseek (bufdesc, bufoffset, SEEK_DATA);
|
|
Packit |
709fb3 |
if (data_start < 0 && errno == ENXIO
|
|
Packit |
709fb3 |
&& usable_st_size (st) && bufoffset < st->st_size)
|
|
Packit |
709fb3 |
data_start = lseek (bufdesc, 0, SEEK_END);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (data_start < 0)
|
|
Packit |
709fb3 |
seek_data_failed = true;
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
totalnl = add_count (totalnl, data_start - bufoffset);
|
|
Packit |
709fb3 |
bufoffset = data_start;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
buflim = readbuf + fillsize;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Initialize the following word, because skip_easy_bytes and some
|
|
Packit |
709fb3 |
matchers read (but do not use) those bytes. This avoids false
|
|
Packit |
709fb3 |
positive reports of these bytes being used uninitialized. */
|
|
Packit |
709fb3 |
memset (buflim, 0, sizeof (uword));
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Mark the part of the buffer not filled by the read or set by
|
|
Packit |
709fb3 |
the above memset call as ASAN-poisoned. */
|
|
Packit |
709fb3 |
asan_poison (buflim + sizeof (uword),
|
|
Packit |
709fb3 |
bufalloc - (buflim - buffer) - sizeof (uword));
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
return cc;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Flags controlling the style of output. */
|
|
Packit |
709fb3 |
static enum
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
BINARY_BINARY_FILES,
|
|
Packit |
709fb3 |
TEXT_BINARY_FILES,
|
|
Packit |
709fb3 |
WITHOUT_MATCH_BINARY_FILES
|
|
Packit |
709fb3 |
} binary_files; /* How to handle binary files. */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Options for output as a list of matching/non-matching files */
|
|
Packit |
709fb3 |
static enum
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
LISTFILES_NONE,
|
|
Packit |
709fb3 |
LISTFILES_MATCHING,
|
|
Packit |
709fb3 |
LISTFILES_NONMATCHING,
|
|
Packit |
709fb3 |
} list_files;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static int filename_mask; /* If zero, output nulls after filenames. */
|
|
Packit |
709fb3 |
static bool out_quiet; /* Suppress all normal output. */
|
|
Packit |
709fb3 |
static bool out_invert; /* Print nonmatching stuff. */
|
|
Packit |
709fb3 |
static int out_file; /* Print filenames. */
|
|
Packit |
709fb3 |
static bool out_line; /* Print line numbers. */
|
|
Packit |
709fb3 |
static bool out_byte; /* Print byte offsets. */
|
|
Packit |
709fb3 |
static intmax_t out_before; /* Lines of leading context. */
|
|
Packit |
709fb3 |
static intmax_t out_after; /* Lines of trailing context. */
|
|
Packit |
709fb3 |
static bool count_matches; /* Count matching lines. */
|
|
Packit |
709fb3 |
static bool no_filenames; /* Suppress file names. */
|
|
Packit |
709fb3 |
static intmax_t max_count; /* Max number of selected
|
|
Packit |
709fb3 |
lines from an input file. */
|
|
Packit |
709fb3 |
static bool line_buffered; /* Use line buffering. */
|
|
Packit |
709fb3 |
static char *label = NULL; /* Fake filename for stdin */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Internal variables to keep track of byte count, context, etc. */
|
|
Packit |
709fb3 |
static uintmax_t totalcc; /* Total character count before bufbeg. */
|
|
Packit |
709fb3 |
static char const *lastnl; /* Pointer after last newline counted. */
|
|
Packit |
709fb3 |
static char *lastout; /* Pointer after last character output;
|
|
Packit |
709fb3 |
NULL if no character has been output
|
|
Packit |
709fb3 |
or if it's conceptually before bufbeg. */
|
|
Packit |
709fb3 |
static intmax_t outleft; /* Maximum number of selected lines. */
|
|
Packit |
709fb3 |
static intmax_t pending; /* Pending lines of output.
|
|
Packit |
709fb3 |
Always kept 0 if out_quiet is true. */
|
|
Packit |
709fb3 |
static bool done_on_match; /* Stop scanning file on first match. */
|
|
Packit |
709fb3 |
static bool exit_on_match; /* Exit on first match. */
|
|
Packit |
709fb3 |
static bool dev_null_output; /* Stdout is known to be /dev/null. */
|
|
Packit |
709fb3 |
static bool binary; /* Use binary rather than text I/O. */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
nlscan (char const *lim)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
size_t newlines = 0;
|
|
Packit |
709fb3 |
char const *beg;
|
|
Packit |
709fb3 |
for (beg = lastnl; beg < lim; beg++)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
beg = memchr (beg, eolbyte, lim - beg);
|
|
Packit |
709fb3 |
if (!beg)
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
newlines++;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
totalnl = add_count (totalnl, newlines);
|
|
Packit |
709fb3 |
lastnl = lim;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Print the current filename. */
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
print_filename (void)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
pr_sgr_start_if (filename_color);
|
|
Packit |
709fb3 |
fputs_errno (input_filename ());
|
|
Packit |
709fb3 |
pr_sgr_end_if (filename_color);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Print a character separator. */
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
print_sep (char sep)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
pr_sgr_start_if (sep_color);
|
|
Packit |
709fb3 |
putchar_errno (sep);
|
|
Packit |
709fb3 |
pr_sgr_end_if (sep_color);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Print a line number or a byte offset. */
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
print_offset (uintmax_t pos, const char *color)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
pr_sgr_start_if (color);
|
|
Packit |
709fb3 |
printf_errno ("%*"PRIuMAX, offset_width, pos);
|
|
Packit |
709fb3 |
pr_sgr_end_if (color);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Print a whole line head (filename, line, byte). The output data
|
|
Packit |
709fb3 |
starts at BEG and contains LEN bytes; it is followed by at least
|
|
Packit |
709fb3 |
sizeof (uword) bytes, the first of which may be temporarily modified.
|
|
Packit |
709fb3 |
The output data comes from what is perhaps a larger input line that
|
|
Packit |
709fb3 |
goes until LIM, where LIM[-1] is an end-of-line byte. Use SEP as
|
|
Packit |
709fb3 |
the separator on output.
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
Return true unless the line was suppressed due to an encoding error. */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
print_line_head (char *beg, size_t len, char const *lim, char sep)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (binary_files != TEXT_BINARY_FILES)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
char ch = beg[len];
|
|
Packit |
709fb3 |
bool encoding_errors = buf_has_encoding_errors (beg, len);
|
|
Packit |
709fb3 |
beg[len] = ch;
|
|
Packit |
709fb3 |
if (encoding_errors)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
encoding_error_output = true;
|
|
Packit |
709fb3 |
return false;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (out_file)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
print_filename ();
|
|
Packit |
709fb3 |
if (filename_mask)
|
|
Packit |
709fb3 |
print_sep (sep);
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
putchar_errno (0);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (out_line)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (lastnl < lim)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
nlscan (beg);
|
|
Packit |
709fb3 |
totalnl = add_count (totalnl, 1);
|
|
Packit |
709fb3 |
lastnl = lim;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
print_offset (totalnl, line_num_color);
|
|
Packit |
709fb3 |
print_sep (sep);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (out_byte)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
uintmax_t pos = add_count (totalcc, beg - bufbeg);
|
|
Packit |
709fb3 |
print_offset (pos, byte_num_color);
|
|
Packit |
709fb3 |
print_sep (sep);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (align_tabs && (out_file | out_line | out_byte) && len != 0)
|
|
Packit |
709fb3 |
putchar_errno ('\t');
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static char *
|
|
Packit |
709fb3 |
print_line_middle (char *beg, char *lim,
|
|
Packit |
709fb3 |
const char *line_color, const char *match_color)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
size_t match_size;
|
|
Packit |
709fb3 |
size_t match_offset;
|
|
Packit |
709fb3 |
char *cur;
|
|
Packit |
709fb3 |
char *mid = NULL;
|
|
Packit |
709fb3 |
char *b;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
for (cur = beg;
|
|
Packit |
709fb3 |
(cur < lim
|
|
Packit |
709fb3 |
&& ((match_offset = execute (compiled_pattern, beg, lim - beg,
|
|
Packit |
709fb3 |
&match_size, cur)) != (size_t) -1));
|
|
Packit |
709fb3 |
cur = b + match_size)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
b = beg + match_offset;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Avoid matching the empty line at the end of the buffer. */
|
|
Packit |
709fb3 |
if (b == lim)
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Avoid hanging on grep --color "" foo */
|
|
Packit |
709fb3 |
if (match_size == 0)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* Make minimal progress; there may be further non-empty matches. */
|
|
Packit |
709fb3 |
/* XXX - Could really advance by one whole multi-octet character. */
|
|
Packit |
709fb3 |
match_size = 1;
|
|
Packit |
709fb3 |
if (!mid)
|
|
Packit |
709fb3 |
mid = cur;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* This function is called on a matching line only,
|
|
Packit |
709fb3 |
but is it selected or rejected/context? */
|
|
Packit |
709fb3 |
if (only_matching)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
char sep = out_invert ? SEP_CHAR_REJECTED : SEP_CHAR_SELECTED;
|
|
Packit |
709fb3 |
if (! print_line_head (b, match_size, lim, sep))
|
|
Packit |
709fb3 |
return NULL;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
pr_sgr_start (line_color);
|
|
Packit |
709fb3 |
if (mid)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
cur = mid;
|
|
Packit |
709fb3 |
mid = NULL;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
fwrite_errno (cur, 1, b - cur);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
pr_sgr_start_if (match_color);
|
|
Packit |
709fb3 |
fwrite_errno (b, 1, match_size);
|
|
Packit |
709fb3 |
pr_sgr_end_if (match_color);
|
|
Packit |
709fb3 |
if (only_matching)
|
|
Packit |
709fb3 |
putchar_errno (eolbyte);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (only_matching)
|
|
Packit |
709fb3 |
cur = lim;
|
|
Packit |
709fb3 |
else if (mid)
|
|
Packit |
709fb3 |
cur = mid;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
return cur;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static char *
|
|
Packit |
709fb3 |
print_line_tail (char *beg, const char *lim, const char *line_color)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
size_t eol_size;
|
|
Packit |
709fb3 |
size_t tail_size;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
eol_size = (lim > beg && lim[-1] == eolbyte);
|
|
Packit |
709fb3 |
eol_size += (lim - eol_size > beg && lim[-(1 + eol_size)] == '\r');
|
|
Packit |
709fb3 |
tail_size = lim - eol_size - beg;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (tail_size > 0)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
pr_sgr_start (line_color);
|
|
Packit |
709fb3 |
fwrite_errno (beg, 1, tail_size);
|
|
Packit |
709fb3 |
beg += tail_size;
|
|
Packit |
709fb3 |
pr_sgr_end (line_color);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
return beg;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
prline (char *beg, char *lim, char sep)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
bool matching;
|
|
Packit |
709fb3 |
const char *line_color;
|
|
Packit |
709fb3 |
const char *match_color;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (!only_matching)
|
|
Packit |
709fb3 |
if (! print_line_head (beg, lim - beg - 1, lim, sep))
|
|
Packit |
709fb3 |
return;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
matching = (sep == SEP_CHAR_SELECTED) ^ out_invert;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (color_option)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
line_color = (((sep == SEP_CHAR_SELECTED)
|
|
Packit |
709fb3 |
^ (out_invert && (color_option < 0)))
|
|
Packit |
709fb3 |
? selected_line_color : context_line_color);
|
|
Packit |
709fb3 |
match_color = (sep == SEP_CHAR_SELECTED
|
|
Packit |
709fb3 |
? selected_match_color : context_match_color);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
line_color = match_color = NULL; /* Shouldn't be used. */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if ((only_matching && matching)
|
|
Packit |
709fb3 |
|| (color_option && (*line_color || *match_color)))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* We already know that non-matching lines have no match (to colorize). */
|
|
Packit |
709fb3 |
if (matching && (only_matching || *match_color))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
beg = print_line_middle (beg, lim, line_color, match_color);
|
|
Packit |
709fb3 |
if (! beg)
|
|
Packit |
709fb3 |
return;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (!only_matching && *line_color)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* This code is exercised at least when grep is invoked like this:
|
|
Packit |
709fb3 |
echo k| GREP_COLORS='sl=01;32' src/grep k --color=always */
|
|
Packit |
709fb3 |
beg = print_line_tail (beg, lim, line_color);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (!only_matching && lim > beg)
|
|
Packit |
709fb3 |
fwrite_errno (beg, 1, lim - beg);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (line_buffered)
|
|
Packit |
709fb3 |
fflush_errno ();
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (stdout_errno)
|
|
Packit |
709fb3 |
die (EXIT_TROUBLE, stdout_errno, _("write error"));
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
lastout = lim;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Print pending lines of trailing context prior to LIM. */
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
prpending (char const *lim)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (!lastout)
|
|
Packit |
709fb3 |
lastout = bufbeg;
|
|
Packit |
709fb3 |
for (; 0 < pending && lastout < lim; pending--)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
char *nl = memchr (lastout, eolbyte, lim - lastout);
|
|
Packit |
709fb3 |
prline (lastout, nl + 1, SEP_CHAR_REJECTED);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Output the lines between BEG and LIM. Deal with context. */
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
prtext (char *beg, char *lim)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
static bool used; /* Avoid printing SEP_STR_GROUP before any output. */
|
|
Packit |
709fb3 |
char eol = eolbyte;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (!out_quiet && pending > 0)
|
|
Packit |
709fb3 |
prpending (beg);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
char *p = beg;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (!out_quiet)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* Deal with leading context. */
|
|
Packit |
709fb3 |
char const *bp = lastout ? lastout : bufbeg;
|
|
Packit |
709fb3 |
intmax_t i;
|
|
Packit |
709fb3 |
for (i = 0; i < out_before; ++i)
|
|
Packit |
709fb3 |
if (p > bp)
|
|
Packit |
709fb3 |
do
|
|
Packit |
709fb3 |
--p;
|
|
Packit |
709fb3 |
while (p[-1] != eol);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Print the group separator unless the output is adjacent to
|
|
Packit |
709fb3 |
the previous output in the file. */
|
|
Packit |
709fb3 |
if ((0 <= out_before || 0 <= out_after) && used
|
|
Packit |
709fb3 |
&& p != lastout && group_separator)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
pr_sgr_start_if (sep_color);
|
|
Packit |
709fb3 |
fputs_errno (group_separator);
|
|
Packit |
709fb3 |
pr_sgr_end_if (sep_color);
|
|
Packit |
709fb3 |
putchar_errno ('\n');
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
while (p < beg)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
char *nl = memchr (p, eol, beg - p);
|
|
Packit |
709fb3 |
nl++;
|
|
Packit |
709fb3 |
prline (p, nl, SEP_CHAR_REJECTED);
|
|
Packit |
709fb3 |
p = nl;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
intmax_t n;
|
|
Packit |
709fb3 |
if (out_invert)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* One or more lines are output. */
|
|
Packit |
709fb3 |
for (n = 0; p < lim && n < outleft; n++)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
char *nl = memchr (p, eol, lim - p);
|
|
Packit |
709fb3 |
nl++;
|
|
Packit |
709fb3 |
if (!out_quiet)
|
|
Packit |
709fb3 |
prline (p, nl, SEP_CHAR_SELECTED);
|
|
Packit |
709fb3 |
p = nl;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* Just one line is output. */
|
|
Packit |
709fb3 |
if (!out_quiet)
|
|
Packit |
709fb3 |
prline (beg, lim, SEP_CHAR_SELECTED);
|
|
Packit |
709fb3 |
n = 1;
|
|
Packit |
709fb3 |
p = lim;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
after_last_match = bufoffset - (buflim - p);
|
|
Packit |
709fb3 |
pending = out_quiet ? 0 : MAX (0, out_after);
|
|
Packit |
709fb3 |
used = true;
|
|
Packit |
709fb3 |
outleft -= n;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Replace all NUL bytes in buffer P (which ends at LIM) with EOL.
|
|
Packit |
709fb3 |
This avoids running out of memory when binary input contains a long
|
|
Packit |
709fb3 |
sequence of zeros, which would otherwise be considered to be part
|
|
Packit |
709fb3 |
of a long line. P[LIM] should be EOL. */
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
zap_nuls (char *p, char *lim, char eol)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (eol)
|
|
Packit |
709fb3 |
while (true)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
*lim = '\0';
|
|
Packit |
709fb3 |
p += strlen (p);
|
|
Packit |
709fb3 |
*lim = eol;
|
|
Packit |
709fb3 |
if (p == lim)
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
do
|
|
Packit |
709fb3 |
*p++ = eol;
|
|
Packit |
709fb3 |
while (!*p);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Scan the specified portion of the buffer, matching lines (or
|
|
Packit |
709fb3 |
between matching lines if OUT_INVERT is true). Return a count of
|
|
Packit |
709fb3 |
lines printed. Replace all NUL bytes with NUL_ZAPPER as we go. */
|
|
Packit |
709fb3 |
static intmax_t
|
|
Packit |
709fb3 |
grepbuf (char *beg, char const *lim)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
intmax_t outleft0 = outleft;
|
|
Packit |
709fb3 |
char *endp;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
for (char *p = beg; p < lim; p = endp)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
size_t match_size;
|
|
Packit |
709fb3 |
size_t match_offset = execute (compiled_pattern, p, lim - p,
|
|
Packit |
709fb3 |
&match_size, NULL);
|
|
Packit |
709fb3 |
if (match_offset == (size_t) -1)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (!out_invert)
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
match_offset = lim - p;
|
|
Packit |
709fb3 |
match_size = 0;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
char *b = p + match_offset;
|
|
Packit |
709fb3 |
endp = b + match_size;
|
|
Packit |
709fb3 |
/* Avoid matching the empty line at the end of the buffer. */
|
|
Packit |
709fb3 |
if (!out_invert && b == lim)
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
if (!out_invert || p < b)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
char *prbeg = out_invert ? p : b;
|
|
Packit |
709fb3 |
char *prend = out_invert ? b : endp;
|
|
Packit |
709fb3 |
prtext (prbeg, prend);
|
|
Packit |
709fb3 |
if (!outleft || done_on_match)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (exit_on_match)
|
|
Packit |
709fb3 |
exit (errseen ? exit_failure : EXIT_SUCCESS);
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
return outleft0 - outleft;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Search a given (non-directory) file. Return a count of lines printed.
|
|
Packit |
709fb3 |
Set *INEOF to true if end-of-file reached. */
|
|
Packit |
709fb3 |
static intmax_t
|
|
Packit |
709fb3 |
grep (int fd, struct stat const *st, bool *ineof)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
intmax_t nlines, i;
|
|
Packit |
709fb3 |
size_t residue, save;
|
|
Packit |
709fb3 |
char oldc;
|
|
Packit |
709fb3 |
char *beg;
|
|
Packit |
709fb3 |
char *lim;
|
|
Packit |
709fb3 |
char eol = eolbyte;
|
|
Packit |
709fb3 |
char nul_zapper = '\0';
|
|
Packit |
709fb3 |
bool done_on_match_0 = done_on_match;
|
|
Packit |
709fb3 |
bool out_quiet_0 = out_quiet;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* The value of NLINES when nulls were first deduced in the input;
|
|
Packit |
709fb3 |
this is not necessarily the same as the number of matching lines
|
|
Packit |
709fb3 |
before the first null. -1 if no input nulls have been deduced. */
|
|
Packit |
709fb3 |
intmax_t nlines_first_null = -1;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (! reset (fd, st))
|
|
Packit |
709fb3 |
return 0;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
totalcc = 0;
|
|
Packit |
709fb3 |
lastout = 0;
|
|
Packit |
709fb3 |
totalnl = 0;
|
|
Packit |
709fb3 |
outleft = max_count;
|
|
Packit |
709fb3 |
after_last_match = 0;
|
|
Packit |
709fb3 |
pending = 0;
|
|
Packit |
709fb3 |
skip_nuls = skip_empty_lines && !eol;
|
|
Packit |
709fb3 |
encoding_error_output = false;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
nlines = 0;
|
|
Packit |
709fb3 |
residue = 0;
|
|
Packit |
709fb3 |
save = 0;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (! fillbuf (save, st))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
suppressible_error (errno);
|
|
Packit |
709fb3 |
return 0;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
offset_width = 0;
|
|
Packit |
709fb3 |
if (align_tabs)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* Width is log of maximum number. Line numbers are origin-1. */
|
|
Packit |
709fb3 |
uintmax_t num = usable_st_size (st) ? st->st_size : UINTMAX_MAX;
|
|
Packit |
709fb3 |
num += out_line && num < UINTMAX_MAX;
|
|
Packit |
709fb3 |
do
|
|
Packit |
709fb3 |
offset_width++;
|
|
Packit |
709fb3 |
while ((num /= 10) != 0);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
for (bool firsttime = true; ; firsttime = false)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (nlines_first_null < 0 && eol && binary_files != TEXT_BINARY_FILES
|
|
Packit |
709fb3 |
&& (buf_has_nulls (bufbeg, buflim - bufbeg)
|
|
Packit |
709fb3 |
|| (firsttime && file_must_have_nulls (buflim - bufbeg, fd, st))))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (binary_files == WITHOUT_MATCH_BINARY_FILES)
|
|
Packit |
709fb3 |
return 0;
|
|
Packit |
709fb3 |
if (!count_matches)
|
|
Packit |
709fb3 |
done_on_match = out_quiet = true;
|
|
Packit |
709fb3 |
nlines_first_null = nlines;
|
|
Packit |
709fb3 |
nul_zapper = eol;
|
|
Packit |
709fb3 |
skip_nuls = skip_empty_lines;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
lastnl = bufbeg;
|
|
Packit |
709fb3 |
if (lastout)
|
|
Packit |
709fb3 |
lastout = bufbeg;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
beg = bufbeg + save;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* no more data to scan (eof) except for maybe a residue -> break */
|
|
Packit |
709fb3 |
if (beg == buflim)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
*ineof = true;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
zap_nuls (beg, buflim, nul_zapper);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Determine new residue (the length of an incomplete line at the end of
|
|
Packit |
709fb3 |
the buffer, 0 means there is no incomplete last line). */
|
|
Packit |
709fb3 |
oldc = beg[-1];
|
|
Packit |
709fb3 |
beg[-1] = eol;
|
|
Packit |
709fb3 |
/* FIXME: use rawmemrchr if/when it exists, since we have ensured
|
|
Packit |
709fb3 |
that this use of memrchr is guaranteed never to return NULL. */
|
|
Packit |
709fb3 |
lim = memrchr (beg - 1, eol, buflim - beg + 1);
|
|
Packit |
709fb3 |
++lim;
|
|
Packit |
709fb3 |
beg[-1] = oldc;
|
|
Packit |
709fb3 |
if (lim == beg)
|
|
Packit |
709fb3 |
lim = beg - residue;
|
|
Packit |
709fb3 |
beg -= residue;
|
|
Packit |
709fb3 |
residue = buflim - lim;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (beg < lim)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (outleft)
|
|
Packit |
709fb3 |
nlines += grepbuf (beg, lim);
|
|
Packit |
709fb3 |
if (pending)
|
|
Packit |
709fb3 |
prpending (lim);
|
|
Packit |
709fb3 |
if ((!outleft && !pending)
|
|
Packit |
709fb3 |
|| (done_on_match && MAX (0, nlines_first_null) < nlines))
|
|
Packit |
709fb3 |
goto finish_grep;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* The last OUT_BEFORE lines at the end of the buffer will be needed as
|
|
Packit |
709fb3 |
leading context if there is a matching line at the begin of the
|
|
Packit |
709fb3 |
next data. Make beg point to their begin. */
|
|
Packit |
709fb3 |
i = 0;
|
|
Packit |
709fb3 |
beg = lim;
|
|
Packit |
709fb3 |
while (i < out_before && beg > bufbeg && beg != lastout)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
++i;
|
|
Packit |
709fb3 |
do
|
|
Packit |
709fb3 |
--beg;
|
|
Packit |
709fb3 |
while (beg[-1] != eol);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Detect whether leading context is adjacent to previous output. */
|
|
Packit |
709fb3 |
if (beg != lastout)
|
|
Packit |
709fb3 |
lastout = 0;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Handle some details and read more data to scan. */
|
|
Packit |
709fb3 |
save = residue + lim - beg;
|
|
Packit |
709fb3 |
if (out_byte)
|
|
Packit |
709fb3 |
totalcc = add_count (totalcc, buflim - bufbeg - save);
|
|
Packit |
709fb3 |
if (out_line)
|
|
Packit |
709fb3 |
nlscan (beg);
|
|
Packit |
709fb3 |
if (! fillbuf (save, st))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
suppressible_error (errno);
|
|
Packit |
709fb3 |
goto finish_grep;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
if (residue)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
*buflim++ = eol;
|
|
Packit |
709fb3 |
if (outleft)
|
|
Packit |
709fb3 |
nlines += grepbuf (bufbeg + save - residue, buflim);
|
|
Packit |
709fb3 |
if (pending)
|
|
Packit |
709fb3 |
prpending (buflim);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
finish_grep:
|
|
Packit |
709fb3 |
done_on_match = done_on_match_0;
|
|
Packit |
709fb3 |
out_quiet = out_quiet_0;
|
|
Packit |
709fb3 |
if (!out_quiet && (encoding_error_output
|
|
Packit |
709fb3 |
|| (0 <= nlines_first_null && nlines_first_null < nlines)))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
printf_errno (_("Binary file %s matches\n"), input_filename ());
|
|
Packit |
709fb3 |
if (line_buffered)
|
|
Packit |
709fb3 |
fflush_errno ();
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
return nlines;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
grepdirent (FTS *fts, FTSENT *ent, bool command_line)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
bool follow;
|
|
Packit |
709fb3 |
command_line &= ent->fts_level == FTS_ROOTLEVEL;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (ent->fts_info == FTS_DP)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (directories == RECURSE_DIRECTORIES && command_line)
|
|
Packit |
709fb3 |
out_file &= ~ (2 * !no_filenames);
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (!command_line
|
|
Packit |
709fb3 |
&& skipped_file (ent->fts_name, false,
|
|
Packit |
709fb3 |
(ent->fts_info == FTS_D || ent->fts_info == FTS_DC
|
|
Packit |
709fb3 |
|| ent->fts_info == FTS_DNR)))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
fts_set (fts, ent, FTS_SKIP);
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
filename = ent->fts_path;
|
|
Packit |
709fb3 |
if (omit_dot_slash && filename[1])
|
|
Packit |
709fb3 |
filename += 2;
|
|
Packit |
709fb3 |
follow = (fts->fts_options & FTS_LOGICAL
|
|
Packit |
709fb3 |
|| (fts->fts_options & FTS_COMFOLLOW && command_line));
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
switch (ent->fts_info)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
case FTS_D:
|
|
Packit |
709fb3 |
if (directories == RECURSE_DIRECTORIES)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
out_file |= 2 * !no_filenames;
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
fts_set (fts, ent, FTS_SKIP);
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case FTS_DC:
|
|
Packit |
709fb3 |
if (!suppress_errors)
|
|
Packit |
709fb3 |
error (0, 0, _("warning: %s: %s"), filename,
|
|
Packit |
709fb3 |
_("recursive directory loop"));
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case FTS_DNR:
|
|
Packit |
709fb3 |
case FTS_ERR:
|
|
Packit |
709fb3 |
case FTS_NS:
|
|
Packit |
709fb3 |
suppressible_error (ent->fts_errno);
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case FTS_DEFAULT:
|
|
Packit |
709fb3 |
case FTS_NSOK:
|
|
Packit |
709fb3 |
if (skip_devices (command_line))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
struct stat *st = ent->fts_statp;
|
|
Packit |
709fb3 |
struct stat st1;
|
|
Packit |
709fb3 |
if (! st->st_mode)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* The file type is not already known. Get the file status
|
|
Packit |
709fb3 |
before opening, since opening might have side effects
|
|
Packit |
709fb3 |
on a device. */
|
|
Packit |
709fb3 |
int flag = follow ? 0 : AT_SYMLINK_NOFOLLOW;
|
|
Packit |
709fb3 |
if (fstatat (fts->fts_cwd_fd, ent->fts_accpath, &st1, flag) != 0)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
suppressible_error (errno);
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
st = &st1;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
if (is_device_mode (st->st_mode))
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case FTS_F:
|
|
Packit |
709fb3 |
case FTS_SLNONE:
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case FTS_SL:
|
|
Packit |
709fb3 |
case FTS_W:
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
default:
|
|
Packit |
709fb3 |
abort ();
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
return grepfile (fts->fts_cwd_fd, ent->fts_accpath, follow, command_line);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* True if errno is ERR after 'open ("symlink", ... O_NOFOLLOW ...)'.
|
|
Packit |
709fb3 |
POSIX specifies ELOOP, but it's EMLINK on FreeBSD and EFTYPE on NetBSD. */
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
open_symlink_nofollow_error (int err)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (err == ELOOP || err == EMLINK)
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
#ifdef EFTYPE
|
|
Packit |
709fb3 |
if (err == EFTYPE)
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
#endif
|
|
Packit |
709fb3 |
return false;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
grepfile (int dirdesc, char const *name, bool follow, bool command_line)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
int oflag = (O_RDONLY | O_NOCTTY
|
|
Packit |
709fb3 |
| (IGNORE_DUPLICATE_BRANCH_WARNING
|
|
Packit |
709fb3 |
(binary ? O_BINARY : 0))
|
|
Packit |
709fb3 |
| (follow ? 0 : O_NOFOLLOW)
|
|
Packit |
709fb3 |
| (skip_devices (command_line) ? O_NONBLOCK : 0));
|
|
Packit |
709fb3 |
int desc = openat_safer (dirdesc, name, oflag);
|
|
Packit |
709fb3 |
if (desc < 0)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (follow || ! open_symlink_nofollow_error (errno))
|
|
Packit |
709fb3 |
suppressible_error (errno);
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
return grepdesc (desc, command_line);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Read all data from FD, with status ST. Return true if successful,
|
|
Packit |
709fb3 |
false (setting errno) otherwise. */
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
drain_input (int fd, struct stat const *st)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
ssize_t nbytes;
|
|
Packit |
709fb3 |
if (S_ISFIFO (st->st_mode) && dev_null_output)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
#ifdef SPLICE_F_MOVE
|
|
Packit |
709fb3 |
/* Should be faster, since it need not copy data to user space. */
|
|
Packit |
709fb3 |
nbytes = splice (fd, NULL, STDOUT_FILENO, NULL,
|
|
Packit |
709fb3 |
INITIAL_BUFSIZE, SPLICE_F_MOVE);
|
|
Packit |
709fb3 |
if (0 <= nbytes || errno != EINVAL)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
while (0 < nbytes)
|
|
Packit |
709fb3 |
nbytes = splice (fd, NULL, STDOUT_FILENO, NULL,
|
|
Packit |
709fb3 |
INITIAL_BUFSIZE, SPLICE_F_MOVE);
|
|
Packit |
709fb3 |
return nbytes == 0;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
#endif
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
while ((nbytes = safe_read (fd, buffer, bufalloc)))
|
|
Packit |
709fb3 |
if (nbytes == SAFE_READ_ERROR)
|
|
Packit |
709fb3 |
return false;
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Finish reading from FD, with status ST and where end-of-file has
|
|
Packit |
709fb3 |
been seen if INEOF. Typically this is a no-op, but when reading
|
|
Packit |
709fb3 |
from standard input this may adjust the file offset or drain a
|
|
Packit |
709fb3 |
pipe. */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
finalize_input (int fd, struct stat const *st, bool ineof)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (fd == STDIN_FILENO
|
|
Packit |
709fb3 |
&& (outleft
|
|
Packit |
709fb3 |
? (!ineof
|
|
Packit |
709fb3 |
&& (seek_failed
|
|
Packit |
709fb3 |
|| (lseek (fd, 0, SEEK_END) < 0
|
|
Packit |
709fb3 |
/* Linux proc file system has EINVAL (Bug#25180). */
|
|
Packit |
709fb3 |
&& errno != EINVAL))
|
|
Packit |
709fb3 |
&& ! drain_input (fd, st))
|
|
Packit |
709fb3 |
: (bufoffset != after_last_match && !seek_failed
|
|
Packit |
709fb3 |
&& lseek (fd, after_last_match, SEEK_SET) < 0)))
|
|
Packit |
709fb3 |
suppressible_error (errno);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
grepdesc (int desc, bool command_line)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
intmax_t count;
|
|
Packit |
709fb3 |
bool status = true;
|
|
Packit |
709fb3 |
bool ineof = false;
|
|
Packit |
709fb3 |
struct stat st;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Get the file status, possibly for the second time. This catches
|
|
Packit |
709fb3 |
a race condition if the directory entry changes after the
|
|
Packit |
709fb3 |
directory entry is read and before the file is opened. For
|
|
Packit |
709fb3 |
example, normally DESC is a directory only at the top level, but
|
|
Packit |
709fb3 |
there is an exception if some other process substitutes a
|
|
Packit |
709fb3 |
directory for a non-directory while 'grep' is running. */
|
|
Packit |
709fb3 |
if (fstat (desc, &st) != 0)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
suppressible_error (errno);
|
|
Packit |
709fb3 |
goto closeout;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (desc != STDIN_FILENO && skip_devices (command_line)
|
|
Packit |
709fb3 |
&& is_device_mode (st.st_mode))
|
|
Packit |
709fb3 |
goto closeout;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (desc != STDIN_FILENO && command_line
|
|
Packit |
709fb3 |
&& skipped_file (filename, true, S_ISDIR (st.st_mode) != 0))
|
|
Packit |
709fb3 |
goto closeout;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (desc != STDIN_FILENO
|
|
Packit |
709fb3 |
&& directories == RECURSE_DIRECTORIES && S_ISDIR (st.st_mode))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* Traverse the directory starting with its full name, because
|
|
Packit |
709fb3 |
unfortunately fts provides no way to traverse the directory
|
|
Packit |
709fb3 |
starting from its file descriptor. */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
FTS *fts;
|
|
Packit |
709fb3 |
FTSENT *ent;
|
|
Packit |
709fb3 |
int opts = fts_options & ~(command_line ? 0 : FTS_COMFOLLOW);
|
|
Packit |
709fb3 |
char *fts_arg[2];
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Close DESC now, to conserve file descriptors if the race
|
|
Packit |
709fb3 |
condition occurs many times in a deep recursion. */
|
|
Packit |
709fb3 |
if (close (desc) != 0)
|
|
Packit |
709fb3 |
suppressible_error (errno);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
fts_arg[0] = (char *) filename;
|
|
Packit |
709fb3 |
fts_arg[1] = NULL;
|
|
Packit |
709fb3 |
fts = fts_open (fts_arg, opts, NULL);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (!fts)
|
|
Packit |
709fb3 |
xalloc_die ();
|
|
Packit |
709fb3 |
while ((ent = fts_read (fts)))
|
|
Packit |
709fb3 |
status &= grepdirent (fts, ent, command_line);
|
|
Packit |
709fb3 |
if (errno)
|
|
Packit |
709fb3 |
suppressible_error (errno);
|
|
Packit |
709fb3 |
if (fts_close (fts) != 0)
|
|
Packit |
709fb3 |
suppressible_error (errno);
|
|
Packit |
709fb3 |
return status;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
if (desc != STDIN_FILENO
|
|
Packit |
709fb3 |
&& ((directories == SKIP_DIRECTORIES && S_ISDIR (st.st_mode))
|
|
Packit |
709fb3 |
|| ((devices == SKIP_DEVICES
|
|
Packit |
709fb3 |
|| (devices == READ_COMMAND_LINE_DEVICES && !command_line))
|
|
Packit |
709fb3 |
&& is_device_mode (st.st_mode))))
|
|
Packit |
709fb3 |
goto closeout;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* If there is a regular file on stdout and the current file refers
|
|
Packit |
709fb3 |
to the same i-node, we have to report the problem and skip it.
|
|
Packit |
709fb3 |
Otherwise when matching lines from some other input reach the
|
|
Packit |
709fb3 |
disk before we open this file, we can end up reading and matching
|
|
Packit |
709fb3 |
those lines and appending them to the file from which we're reading.
|
|
Packit |
709fb3 |
Then we'd have what appears to be an infinite loop that'd terminate
|
|
Packit |
709fb3 |
only upon filling the output file system or reaching a quota.
|
|
Packit |
709fb3 |
However, there is no risk of an infinite loop if grep is generating
|
|
Packit |
709fb3 |
no output, i.e., with --silent, --quiet, -q.
|
|
Packit |
709fb3 |
Similarly, with any of these:
|
|
Packit |
709fb3 |
--max-count=N (-m) (for N >= 2)
|
|
Packit |
709fb3 |
--files-with-matches (-l)
|
|
Packit |
709fb3 |
--files-without-match (-L)
|
|
Packit |
709fb3 |
there is no risk of trouble.
|
|
Packit |
709fb3 |
For --max-count=1, grep stops after printing the first match,
|
|
Packit |
709fb3 |
so there is no risk of malfunction. But even --max-count=2, with
|
|
Packit |
709fb3 |
input==output, while there is no risk of infloop, there is a race
|
|
Packit |
709fb3 |
condition that could result in "alternate" output. */
|
|
Packit |
709fb3 |
if (!out_quiet && list_files == LISTFILES_NONE && 1 < max_count
|
|
Packit |
709fb3 |
&& S_ISREG (st.st_mode) && SAME_INODE (st, out_stat))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (! suppress_errors)
|
|
Packit |
709fb3 |
error (0, 0, _("input file %s is also the output"),
|
|
Packit |
709fb3 |
quote (input_filename ()));
|
|
Packit |
709fb3 |
errseen = true;
|
|
Packit |
709fb3 |
goto closeout;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
count = grep (desc, &st, &ineof);
|
|
Packit |
709fb3 |
if (count_matches)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (out_file)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
print_filename ();
|
|
Packit |
709fb3 |
if (filename_mask)
|
|
Packit |
709fb3 |
print_sep (SEP_CHAR_SELECTED);
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
putchar_errno (0);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
printf_errno ("%" PRIdMAX "\n", count);
|
|
Packit |
709fb3 |
if (line_buffered)
|
|
Packit |
709fb3 |
fflush_errno ();
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
status = !count;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (list_files == LISTFILES_NONE)
|
|
Packit |
709fb3 |
finalize_input (desc, &st, ineof);
|
|
Packit |
709fb3 |
else if (list_files == (status ? LISTFILES_NONMATCHING : LISTFILES_MATCHING))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
print_filename ();
|
|
Packit |
709fb3 |
putchar_errno ('\n' & filename_mask);
|
|
Packit |
709fb3 |
if (line_buffered)
|
|
Packit |
709fb3 |
fflush_errno ();
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
closeout:
|
|
Packit |
709fb3 |
if (desc != STDIN_FILENO && close (desc) != 0)
|
|
Packit |
709fb3 |
suppressible_error (errno);
|
|
Packit |
709fb3 |
return status;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
grep_command_line_arg (char const *arg)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (STREQ (arg, "-"))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
filename = label;
|
|
Packit |
709fb3 |
if (binary)
|
|
Packit |
709fb3 |
xset_binary_mode (STDIN_FILENO, O_BINARY);
|
|
Packit |
709fb3 |
return grepdesc (STDIN_FILENO, true);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
filename = arg;
|
|
Packit |
709fb3 |
return grepfile (AT_FDCWD, arg, true, true);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
_Noreturn void usage (int);
|
|
Packit |
709fb3 |
void
|
|
Packit |
709fb3 |
usage (int status)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (status != 0)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
fprintf (stderr, _("Usage: %s [OPTION]... PATTERN [FILE]...\n"),
|
|
Packit |
709fb3 |
getprogname ());
|
|
Packit |
709fb3 |
fprintf (stderr, _("Try '%s --help' for more information.\n"),
|
|
Packit |
709fb3 |
getprogname ());
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
printf (_("Usage: %s [OPTION]... PATTERN [FILE]...\n"), getprogname ());
|
|
Packit |
709fb3 |
printf (_("Search for PATTERN in each FILE.\n"));
|
|
Packit |
709fb3 |
printf (_("\
|
|
Packit |
709fb3 |
Example: %s -i 'hello world' menu.h main.c\n\
|
|
Packit |
709fb3 |
\n\
|
|
Packit |
709fb3 |
Pattern selection and interpretation:\n"), getprogname ());
|
|
Packit |
709fb3 |
printf (_("\
|
|
Packit |
709fb3 |
-E, --extended-regexp PATTERN is an extended regular expression\n\
|
|
Packit |
709fb3 |
-F, --fixed-strings PATTERN is a set of newline-separated strings\n\
|
|
Packit |
709fb3 |
-G, --basic-regexp PATTERN is a basic regular expression (default)\n\
|
|
Packit |
709fb3 |
-P, --perl-regexp PATTERN is a Perl regular expression\n"));
|
|
Packit |
709fb3 |
/* -X is deliberately undocumented. */
|
|
Packit |
709fb3 |
printf (_("\
|
|
Packit |
709fb3 |
-e, --regexp=PATTERN use PATTERN for matching\n\
|
|
Packit |
709fb3 |
-f, --file=FILE obtain PATTERN from FILE\n\
|
|
Packit |
709fb3 |
-i, --ignore-case ignore case distinctions\n\
|
|
Packit |
709fb3 |
-w, --word-regexp force PATTERN to match only whole words\n\
|
|
Packit |
709fb3 |
-x, --line-regexp force PATTERN to match only whole lines\n\
|
|
Packit |
709fb3 |
-z, --null-data a data line ends in 0 byte, not newline\n"));
|
|
Packit |
709fb3 |
printf (_("\
|
|
Packit |
709fb3 |
\n\
|
|
Packit |
709fb3 |
Miscellaneous:\n\
|
|
Packit |
709fb3 |
-s, --no-messages suppress error messages\n\
|
|
Packit |
709fb3 |
-v, --invert-match select non-matching lines\n\
|
|
Packit |
709fb3 |
-V, --version display version information and exit\n\
|
|
Packit |
709fb3 |
--help display this help text and exit\n"));
|
|
Packit |
709fb3 |
printf (_("\
|
|
Packit |
709fb3 |
\n\
|
|
Packit |
709fb3 |
Output control:\n\
|
|
Packit |
709fb3 |
-m, --max-count=NUM stop after NUM selected lines\n\
|
|
Packit |
709fb3 |
-b, --byte-offset print the byte offset with output lines\n\
|
|
Packit |
709fb3 |
-n, --line-number print line number with output lines\n\
|
|
Packit |
709fb3 |
--line-buffered flush output on every line\n\
|
|
Packit |
709fb3 |
-H, --with-filename print file name with output lines\n\
|
|
Packit |
709fb3 |
-h, --no-filename suppress the file name prefix on output\n\
|
|
Packit |
709fb3 |
--label=LABEL use LABEL as the standard input file name prefix\n\
|
|
Packit |
709fb3 |
"));
|
|
Packit |
709fb3 |
printf (_("\
|
|
Packit |
709fb3 |
-o, --only-matching show only the part of a line matching PATTERN\n\
|
|
Packit |
709fb3 |
-q, --quiet, --silent suppress all normal output\n\
|
|
Packit |
709fb3 |
--binary-files=TYPE assume that binary files are TYPE;\n\
|
|
Packit |
709fb3 |
TYPE is 'binary', 'text', or 'without-match'\n\
|
|
Packit |
709fb3 |
-a, --text equivalent to --binary-files=text\n\
|
|
Packit |
709fb3 |
"));
|
|
Packit |
709fb3 |
printf (_("\
|
|
Packit |
709fb3 |
-I equivalent to --binary-files=without-match\n\
|
|
Packit |
709fb3 |
-d, --directories=ACTION how to handle directories;\n\
|
|
Packit |
709fb3 |
ACTION is 'read', 'recurse', or 'skip'\n\
|
|
Packit |
709fb3 |
-D, --devices=ACTION how to handle devices, FIFOs and sockets;\n\
|
|
Packit |
709fb3 |
ACTION is 'read' or 'skip'\n\
|
|
Packit |
709fb3 |
-r, --recursive like --directories=recurse\n\
|
|
Packit |
709fb3 |
-R, --dereference-recursive likewise, but follow all symlinks\n\
|
|
Packit |
709fb3 |
"));
|
|
Packit |
709fb3 |
printf (_("\
|
|
Packit |
709fb3 |
--include=FILE_PATTERN search only files that match FILE_PATTERN\n\
|
|
Packit |
709fb3 |
--exclude=FILE_PATTERN skip files and directories matching\
|
|
Packit |
709fb3 |
FILE_PATTERN\n\
|
|
Packit |
709fb3 |
--exclude-from=FILE skip files matching any file pattern from FILE\n\
|
|
Packit |
709fb3 |
--exclude-dir=PATTERN directories that match PATTERN will be skipped.\n\
|
|
Packit |
709fb3 |
"));
|
|
Packit |
709fb3 |
printf (_("\
|
|
Packit |
709fb3 |
-L, --files-without-match print only names of FILEs with no selected lines\n\
|
|
Packit |
709fb3 |
-l, --files-with-matches print only names of FILEs with selected lines\n\
|
|
Packit |
709fb3 |
-c, --count print only a count of selected lines per FILE\n\
|
|
Packit |
709fb3 |
-T, --initial-tab make tabs line up (if needed)\n\
|
|
Packit |
709fb3 |
-Z, --null print 0 byte after FILE name\n"));
|
|
Packit |
709fb3 |
printf (_("\
|
|
Packit |
709fb3 |
\n\
|
|
Packit |
709fb3 |
Context control:\n\
|
|
Packit |
709fb3 |
-B, --before-context=NUM print NUM lines of leading context\n\
|
|
Packit |
709fb3 |
-A, --after-context=NUM print NUM lines of trailing context\n\
|
|
Packit |
709fb3 |
-C, --context=NUM print NUM lines of output context\n\
|
|
Packit |
709fb3 |
"));
|
|
Packit |
709fb3 |
printf (_("\
|
|
Packit |
709fb3 |
-NUM same as --context=NUM\n\
|
|
Packit |
cb4c47 |
--group-separator=SEP use SEP as a group separator\n\
|
|
Packit |
cb4c47 |
--no-group-separator use empty string as a group separator\n\
|
|
Packit |
709fb3 |
--color[=WHEN],\n\
|
|
Packit |
709fb3 |
--colour[=WHEN] use markers to highlight the matching strings;\n\
|
|
Packit |
709fb3 |
WHEN is 'always', 'never', or 'auto'\n\
|
|
Packit |
709fb3 |
-U, --binary do not strip CR characters at EOL (MSDOS/Windows)\n\
|
|
Packit |
709fb3 |
\n"));
|
|
Packit |
709fb3 |
printf (_("\
|
|
Packit |
709fb3 |
When FILE is '-', read standard input. With no FILE, read '.' if\n\
|
|
Packit |
709fb3 |
recursive, '-' otherwise. With fewer than two FILEs, assume -h.\n\
|
|
Packit |
709fb3 |
Exit status is 0 if any line is selected, 1 otherwise;\n\
|
|
Packit |
709fb3 |
if any error occurs and -q is not given, the exit status is 2.\n"));
|
|
Packit |
709fb3 |
emit_bug_reporting_address ();
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
exit (status);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Pattern compilers and matchers. */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static struct
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
char const name[12];
|
|
Packit |
709fb3 |
int syntax; /* used if compile == GEAcompile */
|
|
Packit |
709fb3 |
compile_fp_t compile;
|
|
Packit |
709fb3 |
execute_fp_t execute;
|
|
Packit |
709fb3 |
} const matchers[] = {
|
|
Packit |
709fb3 |
{ "grep", RE_SYNTAX_GREP, GEAcompile, EGexecute },
|
|
Packit |
709fb3 |
{ "egrep", RE_SYNTAX_EGREP, GEAcompile, EGexecute },
|
|
Packit |
709fb3 |
{ "fgrep", 0, Fcompile, Fexecute, },
|
|
Packit |
709fb3 |
{ "awk", RE_SYNTAX_AWK, GEAcompile, EGexecute },
|
|
Packit |
709fb3 |
{ "gawk", RE_SYNTAX_GNU_AWK, GEAcompile, EGexecute },
|
|
Packit |
709fb3 |
{ "posixawk", RE_SYNTAX_POSIX_AWK, GEAcompile, EGexecute },
|
|
Packit |
709fb3 |
{ "perl", 0, Pcompile, Pexecute, },
|
|
Packit |
709fb3 |
};
|
|
Packit |
709fb3 |
/* Keep these in sync with the 'matchers' table. */
|
|
Packit |
709fb3 |
enum { E_MATCHER_INDEX = 1, F_MATCHER_INDEX = 2, G_MATCHER_INDEX = 0 };
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Return the index of the matcher corresponding to M if available.
|
|
Packit |
709fb3 |
MATCHER is the index of the previous matcher, or -1 if none.
|
|
Packit |
709fb3 |
Exit in case of conflicts or if M is not available. */
|
|
Packit |
709fb3 |
static int
|
|
Packit |
709fb3 |
setmatcher (char const *m, int matcher)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
for (int i = 0; i < sizeof matchers / sizeof *matchers; i++)
|
|
Packit |
709fb3 |
if (STREQ (m, matchers[i].name))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (0 <= matcher && matcher != i)
|
|
Packit |
709fb3 |
die (EXIT_TROUBLE, 0, _("conflicting matchers specified"));
|
|
Packit |
709fb3 |
return i;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
die (EXIT_TROUBLE, 0, _("invalid matcher %s"), m);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Find the white-space-separated options specified by OPTIONS, and
|
|
Packit |
709fb3 |
using BUF to store copies of these options, set ARGV[0], ARGV[1],
|
|
Packit |
709fb3 |
etc. to the option copies. Return the number N of options found.
|
|
Packit |
709fb3 |
Do not set ARGV[N] to NULL. If ARGV is NULL, do not store ARGV[0]
|
|
Packit |
709fb3 |
etc. Backslash can be used to escape whitespace (and backslashes). */
|
|
Packit |
709fb3 |
static size_t
|
|
Packit |
709fb3 |
prepend_args (char const *options, char *buf, char **argv)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
char const *o = options;
|
|
Packit |
709fb3 |
char *b = buf;
|
|
Packit |
709fb3 |
size_t n = 0;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
for (;;)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
while (c_isspace (to_uchar (*o)))
|
|
Packit |
709fb3 |
o++;
|
|
Packit |
709fb3 |
if (!*o)
|
|
Packit |
709fb3 |
return n;
|
|
Packit |
709fb3 |
if (argv)
|
|
Packit |
709fb3 |
argv[n] = b;
|
|
Packit |
709fb3 |
n++;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
do
|
|
Packit |
709fb3 |
if ((*b++ = *o++) == '\\' && *o)
|
|
Packit |
709fb3 |
b[-1] = *o++;
|
|
Packit |
709fb3 |
while (*o && ! c_isspace (to_uchar (*o)));
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
*b++ = '\0';
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Prepend the whitespace-separated options in OPTIONS to the argument
|
|
Packit |
709fb3 |
vector of a main program with argument count *PARGC and argument
|
|
Packit |
709fb3 |
vector *PARGV. Return the number of options prepended. */
|
|
Packit |
709fb3 |
static int
|
|
Packit |
709fb3 |
prepend_default_options (char const *options, int *pargc, char ***pargv)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (options && *options)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
char *buf = xmalloc (strlen (options) + 1);
|
|
Packit |
709fb3 |
size_t prepended = prepend_args (options, buf, NULL);
|
|
Packit |
709fb3 |
int argc = *pargc;
|
|
Packit |
709fb3 |
char *const *argv = *pargv;
|
|
Packit |
709fb3 |
char **pp;
|
|
Packit |
709fb3 |
enum { MAX_ARGS = MIN (INT_MAX, SIZE_MAX / sizeof *pp - 1) };
|
|
Packit |
709fb3 |
if (MAX_ARGS - argc < prepended)
|
|
Packit |
709fb3 |
xalloc_die ();
|
|
Packit |
709fb3 |
pp = xmalloc ((prepended + argc + 1) * sizeof *pp);
|
|
Packit |
709fb3 |
*pargc = prepended + argc;
|
|
Packit |
709fb3 |
*pargv = pp;
|
|
Packit |
709fb3 |
*pp++ = *argv++;
|
|
Packit |
709fb3 |
pp += prepend_args (options, buf, pp);
|
|
Packit |
709fb3 |
while ((*pp++ = *argv++))
|
|
Packit |
709fb3 |
continue;
|
|
Packit |
709fb3 |
return prepended;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
return 0;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Get the next non-digit option from ARGC and ARGV.
|
|
Packit |
709fb3 |
Return -1 if there are no more options.
|
|
Packit |
709fb3 |
Process any digit options that were encountered on the way,
|
|
Packit |
709fb3 |
and store the resulting integer into *DEFAULT_CONTEXT. */
|
|
Packit |
709fb3 |
static int
|
|
Packit |
709fb3 |
get_nondigit_option (int argc, char *const *argv, intmax_t *default_context)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
static int prev_digit_optind = -1;
|
|
Packit |
709fb3 |
int this_digit_optind;
|
|
Packit |
709fb3 |
bool was_digit;
|
|
Packit |
709fb3 |
char buf[INT_BUFSIZE_BOUND (intmax_t) + 4];
|
|
Packit |
709fb3 |
char *p = buf;
|
|
Packit |
709fb3 |
int opt;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
was_digit = false;
|
|
Packit |
709fb3 |
this_digit_optind = optind;
|
|
Packit |
709fb3 |
while (true)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
opt = getopt_long (argc, (char **) argv, short_options,
|
|
Packit |
709fb3 |
long_options, NULL);
|
|
Packit |
709fb3 |
if (! c_isdigit (opt))
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (prev_digit_optind != this_digit_optind || !was_digit)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* Reset to start another context length argument. */
|
|
Packit |
709fb3 |
p = buf;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* Suppress trivial leading zeros, to avoid incorrect
|
|
Packit |
709fb3 |
diagnostic on strings like 00000000000. */
|
|
Packit |
709fb3 |
p -= buf[0] == '0';
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (p == buf + sizeof buf - 4)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* Too many digits. Append "..." to make context_length_arg
|
|
Packit |
709fb3 |
complain about "X...", where X contains the digits seen
|
|
Packit |
709fb3 |
so far. */
|
|
Packit |
709fb3 |
strcpy (p, "...");
|
|
Packit |
709fb3 |
p += 3;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
*p++ = opt;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
was_digit = true;
|
|
Packit |
709fb3 |
prev_digit_optind = this_digit_optind;
|
|
Packit |
709fb3 |
this_digit_optind = optind;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
if (p != buf)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
*p = '\0';
|
|
Packit |
709fb3 |
context_length_arg (buf, default_context);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
return opt;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Parse GREP_COLORS. The default would look like:
|
|
Packit |
709fb3 |
GREP_COLORS='ms=01;31:mc=01;31:sl=:cx=:fn=35:ln=32:bn=32:se=36'
|
|
Packit |
709fb3 |
with boolean capabilities (ne and rv) unset (i.e., omitted).
|
|
Packit |
709fb3 |
No character escaping is needed or supported. */
|
|
Packit |
709fb3 |
static void
|
|
Packit |
709fb3 |
parse_grep_colors (void)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
const char *p;
|
|
Packit |
709fb3 |
char *q;
|
|
Packit |
709fb3 |
char *name;
|
|
Packit |
709fb3 |
char *val;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
p = getenv ("GREP_COLORS"); /* Plural! */
|
|
Packit |
709fb3 |
if (p == NULL || *p == '\0')
|
|
Packit |
709fb3 |
return;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Work off a writable copy. */
|
|
Packit |
709fb3 |
q = xstrdup (p);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
name = q;
|
|
Packit |
709fb3 |
val = NULL;
|
|
Packit |
709fb3 |
/* From now on, be well-formed or you're gone. */
|
|
Packit |
709fb3 |
for (;;)
|
|
Packit |
709fb3 |
if (*q == ':' || *q == '\0')
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
char c = *q;
|
|
Packit |
709fb3 |
struct color_cap const *cap;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
*q++ = '\0'; /* Terminate name or val. */
|
|
Packit |
709fb3 |
/* Empty name without val (empty cap)
|
|
Packit |
709fb3 |
* won't match and will be ignored. */
|
|
Packit |
709fb3 |
for (cap = color_dict; cap->name; cap++)
|
|
Packit |
709fb3 |
if (STREQ (cap->name, name))
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
/* If name unknown, go on for forward compatibility. */
|
|
Packit |
709fb3 |
if (cap->var && val)
|
|
Packit |
709fb3 |
*(cap->var) = val;
|
|
Packit |
709fb3 |
if (cap->fct)
|
|
Packit |
709fb3 |
cap->fct ();
|
|
Packit |
709fb3 |
if (c == '\0')
|
|
Packit |
709fb3 |
return;
|
|
Packit |
709fb3 |
name = q;
|
|
Packit |
709fb3 |
val = NULL;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else if (*q == '=')
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (q == name || val)
|
|
Packit |
709fb3 |
return;
|
|
Packit |
709fb3 |
*q++ = '\0'; /* Terminate name. */
|
|
Packit |
709fb3 |
val = q; /* Can be the empty string. */
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else if (val == NULL)
|
|
Packit |
709fb3 |
q++; /* Accumulate name. */
|
|
Packit |
709fb3 |
else if (*q == ';' || c_isdigit (*q))
|
|
Packit |
709fb3 |
q++; /* Accumulate val. Protect the terminal from being sent crap. */
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
return;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Return true if PAT (of length PATLEN) contains an encoding error. */
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
contains_encoding_error (char const *pat, size_t patlen)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
mbstate_t mbs = { 0 };
|
|
Packit |
709fb3 |
size_t i, charlen;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
for (i = 0; i < patlen; i += charlen)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
charlen = mb_clen (pat + i, patlen - i, &mbs);
|
|
Packit |
709fb3 |
if ((size_t) -2 <= charlen)
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
return false;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Return the number of bytes in the initial character of PAT, of size
|
|
Packit |
709fb3 |
PATLEN, if Fcompile can handle that character. Return -1 if
|
|
Packit |
709fb3 |
Fcompile cannot handle it. MBS is the multibyte conversion state.
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
Fcompile can handle a character C if C is single-byte, or if C has no
|
|
Packit |
709fb3 |
case folded counterparts and toupper translates none of its bytes. */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static int
|
|
Packit |
709fb3 |
fgrep_icase_charlen (char const *pat, size_t patlen, mbstate_t *mbs)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
int n = localeinfo.sbclen[to_uchar (*pat)];
|
|
Packit |
709fb3 |
if (n < 0)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
wchar_t wc;
|
|
Packit |
709fb3 |
wchar_t folded[CASE_FOLDED_BUFSIZE];
|
|
Packit |
709fb3 |
size_t wn = mbrtowc (&wc, pat, patlen, mbs);
|
|
Packit |
709fb3 |
if (MB_LEN_MAX < wn || case_folded_counterparts (wc, folded))
|
|
Packit |
709fb3 |
return -1;
|
|
Packit |
709fb3 |
for (int i = wn; 0 < --i; )
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
unsigned char c = pat[i];
|
|
Packit |
709fb3 |
if (toupper (c) != c)
|
|
Packit |
709fb3 |
return -1;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
n = wn;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
return n;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Return true if the -F patterns PAT, of size PATLEN, contain only
|
|
Packit |
709fb3 |
single-byte characters or characters not subject to case folding,
|
|
Packit |
709fb3 |
and so can be processed by Fcompile. */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static bool
|
|
Packit |
709fb3 |
fgrep_icase_available (char const *pat, size_t patlen)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
mbstate_t mbs = {0,};
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
for (size_t i = 0; i < patlen; )
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
int n = fgrep_icase_charlen (pat + i, patlen - i, &mbs);
|
|
Packit |
709fb3 |
if (n < 0)
|
|
Packit |
709fb3 |
return false;
|
|
Packit |
709fb3 |
i += n;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
return true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Change the pattern *KEYS_P, of size *LEN_P, from fgrep to grep style. */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
void
|
|
Packit |
709fb3 |
fgrep_to_grep_pattern (char **keys_p, size_t *len_p)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
size_t len = *len_p;
|
|
Packit |
709fb3 |
char *keys = *keys_p;
|
|
Packit |
709fb3 |
mbstate_t mb_state = { 0 };
|
|
Packit |
709fb3 |
char *new_keys = xnmalloc (len + 1, 2);
|
|
Packit |
709fb3 |
char *p = new_keys;
|
|
Packit |
709fb3 |
size_t n;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
for (; len; keys += n, len -= n)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
n = mb_clen (keys, len, &mb_state);
|
|
Packit |
709fb3 |
switch (n)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
case (size_t) -2:
|
|
Packit |
709fb3 |
n = len;
|
|
Packit |
709fb3 |
FALLTHROUGH;
|
|
Packit |
709fb3 |
default:
|
|
Packit |
709fb3 |
p = mempcpy (p, keys, n);
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case (size_t) -1:
|
|
Packit |
709fb3 |
memset (&mb_state, 0, sizeof mb_state);
|
|
Packit |
709fb3 |
n = 1;
|
|
Packit |
709fb3 |
FALLTHROUGH;
|
|
Packit |
709fb3 |
case 1:
|
|
Packit |
709fb3 |
switch (*keys)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
case '$': case '*': case '.': case '[': case '\\': case '^':
|
|
Packit |
709fb3 |
*p++ = '\\'; break;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
*p++ = *keys;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
free (*keys_p);
|
|
Packit |
709fb3 |
*keys_p = new_keys;
|
|
Packit |
709fb3 |
*len_p = p - new_keys;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* If it is easy, convert the MATCHER-style patterns KEYS (of size
|
|
Packit |
709fb3 |
*LEN_P) to -F style, update *LEN_P to a possibly-smaller value, and
|
|
Packit |
709fb3 |
return F_MATCHER_INDEX. If not, leave KEYS and *LEN_P alone and
|
|
Packit |
709fb3 |
return MATCHER. This function is conservative and sometimes misses
|
|
Packit |
709fb3 |
conversions, e.g., it does not convert the -E pattern "(a|a|[aa])"
|
|
Packit |
709fb3 |
to the -F pattern "a". */
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
static int
|
|
Packit |
709fb3 |
try_fgrep_pattern (int matcher, char *keys, size_t *len_p)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
int result = matcher;
|
|
Packit |
709fb3 |
size_t len = *len_p;
|
|
Packit |
709fb3 |
char *new_keys = xmalloc (len + 1);
|
|
Packit |
709fb3 |
char *p = new_keys;
|
|
Packit |
709fb3 |
char const *q = keys;
|
|
Packit |
709fb3 |
mbstate_t mb_state = { 0 };
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
while (len != 0)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
switch (*q)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
case '$': case '*': case '.': case '[': case '^':
|
|
Packit |
709fb3 |
goto fail;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case '(': case '+': case '?': case '{': case '|':
|
|
Packit |
709fb3 |
if (matcher != G_MATCHER_INDEX)
|
|
Packit |
709fb3 |
goto fail;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case '\\':
|
|
Packit |
709fb3 |
if (1 < len)
|
|
Packit |
709fb3 |
switch (q[1])
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
case '\n':
|
|
Packit |
709fb3 |
case 'B': case 'S': case 'W': case'\'': case '<':
|
|
Packit |
709fb3 |
case 'b': case 's': case 'w': case '`': case '>':
|
|
Packit |
709fb3 |
case '1': case '2': case '3': case '4':
|
|
Packit |
709fb3 |
case '5': case '6': case '7': case '8': case '9':
|
|
Packit |
709fb3 |
goto fail;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case '(': case '+': case '?': case '{': case '|':
|
|
Packit |
709fb3 |
if (matcher == G_MATCHER_INDEX)
|
|
Packit |
709fb3 |
goto fail;
|
|
Packit |
709fb3 |
FALLTHROUGH;
|
|
Packit |
709fb3 |
default:
|
|
Packit |
709fb3 |
q++, len--;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
size_t n;
|
|
Packit |
709fb3 |
if (match_icase)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
int ni = fgrep_icase_charlen (q, len, &mb_state);
|
|
Packit |
709fb3 |
if (ni < 0)
|
|
Packit |
709fb3 |
goto fail;
|
|
Packit |
709fb3 |
n = ni;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
n = mb_clen (q, len, &mb_state);
|
|
Packit |
709fb3 |
if (MB_LEN_MAX < n)
|
|
Packit |
709fb3 |
goto fail;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
p = mempcpy (p, q, n);
|
|
Packit |
709fb3 |
q += n;
|
|
Packit |
709fb3 |
len -= n;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (*len_p != p - new_keys)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
*len_p = p - new_keys;
|
|
Packit |
709fb3 |
memcpy (keys, new_keys, p - new_keys);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
result = F_MATCHER_INDEX;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
fail:
|
|
Packit |
709fb3 |
free (new_keys);
|
|
Packit |
709fb3 |
return result;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
int
|
|
Packit |
709fb3 |
main (int argc, char **argv)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
char *keys = NULL;
|
|
Packit |
709fb3 |
size_t keycc = 0, oldcc, keyalloc = 0;
|
|
Packit |
709fb3 |
int matcher = -1;
|
|
Packit |
709fb3 |
bool with_filenames = false;
|
|
Packit |
709fb3 |
size_t cc;
|
|
Packit |
709fb3 |
int opt, prepended;
|
|
Packit |
709fb3 |
int prev_optind, last_recursive;
|
|
Packit |
709fb3 |
int fread_errno;
|
|
Packit |
709fb3 |
intmax_t default_context;
|
|
Packit |
709fb3 |
FILE *fp;
|
|
Packit |
709fb3 |
exit_failure = EXIT_TROUBLE;
|
|
Packit |
709fb3 |
initialize_main (&argc, &argv);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
eolbyte = '\n';
|
|
Packit |
709fb3 |
filename_mask = ~0;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
max_count = INTMAX_MAX;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* The value -1 means to use DEFAULT_CONTEXT. */
|
|
Packit |
709fb3 |
out_after = out_before = -1;
|
|
Packit |
709fb3 |
/* Default before/after context: changed by -C/-NUM options */
|
|
Packit |
709fb3 |
default_context = -1;
|
|
Packit |
709fb3 |
/* Changed by -o option */
|
|
Packit |
709fb3 |
only_matching = false;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Internationalization. */
|
|
Packit |
709fb3 |
#if defined HAVE_SETLOCALE
|
|
Packit |
709fb3 |
setlocale (LC_ALL, "");
|
|
Packit |
709fb3 |
#endif
|
|
Packit |
709fb3 |
#if defined ENABLE_NLS
|
|
Packit |
709fb3 |
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
Packit |
709fb3 |
textdomain (PACKAGE);
|
|
Packit |
709fb3 |
#endif
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
init_localeinfo (&localeinfo);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
atexit (clean_up_stdout);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
last_recursive = 0;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
prepended = prepend_default_options (getenv ("GREP_OPTIONS"), &argc, &argv);
|
|
Packit |
709fb3 |
if (prepended)
|
|
Packit |
709fb3 |
error (0, 0, _("warning: GREP_OPTIONS is deprecated;"
|
|
Packit |
709fb3 |
" please use an alias or script"));
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
while (prev_optind = optind,
|
|
Packit |
709fb3 |
(opt = get_nondigit_option (argc, argv, &default_context)) != -1)
|
|
Packit |
709fb3 |
switch (opt)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
case 'A':
|
|
Packit |
709fb3 |
context_length_arg (optarg, &out_after);
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'B':
|
|
Packit |
709fb3 |
context_length_arg (optarg, &out_before);
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'C':
|
|
Packit |
709fb3 |
/* Set output match context, but let any explicit leading or
|
|
Packit |
709fb3 |
trailing amount specified with -A or -B stand. */
|
|
Packit |
709fb3 |
context_length_arg (optarg, &default_context);
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'D':
|
|
Packit |
709fb3 |
if (STREQ (optarg, "read"))
|
|
Packit |
709fb3 |
devices = READ_DEVICES;
|
|
Packit |
709fb3 |
else if (STREQ (optarg, "skip"))
|
|
Packit |
709fb3 |
devices = SKIP_DEVICES;
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
die (EXIT_TROUBLE, 0, _("unknown devices method"));
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'E':
|
|
Packit |
709fb3 |
matcher = setmatcher ("egrep", matcher);
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'F':
|
|
Packit |
709fb3 |
matcher = setmatcher ("fgrep", matcher);
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'P':
|
|
Packit |
709fb3 |
matcher = setmatcher ("perl", matcher);
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'G':
|
|
Packit |
709fb3 |
matcher = setmatcher ("grep", matcher);
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'X': /* undocumented on purpose */
|
|
Packit |
709fb3 |
matcher = setmatcher (optarg, matcher);
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'H':
|
|
Packit |
709fb3 |
with_filenames = true;
|
|
Packit |
709fb3 |
no_filenames = false;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'I':
|
|
Packit |
709fb3 |
binary_files = WITHOUT_MATCH_BINARY_FILES;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'T':
|
|
Packit |
709fb3 |
align_tabs = true;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'U':
|
|
Packit |
709fb3 |
if (O_BINARY)
|
|
Packit |
709fb3 |
binary = true;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'u':
|
|
Packit |
709fb3 |
/* Obsolete option; it has no effect. FIXME: Diagnose use of
|
|
Packit |
709fb3 |
this option starting in (say) the year 2020. */
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'V':
|
|
Packit |
709fb3 |
show_version = true;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'a':
|
|
Packit |
709fb3 |
binary_files = TEXT_BINARY_FILES;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'b':
|
|
Packit |
709fb3 |
out_byte = true;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'c':
|
|
Packit |
709fb3 |
count_matches = true;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'd':
|
|
Packit |
709fb3 |
directories = XARGMATCH ("--directories", optarg,
|
|
Packit |
709fb3 |
directories_args, directories_types);
|
|
Packit |
709fb3 |
if (directories == RECURSE_DIRECTORIES)
|
|
Packit |
709fb3 |
last_recursive = prev_optind;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'e':
|
|
Packit |
709fb3 |
cc = strlen (optarg);
|
|
Packit |
709fb3 |
if (keyalloc < keycc + cc + 1)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
keyalloc = keycc + cc + 1;
|
|
Packit |
709fb3 |
keys = x2realloc (keys, &keyalloc);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
oldcc = keycc;
|
|
Packit |
709fb3 |
memcpy (keys + oldcc, optarg, cc);
|
|
Packit |
709fb3 |
keycc += cc;
|
|
Packit |
709fb3 |
keys[keycc++] = '\n';
|
|
Packit |
709fb3 |
fl_add (keys + oldcc, cc + 1, "");
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'f':
|
|
Packit |
709fb3 |
if (STREQ (optarg, "-"))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (binary)
|
|
Packit |
709fb3 |
xset_binary_mode (STDIN_FILENO, O_BINARY);
|
|
Packit |
709fb3 |
fp = stdin;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
fp = fopen (optarg, binary ? "rb" : "r");
|
|
Packit |
709fb3 |
if (!fp)
|
|
Packit |
709fb3 |
die (EXIT_TROUBLE, errno, "%s", optarg);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
oldcc = keycc;
|
|
Packit |
709fb3 |
for (;; keycc += cc)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (keyalloc <= keycc + 1)
|
|
Packit |
709fb3 |
keys = x2realloc (keys, &keyalloc);
|
|
Packit |
709fb3 |
cc = fread (keys + keycc, 1, keyalloc - (keycc + 1), fp);
|
|
Packit |
709fb3 |
if (cc == 0)
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
fread_errno = errno;
|
|
Packit |
709fb3 |
if (ferror (fp))
|
|
Packit |
709fb3 |
die (EXIT_TROUBLE, fread_errno, "%s", optarg);
|
|
Packit |
709fb3 |
if (fp != stdin)
|
|
Packit |
709fb3 |
fclose (fp);
|
|
Packit |
709fb3 |
/* Append final newline if file ended in non-newline. */
|
|
Packit |
709fb3 |
if (oldcc != keycc && keys[keycc - 1] != '\n')
|
|
Packit |
709fb3 |
keys[keycc++] = '\n';
|
|
Packit |
709fb3 |
fl_add (keys + oldcc, keycc - oldcc, optarg);
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'h':
|
|
Packit |
709fb3 |
with_filenames = false;
|
|
Packit |
709fb3 |
no_filenames = true;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'i':
|
|
Packit |
709fb3 |
case 'y': /* For old-timers . . . */
|
|
Packit |
709fb3 |
match_icase = true;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'L':
|
|
Packit |
709fb3 |
/* Like -l, except list files that don't contain matches.
|
|
Packit |
709fb3 |
Inspired by the same option in Hume's gre. */
|
|
Packit |
709fb3 |
list_files = LISTFILES_NONMATCHING;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'l':
|
|
Packit |
709fb3 |
list_files = LISTFILES_MATCHING;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'm':
|
|
Packit |
709fb3 |
switch (xstrtoimax (optarg, 0, 10, &max_count, ""))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
case LONGINT_OK:
|
|
Packit |
709fb3 |
case LONGINT_OVERFLOW:
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
default:
|
|
Packit |
709fb3 |
die (EXIT_TROUBLE, 0, _("invalid max count"));
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'n':
|
|
Packit |
709fb3 |
out_line = true;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'o':
|
|
Packit |
709fb3 |
only_matching = true;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'q':
|
|
Packit |
709fb3 |
exit_on_match = true;
|
|
Packit |
709fb3 |
exit_failure = 0;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'R':
|
|
Packit |
709fb3 |
fts_options = basic_fts_options | FTS_LOGICAL;
|
|
Packit |
709fb3 |
FALLTHROUGH;
|
|
Packit |
709fb3 |
case 'r':
|
|
Packit |
709fb3 |
directories = RECURSE_DIRECTORIES;
|
|
Packit |
709fb3 |
last_recursive = prev_optind;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 's':
|
|
Packit |
709fb3 |
suppress_errors = true;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'v':
|
|
Packit |
709fb3 |
out_invert = true;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'w':
|
|
Packit |
709fb3 |
wordinit ();
|
|
Packit |
709fb3 |
match_words = true;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'x':
|
|
Packit |
709fb3 |
match_lines = true;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'Z':
|
|
Packit |
709fb3 |
filename_mask = 0;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 'z':
|
|
Packit |
709fb3 |
eolbyte = '\0';
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case BINARY_FILES_OPTION:
|
|
Packit |
709fb3 |
if (STREQ (optarg, "binary"))
|
|
Packit |
709fb3 |
binary_files = BINARY_BINARY_FILES;
|
|
Packit |
709fb3 |
else if (STREQ (optarg, "text"))
|
|
Packit |
709fb3 |
binary_files = TEXT_BINARY_FILES;
|
|
Packit |
709fb3 |
else if (STREQ (optarg, "without-match"))
|
|
Packit |
709fb3 |
binary_files = WITHOUT_MATCH_BINARY_FILES;
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
die (EXIT_TROUBLE, 0, _("unknown binary-files type"));
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case COLOR_OPTION:
|
|
Packit |
709fb3 |
if (optarg)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (!strcasecmp (optarg, "always") || !strcasecmp (optarg, "yes")
|
|
Packit |
709fb3 |
|| !strcasecmp (optarg, "force"))
|
|
Packit |
709fb3 |
color_option = 1;
|
|
Packit |
709fb3 |
else if (!strcasecmp (optarg, "never") || !strcasecmp (optarg, "no")
|
|
Packit |
709fb3 |
|| !strcasecmp (optarg, "none"))
|
|
Packit |
709fb3 |
color_option = 0;
|
|
Packit |
709fb3 |
else if (!strcasecmp (optarg, "auto") || !strcasecmp (optarg, "tty")
|
|
Packit |
709fb3 |
|| !strcasecmp (optarg, "if-tty"))
|
|
Packit |
709fb3 |
color_option = 2;
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
show_help = 1;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
color_option = 2;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case EXCLUDE_OPTION:
|
|
Packit |
709fb3 |
case INCLUDE_OPTION:
|
|
Packit |
709fb3 |
for (int cmd = 0; cmd < 2; cmd++)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (!excluded_patterns[cmd])
|
|
Packit |
709fb3 |
excluded_patterns[cmd] = new_exclude ();
|
|
Packit |
709fb3 |
add_exclude (excluded_patterns[cmd], optarg,
|
|
Packit |
709fb3 |
((opt == INCLUDE_OPTION ? EXCLUDE_INCLUDE : 0)
|
|
Packit |
709fb3 |
| exclude_options (cmd)));
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
case EXCLUDE_FROM_OPTION:
|
|
Packit |
709fb3 |
for (int cmd = 0; cmd < 2; cmd++)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (!excluded_patterns[cmd])
|
|
Packit |
709fb3 |
excluded_patterns[cmd] = new_exclude ();
|
|
Packit |
709fb3 |
if (add_exclude_file (add_exclude, excluded_patterns[cmd],
|
|
Packit |
709fb3 |
optarg, exclude_options (cmd), '\n')
|
|
Packit |
709fb3 |
!= 0)
|
|
Packit |
709fb3 |
die (EXIT_TROUBLE, errno, "%s", optarg);
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case EXCLUDE_DIRECTORY_OPTION:
|
|
Packit |
709fb3 |
strip_trailing_slashes (optarg);
|
|
Packit |
709fb3 |
for (int cmd = 0; cmd < 2; cmd++)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (!excluded_directory_patterns[cmd])
|
|
Packit |
709fb3 |
excluded_directory_patterns[cmd] = new_exclude ();
|
|
Packit |
709fb3 |
add_exclude (excluded_directory_patterns[cmd], optarg,
|
|
Packit |
709fb3 |
exclude_options (cmd));
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case GROUP_SEPARATOR_OPTION:
|
|
Packit |
709fb3 |
group_separator = optarg;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case LINE_BUFFERED_OPTION:
|
|
Packit |
709fb3 |
line_buffered = true;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case LABEL_OPTION:
|
|
Packit |
709fb3 |
label = optarg;
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
case 0:
|
|
Packit |
709fb3 |
/* long options */
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
default:
|
|
Packit |
709fb3 |
usage (EXIT_TROUBLE);
|
|
Packit |
709fb3 |
break;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (show_version)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
version_etc (stdout, getprogname (), PACKAGE_NAME, VERSION, AUTHORS,
|
|
Packit |
709fb3 |
(char *) NULL);
|
|
Packit |
709fb3 |
return EXIT_SUCCESS;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (show_help)
|
|
Packit |
709fb3 |
usage (EXIT_SUCCESS);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (keys)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (keycc == 0)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* No keys were specified (e.g. -f /dev/null). Match nothing. */
|
|
Packit |
709fb3 |
out_invert ^= true;
|
|
Packit |
709fb3 |
match_lines = match_words = false;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
/* Strip trailing newline. */
|
|
Packit |
709fb3 |
--keycc;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else if (optind < argc)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* Make a copy so that it can be reallocated or freed later. */
|
|
Packit |
709fb3 |
keycc = strlen (argv[optind]);
|
|
Packit |
709fb3 |
keys = xmemdup (argv[optind++], keycc + 1);
|
|
Packit |
709fb3 |
fl_add (keys, keycc, "");
|
|
Packit |
709fb3 |
n_patterns++;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
usage (EXIT_TROUBLE);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
bool possibly_tty = false;
|
|
Packit |
709fb3 |
struct stat tmp_stat;
|
|
Packit |
709fb3 |
if (! exit_on_match && fstat (STDOUT_FILENO, &tmp_stat) == 0)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
if (S_ISREG (tmp_stat.st_mode))
|
|
Packit |
709fb3 |
out_stat = tmp_stat;
|
|
Packit |
709fb3 |
else if (S_ISCHR (tmp_stat.st_mode))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
struct stat null_stat;
|
|
Packit |
709fb3 |
if (stat ("/dev/null", &null_stat) == 0
|
|
Packit |
709fb3 |
&& SAME_INODE (tmp_stat, null_stat))
|
|
Packit |
709fb3 |
dev_null_output = true;
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
possibly_tty = true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* POSIX says -c, -l and -q are mutually exclusive. In this
|
|
Packit |
709fb3 |
implementation, -q overrides -l and -L, which in turn override -c. */
|
|
Packit |
709fb3 |
if (exit_on_match | dev_null_output)
|
|
Packit |
709fb3 |
list_files = LISTFILES_NONE;
|
|
Packit |
709fb3 |
if ((exit_on_match | dev_null_output) || list_files != LISTFILES_NONE)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
count_matches = false;
|
|
Packit |
709fb3 |
done_on_match = true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
out_quiet = count_matches | done_on_match;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (out_after < 0)
|
|
Packit |
709fb3 |
out_after = default_context;
|
|
Packit |
709fb3 |
if (out_before < 0)
|
|
Packit |
709fb3 |
out_before = default_context;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* If it is easy to see that matching cannot succeed (e.g., 'grep -f
|
|
Packit |
709fb3 |
/dev/null'), fail without reading the input. */
|
|
Packit |
709fb3 |
if ((max_count == 0
|
|
Packit |
709fb3 |
|| (keycc == 0 && out_invert && !match_lines && !match_words))
|
|
Packit |
709fb3 |
&& list_files != LISTFILES_NONMATCHING)
|
|
Packit |
709fb3 |
return EXIT_FAILURE;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (color_option == 2)
|
|
Packit |
709fb3 |
color_option = possibly_tty && should_colorize () && isatty (STDOUT_FILENO);
|
|
Packit |
709fb3 |
init_colorize ();
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (color_option)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
/* Legacy. */
|
|
Packit |
709fb3 |
char *userval = getenv ("GREP_COLOR");
|
|
Packit |
709fb3 |
if (userval != NULL && *userval != '\0')
|
|
Packit |
709fb3 |
selected_match_color = context_match_color = userval;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* New GREP_COLORS has priority. */
|
|
Packit |
709fb3 |
parse_grep_colors ();
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
initialize_unibyte_mask ();
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (matcher < 0)
|
|
Packit |
709fb3 |
matcher = G_MATCHER_INDEX;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* In a single-byte locale, switch from -F to -G if it is a single
|
|
Packit |
709fb3 |
pattern that matches words, where -G is typically faster. In a
|
|
Packit |
709fb3 |
multi-byte locale, switch if the patterns have an encoding error
|
|
Packit |
709fb3 |
(where -F does not work) or if -i and the patterns will not work
|
|
Packit |
709fb3 |
for -iF. */
|
|
Packit |
709fb3 |
if (matcher == F_MATCHER_INDEX
|
|
Packit |
709fb3 |
&& (! localeinfo.multibyte
|
|
Packit |
709fb3 |
? n_patterns == 1 && match_words
|
|
Packit |
709fb3 |
: (contains_encoding_error (keys, keycc)
|
|
Packit |
709fb3 |
|| (match_icase && !fgrep_icase_available (keys, keycc)))))
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
fgrep_to_grep_pattern (&keys, &keycc);
|
|
Packit |
709fb3 |
matcher = G_MATCHER_INDEX;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
/* With two or more patterns, if -F works then switch from either -E
|
|
Packit |
709fb3 |
or -G, as -F is probably faster then. */
|
|
Packit |
709fb3 |
else if ((matcher == G_MATCHER_INDEX || matcher == E_MATCHER_INDEX)
|
|
Packit |
709fb3 |
&& 1 < n_patterns)
|
|
Packit |
709fb3 |
matcher = try_fgrep_pattern (matcher, keys, &keycc);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
execute = matchers[matcher].execute;
|
|
Packit |
709fb3 |
compiled_pattern = matchers[matcher].compile (keys, keycc,
|
|
Packit |
709fb3 |
matchers[matcher].syntax);
|
|
Packit |
709fb3 |
/* We need one byte prior and one after. */
|
|
Packit |
709fb3 |
char eolbytes[3] = { 0, eolbyte, 0 };
|
|
Packit |
709fb3 |
size_t match_size;
|
|
Packit |
709fb3 |
skip_empty_lines = ((execute (compiled_pattern, eolbytes + 1, 1,
|
|
Packit |
709fb3 |
&match_size, NULL) == 0)
|
|
Packit |
709fb3 |
== out_invert);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if ((argc - optind > 1 && !no_filenames) || with_filenames)
|
|
Packit |
709fb3 |
out_file = 1;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (binary)
|
|
Packit |
709fb3 |
xset_binary_mode (STDOUT_FILENO, O_BINARY);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* Prefer sysconf for page size, as getpagesize typically returns int. */
|
|
Packit |
709fb3 |
#ifdef _SC_PAGESIZE
|
|
Packit |
709fb3 |
long psize = sysconf (_SC_PAGESIZE);
|
|
Packit |
709fb3 |
#else
|
|
Packit |
709fb3 |
long psize = getpagesize ();
|
|
Packit |
709fb3 |
#endif
|
|
Packit |
709fb3 |
if (! (0 < psize && psize <= (SIZE_MAX - sizeof (uword)) / 2))
|
|
Packit |
709fb3 |
abort ();
|
|
Packit |
709fb3 |
pagesize = psize;
|
|
Packit |
709fb3 |
bufalloc = ALIGN_TO (INITIAL_BUFSIZE, pagesize) + pagesize + sizeof (uword);
|
|
Packit |
709fb3 |
buffer = xmalloc (bufalloc);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
if (fts_options & FTS_LOGICAL && devices == READ_COMMAND_LINE_DEVICES)
|
|
Packit |
709fb3 |
devices = READ_DEVICES;
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
char *const *files;
|
|
Packit |
709fb3 |
if (optind < argc)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
files = argv + optind;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else if (directories == RECURSE_DIRECTORIES && prepended < last_recursive)
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
static char *const cwd_only[] = { (char *) ".", NULL };
|
|
Packit |
709fb3 |
files = cwd_only;
|
|
Packit |
709fb3 |
omit_dot_slash = true;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
else
|
|
Packit |
709fb3 |
{
|
|
Packit |
709fb3 |
static char *const stdin_only[] = { (char *) "-", NULL };
|
|
Packit |
709fb3 |
files = stdin_only;
|
|
Packit |
709fb3 |
}
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
bool status = true;
|
|
Packit |
709fb3 |
do
|
|
Packit |
709fb3 |
status &= grep_command_line_arg (*files++);
|
|
Packit |
709fb3 |
while (*files != NULL);
|
|
Packit |
709fb3 |
|
|
Packit |
709fb3 |
/* We register via atexit to test stdout. */
|
|
Packit |
709fb3 |
return errseen ? EXIT_TROUBLE : status;
|
|
Packit |
709fb3 |
}
|