main.rs 5.7 KB
Newer Older
B
Benjamin Sago 已提交
1
#![feature(collections, convert, core, exit_status, fs_mode)]
2

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

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

B
Ben S 已提交
19

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

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

B
Ben S 已提交
27 28
use dir::Dir;
use file::File;
29
use options::{Options, View};
B
Ben S 已提交
30

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

B
Ben S 已提交
41

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

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

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

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


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

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

        for file in files.iter() {
B
Ben S 已提交
81
            let tx = tx.clone();
B
bp 已提交
82 83 84
            let file = file.clone();

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

        // 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 已提交
116
    }
B
Ben S 已提交
117

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

124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
    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.
153
                    if let Some(recurse_opts) = self.options.dir_action.recurse_options() {
154
                        let depth = dir_path.components().filter(|&c| c != Component::CurDir).count() + 1;
155
                        if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) {
156
                            for dir in files.iter().filter(|f| f.is_directory()).rev() {
157 158
                                self.dirs.push(dir.path.clone());
                            }
159
                        }
B
Ben S 已提交
160 161
                    }

162 163 164 165 166
                    if self.count > 1 {
                        println!("{}:", dir_path.display());
                    }
                    self.count += 1;

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

177 178 179 180
    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),
181
            View::Lines(l)    => l.view(files),
182
        }
183
    }
184
}
185

B
Ben S 已提交
186

B
Ben S 已提交
187
#[cfg(not(test))]
188
fn main() {
B
Ben S 已提交
189
    let args: Vec<String> = env::args().collect();
190

191
    match Options::getopts(args.tail()) {
192 193
        Ok((options, paths)) => {
            let mut exa = Exa::new(options);
B
bp 已提交
194
            exa.load(&paths);
195 196 197
            exa.print_files();
            exa.print_dirs();
        },
198
        Err(e) => {
199
            println!("{}", e);
B
Ben S 已提交
200
            env::set_exit_status(e.error_code());
201
        },
202 203
    };
}