summaryrefslogtreecommitdiffstats
path: root/grep-printer/src
diff options
context:
space:
mode:
Diffstat (limited to 'grep-printer/src')
-rw-r--r--grep-printer/src/standard.rs59
-rw-r--r--grep-printer/src/summary.rs28
2 files changed, 85 insertions, 2 deletions
diff --git a/grep-printer/src/standard.rs b/grep-printer/src/standard.rs
index 6ead1db6..068f96a4 100644
--- a/grep-printer/src/standard.rs
+++ b/grep-printer/src/standard.rs
@@ -5,6 +5,7 @@ use std::path::Path;
use std::sync::Arc;
use std::time::Instant;
+use bstr::BStr;
use grep_matcher::{Match, Matcher};
use grep_searcher::{
LineStep, Searcher,
@@ -743,6 +744,11 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for StandardSink<'p, 's, M, W> {
stats.add_matches(self.standard.matches.len() as u64);
stats.add_matched_lines(mat.lines().count() as u64);
}
+ if searcher.binary_detection().convert_byte().is_some() {
+ if self.binary_byte_offset.is_some() {
+ return Ok(false);
+ }
+ }
StandardImpl::from_match(searcher, self, mat).sink()?;
Ok(!self.should_quit())
@@ -764,6 +770,12 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for StandardSink<'p, 's, M, W> {
self.record_matches(ctx.bytes())?;
self.replace(ctx.bytes())?;
}
+ if searcher.binary_detection().convert_byte().is_some() {
+ if self.binary_byte_offset.is_some() {
+ return Ok(false);
+ }
+ }
+
StandardImpl::from_context(searcher, self, ctx).sink()?;
Ok(!self.should_quit())
}
@@ -776,6 +788,15 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for StandardSink<'p, 's, M, W> {
Ok(true)
}
+ fn binary_data(
+ &mut self,
+ _searcher: &Searcher,
+ binary_byte_offset: u64,
+ ) -> Result<bool, io::Error> {
+ self.binary_byte_offset = Some(binary_byte_offset);
+ Ok(true)
+ }
+
fn begin(
&mut self,
_searcher: &Searcher,
@@ -793,10 +814,12 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for StandardSink<'p, 's, M, W> {
fn finish(
&mut self,
- _searcher: &Searcher,
+ searcher: &Searcher,
finish: &SinkFinish,
) -> Result<(), io::Error> {
- self.binary_byte_offset = finish.binary_byte_offset();
+ if let Some(offset) = self.binary_byte_offset {
+ StandardImpl::new(searcher, self).write_binary_message(offset)?;
+ }
if let Some(stats) = self.stats.as_mut() {
stats.add_elapsed(self.start_time.elapsed());
stats.add_searches(1);
@@ -1314,6 +1337,38 @@ impl<'a, M: Matcher, W: WriteColor> StandardImpl<'a, M, W> {
Ok(())
}
+ fn write_binary_message(&self, offset: u64) -> io::Result<()> {
+ if self.sink.match_count == 0 {
+ return Ok(());
+ }
+
+ let bin = self.searcher.binary_detection();
+ if let Some(byte) = bin.quit_byte() {
+ self.write(b"WARNING: stopped searching binary file ")?;
+ if let Some(path) = self.path() {
+ self.write_spec(self.config().colors.path(), path.as_bytes())?;
+ self.write(b" ")?;
+ }
+ let remainder = format!(
+ "after match (found {:?} byte around offset {})\n",
+ BStr::new(&[byte]), offset,
+ );
+ self.write(remainder.as_bytes())?;
+ } else if let Some(byte) = bin.convert_byte() {
+ self.write(b"Binary file ")?;
+ if let Some(path) = self.path() {
+ self.write_spec(self.config().colors.path(), path.as_bytes())?;
+ self.write(b" ")?;
+ }
+ let remainder = format!(
+ "matches (found {:?} byte around offset {})\n",
+ BStr::new(&[byte]), offset,
+ );
+ self.write(remainder.as_bytes())?;
+ }
+ Ok(())
+ }
+
fn write_context_separator(&self) -> io::Result<()> {
if let Some(ref sep) = *self.config().separator_context {
self.write(sep)?;
diff --git a/grep-printer/src/summary.rs b/grep-printer/src/summary.rs
index deb7e609..a1c7785e 100644
--- a/grep-printer/src/summary.rs
+++ b/grep-printer/src/summary.rs
@@ -636,6 +636,34 @@ impl<'p, 's, M: Matcher, W: WriteColor> Sink for SummarySink<'p, 's, M, W> {
stats.add_bytes_searched(finish.byte_count());
stats.add_bytes_printed(self.summary.wtr.borrow().count());
}
+ // If our binary detection method says to quit after seeing binary
+ // data, then we shouldn't print any results at all, even if we've
+ // found a match before detecting binary data. The intent here is to
+ // keep BinaryDetection::quit as a form of filter. Otherwise, we can
+ // present a matching file with a smaller number of matches than
+ // there might be, which can be quite misleading.
+ //
+ // If our binary detection method is to convert binary data, then we
+ // don't quit and therefore search the entire contents of the file.
+ //
+ // There is an unfortunate inconsistency here. Namely, when using
+ // Quiet or PathWithMatch, then the printer can quit after the first
+ // match seen, which could be long before seeing binary data. This
+ // means that using PathWithMatch can print a path where as using
+ // Count might not print it at all because of binary data.
+ //
+ // It's not possible to fix this without also potentially significantly
+ // impacting the performance of Quiet or PathWithMatch, so we accept
+ // the bug.
+ if self.binary_byte_offset.is_some()
+ && searcher.binary_detection().quit_byte().is_some()
+ {
+ // Squash the match count. The statistics reported will still
+ // contain the match count, but the "official" match count should
+ // be zero.
+ self.match_count = 0;
+ return Ok(());
+ }
let show_count =
!self.summary.config.exclude_zero