exa.rs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. #![warn(trivial_casts, trivial_numeric_casts)]
  2. #![warn(unused_results)]
  3. extern crate ansi_term;
  4. extern crate datetime;
  5. extern crate getopts;
  6. extern crate glob;
  7. extern crate libc;
  8. extern crate locale;
  9. extern crate natord;
  10. extern crate num_cpus;
  11. extern crate number_prefix;
  12. extern crate scoped_threadpool;
  13. extern crate term_grid;
  14. extern crate unicode_width;
  15. extern crate users;
  16. extern crate zoneinfo_compiled;
  17. #[cfg(feature="git")] extern crate git2;
  18. use std::ffi::OsStr;
  19. use std::io::{stderr, Write, Result as IOResult};
  20. use std::path::{Component, Path};
  21. use ansi_term::{ANSIStrings, Style};
  22. use fs::{Dir, File};
  23. use options::{Options, View, Mode};
  24. pub use options::Misfire;
  25. use output::{escape, lines};
  26. mod fs;
  27. mod info;
  28. mod options;
  29. mod output;
  30. mod term;
  31. /// The main program wrapper.
  32. pub struct Exa<'w, W: Write + 'w> {
  33. /// List of command-line options, having been successfully parsed.
  34. pub options: Options,
  35. /// The output handle that we write to. When running the program normally,
  36. /// this will be `std::io::Stdout`, but it can accept any struct that’s
  37. /// `Write` so we can write into, say, a vector for testing.
  38. pub writer: &'w mut W,
  39. /// List of the free command-line arguments that should correspond to file
  40. /// names (anything that isn’t an option).
  41. pub args: Vec<String>,
  42. }
  43. impl<'w, W: Write + 'w> Exa<'w, W> {
  44. pub fn new<C>(args: C, writer: &'w mut W) -> Result<Exa<'w, W>, Misfire>
  45. where C: IntoIterator, C::Item: AsRef<OsStr> {
  46. Options::getopts(args).map(move |(options, args)| {
  47. Exa { options, writer, args }
  48. })
  49. }
  50. pub fn run(&mut self) -> IOResult<i32> {
  51. let mut files = Vec::new();
  52. let mut dirs = Vec::new();
  53. let mut exit_status = 0;
  54. // List the current directory by default, like ls.
  55. if self.args.is_empty() {
  56. self.args.push(".".to_owned());
  57. }
  58. for file_name in &self.args {
  59. match File::from_path(Path::new(&file_name), None) {
  60. Err(e) => {
  61. exit_status = 2;
  62. writeln!(stderr(), "{}: {}", file_name, e)?;
  63. },
  64. Ok(f) => {
  65. if f.is_directory() && !self.options.dir_action.treat_dirs_as_files() {
  66. match f.to_dir(self.options.should_scan_for_git()) {
  67. Ok(d) => dirs.push(d),
  68. Err(e) => writeln!(stderr(), "{}: {}", file_name, e)?,
  69. }
  70. }
  71. else {
  72. files.push(f);
  73. }
  74. },
  75. }
  76. }
  77. // We want to print a directory’s name before we list it, *except* in
  78. // the case where it’s the only directory, *except* if there are any
  79. // files to print as well. (It’s a double negative)
  80. let no_files = files.is_empty();
  81. let is_only_dir = dirs.len() == 1 && no_files;
  82. self.options.filter.filter_argument_files(&mut files);
  83. self.print_files(None, files)?;
  84. self.print_dirs(dirs, no_files, is_only_dir, exit_status)
  85. }
  86. fn print_dirs(&mut self, dir_files: Vec<Dir>, mut first: bool, is_only_dir: bool, exit_status: i32) -> IOResult<i32> {
  87. for dir in dir_files {
  88. // Put a gap between directories, or between the list of files and
  89. // the first directory.
  90. if first {
  91. first = false;
  92. }
  93. else {
  94. write!(self.writer, "\n")?;
  95. }
  96. if !is_only_dir {
  97. let mut bits = Vec::new();
  98. escape(dir.path.display().to_string(), &mut bits, Style::default(), Style::default());
  99. writeln!(self.writer, "{}:", ANSIStrings(&bits))?;
  100. }
  101. let mut children = Vec::new();
  102. for file in dir.files() {
  103. match file {
  104. Ok(file) => children.push(file),
  105. Err((path, e)) => writeln!(stderr(), "[{}: {}]", path.display(), e)?,
  106. }
  107. };
  108. self.options.filter.filter_child_files(&mut children);
  109. self.options.filter.sort_files(&mut children);
  110. if let Some(recurse_opts) = self.options.dir_action.recurse_options() {
  111. let depth = dir.path.components().filter(|&c| c != Component::CurDir).count() + 1;
  112. if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) {
  113. let mut child_dirs = Vec::new();
  114. for child_dir in children.iter().filter(|f| f.is_directory()) {
  115. match child_dir.to_dir(false) {
  116. Ok(d) => child_dirs.push(d),
  117. Err(e) => writeln!(stderr(), "{}: {}", child_dir.path.display(), e)?,
  118. }
  119. }
  120. self.print_files(Some(&dir), children)?;
  121. match self.print_dirs(child_dirs, false, false, exit_status) {
  122. Ok(_) => (),
  123. Err(e) => return Err(e),
  124. }
  125. continue;
  126. }
  127. }
  128. self.print_files(Some(&dir), children)?;
  129. }
  130. Ok(exit_status)
  131. }
  132. /// Prints the list of files using whichever view is selected.
  133. /// For various annoying logistical reasons, each one handles
  134. /// printing differently...
  135. fn print_files(&mut self, dir: Option<&Dir>, files: Vec<File>) -> IOResult<()> {
  136. if !files.is_empty() {
  137. let View { ref mode, colours, classify } = self.options.view;
  138. match *mode {
  139. Mode::Grid(ref g) => g.view(&files, self.writer, &colours, classify),
  140. Mode::Details(ref d) => d.view(dir, files, self.writer, &colours, classify),
  141. Mode::GridDetails(ref gd) => gd.view(dir, files, self.writer, &colours, classify),
  142. Mode::Lines => lines::view(files, self.writer, &colours, classify),
  143. }
  144. }
  145. else {
  146. Ok(())
  147. }
  148. }
  149. }