main.rs 6.3 KB
Newer Older
B
Ben S 已提交
1
#![feature(collections, core, exit_status, io, libc, old_io, old_path, os, std_misc)]
2 3

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

7
extern crate ansi_term;
8
extern crate datetime;
9
extern crate getopts;
10
extern crate locale;
11
extern crate natord;
12
extern crate number_prefix;
13
extern crate pad;
14
extern crate users;
15

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

B
Ben S 已提交
19
use std::env;
B
Ben S 已提交
20
use std::old_io::{fs, FileType};
B
bp 已提交
21 22 23
use std::os::num_cpus;
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;
B
Ben S 已提交
32
pub mod file;
33
pub mod filetype;
B
Ben S 已提交
34
pub mod options;
35
pub mod output;
36
pub mod term;
N
nwin 已提交
37
pub mod xattr;
B
Ben S 已提交
38

B
Ben S 已提交
39
#[cfg(not(test))]
40 41 42 43 44 45
struct Exa<'a> {
    count:   usize,
    options: Options,
    dirs:    Vec<Path>,
    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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113

        let is_tree = self.options.dir_action.is_tree();
        let total_files = files.len();

        // Denotes the maxinum number of concurrent threads
        let (thread_capacity_tx, thread_capacity_rs) = sync_channel(8 * num_cpus());

        // Communication between consumer thread and producer threads
        enum StatResult<'a> {
            File(File<'a>),
            Path(Path)
        }
        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),
                        StatResult::Path(path) => self.dirs.push(path)
                    },
                    Err(_) => unreachable!()
                }
                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 || {
                let path = Path::new(file.clone());
                match fs::stat(&path) {
                    Ok(stat) => {
                        if stat.kind == FileType::Directory {
                            if is_tree {
                                let _ = results_tx.send(StatResult::File(File::with_stat(stat, &path, None, true)));
                            }
                            else {
                                let _ = results_tx.send(StatResult::Path(path));
                            }
114 115
                        }
                        else {
B
bp 已提交
116
                            let _ = results_tx.send(StatResult::File(File::with_stat(stat, &path, None, false)));
117 118
                        }
                    }
B
bp 已提交
119
                    Err(e) => println!("{}: {}", file, e),
120
                }
B
bp 已提交
121
            });
122
        }
B
bp 已提交
123

B
Ben S 已提交
124
    }
B
Ben S 已提交
125

126 127
    fn print_files(&self) {
        if !self.files.is_empty() {
B
Ben S 已提交
128
            self.print(None, &self.files[..]);
B
Ben S 已提交
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 156 157 158 159 160
    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.
161
                    if let Some(recurse_opts) = self.options.dir_action.recurse_options() {
162 163
                        let depth = dir_path.components().filter(|&c| c != b".").count() + 1;
                        if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) {
164 165 166
                            for dir in files.iter().filter(|f| f.stat.kind == FileType::Directory).rev() {
                                self.dirs.push(dir.path.clone());
                            }
167
                        }
B
Ben S 已提交
168 169
                    }

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

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

185 186 187 188 189 190
    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),
        }
191
    }
192
}
193

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

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