main.rs 4.5 KB
Newer Older
N
nwin 已提交
1
#![feature(collections, core, env, libc, old_io, old_path, plugin, std_misc)]
N
nwin 已提交
2 3 4
// Other platforms then macos don’t need std_misc but you can’t 
// use #[cfg] on features.
#![allow(unused_features)] 
5

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

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

B
Ben S 已提交
18
use std::env;
B
Ben S 已提交
19
use std::old_io::{fs, FileType};
B
Ben S 已提交
20

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

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

35 36 37 38 39 40
struct Exa<'a> {
    count:   usize,
    options: Options,
    dirs:    Vec<Path>,
    files:   Vec<File<'a>>,
}
B
Benjamin Sago 已提交
41

42 43 44 45 46 47 48 49
impl<'a> Exa<'a> {
    fn new(options: Options) -> Exa<'a> {
        Exa {
            count: 0,
            options: options,
            dirs: Vec::new(),
            files: Vec::new(),
        }
B
Ben S 已提交
50
    }
B
Ben S 已提交
51

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
    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 {
                        if self.options.dir_action == DirAction::Tree {
                            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 已提交
74

75 76
            self.count += 1;
        }
B
Ben S 已提交
77
    }
B
Ben S 已提交
78

79 80
    fn print_files(&self) {
        if !self.files.is_empty() {
B
Ben S 已提交
81
            self.print(None, &self.files[..]);
B
Ben S 已提交
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 114 115 116 117
    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.
                    if self.options.dir_action == DirAction::Recurse {
                        for dir in files.iter().filter(|f| f.stat.kind == FileType::Directory).rev() {
                            self.dirs.push(dir.path.clone());
                        }
B
Ben S 已提交
118 119
                    }

120 121 122 123 124
                    if self.count > 1 {
                        println!("{}:", dir_path.display());
                    }
                    self.count += 1;

B
Ben S 已提交
125
                    self.print(Some(dir), &files[..]);
126 127 128 129
                }
                Err(e) => {
                    println!("{}: {}", dir_path.display(), e);
                    return;
130
                }
131 132 133
            };
        }
    }
134

135 136 137 138 139 140
    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),
        }
141
    }
142
}
143 144

fn main() {
B
Ben S 已提交
145
    let args: Vec<String> = env::args().collect();
146

147
    match Options::getopts(args.tail()) {
148 149 150 151 152 153
        Ok((options, paths)) => {
            let mut exa = Exa::new(options);
            exa.load(paths.iter());
            exa.print_files();
            exa.print_dirs();
        },
154
        Err(e) => {
155
            println!("{}", e);
B
Ben S 已提交
156
            env::set_exit_status(e.error_code());
157
        },
158 159
    };
}