main.rs 5.9 KB
Newer Older
1
#![feature(convert, fs_mode)]
B
Ben S 已提交
2
#![feature(slice_splits, vec_resize)]
3

4
extern crate ansi_term;
5
extern crate datetime;
6
extern crate getopts;
7
extern crate libc;
8
extern crate locale;
9
extern crate natord;
B
Benjamin Sago 已提交
10
extern crate num_cpus;
11
extern crate number_prefix;
12
extern crate pad;
B
Ben S 已提交
13
extern crate term_grid;
B
Ben S 已提交
14
extern crate threadpool;
B
Ben S 已提交
15
extern crate unicode_width;
B
Ben S 已提交
16
extern crate users;
B
Ben S 已提交
17

B
Ben S 已提交
18 19 20
#[cfg(feature="git")]
extern crate git2;

B
Ben S 已提交
21

B
Ben S 已提交
22
use std::env;
23 24
use std::fs;
use std::path::{Component, Path, PathBuf};
25
use std::process;
B
Ben S 已提交
26
use std::sync::mpsc::channel;
B
Ben S 已提交
27 28

use threadpool::ThreadPool;
B
Ben S 已提交
29

B
Ben S 已提交
30 31
use dir::Dir;
use file::File;
32
use options::{Options, View};
B
Ben S 已提交
33

34
mod colours;
35 36 37 38 39 40 41 42
mod column;
mod dir;
mod feature;
mod file;
mod filetype;
mod options;
mod output;
mod term;
B
Ben S 已提交
43

B
Ben S 已提交
44

B
Ben S 已提交
45
#[cfg(not(test))]
B
Ben S 已提交
46
struct Exa<'dir> {
47 48
    count:   usize,
    options: Options,
49
    dirs:    Vec<PathBuf>,
B
Ben S 已提交
50
    files:   Vec<File<'dir>>,
51
}
B
Benjamin Sago 已提交
52

B
Ben S 已提交
53
#[cfg(not(test))]
B
Ben S 已提交
54 55
impl<'dir> Exa<'dir> {
    fn new(options: Options) -> Exa<'dir> {
56 57 58 59 60 61
        Exa {
            count: 0,
            options: options,
            dirs: Vec::new(),
            files: Vec::new(),
        }
B
Ben S 已提交
62
    }
B
Ben S 已提交
63

B
bp 已提交
64
    fn load(&mut self, files: &[String]) {
B
Ben S 已提交
65

66 67 68
        // Separate the user-supplied paths into directories and files.
        // Files are shown first, and then each directory is expanded
        // and listed second.
69
        let is_tree = self.options.dir_action.is_tree() || self.options.dir_action.is_as_file();
B
bp 已提交
70 71 72 73
        let total_files = files.len();


        // Communication between consumer thread and producer threads
B
Ben S 已提交
74 75
        enum StatResult<'dir> {
            File(File<'dir>),
B
Ben S 已提交
76
            Dir(PathBuf),
77
            Error
B
bp 已提交
78
        }
79

B
Ben S 已提交
80 81
        let pool = ThreadPool::new(8 * num_cpus::get());
        let (tx, rx) = channel();
B
bp 已提交
82 83

        for file in files.iter() {
B
Ben S 已提交
84
            let tx = tx.clone();
B
bp 已提交
85 86 87
            let file = file.clone();

            // Spawn producer thread
B
Ben S 已提交
88
            pool.execute(move || {
89
                let path = Path::new(&*file);
B
Ben S 已提交
90
                let _ = tx.send(match fs::metadata(&path) {
B
Ben S 已提交
91 92 93
                    Ok(metadata) => {
                        if !metadata.is_dir() {
                            StatResult::File(File::with_metadata(metadata, &path, None, false))
94 95
                        }
                        else if is_tree {
B
Ben S 已提交
96
                            StatResult::File(File::with_metadata(metadata, &path, None, true))
97 98
                        }
                        else {
B
Ben S 已提交
99
                            StatResult::Dir(path.to_path_buf())
100 101
                        }
                    }
102 103
                    Err(e) => {
                        println!("{}: {}", file, e);
104
                        StatResult::Error
105
                    }
106
                });
B
bp 已提交
107
            });
108
        }
B
Ben S 已提交
109 110 111 112 113 114 115 116 117 118

        // Spawn consumer thread
        for result in rx.iter().take(total_files) {
            match result {
                StatResult::File(file)  => self.files.push(file),
                StatResult::Dir(path)   => self.dirs.push(path),
                StatResult::Error       => ()
            }
            self.count += 1;
        }
B
Ben S 已提交
119
    }
B
Ben S 已提交
120

121 122
    fn print_files(&self) {
        if !self.files.is_empty() {
B
Ben S 已提交
123
            self.print(None, &self.files[..]);
B
Ben S 已提交
124
        }
125
    }
126

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
    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");
            }

147
            match Dir::readdir(&dir_path, self.options.should_scan_for_git()) {
148
                Ok(ref dir) => {
149
                    let mut files = dir.files(false).flat_map(|f| f).collect();
150 151 152 153 154 155
                    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.
156
                    if let Some(recurse_opts) = self.options.dir_action.recurse_options() {
157
                        let depth = dir_path.components().filter(|&c| c != Component::CurDir).count() + 1;
158
                        if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) {
159
                            for dir in files.iter().filter(|f| f.is_directory()).rev() {
160 161
                                self.dirs.push(dir.path.clone());
                            }
162
                        }
B
Ben S 已提交
163 164
                    }

165 166 167 168 169
                    if self.count > 1 {
                        println!("{}:", dir_path.display());
                    }
                    self.count += 1;

B
Ben S 已提交
170
                    self.print(Some(dir), &files[..]);
171 172 173 174
                }
                Err(e) => {
                    println!("{}: {}", dir_path.display(), e);
                    return;
175
                }
176 177 178
            };
        }
    }
179

180 181
    fn print(&self, dir: Option<&Dir>, files: &[File]) {
        match self.options.view {
B
Ben S 已提交
182 183 184 185
            View::Grid(g)         => g.view(files),
            View::Details(d)      => d.view(dir, files),
            View::GridDetails(gd) => gd.view(dir, files),
            View::Lines(l)        => l.view(files),
186
        }
187
    }
188
}
189

B
Ben S 已提交
190

B
Ben S 已提交
191
#[cfg(not(test))]
192
fn main() {
B
Ben S 已提交
193
    let args: Vec<String> = env::args().skip(1).collect();
194

B
Ben S 已提交
195
    match Options::getopts(&args) {
196 197
        Ok((options, paths)) => {
            let mut exa = Exa::new(options);
B
bp 已提交
198
            exa.load(&paths);
199 200 201
            exa.print_files();
            exa.print_dirs();
        },
202
        Err(e) => {
203
            println!("{}", e);
204
            process::exit(e.error_code());
205
        },
206 207
    };
}