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

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

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

B
Ben S 已提交
22

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

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

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

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

B
Ben S 已提交
45

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

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

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

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


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

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

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

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

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

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

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

145
            match Dir::readdir(&dir_path, self.options.should_scan_for_git()) {
146
                Ok(ref dir) => {
B
Ben S 已提交
147 148
                    let mut files = Vec::new();

149
                    for file in dir.files() {
B
Ben S 已提交
150 151 152 153 154 155
                        match file {
                            Ok(file) => files.push(file),
                            Err((path, e))   => println!("[{}: {}]", path.display(), e),
                        }
                    }

156 157 158 159 160 161
                    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.
162
                    if let Some(recurse_opts) = self.options.dir_action.recurse_options() {
163
                        let depth = dir_path.components().filter(|&c| c != Component::CurDir).count() + 1;
164
                        if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) {
165
                            for dir in files.iter().filter(|f| f.is_directory()).rev() {
166 167
                                self.dirs.push(dir.path.clone());
                            }
168
                        }
B
Ben S 已提交
169 170
                    }

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

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

186 187
    fn print(&self, dir: Option<&Dir>, files: &[File]) {
        match self.options.view {
B
Ben S 已提交
188 189 190 191
            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),
192
        }
193
    }
194
}
195

B
Ben S 已提交
196

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

B
Ben S 已提交
201
    match Options::getopts(&args) {
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);
210
            process::exit(e.error_code());
211
        },
212 213
    };
}