main.rs 5.8 KB
Newer Older
1
#![feature(convert, fs_mode)]
B
Ben S 已提交
2
#![feature(slice_extras, 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 147 148 149 150 151 152 153 154 155
    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.
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 182 183
    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),
184
            View::Lines(l)    => l.view(files),
185
        }
186
    }
187
}
188

B
Ben S 已提交
189

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

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