main.rs 6.4 KB
Newer Older
1
#![feature(convert, core, exit_status, fs_ext, fs_time, io, libc, os, scoped, std_misc, unicode)]
B
Ben S 已提交
2
#![allow(deprecated)]
3 4

// Other platforms than macos don't need std_misc but you can't
N
nwin 已提交
5
// use #[cfg] on features.
6
#![allow(unused_features)]
7

8
extern crate ansi_term;
9
extern crate datetime;
10
extern crate getopts;
11
extern crate locale;
12
extern crate natord;
B
Benjamin Sago 已提交
13
extern crate num_cpus;
14
extern crate number_prefix;
15
extern crate pad;
16
extern crate users;
17

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

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

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

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

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

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

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

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

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

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

B
bp 已提交
78 79 80 81 82 83 84 85 86 87 88 89 90
        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),
91 92
                        StatResult::Path(path) => self.dirs.push(path),
                        StatResult::Error      => ()
B
bp 已提交
93
                    },
94
                    Err(_) => unreachable!(),
B
bp 已提交
95 96 97 98 99 100 101 102 103 104 105 106 107 108
                }
                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 || {
109 110
                let path = Path::new(&*file);
                let _ = results_tx.send(match fs::metadata(&path) {
B
bp 已提交
111
                    Ok(stat) => {
112
                        if !stat.is_dir() {
113 114 115 116
                            StatResult::File(File::with_stat(stat, &path, None, false))
                        }
                        else if is_tree {
                            StatResult::File(File::with_stat(stat, &path, None, true))
117 118
                        }
                        else {
119
                            StatResult::Path(path.to_path_buf())
120 121
                        }
                    }
122 123
                    Err(e) => {
                        println!("{}: {}", file, e);
124
                        StatResult::Error
125
                    }
126
                });
B
bp 已提交
127
            });
128
        }
B
Ben S 已提交
129
    }
B
Ben S 已提交
130

131 132
    fn print_files(&self) {
        if !self.files.is_empty() {
B
Ben S 已提交
133
            self.print(None, &self.files[..]);
B
Ben S 已提交
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 164 165
    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.
166
                    if let Some(recurse_opts) = self.options.dir_action.recurse_options() {
167
                        let depth = dir_path.components().filter(|&c| c != Component::CurDir).count() + 1;
168
                        if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) {
169
                            for dir in files.iter().filter(|f| f.is_directory()).rev() {
170 171
                                self.dirs.push(dir.path.clone());
                            }
172
                        }
B
Ben S 已提交
173 174
                    }

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

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

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

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

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