2015-06-21 11:52:53 +00:00
|
|
|
#![feature(convert, fs_mode)]
|
|
|
|
#![feature(slice_extras, iter_arith, vec_resize)]
|
2015-02-24 16:05:25 +00:00
|
|
|
|
2014-07-01 18:00:36 +00:00
|
|
|
extern crate ansi_term;
|
2015-02-09 16:33:27 +00:00
|
|
|
extern crate datetime;
|
2015-01-31 16:10:40 +00:00
|
|
|
extern crate getopts;
|
2015-05-16 12:17:50 +00:00
|
|
|
extern crate libc;
|
2015-02-10 16:08:10 +00:00
|
|
|
extern crate locale;
|
2015-01-31 16:10:40 +00:00
|
|
|
extern crate natord;
|
2015-04-03 22:14:49 +00:00
|
|
|
extern crate num_cpus;
|
2014-12-18 07:00:31 +00:00
|
|
|
extern crate number_prefix;
|
2015-02-09 16:33:27 +00:00
|
|
|
extern crate pad;
|
2015-06-05 02:04:56 +00:00
|
|
|
extern crate threadpool;
|
2015-04-23 12:46:37 +00:00
|
|
|
extern crate unicode_width;
|
2015-06-08 20:33:39 +00:00
|
|
|
extern crate users;
|
2015-04-23 12:46:37 +00:00
|
|
|
|
2015-01-27 15:01:17 +00:00
|
|
|
#[cfg(feature="git")]
|
|
|
|
extern crate git2;
|
|
|
|
|
2015-06-08 20:33:39 +00:00
|
|
|
|
2015-02-05 15:25:59 +00:00
|
|
|
use std::env;
|
Use new io + path + fs libraries (LOTS OF CHANGES)
Exa now uses the new IO, Path, and Filesystem libraries that have been out for a while now.
Unfortunately, the new libraries don't *entirely* cover the range of the old libraries just yet: in particular, to become more cross-platform, the data in `UnstableFileStat` isn't available in the Unix `MetadataExt` yet. Much of this is contained in rust-lang/rfcs#1044 (which is due to be implemented in rust-lang/rust#14711), but it's not *entirely* there yet.
As such, this commits a serious loss of functionality: no symlink viewing, no hard links or blocks, or users or groups. Also, some of the code could now be optimised. I just wanted to commit this to sort out most of the 'teething problems' of having a different path system in advance.
Here's an example problem that took ages to fix for you, just because you read this far: when I first got exa to compile, it worked mostly fine, except calling `exa` by itself didn't list the current directory. I traced where the command-line options were being generated, to where files and directories were sorted, to where the threads were spawned... and the problem turned out to be that it was using the full path as the file name, rather than just the last component, and these paths happened to begin with `.`, so it thought they were dotfiles.
2015-04-23 12:00:34 +00:00
|
|
|
use std::fs;
|
|
|
|
use std::path::{Component, Path, PathBuf};
|
2015-06-21 11:52:53 +00:00
|
|
|
use std::process;
|
2015-06-08 20:33:39 +00:00
|
|
|
use std::sync::mpsc::channel;
|
2015-06-05 02:04:56 +00:00
|
|
|
|
|
|
|
use threadpool::ThreadPool;
|
2014-05-04 20:33:14 +00:00
|
|
|
|
2014-12-12 11:17:55 +00:00
|
|
|
use dir::Dir;
|
|
|
|
use file::File;
|
2015-02-24 16:05:25 +00:00
|
|
|
use options::{Options, View};
|
2014-12-12 11:17:55 +00:00
|
|
|
|
2015-05-09 22:57:18 +00:00
|
|
|
mod colours;
|
2015-05-07 21:20:24 +00:00
|
|
|
mod column;
|
|
|
|
mod dir;
|
|
|
|
mod feature;
|
|
|
|
mod file;
|
|
|
|
mod filetype;
|
|
|
|
mod options;
|
|
|
|
mod output;
|
|
|
|
mod term;
|
2014-05-04 16:01:54 +00:00
|
|
|
|
2015-06-05 02:04:56 +00:00
|
|
|
|
2015-02-26 07:42:37 +00:00
|
|
|
#[cfg(not(test))]
|
2015-05-12 14:38:12 +00:00
|
|
|
struct Exa<'dir> {
|
2015-02-05 14:39:56 +00:00
|
|
|
count: usize,
|
|
|
|
options: Options,
|
Use new io + path + fs libraries (LOTS OF CHANGES)
Exa now uses the new IO, Path, and Filesystem libraries that have been out for a while now.
Unfortunately, the new libraries don't *entirely* cover the range of the old libraries just yet: in particular, to become more cross-platform, the data in `UnstableFileStat` isn't available in the Unix `MetadataExt` yet. Much of this is contained in rust-lang/rfcs#1044 (which is due to be implemented in rust-lang/rust#14711), but it's not *entirely* there yet.
As such, this commits a serious loss of functionality: no symlink viewing, no hard links or blocks, or users or groups. Also, some of the code could now be optimised. I just wanted to commit this to sort out most of the 'teething problems' of having a different path system in advance.
Here's an example problem that took ages to fix for you, just because you read this far: when I first got exa to compile, it worked mostly fine, except calling `exa` by itself didn't list the current directory. I traced where the command-line options were being generated, to where files and directories were sorted, to where the threads were spawned... and the problem turned out to be that it was using the full path as the file name, rather than just the last component, and these paths happened to begin with `.`, so it thought they were dotfiles.
2015-04-23 12:00:34 +00:00
|
|
|
dirs: Vec<PathBuf>,
|
2015-05-12 14:38:12 +00:00
|
|
|
files: Vec<File<'dir>>,
|
2015-02-05 14:39:56 +00:00
|
|
|
}
|
2015-01-12 18:44:39 +00:00
|
|
|
|
2015-02-26 07:42:37 +00:00
|
|
|
#[cfg(not(test))]
|
2015-05-12 14:38:12 +00:00
|
|
|
impl<'dir> Exa<'dir> {
|
|
|
|
fn new(options: Options) -> Exa<'dir> {
|
2015-02-05 14:39:56 +00:00
|
|
|
Exa {
|
|
|
|
count: 0,
|
|
|
|
options: options,
|
|
|
|
dirs: Vec::new(),
|
|
|
|
files: Vec::new(),
|
|
|
|
}
|
2014-11-26 08:05:07 +00:00
|
|
|
}
|
2014-11-25 01:27:26 +00:00
|
|
|
|
2015-03-04 02:48:36 +00:00
|
|
|
fn load(&mut self, files: &[String]) {
|
2015-06-05 02:04:56 +00:00
|
|
|
|
2015-02-05 14:39:56 +00:00
|
|
|
// Separate the user-supplied paths into directories and files.
|
|
|
|
// Files are shown first, and then each directory is expanded
|
|
|
|
// and listed second.
|
Use new io + path + fs libraries (LOTS OF CHANGES)
Exa now uses the new IO, Path, and Filesystem libraries that have been out for a while now.
Unfortunately, the new libraries don't *entirely* cover the range of the old libraries just yet: in particular, to become more cross-platform, the data in `UnstableFileStat` isn't available in the Unix `MetadataExt` yet. Much of this is contained in rust-lang/rfcs#1044 (which is due to be implemented in rust-lang/rust#14711), but it's not *entirely* there yet.
As such, this commits a serious loss of functionality: no symlink viewing, no hard links or blocks, or users or groups. Also, some of the code could now be optimised. I just wanted to commit this to sort out most of the 'teething problems' of having a different path system in advance.
Here's an example problem that took ages to fix for you, just because you read this far: when I first got exa to compile, it worked mostly fine, except calling `exa` by itself didn't list the current directory. I traced where the command-line options were being generated, to where files and directories were sorted, to where the threads were spawned... and the problem turned out to be that it was using the full path as the file name, rather than just the last component, and these paths happened to begin with `.`, so it thought they were dotfiles.
2015-04-23 12:00:34 +00:00
|
|
|
let is_tree = self.options.dir_action.is_tree() || self.options.dir_action.is_as_file();
|
2015-03-04 02:48:36 +00:00
|
|
|
let total_files = files.len();
|
|
|
|
|
|
|
|
|
|
|
|
// Communication between consumer thread and producer threads
|
2015-05-12 14:38:12 +00:00
|
|
|
enum StatResult<'dir> {
|
|
|
|
File(File<'dir>),
|
2015-05-21 15:09:16 +00:00
|
|
|
Dir(PathBuf),
|
2015-03-04 03:41:30 +00:00
|
|
|
Error
|
2015-03-04 02:48:36 +00:00
|
|
|
}
|
Use new io + path + fs libraries (LOTS OF CHANGES)
Exa now uses the new IO, Path, and Filesystem libraries that have been out for a while now.
Unfortunately, the new libraries don't *entirely* cover the range of the old libraries just yet: in particular, to become more cross-platform, the data in `UnstableFileStat` isn't available in the Unix `MetadataExt` yet. Much of this is contained in rust-lang/rfcs#1044 (which is due to be implemented in rust-lang/rust#14711), but it's not *entirely* there yet.
As such, this commits a serious loss of functionality: no symlink viewing, no hard links or blocks, or users or groups. Also, some of the code could now be optimised. I just wanted to commit this to sort out most of the 'teething problems' of having a different path system in advance.
Here's an example problem that took ages to fix for you, just because you read this far: when I first got exa to compile, it worked mostly fine, except calling `exa` by itself didn't list the current directory. I traced where the command-line options were being generated, to where files and directories were sorted, to where the threads were spawned... and the problem turned out to be that it was using the full path as the file name, rather than just the last component, and these paths happened to begin with `.`, so it thought they were dotfiles.
2015-04-23 12:00:34 +00:00
|
|
|
|
2015-06-05 02:04:56 +00:00
|
|
|
let pool = ThreadPool::new(8 * num_cpus::get());
|
|
|
|
let (tx, rx) = channel();
|
2015-03-04 02:48:36 +00:00
|
|
|
|
|
|
|
for file in files.iter() {
|
2015-06-05 02:04:56 +00:00
|
|
|
let tx = tx.clone();
|
2015-03-04 02:48:36 +00:00
|
|
|
let file = file.clone();
|
|
|
|
|
|
|
|
// Spawn producer thread
|
2015-06-05 02:04:56 +00:00
|
|
|
pool.execute(move || {
|
Use new io + path + fs libraries (LOTS OF CHANGES)
Exa now uses the new IO, Path, and Filesystem libraries that have been out for a while now.
Unfortunately, the new libraries don't *entirely* cover the range of the old libraries just yet: in particular, to become more cross-platform, the data in `UnstableFileStat` isn't available in the Unix `MetadataExt` yet. Much of this is contained in rust-lang/rfcs#1044 (which is due to be implemented in rust-lang/rust#14711), but it's not *entirely* there yet.
As such, this commits a serious loss of functionality: no symlink viewing, no hard links or blocks, or users or groups. Also, some of the code could now be optimised. I just wanted to commit this to sort out most of the 'teething problems' of having a different path system in advance.
Here's an example problem that took ages to fix for you, just because you read this far: when I first got exa to compile, it worked mostly fine, except calling `exa` by itself didn't list the current directory. I traced where the command-line options were being generated, to where files and directories were sorted, to where the threads were spawned... and the problem turned out to be that it was using the full path as the file name, rather than just the last component, and these paths happened to begin with `.`, so it thought they were dotfiles.
2015-04-23 12:00:34 +00:00
|
|
|
let path = Path::new(&*file);
|
2015-06-05 02:04:56 +00:00
|
|
|
let _ = tx.send(match fs::metadata(&path) {
|
2015-05-16 17:16:35 +00:00
|
|
|
Ok(metadata) => {
|
|
|
|
if !metadata.is_dir() {
|
|
|
|
StatResult::File(File::with_metadata(metadata, &path, None, false))
|
2015-03-04 03:57:48 +00:00
|
|
|
}
|
|
|
|
else if is_tree {
|
2015-05-16 17:16:35 +00:00
|
|
|
StatResult::File(File::with_metadata(metadata, &path, None, true))
|
2015-02-05 14:39:56 +00:00
|
|
|
}
|
|
|
|
else {
|
2015-05-21 15:09:16 +00:00
|
|
|
StatResult::Dir(path.to_path_buf())
|
2015-02-05 14:39:56 +00:00
|
|
|
}
|
|
|
|
}
|
2015-03-04 03:41:30 +00:00
|
|
|
Err(e) => {
|
|
|
|
println!("{}: {}", file, e);
|
2015-03-04 03:57:48 +00:00
|
|
|
StatResult::Error
|
2015-03-04 03:41:30 +00:00
|
|
|
}
|
2015-03-04 03:57:48 +00:00
|
|
|
});
|
2015-03-04 02:48:36 +00:00
|
|
|
});
|
2015-02-05 14:39:56 +00:00
|
|
|
}
|
2015-06-05 02:04:56 +00:00
|
|
|
|
|
|
|
// Spawn consumer thread
|
|
|
|
for result in rx.iter().take(total_files) {
|
|
|
|
match result {
|
|
|
|
StatResult::File(file) => self.files.push(file),
|
|
|
|
StatResult::Dir(path) => self.dirs.push(path),
|
|
|
|
StatResult::Error => ()
|
|
|
|
}
|
|
|
|
self.count += 1;
|
|
|
|
}
|
2014-11-26 08:05:07 +00:00
|
|
|
}
|
2014-12-12 11:17:55 +00:00
|
|
|
|
2015-02-05 14:39:56 +00:00
|
|
|
fn print_files(&self) {
|
|
|
|
if !self.files.is_empty() {
|
2015-02-21 13:54:35 +00:00
|
|
|
self.print(None, &self.files[..]);
|
2014-06-20 20:07:53 +00:00
|
|
|
}
|
2015-02-05 14:39:56 +00:00
|
|
|
}
|
2014-06-21 17:12:29 +00:00
|
|
|
|
2015-02-05 14:39:56 +00:00
|
|
|
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.
|
2015-02-24 16:05:25 +00:00
|
|
|
if let Some(recurse_opts) = self.options.dir_action.recurse_options() {
|
Use new io + path + fs libraries (LOTS OF CHANGES)
Exa now uses the new IO, Path, and Filesystem libraries that have been out for a while now.
Unfortunately, the new libraries don't *entirely* cover the range of the old libraries just yet: in particular, to become more cross-platform, the data in `UnstableFileStat` isn't available in the Unix `MetadataExt` yet. Much of this is contained in rust-lang/rfcs#1044 (which is due to be implemented in rust-lang/rust#14711), but it's not *entirely* there yet.
As such, this commits a serious loss of functionality: no symlink viewing, no hard links or blocks, or users or groups. Also, some of the code could now be optimised. I just wanted to commit this to sort out most of the 'teething problems' of having a different path system in advance.
Here's an example problem that took ages to fix for you, just because you read this far: when I first got exa to compile, it worked mostly fine, except calling `exa` by itself didn't list the current directory. I traced where the command-line options were being generated, to where files and directories were sorted, to where the threads were spawned... and the problem turned out to be that it was using the full path as the file name, rather than just the last component, and these paths happened to begin with `.`, so it thought they were dotfiles.
2015-04-23 12:00:34 +00:00
|
|
|
let depth = dir_path.components().filter(|&c| c != Component::CurDir).count() + 1;
|
2015-02-24 16:08:22 +00:00
|
|
|
if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) {
|
Use new io + path + fs libraries (LOTS OF CHANGES)
Exa now uses the new IO, Path, and Filesystem libraries that have been out for a while now.
Unfortunately, the new libraries don't *entirely* cover the range of the old libraries just yet: in particular, to become more cross-platform, the data in `UnstableFileStat` isn't available in the Unix `MetadataExt` yet. Much of this is contained in rust-lang/rfcs#1044 (which is due to be implemented in rust-lang/rust#14711), but it's not *entirely* there yet.
As such, this commits a serious loss of functionality: no symlink viewing, no hard links or blocks, or users or groups. Also, some of the code could now be optimised. I just wanted to commit this to sort out most of the 'teething problems' of having a different path system in advance.
Here's an example problem that took ages to fix for you, just because you read this far: when I first got exa to compile, it worked mostly fine, except calling `exa` by itself didn't list the current directory. I traced where the command-line options were being generated, to where files and directories were sorted, to where the threads were spawned... and the problem turned out to be that it was using the full path as the file name, rather than just the last component, and these paths happened to begin with `.`, so it thought they were dotfiles.
2015-04-23 12:00:34 +00:00
|
|
|
for dir in files.iter().filter(|f| f.is_directory()).rev() {
|
2015-02-24 16:05:25 +00:00
|
|
|
self.dirs.push(dir.path.clone());
|
|
|
|
}
|
2015-02-05 14:39:56 +00:00
|
|
|
}
|
2015-02-01 02:14:31 +00:00
|
|
|
}
|
|
|
|
|
2015-02-05 14:39:56 +00:00
|
|
|
if self.count > 1 {
|
|
|
|
println!("{}:", dir_path.display());
|
|
|
|
}
|
|
|
|
self.count += 1;
|
|
|
|
|
2015-02-21 13:54:35 +00:00
|
|
|
self.print(Some(dir), &files[..]);
|
2015-02-05 14:39:56 +00:00
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
println!("{}: {}", dir_path.display(), e);
|
|
|
|
return;
|
2014-07-22 21:19:51 +00:00
|
|
|
}
|
2015-02-05 14:39:56 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2014-07-22 21:19:51 +00:00
|
|
|
|
2015-02-05 14:39:56 +00:00
|
|
|
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),
|
2015-05-09 22:57:18 +00:00
|
|
|
View::Lines(l) => l.view(files),
|
2015-02-05 14:39:56 +00:00
|
|
|
}
|
2014-06-21 17:12:29 +00:00
|
|
|
}
|
2014-07-05 21:36:43 +00:00
|
|
|
}
|
2015-01-12 21:14:27 +00:00
|
|
|
|
2015-06-08 20:33:39 +00:00
|
|
|
|
2015-02-26 07:42:37 +00:00
|
|
|
#[cfg(not(test))]
|
2015-01-12 21:14:27 +00:00
|
|
|
fn main() {
|
2015-02-12 22:33:01 +00:00
|
|
|
let args: Vec<String> = env::args().collect();
|
2015-01-12 21:14:27 +00:00
|
|
|
|
2015-01-12 21:47:05 +00:00
|
|
|
match Options::getopts(args.tail()) {
|
2015-02-05 14:39:56 +00:00
|
|
|
Ok((options, paths)) => {
|
|
|
|
let mut exa = Exa::new(options);
|
2015-03-04 02:48:36 +00:00
|
|
|
exa.load(&paths);
|
2015-02-05 14:39:56 +00:00
|
|
|
exa.print_files();
|
|
|
|
exa.print_dirs();
|
|
|
|
},
|
2015-01-23 19:27:06 +00:00
|
|
|
Err(e) => {
|
2015-01-12 21:14:27 +00:00
|
|
|
println!("{}", e);
|
2015-06-21 11:52:53 +00:00
|
|
|
process::exit(e.error_code());
|
2015-01-12 23:31:30 +00:00
|
|
|
},
|
2015-01-12 21:14:27 +00:00
|
|
|
};
|
|
|
|
}
|