main.rs 4.8 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
Ben S 已提交
21

B
Ben S 已提交
22 23
use dir::Dir;
use file::File;
24
use options::{Options, View};
25
use output::lines_view;
B
Ben S 已提交
26

B
Ben S 已提交
27
pub mod column;
B
Ben S 已提交
28
pub mod dir;
B
Ben S 已提交
29
pub mod file;
30
pub mod filetype;
B
Ben S 已提交
31
pub mod options;
32
pub mod output;
33
pub mod term;
N
nwin 已提交
34
pub mod xattr;
B
Ben S 已提交
35

B
Ben S 已提交
36
#[cfg(not(test))]
37 38 39 40 41 42
struct Exa<'a> {
    count:   usize,
    options: Options,
    dirs:    Vec<Path>,
    files:   Vec<File<'a>>,
}
B
Benjamin Sago 已提交
43

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

55 56 57 58 59 60 61 62 63
    fn load<T>(&mut self, iter: T) where T: Iterator<Item = &'a String> {
        // Separate the user-supplied paths into directories and files.
        // Files are shown first, and then each directory is expanded
        // and listed second.
        for file in iter {
            let path = Path::new(file);
            match fs::stat(&path) {
                Ok(stat) => {
                    if stat.kind == FileType::Directory {
64
                        if self.options.dir_action.is_tree() {
65 66 67 68 69 70 71 72 73 74 75 76
                            self.files.push(File::with_stat(stat, &path, None, true));
                        }
                        else {
                            self.dirs.push(path);
                        }
                    }
                    else {
                        self.files.push(File::with_stat(stat, &path, None, false));
                    }
                }
                Err(e) => println!("{}: {}", file, e),
            }
B
Ben S 已提交
77

78 79
            self.count += 1;
        }
B
Ben S 已提交
80
    }
B
Ben S 已提交
81

82 83
    fn print_files(&self) {
        if !self.files.is_empty() {
B
Ben S 已提交
84
            self.print(None, &self.files[..]);
B
Ben S 已提交
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 114 115 116
    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.
117
                    if let Some(recurse_opts) = self.options.dir_action.recurse_options() {
118 119
                        let depth = dir_path.components().filter(|&c| c != b".").count() + 1;
                        if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) {
120 121 122
                            for dir in files.iter().filter(|f| f.stat.kind == FileType::Directory).rev() {
                                self.dirs.push(dir.path.clone());
                            }
123
                        }
B
Ben S 已提交
124 125
                    }

126 127 128 129 130
                    if self.count > 1 {
                        println!("{}:", dir_path.display());
                    }
                    self.count += 1;

B
Ben S 已提交
131
                    self.print(Some(dir), &files[..]);
132 133 134 135
                }
                Err(e) => {
                    println!("{}: {}", dir_path.display(), e);
                    return;
136
                }
137 138 139
            };
        }
    }
140

141 142 143 144 145 146
    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),
        }
147
    }
148
}
149

B
Ben S 已提交
150
#[cfg(not(test))]
151
fn main() {
B
Ben S 已提交
152
    let args: Vec<String> = env::args().collect();
153

154
    match Options::getopts(args.tail()) {
155 156 157 158 159 160
        Ok((options, paths)) => {
            let mut exa = Exa::new(options);
            exa.load(paths.iter());
            exa.print_files();
            exa.print_dirs();
        },
161
        Err(e) => {
162
            println!("{}", e);
B
Ben S 已提交
163
            env::set_exit_status(e.error_code());
164
        },
165 166
    };
}