main.rs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. #![feature(collections, core, env, io, libc, old_io, old_path, os, std_misc)]
  2. // Other platforms than macos don't need std_misc but you can't
  3. // use #[cfg] on features.
  4. #![allow(unused_features)]
  5. extern crate ansi_term;
  6. extern crate datetime;
  7. extern crate getopts;
  8. extern crate locale;
  9. extern crate natord;
  10. extern crate number_prefix;
  11. extern crate pad;
  12. extern crate users;
  13. #[cfg(feature="git")]
  14. extern crate git2;
  15. use std::env;
  16. use std::old_io::{fs, FileType};
  17. use dir::Dir;
  18. use file::File;
  19. use options::{Options, View};
  20. use output::lines_view;
  21. pub mod column;
  22. pub mod dir;
  23. pub mod file;
  24. pub mod filetype;
  25. pub mod options;
  26. pub mod output;
  27. pub mod term;
  28. pub mod xattr;
  29. #[cfg(not(test))]
  30. struct Exa<'a> {
  31. count: usize,
  32. options: Options,
  33. dirs: Vec<Path>,
  34. files: Vec<File<'a>>,
  35. }
  36. #[cfg(not(test))]
  37. impl<'a> Exa<'a> {
  38. fn new(options: Options) -> Exa<'a> {
  39. Exa {
  40. count: 0,
  41. options: options,
  42. dirs: Vec::new(),
  43. files: Vec::new(),
  44. }
  45. }
  46. fn load<T>(&mut self, iter: T) where T: Iterator<Item = &'a String> {
  47. // Separate the user-supplied paths into directories and files.
  48. // Files are shown first, and then each directory is expanded
  49. // and listed second.
  50. for file in iter {
  51. let path = Path::new(file);
  52. match fs::stat(&path) {
  53. Ok(stat) => {
  54. if stat.kind == FileType::Directory {
  55. if self.options.dir_action.is_tree() {
  56. self.files.push(File::with_stat(stat, &path, None, true));
  57. }
  58. else {
  59. self.dirs.push(path);
  60. }
  61. }
  62. else {
  63. self.files.push(File::with_stat(stat, &path, None, false));
  64. }
  65. }
  66. Err(e) => println!("{}: {}", file, e),
  67. }
  68. self.count += 1;
  69. }
  70. }
  71. fn print_files(&self) {
  72. if !self.files.is_empty() {
  73. self.print(None, &self.files[..]);
  74. }
  75. }
  76. fn print_dirs(&mut self) {
  77. let mut first = self.files.is_empty();
  78. // Directories are put on a stack rather than just being iterated through,
  79. // as the vector can change as more directories are added.
  80. loop {
  81. let dir_path = match self.dirs.pop() {
  82. None => break,
  83. Some(f) => f,
  84. };
  85. // Put a gap between directories, or between the list of files and the
  86. // first directory.
  87. if first {
  88. first = false;
  89. }
  90. else {
  91. print!("\n");
  92. }
  93. match Dir::readdir(&dir_path) {
  94. Ok(ref dir) => {
  95. let mut files = dir.files(false);
  96. self.options.transform_files(&mut files);
  97. // When recursing, add any directories to the dirs stack
  98. // backwards: the *last* element of the stack is used each
  99. // time, so by inserting them backwards, they get displayed in
  100. // the correct sort order.
  101. if let Some(recurse_opts) = self.options.dir_action.recurse_options() {
  102. let depth = dir_path.components().filter(|&c| c != b".").count() + 1;
  103. if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) {
  104. for dir in files.iter().filter(|f| f.stat.kind == FileType::Directory).rev() {
  105. self.dirs.push(dir.path.clone());
  106. }
  107. }
  108. }
  109. if self.count > 1 {
  110. println!("{}:", dir_path.display());
  111. }
  112. self.count += 1;
  113. self.print(Some(dir), &files[..]);
  114. }
  115. Err(e) => {
  116. println!("{}: {}", dir_path.display(), e);
  117. return;
  118. }
  119. };
  120. }
  121. }
  122. fn print(&self, dir: Option<&Dir>, files: &[File]) {
  123. match self.options.view {
  124. View::Grid(g) => g.view(files),
  125. View::Details(d) => d.view(dir, files),
  126. View::Lines => lines_view(files),
  127. }
  128. }
  129. }
  130. #[cfg(not(test))]
  131. fn main() {
  132. let args: Vec<String> = env::args().collect();
  133. match Options::getopts(args.tail()) {
  134. Ok((options, paths)) => {
  135. let mut exa = Exa::new(options);
  136. exa.load(paths.iter());
  137. exa.print_files();
  138. exa.print_dirs();
  139. },
  140. Err(e) => {
  141. println!("{}", e);
  142. env::set_exit_status(e.error_code());
  143. },
  144. };
  145. }