main.rs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. #![feature(collections, core, exit_status, io, libc, old_fs, old_io, old_path, os, std_misc, unicode)]
  2. #![allow(deprecated)]
  3. // Other platforms than macos don't need std_misc but you can't
  4. // use #[cfg] on features.
  5. #![allow(unused_features)]
  6. extern crate ansi_term;
  7. extern crate datetime;
  8. extern crate getopts;
  9. extern crate locale;
  10. extern crate natord;
  11. extern crate number_prefix;
  12. extern crate pad;
  13. extern crate unicode;
  14. extern crate users;
  15. #[cfg(feature="git")]
  16. extern crate git2;
  17. use std::env;
  18. use std::old_io::{fs, FileType};
  19. use std::old_path::GenericPath;
  20. use std::old_path::posix::Path;
  21. use std::os::num_cpus;
  22. use std::sync::mpsc::{channel, sync_channel};
  23. use std::thread;
  24. use dir::Dir;
  25. use file::File;
  26. use options::{Options, View};
  27. use output::lines_view;
  28. pub mod column;
  29. pub mod dir;
  30. pub mod feature;
  31. pub mod file;
  32. pub mod filetype;
  33. pub mod options;
  34. pub mod output;
  35. pub mod term;
  36. #[cfg(not(test))]
  37. struct Exa<'a> {
  38. count: usize,
  39. options: Options,
  40. dirs: Vec<Path>,
  41. files: Vec<File<'a>>,
  42. }
  43. #[cfg(not(test))]
  44. impl<'a> Exa<'a> {
  45. fn new(options: Options) -> Exa<'a> {
  46. Exa {
  47. count: 0,
  48. options: options,
  49. dirs: Vec::new(),
  50. files: Vec::new(),
  51. }
  52. }
  53. fn load(&mut self, files: &[String]) {
  54. // Separate the user-supplied paths into directories and files.
  55. // Files are shown first, and then each directory is expanded
  56. // and listed second.
  57. let is_tree = self.options.dir_action.is_tree();
  58. let total_files = files.len();
  59. // Denotes the maxinum number of concurrent threads
  60. let (thread_capacity_tx, thread_capacity_rs) = sync_channel(8 * num_cpus());
  61. // Communication between consumer thread and producer threads
  62. enum StatResult<'a> {
  63. File(File<'a>),
  64. Path(Path),
  65. Error
  66. }
  67. let (results_tx, results_rx) = channel();
  68. // Spawn consumer thread
  69. let _consumer = thread::scoped(move || {
  70. for _ in 0..total_files {
  71. // Make room for more producer threads
  72. let _ = thread_capacity_rs.recv();
  73. // Receive a producer's result
  74. match results_rx.recv() {
  75. Ok(result) => match result {
  76. StatResult::File(file) => self.files.push(file),
  77. StatResult::Path(path) => self.dirs.push(path),
  78. StatResult::Error => ()
  79. },
  80. Err(_) => unreachable!()
  81. }
  82. self.count += 1;
  83. }
  84. });
  85. for file in files.iter() {
  86. let file = file.clone();
  87. let results_tx = results_tx.clone();
  88. // Block until there is room for another thread
  89. let _ = thread_capacity_tx.send(());
  90. // Spawn producer thread
  91. thread::spawn(move || {
  92. let path = Path::new(file.clone());
  93. let _ = results_tx.send(match fs::stat(&path) {
  94. Ok(stat) => {
  95. if stat.kind != FileType::Directory {
  96. StatResult::File(File::with_stat(stat, &path, None, false))
  97. }
  98. else if is_tree {
  99. StatResult::File(File::with_stat(stat, &path, None, true))
  100. }
  101. else {
  102. StatResult::Path(path)
  103. }
  104. }
  105. Err(e) => {
  106. println!("{}: {}", file, e);
  107. StatResult::Error
  108. }
  109. });
  110. });
  111. }
  112. }
  113. fn print_files(&self) {
  114. if !self.files.is_empty() {
  115. self.print(None, &self.files[..]);
  116. }
  117. }
  118. fn print_dirs(&mut self) {
  119. let mut first = self.files.is_empty();
  120. // Directories are put on a stack rather than just being iterated through,
  121. // as the vector can change as more directories are added.
  122. loop {
  123. let dir_path = match self.dirs.pop() {
  124. None => break,
  125. Some(f) => f,
  126. };
  127. // Put a gap between directories, or between the list of files and the
  128. // first directory.
  129. if first {
  130. first = false;
  131. }
  132. else {
  133. print!("\n");
  134. }
  135. match Dir::readdir(&dir_path) {
  136. Ok(ref dir) => {
  137. let mut files = dir.files(false);
  138. self.options.transform_files(&mut files);
  139. // When recursing, add any directories to the dirs stack
  140. // backwards: the *last* element of the stack is used each
  141. // time, so by inserting them backwards, they get displayed in
  142. // the correct sort order.
  143. if let Some(recurse_opts) = self.options.dir_action.recurse_options() {
  144. let depth = dir_path.components().filter(|&c| c != b".").count() + 1;
  145. if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) {
  146. for dir in files.iter().filter(|f| f.stat.kind == FileType::Directory).rev() {
  147. self.dirs.push(dir.path.clone());
  148. }
  149. }
  150. }
  151. if self.count > 1 {
  152. println!("{}:", dir_path.display());
  153. }
  154. self.count += 1;
  155. self.print(Some(dir), &files[..]);
  156. }
  157. Err(e) => {
  158. println!("{}: {}", dir_path.display(), e);
  159. return;
  160. }
  161. };
  162. }
  163. }
  164. fn print(&self, dir: Option<&Dir>, files: &[File]) {
  165. match self.options.view {
  166. View::Grid(g) => g.view(files),
  167. View::Details(d) => d.view(dir, files),
  168. View::Lines => lines_view(files),
  169. }
  170. }
  171. }
  172. #[cfg(not(test))]
  173. fn main() {
  174. let args: Vec<String> = env::args().collect();
  175. match Options::getopts(args.tail()) {
  176. Ok((options, paths)) => {
  177. let mut exa = Exa::new(options);
  178. exa.load(&paths);
  179. exa.print_files();
  180. exa.print_dirs();
  181. },
  182. Err(e) => {
  183. println!("{}", e);
  184. env::set_exit_status(e.error_code());
  185. },
  186. };
  187. }