main.rs 5.7 KB
Newer Older
1 2
#![feature(convert, fs_mode)]
#![feature(slice_extras, iter_arith, 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 threadpool;
B
Ben S 已提交
14
extern crate unicode_width;
B
Ben S 已提交
15
extern crate users;
B
Ben S 已提交
16

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

B
Ben S 已提交
20

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

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

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

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

B
Ben S 已提交
43

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

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

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

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


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

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

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

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

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

120 121
    fn print_files(&self) {
        if !self.files.is_empty() {
B
Ben S 已提交
122
            self.print(None, &self.files[..]);
B
Ben S 已提交
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 153 154
    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.
155
                    if let Some(recurse_opts) = self.options.dir_action.recurse_options() {
156
                        let depth = dir_path.components().filter(|&c| c != Component::CurDir).count() + 1;
157
                        if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) {
158
                            for dir in files.iter().filter(|f| f.is_directory()).rev() {
159 160
                                self.dirs.push(dir.path.clone());
                            }
161
                        }
B
Ben S 已提交
162 163
                    }

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

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

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

B
Ben S 已提交
188

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

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