|
@@ -1,7 +1,6 @@
|
|
|
extern crate getopts;
|
|
extern crate getopts;
|
|
|
|
|
|
|
|
use file::File;
|
|
use file::File;
|
|
|
-use std::cmp::lexical_ordering;
|
|
|
|
|
use column::{Column, Permissions, FileName, FileSize, User, Group, HardLinks, Inode, Blocks};
|
|
use column::{Column, Permissions, FileName, FileSize, User, Group, HardLinks, Inode, Blocks};
|
|
|
use std::ascii::StrAsciiExt;
|
|
use std::ascii::StrAsciiExt;
|
|
|
|
|
|
|
@@ -9,15 +8,6 @@ pub enum SortField {
|
|
|
Name, Extension, Size
|
|
Name, Extension, Size
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-pub struct Options {
|
|
|
|
|
- pub showInvisibles: bool,
|
|
|
|
|
- pub sortField: SortField,
|
|
|
|
|
- pub reverse: bool,
|
|
|
|
|
- pub dirs: Vec<String>,
|
|
|
|
|
- pub columns: Vec<Column>,
|
|
|
|
|
- pub header: bool,
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
impl SortField {
|
|
impl SortField {
|
|
|
fn from_word(word: String) -> SortField {
|
|
fn from_word(word: String) -> SortField {
|
|
|
match word.as_slice() {
|
|
match word.as_slice() {
|
|
@@ -29,6 +19,21 @@ impl SortField {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+pub enum View {
|
|
|
|
|
+ Lines(Vec<Column>),
|
|
|
|
|
+ Grid(bool),
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+pub struct Options {
|
|
|
|
|
+ pub show_invisibles: bool,
|
|
|
|
|
+ pub sort_field: SortField,
|
|
|
|
|
+ pub reverse: bool,
|
|
|
|
|
+ pub dirs: Vec<String>,
|
|
|
|
|
+ pub view: View,
|
|
|
|
|
+ pub header: bool,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
impl Options {
|
|
impl Options {
|
|
|
pub fn getopts(args: Vec<String>) -> Result<Options, getopts::Fail_> {
|
|
pub fn getopts(args: Vec<String>) -> Result<Options, getopts::Fail_> {
|
|
|
let opts = [
|
|
let opts = [
|
|
@@ -36,26 +41,37 @@ impl Options {
|
|
|
getopts::optflag("b", "binary", "use binary prefixes in file sizes"),
|
|
getopts::optflag("b", "binary", "use binary prefixes in file sizes"),
|
|
|
getopts::optflag("g", "group", "show group as well as user"),
|
|
getopts::optflag("g", "group", "show group as well as user"),
|
|
|
getopts::optflag("h", "header", "show a header row at the top"),
|
|
getopts::optflag("h", "header", "show a header row at the top"),
|
|
|
|
|
+ getopts::optflag("H", "links", "show number of hard links"),
|
|
|
|
|
+ getopts::optflag("l", "long", "display extended details and attributes"),
|
|
|
getopts::optflag("i", "inode", "show each file's inode number"),
|
|
getopts::optflag("i", "inode", "show each file's inode number"),
|
|
|
- getopts::optflag("l", "links", "show number of hard links"),
|
|
|
|
|
getopts::optflag("r", "reverse", "reverse order of files"),
|
|
getopts::optflag("r", "reverse", "reverse order of files"),
|
|
|
getopts::optopt("s", "sort", "field to sort by", "WORD"),
|
|
getopts::optopt("s", "sort", "field to sort by", "WORD"),
|
|
|
getopts::optflag("S", "blocks", "show number of file system blocks"),
|
|
getopts::optflag("S", "blocks", "show number of file system blocks"),
|
|
|
|
|
+ getopts::optflag("x", "across", "sort multi-column view entries across"),
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
match getopts::getopts(args.tail(), opts) {
|
|
match getopts::getopts(args.tail(), opts) {
|
|
|
Err(f) => Err(f),
|
|
Err(f) => Err(f),
|
|
|
Ok(matches) => Ok(Options {
|
|
Ok(matches) => Ok(Options {
|
|
|
- showInvisibles: matches.opt_present("all"),
|
|
|
|
|
|
|
+ show_invisibles: matches.opt_present("all"),
|
|
|
reverse: matches.opt_present("reverse"),
|
|
reverse: matches.opt_present("reverse"),
|
|
|
header: matches.opt_present("header"),
|
|
header: matches.opt_present("header"),
|
|
|
- sortField: matches.opt_str("sort").map(|word| SortField::from_word(word)).unwrap_or(Name),
|
|
|
|
|
|
|
+ sort_field: matches.opt_str("sort").map(|word| SortField::from_word(word)).unwrap_or(Name),
|
|
|
dirs: if matches.free.is_empty() { vec![ ".".to_string() ] } else { matches.free.clone() },
|
|
dirs: if matches.free.is_empty() { vec![ ".".to_string() ] } else { matches.free.clone() },
|
|
|
- columns: Options::columns(matches),
|
|
|
|
|
|
|
+ view: Options::view(matches),
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
|
|
+ fn view(matches: getopts::Matches) -> View {
|
|
|
|
|
+ if matches.opt_present("long") {
|
|
|
|
|
+ Lines(Options::columns(matches))
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ Grid(matches.opt_present("across"))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
fn columns(matches: getopts::Matches) -> Vec<Column> {
|
|
fn columns(matches: getopts::Matches) -> Vec<Column> {
|
|
|
let mut columns = vec![];
|
|
let mut columns = vec![];
|
|
|
|
|
|
|
@@ -87,7 +103,7 @@ impl Options {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
fn should_display(&self, f: &File) -> bool {
|
|
fn should_display(&self, f: &File) -> bool {
|
|
|
- if self.showInvisibles {
|
|
|
|
|
|
|
+ if self.show_invisibles {
|
|
|
true
|
|
true
|
|
|
} else {
|
|
} else {
|
|
|
!f.name.as_slice().starts_with(".")
|
|
!f.name.as_slice().starts_with(".")
|
|
@@ -99,13 +115,13 @@ impl Options {
|
|
|
.filter(|&f| self.should_display(f))
|
|
.filter(|&f| self.should_display(f))
|
|
|
.collect();
|
|
.collect();
|
|
|
|
|
|
|
|
- match self.sortField {
|
|
|
|
|
|
|
+ match self.sort_field {
|
|
|
Name => files.sort_by(|a, b| a.parts.cmp(&b.parts)),
|
|
Name => files.sort_by(|a, b| a.parts.cmp(&b.parts)),
|
|
|
Size => files.sort_by(|a, b| a.stat.size.cmp(&b.stat.size)),
|
|
Size => files.sort_by(|a, b| a.stat.size.cmp(&b.stat.size)),
|
|
|
Extension => files.sort_by(|a, b| {
|
|
Extension => files.sort_by(|a, b| {
|
|
|
let exts = a.ext.clone().map(|e| e.as_slice().to_ascii_lower()).cmp(&b.ext.clone().map(|e| e.as_slice().to_ascii_lower()));
|
|
let exts = a.ext.clone().map(|e| e.as_slice().to_ascii_lower()).cmp(&b.ext.clone().map(|e| e.as_slice().to_ascii_lower()));
|
|
|
let names = a.name.as_slice().to_ascii_lower().cmp(&b.name.as_slice().to_ascii_lower());
|
|
let names = a.name.as_slice().to_ascii_lower().cmp(&b.name.as_slice().to_ascii_lower());
|
|
|
- lexical_ordering(exts, names)
|
|
|
|
|
|
|
+ exts.cmp(&names)
|
|
|
}),
|
|
}),
|
|
|
}
|
|
}
|
|
|
|
|
|