From 766cfe1d2aa430fb67193b9d445dfa53a4b94380 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 6 Dec 2021 10:23:43 +0100 Subject: [PATCH 1/2] Add src/read_until_with_limit.rs --- src/read_until_with_limit.rs | 58 ++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/read_until_with_limit.rs diff --git a/src/read_until_with_limit.rs b/src/read_until_with_limit.rs new file mode 100644 index 0000000000..005f93e9c4 --- /dev/null +++ b/src/read_until_with_limit.rs @@ -0,0 +1,58 @@ +//! This file contains a pure copy of code from +//! +//! licensed under +//! which says: +//! +//! ```txt +//! Permission is hereby granted, free of charge, to any +//! person obtaining a copy of this software and associated +//! documentation files (the "Software"), to deal in the +//! Software without restriction, including without +//! limitation the rights to use, copy, modify, merge, +//! publish, distribute, sublicense, and/or sell copies of +//! the Software, and to permit persons to whom the Software +//! is furnished to do so, subject to the following +//! conditions: +//! +//! The above copyright notice and this permission notice +//! shall be included in all copies or substantial portions +//! of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +//! ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//! TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +//! PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +//! SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +//! CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +//! OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +//! IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +//! DEALINGS IN THE SOFTWARE. +//! ``` + +fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { + let mut read = 0; + loop { + let (done, used) = { + let available = match r.fill_buf() { + Ok(n) => n, + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + }; + match memchr::memchr(delim, available) { + Some(i) => { + buf.extend_from_slice(&available[..=i]); + (true, i + 1) + } + None => { + buf.extend_from_slice(available); + (false, available.len()) + } + } + }; + r.consume(used); + read += used; + if done || used == 0 { + return Ok(read); + } + } +} From 61416245d13aa2aba4202f358a5f8f1fe356f0c0 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 6 Dec 2021 11:36:42 +0100 Subject: [PATCH 2/2] Raise an error if lines are longer than 100_000 bytes --- src/input.rs | 16 ++++++++++++---- src/lib.rs | 1 + src/read_until_with_limit.rs | 23 ++++++++++++++++++++--- tests/integration_tests.rs | 12 ++++++++++++ 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/input.rs b/src/input.rs index ffaca0ae0b..a52f890481 100644 --- a/src/input.rs +++ b/src/input.rs @@ -251,7 +251,7 @@ pub(crate) struct InputReader<'a> { impl<'a> InputReader<'a> { fn new(mut reader: R) -> InputReader<'a> { let mut first_line = vec![]; - reader.read_until(b'\n', &mut first_line).ok(); + Self::limited_read_until(&mut reader, b'\n', &mut first_line).ok(); let content_type = if first_line.is_empty() { None @@ -260,7 +260,7 @@ impl<'a> InputReader<'a> { }; if content_type == Some(ContentType::UTF_16LE) { - reader.read_until(0x00, &mut first_line).ok(); + Self::limited_read_until(&mut reader, 0x00, &mut first_line).ok(); } InputReader { @@ -276,14 +276,22 @@ impl<'a> InputReader<'a> { return Ok(true); } - let res = self.inner.read_until(b'\n', buf).map(|size| size > 0)?; + let res = Self::limited_read_until(&mut self.inner, b'\n', buf).map(|size| size > 0)?; if self.content_type == Some(ContentType::UTF_16LE) { - let _ = self.inner.read_until(0x00, buf); + let _ = Self::limited_read_until(&mut self.inner, 0x00, buf); } Ok(res) } + + fn limited_read_until( + reader: &mut R, + delim: u8, + buf: &mut Vec, + ) -> io::Result { + crate::read_until_with_limit::read_until_with_limit(reader, delim, buf, 100_000) + } } #[test] diff --git a/src/lib.rs b/src/lib.rs index 02e1fefca3..9ff740ab11 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,6 +43,7 @@ pub(crate) mod paging; mod preprocessor; mod pretty_printer; pub(crate) mod printer; +mod read_until_with_limit; pub mod style; pub(crate) mod syntax_mapping; mod terminal; diff --git a/src/read_until_with_limit.rs b/src/read_until_with_limit.rs index 005f93e9c4..cee7089fec 100644 --- a/src/read_until_with_limit.rs +++ b/src/read_until_with_limit.rs @@ -1,4 +1,4 @@ -//! This file contains a pure copy of code from +//! This file is based on code from //! //! licensed under //! which says: @@ -28,8 +28,18 @@ //! IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //! DEALINGS IN THE SOFTWARE. //! ``` +//! +//! To see the changes that has been made to the file you can run `git diff +//! 766cfe1 -- src/read_until_with_limit.rs`. + +use std::io::{BufRead, Error, ErrorKind, Result}; -fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { +pub(crate) fn read_until_with_limit( + r: &mut R, + delim: u8, + buf: &mut Vec, + limit: usize, +) -> Result { let mut read = 0; loop { let (done, used) = { @@ -38,7 +48,7 @@ fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> R Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, Err(e) => return Err(e), }; - match memchr::memchr(delim, available) { + match available.iter().position(|b| *b == delim) { Some(i) => { buf.extend_from_slice(&available[..=i]); (true, i + 1) @@ -51,6 +61,13 @@ fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> R }; r.consume(used); read += used; + if read > limit { + let bat_error = crate::error::Error::Msg(format!( + "Lines longer than {} bytes are not supported. Try auto-formatting your file first.", + limit + )); + return Err(Error::new(ErrorKind::Other, bat_error)); + } if done || used == 0 { return Ok(read); } diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 17e9dc4e1f..c3ce2409db 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -344,6 +344,18 @@ fn no_args_doesnt_break() { assert!(exit_status.success()); } +#[cfg(unix)] +#[test] +fn dev_zero() { + bat() + .arg("/dev/zero") + .assert() + .failure() + .stderr(predicate::str::contains( + "Lines longer than 100000 bytes are not supported. Try auto-formatting your file first", + )); +} + #[test] fn tabs_numbers() { bat()