Prevent infinite loop with -aaR

This happened because exa would recurse into `.` over and over again. There was nothing distinguishing the pseudo-entry for `.` that was being added by `--a` from a `.` passed in on the command-line, so it was looping forever.

It gets fixed by having the File value keep track of whether it’s an --all --all entry, and not recursing into directories with this field set.

Fixes #515
This commit is contained in:
Benjamin Sago 2019-07-13 21:14:42 +01:00
parent 7dada93c3e
commit e936d7e09f
3 changed files with 34 additions and 5 deletions

View File

@ -191,7 +191,7 @@ impl<'args, 'w, W: Write + 'w> Exa<'args, 'w, W> {
if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) { if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) {
let mut child_dirs = Vec::new(); let mut child_dirs = Vec::new();
for child_dir in children.iter().filter(|f| f.is_directory()) { for child_dir in children.iter().filter(|f| f.is_directory() && !f.is_all_all) {
match child_dir.to_dir() { match child_dir.to_dir() {
Ok(d) => child_dirs.push(d), Ok(d) => child_dirs.push(d),
Err(e) => writeln!(stderr(), "{}: {}", child_dir.path.display(), e)?, Err(e) => writeln!(stderr(), "{}: {}", child_dir.path.display(), e)?,

View File

@ -140,12 +140,12 @@ impl<'dir, 'ig> Iterator for Files<'dir, 'ig> {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if let Dots::DotNext = self.dots { if let Dots::DotNext = self.dots {
self.dots = Dots::DotDotNext; self.dots = Dots::DotDotNext;
Some(File::new(self.dir.path.to_path_buf(), self.dir, String::from(".")) Some(File::new_aa_current(self.dir)
.map_err(|e| (Path::new(".").to_path_buf(), e))) .map_err(|e| (Path::new(".").to_path_buf(), e)))
} }
else if let Dots::DotDotNext = self.dots { else if let Dots::DotDotNext = self.dots {
self.dots = Dots::FilesNext; self.dots = Dots::FilesNext;
Some(File::new(self.parent(), self.dir, String::from("..")) Some(File::new_aa_parent(self.parent(), self.dir)
.map_err(|e| (self.parent(), e))) .map_err(|e| (self.parent(), e)))
} }
else { else {

View File

@ -55,6 +55,13 @@ pub struct File<'dir> {
/// contain a reference to it, which is used in certain operations (such /// contain a reference to it, which is used in certain operations (such
/// as looking up compiled files). /// as looking up compiled files).
pub parent_dir: Option<&'dir Dir>, pub parent_dir: Option<&'dir Dir>,
/// Whether this is one of the two `--all all` directories, `.` and `..`.
///
/// Unlike all other entries, these are not returned as part of the
/// directory's children, and are in fact added specifically by exa; this
/// means that they should be skipped when recursing.
pub is_all_all: bool,
} }
impl<'dir> File<'dir> { impl<'dir> File<'dir> {
@ -68,8 +75,30 @@ impl<'dir> File<'dir> {
debug!("Statting file {:?}", &path); debug!("Statting file {:?}", &path);
let metadata = fs::symlink_metadata(&path)?; let metadata = fs::symlink_metadata(&path)?;
let is_all_all = false;
Ok(File { path, parent_dir, metadata, ext, name }) Ok(File { path, parent_dir, metadata, ext, name, is_all_all })
}
pub fn new_aa_current(parent_dir: &'dir Dir) -> IOResult<File<'dir>> {
let path = parent_dir.path.to_path_buf();
let ext = File::ext(&path);
debug!("Statting file {:?}", &path);
let metadata = fs::symlink_metadata(&path)?;
let is_all_all = true;
Ok(File { path, parent_dir: Some(parent_dir), metadata, ext, name: ".".to_string(), is_all_all })
}
pub fn new_aa_parent(path: PathBuf, parent_dir: &'dir Dir) -> IOResult<File<'dir>> {
let ext = File::ext(&path);
debug!("Statting file {:?}", &path);
let metadata = fs::symlink_metadata(&path)?;
let is_all_all = true;
Ok(File { path, parent_dir: Some(parent_dir), metadata, ext, name: "..".to_string(), is_all_all })
} }
/// A files name is derived from its string. This needs to handle directories /// A files name is derived from its string. This needs to handle directories
@ -219,7 +248,7 @@ impl<'dir> File<'dir> {
Ok(metadata) => { Ok(metadata) => {
let ext = File::ext(&path); let ext = File::ext(&path);
let name = File::filename(&path); let name = File::filename(&path);
FileTarget::Ok(Box::new(File { parent_dir: None, path, ext, metadata, name })) FileTarget::Ok(Box::new(File { parent_dir: None, path, ext, metadata, name, is_all_all: false }))
} }
Err(e) => { Err(e) => {
error!("Error following link {:?}: {:#?}", &path, e); error!("Error following link {:?}: {:#?}", &path, e);