main.rs 4.5 KB
Newer Older
1 2 3 4
#![feature(collections, core, env, io, libc, os, path, plugin, std_misc)]

#[plugin] #[no_link]
extern crate datetime_macros;
B
Ben S 已提交
5

6
extern crate ansi_term;
7
extern crate datetime;
8 9
extern crate getopts;
extern crate natord;
10
extern crate number_prefix;
11
extern crate pad;
12
extern crate users;
13

B
Ben S 已提交
14 15 16
#[cfg(feature="git")]
extern crate git2;

B
Ben S 已提交
17
use std::env;
B
Ben S 已提交
18
use std::old_io::{fs, FileType};
B
Ben S 已提交
19

B
Ben S 已提交
20 21
use dir::Dir;
use file::File;
22 23
use options::{Options, View, DirAction};
use output::lines_view;
B
Ben S 已提交
24

B
Ben S 已提交
25
pub mod column;
B
Ben S 已提交
26
pub mod dir;
B
Ben S 已提交
27
pub mod file;
28
pub mod filetype;
B
Ben S 已提交
29
pub mod options;
30
pub mod output;
31
pub mod term;
B
Ben S 已提交
32

33 34 35 36 37 38
struct Exa<'a> {
    count:   usize,
    options: Options,
    dirs:    Vec<Path>,
    files:   Vec<File<'a>>,
}
B
Benjamin Sago 已提交
39

40 41 42 43 44 45 46 47
impl<'a> Exa<'a> {
    fn new(options: Options) -> Exa<'a> {
        Exa {
            count: 0,
            options: options,
            dirs: Vec::new(),
            files: Vec::new(),
        }
B
Ben S 已提交
48
    }
B
Ben S 已提交
49

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
    fn load<T>(&mut self, iter: T) where T: Iterator<Item = &'a String> {
        // Separate the user-supplied paths into directories and files.
        // Files are shown first, and then each directory is expanded
        // and listed second.
        for file in iter {
            let path = Path::new(file);
            match fs::stat(&path) {
                Ok(stat) => {
                    if stat.kind == FileType::Directory {
                        if self.options.dir_action == DirAction::Tree {
                            self.files.push(File::with_stat(stat, &path, None, true));
                        }
                        else {
                            self.dirs.push(path);
                        }
                    }
                    else {
                        self.files.push(File::with_stat(stat, &path, None, false));
                    }
                }
                Err(e) => println!("{}: {}", file, e),
            }
B
Ben S 已提交
72

73 74
            self.count += 1;
        }
B
Ben S 已提交
75
    }
B
Ben S 已提交
76

77 78 79
    fn print_files(&self) {
        if !self.files.is_empty() {
            self.print(None, &self.files[]);
B
Ben S 已提交
80
        }
81
    }
82

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
    fn print_dirs(&mut self) {
        let mut first = self.files.is_empty();

        // Directories are put on a stack rather than just being iterated through,
        // as the vector can change as more directories are added.
        loop {
            let dir_path = match self.dirs.pop() {
                None => break,
                Some(f) => f,
            };

            // Put a gap between directories, or between the list of files and the
            // first directory.
            if first {
                first = false;
            }
            else {
                print!("\n");
            }

            match Dir::readdir(&dir_path) {
                Ok(ref dir) => {
                    let mut files = dir.files(false);
                    self.options.transform_files(&mut files);

                    // When recursing, add any directories to the dirs stack
                    // backwards: the *last* element of the stack is used each
                    // time, so by inserting them backwards, they get displayed in
                    // the correct sort order.
                    if self.options.dir_action == DirAction::Recurse {
                        for dir in files.iter().filter(|f| f.stat.kind == FileType::Directory).rev() {
                            self.dirs.push(dir.path.clone());
                        }
B
Ben S 已提交
116 117
                    }

118 119 120 121 122 123 124 125 126 127
                    if self.count > 1 {
                        println!("{}:", dir_path.display());
                    }
                    self.count += 1;

                    self.print(Some(dir), &files[]);
                }
                Err(e) => {
                    println!("{}: {}", dir_path.display(), e);
                    return;
128
                }
129 130 131
            };
        }
    }
132

133 134 135 136 137 138
    fn print(&self, dir: Option<&Dir>, files: &[File]) {
        match self.options.view {
            View::Grid(g)     => g.view(files),
            View::Details(d)  => d.view(dir, files),
            View::Lines       => lines_view(files),
        }
139
    }
140
}
141 142

fn main() {
B
Ben S 已提交
143
    let args = args();
144

145
    match Options::getopts(args.tail()) {
146 147 148 149 150 151
        Ok((options, paths)) => {
            let mut exa = Exa::new(options);
            exa.load(paths.iter());
            exa.print_files();
            exa.print_dirs();
        },
152
        Err(e) => {
153
            println!("{}", e);
B
Ben S 已提交
154
            env::set_exit_status(e.error_code());
155
        },
156 157
    };
}
B
Ben S 已提交
158 159 160 161

fn args() -> Vec<String> {
    env::args().map(|arg| arg.to_string_lossy().into_owned()).collect()
}