main.rs 4.4 KB
Newer Older
B
Ben S 已提交
1
#![feature(collections, core, env, io, libc, os, path, std_misc)]
B
Ben S 已提交
2

3
extern crate ansi_term;
4 5
extern crate getopts;
extern crate natord;
6 7
extern crate number_prefix;
extern crate users;
8

B
Ben S 已提交
9 10 11
#[cfg(feature="git")]
extern crate git2;

B
Ben S 已提交
12
use std::env;
B
Ben S 已提交
13
use std::old_io::{fs, FileType};
B
Ben S 已提交
14

B
Ben S 已提交
15 16
use dir::Dir;
use file::File;
17 18
use options::{Options, View, DirAction};
use output::lines_view;
B
Ben S 已提交
19

B
Ben S 已提交
20
pub mod column;
B
Ben S 已提交
21
pub mod dir;
B
Ben S 已提交
22
pub mod file;
23
pub mod filetype;
B
Ben S 已提交
24
pub mod options;
25
pub mod output;
26
pub mod term;
B
Ben S 已提交
27

28 29 30 31 32 33
struct Exa<'a> {
    count:   usize,
    options: Options,
    dirs:    Vec<Path>,
    files:   Vec<File<'a>>,
}
B
Benjamin Sago 已提交
34

35 36 37 38 39 40 41 42
impl<'a> Exa<'a> {
    fn new(options: Options) -> Exa<'a> {
        Exa {
            count: 0,
            options: options,
            dirs: Vec::new(),
            files: Vec::new(),
        }
B
Ben S 已提交
43
    }
B
Ben S 已提交
44

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
    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 已提交
67

68 69
            self.count += 1;
        }
B
Ben S 已提交
70
    }
B
Ben S 已提交
71

72 73 74
    fn print_files(&self) {
        if !self.files.is_empty() {
            self.print(None, &self.files[]);
B
Ben S 已提交
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
    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 已提交
111 112
                    }

113 114 115 116 117 118 119 120 121 122
                    if self.count > 1 {
                        println!("{}:", dir_path.display());
                    }
                    self.count += 1;

                    self.print(Some(dir), &files[]);
                }
                Err(e) => {
                    println!("{}: {}", dir_path.display(), e);
                    return;
123
                }
124 125 126
            };
        }
    }
127

128 129 130 131 132 133
    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),
        }
134
    }
135
}
136 137

fn main() {
B
Ben S 已提交
138
    let args = args();
139

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

fn args() -> Vec<String> {
    env::args().map(|arg| arg.to_string_lossy().into_owned()).collect()
}