Browse Source

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
Benjamin Sago 6 years ago
parent
commit
e936d7e09f
3 changed files with 34 additions and 5 deletions
  1. 1 1
      src/exa.rs
  2. 2 2
      src/fs/dir.rs
  3. 31 2
      src/fs/file.rs

+ 1 - 1
src/exa.rs

@@ -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)?,

+ 2 - 2
src/fs/dir.rs

@@ -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 {

+ 31 - 2
src/fs/file.rs

@@ -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, 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, metadata, ext, name })
+        Ok(File { path, parent_dir: Some(parent_dir), metadata, ext, name: "..".to_string(), is_all_all })
     }
     }
 
 
     /// A file’s name is derived from its string. This needs to handle directories
     /// A file’s 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);