Change circle detection to use new more conservative method and run in main loop instead of before the loop

This commit is contained in:
Niklas Mohrin 2021-02-27 15:32:07 +01:00 committed by David Peter
parent a98811b6d7
commit 694b31909a
5 changed files with 55 additions and 42 deletions

7
Cargo.lock generated
View File

@ -98,6 +98,7 @@ dependencies = [
"git2", "git2",
"globset", "globset",
"lazy_static", "lazy_static",
"nix",
"path_abs", "path_abs",
"predicates", "predicates",
"semver", "semver",
@ -213,12 +214,12 @@ dependencies = [
[[package]] [[package]]
name = "clircle" name = "clircle"
version = "0.2.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e27a01e782190a8314e65cc94274d9bbcd52e05a9d15b437fe2b31259b854b0d" checksum = "e68bbd985a63de680ab4d1ad77b6306611a8f961b282c8b5ab513e6de934e396"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"nix", "libc",
"serde", "serde",
"winapi", "winapi",
] ]

View File

@ -48,7 +48,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.8" serde_yaml = "0.8"
semver = "0.11" semver = "0.11"
path_abs = { version = "0.5", default-features = false } path_abs = { version = "0.5", default-features = false }
clircle = "0.2.0" clircle = "0.3"
bugreport = "0.3" bugreport = "0.3"
dirs-next = { version = "2.0.0", optional = true } dirs-next = { version = "2.0.0", optional = true }

View File

@ -329,7 +329,7 @@ mod tests {
let input = Input::ordinary_file(file_path.as_os_str()); let input = Input::ordinary_file(file_path.as_os_str());
let dummy_stdin: &[u8] = &[]; let dummy_stdin: &[u8] = &[];
let mut opened_input = input.open(dummy_stdin).unwrap(); let mut opened_input = input.open(dummy_stdin, None).unwrap();
self.assets self.assets
.get_syntax(None, &mut opened_input, &self.syntax_mapping) .get_syntax(None, &mut opened_input, &self.syntax_mapping)
@ -343,7 +343,7 @@ mod tests {
let input = Input::from_reader(Box::new(BufReader::new(first_line.as_bytes()))) let input = Input::from_reader(Box::new(BufReader::new(first_line.as_bytes())))
.with_name(Some(file_path.as_os_str())); .with_name(Some(file_path.as_os_str()));
let dummy_stdin: &[u8] = &[]; let dummy_stdin: &[u8] = &[];
let mut opened_input = input.open(dummy_stdin).unwrap(); let mut opened_input = input.open(dummy_stdin, None).unwrap();
self.assets self.assets
.get_syntax(None, &mut opened_input, &self.syntax_mapping) .get_syntax(None, &mut opened_input, &self.syntax_mapping)
@ -367,7 +367,7 @@ mod tests {
fn syntax_for_stdin_with_content(&self, file_name: &str, content: &[u8]) -> String { fn syntax_for_stdin_with_content(&self, file_name: &str, content: &[u8]) -> String {
let input = Input::stdin().with_name(Some(OsStr::new(file_name))); let input = Input::stdin().with_name(Some(OsStr::new(file_name)));
let mut opened_input = input.open(content).unwrap(); let mut opened_input = input.open(content, None).unwrap();
self.assets self.assets
.get_syntax(None, &mut opened_input, &self.syntax_mapping) .get_syntax(None, &mut opened_input, &self.syntax_mapping)
@ -523,7 +523,7 @@ mod tests {
let input = Input::ordinary_file(file_path_symlink.as_os_str()); let input = Input::ordinary_file(file_path_symlink.as_os_str());
let dummy_stdin: &[u8] = &[]; let dummy_stdin: &[u8] = &[];
let mut opened_input = input.open(dummy_stdin).unwrap(); let mut opened_input = input.open(dummy_stdin, None).unwrap();
assert_eq!( assert_eq!(
test.assets test.assets

View File

@ -1,4 +1,3 @@
use std::convert::TryFrom;
use std::io::{self, Write}; use std::io::{self, Write};
use crate::assets::HighlightingAssets; use crate::assets::HighlightingAssets;
@ -15,6 +14,8 @@ use crate::output::OutputType;
use crate::paging::PagingMode; use crate::paging::PagingMode;
use crate::printer::{InteractivePrinter, Printer, SimplePrinter}; use crate::printer::{InteractivePrinter, Printer, SimplePrinter};
use clircle::Clircle;
pub struct Controller<'a> { pub struct Controller<'a> {
config: &'a Config<'a>, config: &'a Config<'a>,
assets: &'a HighlightingAssets, assets: &'a HighlightingAssets,
@ -67,12 +68,10 @@ impl<'b> Controller<'b> {
} }
let attached_to_pager = output_type.is_pager(); let attached_to_pager = output_type.is_pager();
let rw_cycle_detected = !attached_to_pager && { let stdout_identifier = if cfg!(windows) || attached_to_pager {
let identifiers: Vec<_> = inputs None
.iter() } else {
.flat_map(clircle::Identifier::try_from) clircle::Identifier::stdout()
.collect();
clircle::stdout_among_inputs(&identifiers)
}; };
let writer = output_type.handle()?; let writer = output_type.handle()?;
@ -87,13 +86,8 @@ impl<'b> Controller<'b> {
} }
}; };
if rw_cycle_detected {
print_error(&"The output file is also an input!".into(), writer);
return Ok(false);
}
for (index, input) in inputs.into_iter().enumerate() { for (index, input) in inputs.into_iter().enumerate() {
match input.open(io::stdin().lock()) { match input.open(io::stdin().lock(), stdout_identifier.as_ref()) {
Err(error) => { Err(error) => {
print_error(&error, writer); print_error(&error, writer);
no_errors = false; no_errors = false;

View File

@ -2,8 +2,8 @@ use std::convert::TryFrom;
use std::ffi::{OsStr, OsString}; use std::ffi::{OsStr, OsString};
use std::fs::File; use std::fs::File;
use std::io::{self, BufRead, BufReader, Read}; use std::io::{self, BufRead, BufReader, Read};
use std::path::Path;
use clircle::{Clircle, Identifier};
use content_inspector::{self, ContentType}; use content_inspector::{self, ContentType};
use crate::error::*; use crate::error::*;
@ -157,25 +157,55 @@ impl<'a> Input<'a> {
&mut self.description &mut self.description
} }
pub(crate) fn open<R: BufRead + 'a>(self, stdin: R) -> Result<OpenedInput<'a>> { pub(crate) fn open<R: BufRead + 'a>(
self,
stdin: R,
stdout_identifier: Option<&Identifier>,
) -> Result<OpenedInput<'a>> {
let description = self.description().clone(); let description = self.description().clone();
match self.kind { match self.kind {
InputKind::StdIn => Ok(OpenedInput { InputKind::StdIn => {
kind: OpenedInputKind::StdIn, if let Some(stdout) = stdout_identifier {
description, let input_identifier = Identifier::try_from(clircle::Stdio::Stdin)
metadata: self.metadata, .map_err(|e| format!("Stdin: Error identifying file: {}", e))?;
reader: InputReader::new(stdin), if stdout.surely_conflicts_with(&input_identifier) {
}), return Err("IO circle detected. The input from stdin is also an output. Aborting to avoid infinite loop.".into());
}
}
Ok(OpenedInput {
kind: OpenedInputKind::StdIn,
description,
metadata: self.metadata,
reader: InputReader::new(stdin),
})
}
InputKind::OrdinaryFile(path) => Ok(OpenedInput { InputKind::OrdinaryFile(path) => Ok(OpenedInput {
kind: OpenedInputKind::OrdinaryFile(path.clone()), kind: OpenedInputKind::OrdinaryFile(path.clone()),
description, description,
metadata: self.metadata, metadata: self.metadata,
reader: { reader: {
let file = File::open(&path) let mut file = File::open(&path)
.map_err(|e| format!("'{}': {}", path.to_string_lossy(), e))?; .map_err(|e| format!("'{}': {}", path.to_string_lossy(), e))?;
if file.metadata()?.is_dir() { if file.metadata()?.is_dir() {
return Err(format!("'{}' is a directory.", path.to_string_lossy()).into()); return Err(format!("'{}' is a directory.", path.to_string_lossy()).into());
} }
if let Some(stdout) = stdout_identifier {
let input_identifier = Identifier::try_from(file).map_err(|e| {
format!("{}: Error identifying file: {}", path.to_string_lossy(), e)
})?;
if stdout.surely_conflicts_with(&input_identifier) {
return Err(format!(
"IO circle detected. The input from '{}' is also an output. Aborting to avoid infinite loop.",
path.to_string_lossy()
)
.into());
}
file = input_identifier.into_inner().expect("The file was lost in the clircle::Identifier, this should not have happended...");
}
InputReader::new(BufReader::new(file)) InputReader::new(BufReader::new(file))
}, },
}), }),
@ -189,18 +219,6 @@ impl<'a> Input<'a> {
} }
} }
impl TryFrom<&'_ Input<'_>> for clircle::Identifier {
type Error = ();
fn try_from(input: &Input) -> std::result::Result<clircle::Identifier, ()> {
match input.kind {
InputKind::OrdinaryFile(ref path) => Self::try_from(Path::new(path)).map_err(|_| ()),
InputKind::StdIn => Self::try_from(clircle::Stdio::Stdin).map_err(|_| ()),
InputKind::CustomReader(_) => Err(()),
}
}
}
pub(crate) struct InputReader<'a> { pub(crate) struct InputReader<'a> {
inner: Box<dyn BufRead + 'a>, inner: Box<dyn BufRead + 'a>,
pub(crate) first_line: Vec<u8>, pub(crate) first_line: Vec<u8>,