main.rs 6.4 KB
Newer Older
B
Ben S 已提交
1
#![feature(collections, convert, core, exit_status, file_type, fs_ext, fs_mode)]
B
Ben S 已提交
2
#![feature(libc, metadata_ext, raw_ext, scoped, symlink_metadata)]
3

4
extern crate ansi_term;
5
extern crate datetime;
6
extern crate getopts;
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;
12
extern crate users;
B
Ben S 已提交
13 14
extern crate unicode_width;

15

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

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

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

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

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

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

B
bp 已提交
58
    fn load(&mut self, files: &[String]) {
59 60 61
        // Separate the user-supplied paths into directories and files.
        // Files are shown first, and then each directory is expanded
        // and listed second.
B
bp 已提交
62

63
        let is_tree = self.options.dir_action.is_tree() || self.options.dir_action.is_as_file();
B
bp 已提交
64 65 66
        let total_files = files.len();

        // Denotes the maxinum number of concurrent threads
B
Benjamin Sago 已提交
67
        let (thread_capacity_tx, thread_capacity_rs) = sync_channel(8 * num_cpus::get());
B
bp 已提交
68 69 70 71

        // Communication between consumer thread and producer threads
        enum StatResult<'a> {
            File(File<'a>),
72
            Path(PathBuf),
73
            Error
B
bp 已提交
74
        }
75

B
bp 已提交
76 77 78 79 80 81 82 83 84 85 86 87 88
        let (results_tx, results_rx) = channel();

        // Spawn consumer thread
        let _consumer = thread::scoped(move || {
            for _ in 0..total_files {

                // Make room for more producer threads
                let _ = thread_capacity_rs.recv();

                // Receive a producer's result
                match results_rx.recv() {
                    Ok(result) => match result {
                        StatResult::File(file) => self.files.push(file),
89 90
                        StatResult::Path(path) => self.dirs.push(path),
                        StatResult::Error      => ()
B
bp 已提交
91
                    },
92
                    Err(_) => unreachable!(),
B
bp 已提交
93 94 95 96 97 98 99 100 101 102 103 104 105 106
                }
                self.count += 1;
            }
        });

        for file in files.iter() {
            let file = file.clone();
            let results_tx = results_tx.clone();

            // Block until there is room for another thread
            let _ = thread_capacity_tx.send(());

            // Spawn producer thread
            thread::spawn(move || {
107 108
                let path = Path::new(&*file);
                let _ = results_tx.send(match fs::metadata(&path) {
B
bp 已提交
109
                    Ok(stat) => {
110
                        if !stat.is_dir() {
111 112 113 114
                            StatResult::File(File::with_stat(stat, &path, None, false))
                        }
                        else if is_tree {
                            StatResult::File(File::with_stat(stat, &path, None, true))
115 116
                        }
                        else {
117
                            StatResult::Path(path.to_path_buf())
118 119
                        }
                    }
120 121
                    Err(e) => {
                        println!("{}: {}", file, e);
122
                        StatResult::Error
123
                    }
124
                });
B
bp 已提交
125
            });
126
        }
B
Ben S 已提交
127
    }
B
Ben S 已提交
128

129 130
    fn print_files(&self) {
        if !self.files.is_empty() {
B
Ben S 已提交
131
            self.print(None, &self.files[..]);
B
Ben S 已提交
132
        }
133
    }
134

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
    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.
164
                    if let Some(recurse_opts) = self.options.dir_action.recurse_options() {
165
                        let depth = dir_path.components().filter(|&c| c != Component::CurDir).count() + 1;
166
                        if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) {
167
                            for dir in files.iter().filter(|f| f.is_directory()).rev() {
168 169
                                self.dirs.push(dir.path.clone());
                            }
170
                        }
B
Ben S 已提交
171 172
                    }

173 174 175 176 177
                    if self.count > 1 {
                        println!("{}:", dir_path.display());
                    }
                    self.count += 1;

B
Ben S 已提交
178
                    self.print(Some(dir), &files[..]);
179 180 181 182
                }
                Err(e) => {
                    println!("{}: {}", dir_path.display(), e);
                    return;
183
                }
184 185 186
            };
        }
    }
187

188 189 190 191 192 193
    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),
        }
194
    }
195
}
196

B
Ben S 已提交
197
#[cfg(not(test))]
198
fn main() {
B
Ben S 已提交
199
    let args: Vec<String> = env::args().collect();
200

201
    match Options::getopts(args.tail()) {
202 203
        Ok((options, paths)) => {
            let mut exa = Exa::new(options);
B
bp 已提交
204
            exa.load(&paths);
205 206 207
            exa.print_files();
            exa.print_dirs();
        },
208
        Err(e) => {
209
            println!("{}", e);
B
Ben S 已提交
210
            env::set_exit_status(e.error_code());
211
        },
212 213
    };
}