on master help sign in
Change circle detection to use new more conservative method and run in main loop instead of before the loop
commit694b31909a38989fbb153164f0218f1f0377d2c9 authorNiklas Mohrin <dev@niklasmohrin.de> dateSat 27 Feb 2021, 15:32 (6 years ago) parenta98811b
5 files changed+55−42
Cargo.lock+4−3 Cargo.toml+1−1 src/assets.rs+4−4 src/controller.rs+7−13 src/input.rs+39−21
Cargo.lock+4−3
@@ -98,6 +98,7 @@ dependencies = [
9898 "git2",
9999 "globset",
100100 "lazy_static",
101+ "nix",
101102 "path_abs",
102103 "predicates",
103104 "semver",
@@ -213,12 +214,12 @@ dependencies = [
213214
214215[[package]]
215216name = "clircle"
216−version = "0.2.0"
217+version = "0.3.0"
217218source = "registry+https://github.com/rust-lang/crates.io-index"
218−checksum = "e27a01e782190a8314e65cc94274d9bbcd52e05a9d15b437fe2b31259b854b0d"
219+checksum = "e68bbd985a63de680ab4d1ad77b6306611a8f961b282c8b5ab513e6de934e396"
219220dependencies = [
220221 "cfg-if",
221− "nix",
222+ "libc",
222223 "serde",
223224 "winapi",
224225]
Cargo.toml+1−1
@@ -48,7 +48,7 @@ serde = { version = "1.0", features = ["derive"] }
4848serde_yaml = "0.8"
4949semver = "0.11"
5050path_abs = { version = "0.5", default-features = false }
51−clircle = "0.2.0"
51+clircle = "0.3"
5252bugreport = "0.3"
5353dirs-next = { version = "2.0.0", optional = true }
5454
src/assets.rs+4−4
@@ -329,7 +329,7 @@ mod tests {
329329
330330 let input = Input::ordinary_file(file_path.as_os_str());
331331 let dummy_stdin: &[u8] = &[];
332− let mut opened_input = input.open(dummy_stdin).unwrap();
332+ let mut opened_input = input.open(dummy_stdin, None).unwrap();
333333
334334 self.assets
335335 .get_syntax(None, &mut opened_input, &self.syntax_mapping)
@@ -343,7 +343,7 @@ mod tests {
343343 let input = Input::from_reader(Box::new(BufReader::new(first_line.as_bytes())))
344344 .with_name(Some(file_path.as_os_str()));
345345 let dummy_stdin: &[u8] = &[];
346− let mut opened_input = input.open(dummy_stdin).unwrap();
346+ let mut opened_input = input.open(dummy_stdin, None).unwrap();
347347
348348 self.assets
349349 .get_syntax(None, &mut opened_input, &self.syntax_mapping)
@@ -367,7 +367,7 @@ mod tests {
367367
368368 fn syntax_for_stdin_with_content(&self, file_name: &str, content: &[u8]) -> String {
369369 let input = Input::stdin().with_name(Some(OsStr::new(file_name)));
370− let mut opened_input = input.open(content).unwrap();
370+ let mut opened_input = input.open(content, None).unwrap();
371371
372372 self.assets
373373 .get_syntax(None, &mut opened_input, &self.syntax_mapping)
@@ -523,7 +523,7 @@ mod tests {
523523
524524 let input = Input::ordinary_file(file_path_symlink.as_os_str());
525525 let dummy_stdin: &[u8] = &[];
526− let mut opened_input = input.open(dummy_stdin).unwrap();
526+ let mut opened_input = input.open(dummy_stdin, None).unwrap();
527527
528528 assert_eq!(
529529 test.assets
src/controller.rs+7−13
@@ -1,4 +1,3 @@
1−use std::convert::TryFrom;
21use std::io::{self, Write};
32
43use crate::assets::HighlightingAssets;
@@ -15,6 +14,8 @@ use crate::output::OutputType;
1514use crate::paging::PagingMode;
1615use crate::printer::{InteractivePrinter, Printer, SimplePrinter};
1716
17+use clircle::Clircle;
18+
1819pub struct Controller<'a> {
1920 config: &'a Config<'a>,
2021 assets: &'a HighlightingAssets,
@@ -67,12 +68,10 @@ impl<'b> Controller<'b> {
6768 }
6869
6970 let attached_to_pager = output_type.is_pager();
70− let rw_cycle_detected = !attached_to_pager && {
71− let identifiers: Vec<_> = inputs
72− .iter()
73− .flat_map(clircle::Identifier::try_from)
74− .collect();
75− clircle::stdout_among_inputs(&identifiers)
71+ let stdout_identifier = if cfg!(windows) || attached_to_pager {
72+ None
73+ } else {
74+ clircle::Identifier::stdout()
7675 };
7776
7877 let writer = output_type.handle()?;
@@ -87,13 +86,8 @@ impl<'b> Controller<'b> {
8786 }
8887 };
8988
90− if rw_cycle_detected {
91− print_error(&"The output file is also an input!".into(), writer);
92− return Ok(false);
93− }
94−
9589 for (index, input) in inputs.into_iter().enumerate() {
96− match input.open(io::stdin().lock()) {
90+ match input.open(io::stdin().lock(), stdout_identifier.as_ref()) {
9791 Err(error) => {
9892 print_error(&error, writer);
9993 no_errors = false;
src/input.rs+39−21
@@ -2,8 +2,8 @@ use std::convert::TryFrom;
22use std::ffi::{OsStr, OsString};
33use std::fs::File;
44use std::io::{self, BufRead, BufReader, Read};
5−use std::path::Path;
65
6+use clircle::{Clircle, Identifier};
77use content_inspector::{self, ContentType};
88
99use crate::error::*;
@@ -157,25 +157,55 @@ impl<'a> Input<'a> {
157157 &mut self.description
158158 }
159159
160− pub(crate) fn open<R: BufRead + 'a>(self, stdin: R) -> Result<OpenedInput<'a>> {
160+ pub(crate) fn open<R: BufRead + 'a>(
161+ self,
162+ stdin: R,
163+ stdout_identifier: Option<&Identifier>,
164+ ) -> Result<OpenedInput<'a>> {
161165 let description = self.description().clone();
162166 match self.kind {
163− InputKind::StdIn => Ok(OpenedInput {
164− kind: OpenedInputKind::StdIn,
165− description,
166− metadata: self.metadata,
167− reader: InputReader::new(stdin),
168− }),
167+ InputKind::StdIn => {
168+ if let Some(stdout) = stdout_identifier {
169+ let input_identifier = Identifier::try_from(clircle::Stdio::Stdin)
170+ .map_err(|e| format!("Stdin: Error identifying file: {}", e))?;
171+ if stdout.surely_conflicts_with(&input_identifier) {
172+ return Err("IO circle detected. The input from stdin is also an output. Aborting to avoid infinite loop.".into());
173+ }
174+ }
175+
176+ Ok(OpenedInput {
177+ kind: OpenedInputKind::StdIn,
178+ description,
179+ metadata: self.metadata,
180+ reader: InputReader::new(stdin),
181+ })
182+ }
183+
169184 InputKind::OrdinaryFile(path) => Ok(OpenedInput {
170185 kind: OpenedInputKind::OrdinaryFile(path.clone()),
171186 description,
172187 metadata: self.metadata,
173188 reader: {
174− let file = File::open(&path)
189+ let mut file = File::open(&path)
175190 .map_err(|e| format!("'{}': {}", path.to_string_lossy(), e))?;
176191 if file.metadata()?.is_dir() {
177192 return Err(format!("'{}' is a directory.", path.to_string_lossy()).into());
178193 }
194+
195+ if let Some(stdout) = stdout_identifier {
196+ let input_identifier = Identifier::try_from(file).map_err(|e| {
197+ format!("{}: Error identifying file: {}", path.to_string_lossy(), e)
198+ })?;
199+ if stdout.surely_conflicts_with(&input_identifier) {
200+ return Err(format!(
201+ "IO circle detected. The input from '{}' is also an output. Aborting to avoid infinite loop.",
202+ path.to_string_lossy()
203+ )
204+ .into());
205+ }
206+ file = input_identifier.into_inner().expect("The file was lost in the clircle::Identifier, this should not have happended...");
207+ }
208+
179209 InputReader::new(BufReader::new(file))
180210 },
181211 }),
@@ -189,18 +219,6 @@ impl<'a> Input<'a> {
189219 }
190220}
191221
192−impl TryFrom<&'_ Input<'_>> for clircle::Identifier {
193− type Error = ();
194−
195− fn try_from(input: &Input) -> std::result::Result<clircle::Identifier, ()> {
196− match input.kind {
197− InputKind::OrdinaryFile(ref path) => Self::try_from(Path::new(path)).map_err(|_| ()),
198− InputKind::StdIn => Self::try_from(clircle::Stdio::Stdin).map_err(|_| ()),
199− InputKind::CustomReader(_) => Err(()),
200− }
201− }
202−}
203−
204222pub(crate) struct InputReader<'a> {
205223 inner: Box<dyn BufRead + 'a>,
206224 pub(crate) first_line: Vec<u8>,