main.rs 6.5 KB
Newer Older
B
Ben S 已提交
1
#![feature(collections, core, exit_status, io, libc, old_fs, old_io, old_path, os, 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;
13
extern crate number_prefix;
14
extern crate pad;
B
Ben S 已提交
15
extern crate unicode;
16
extern crate users;
17

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

B
Ben S 已提交
21
use std::env;
B
Ben S 已提交
22
use std::old_io::{fs, FileType};
B
Ben S 已提交
23 24
use std::old_path::GenericPath;
use std::old_path::posix::Path;
B
bp 已提交
25 26 27
use std::os::num_cpus;
use std::sync::mpsc::{channel, sync_channel};
use std::thread;
B
Ben S 已提交
28

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

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

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

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

B
bp 已提交
62
    fn load(&mut self, files: &[String]) {
63 64 65
        // Separate the user-supplied paths into directories and files.
        // Files are shown first, and then each directory is expanded
        // and listed second.
B
bp 已提交
66 67 68 69 70 71 72 73 74 75

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

B
Ben S 已提交
131
    }
B
Ben S 已提交
132

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

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

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

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

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

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