GNU grep 2.24 RTFS
Conclusion: 2 and 2 cases only:
NUL
, e.g. printf 'a\0' | grep 'a'
encoding error according to the C99 mbrlen()
, e.g.:
export LC_CTYPE='en_US.UTF-8'
printf 'a\x80' | grep 'a'
because \x80
cannot be the first byte of an UTF-8 Unicode point: UTF-8 - Description | en.wikipedia.org
Those checks are only done up to the Nth byte of the input, where N = TODO (32KiB in one test system). If the check would fail after the Nth byte, the file is still considered a text file. (mentioned by Stéphane Chazelas).
Only up to the first buffer read
So if a NUL or encoding error happens in the middle of a very large file, it might be grepped anyways.
I imagine this is for performance reasons.
E.g.: this prints the line:
printf '%10000000s\n\x80a' | grep 'a'
but this does not:
printf '%10s\n\x80a' | grep 'a'
The actual buffer size depends on how the file is read. E.g. compare:
export LC_CTYPE='en_US.UTF-8'
(printf '\n\x80a') | grep 'a'
(printf '\n'; sleep 1; printf '\x80a') | grep 'a'
With the sleep
, the first line gets passed to grep even if it is only 1 byte long because the process goes to sleep, and the second read does not check if the file is binary.
RTFS
git clone git://git.savannah.gnu.org/grep.git
cd grep
git checkout v2.24
Find where the stderr error message is encoded:
git grep 'Binary file'
Leads us to src/grep.c
:
if (!out_quiet && (encoding_error_output
|| (0 <= nlines_first_null && nlines_first_null < nlines)))
{
printf (_("Binary file %s matches\n"), filename);
If those variables were well named, we basically reached the conclusion.
encoding_error_output
Quick grepping for encoding_error_output
shows that the only code path that can modify it goes through buf_has_encoding_errors
:
clen = mbrlen (p, buf + size - p, &mbs);
if ((size_t) -2 <= clen)
return true;
then just man mbrlen
.
nlines_first_null and nlines
Initialized as:
intmax_t nlines_first_null = -1;
/* removed for brevity */
nlines = 0;
so when a null is found 0 <= nlines_first_null
becomes true.
TODO when can nlines_first_null < nlines
ever be false? I got lazy.
POSIX
Does not define binary options for grep, and GNU grep does not document it, so RTFS is the only way.
--null-data
may be useful ifNUL
is the delimiter.