|
@@ -52,12 +52,15 @@ impl Options {
|
|
|
opts.optflag("H", "links", "show number of hard links");
|
|
opts.optflag("H", "links", "show number of hard links");
|
|
|
opts.optflag("i", "inode", "show each file's inode number");
|
|
opts.optflag("i", "inode", "show each file's inode number");
|
|
|
opts.optflag("l", "long", "display extended details and attributes");
|
|
opts.optflag("l", "long", "display extended details and attributes");
|
|
|
|
|
+ opts.optflag("m", "modified", "display timestamp of most recent modification");
|
|
|
opts.optflag("r", "reverse", "reverse order of files");
|
|
opts.optflag("r", "reverse", "reverse order of files");
|
|
|
opts.optflag("R", "recurse", "recurse into directories");
|
|
opts.optflag("R", "recurse", "recurse into directories");
|
|
|
opts.optopt ("s", "sort", "field to sort by", "WORD");
|
|
opts.optopt ("s", "sort", "field to sort by", "WORD");
|
|
|
opts.optflag("S", "blocks", "show number of file system blocks");
|
|
opts.optflag("S", "blocks", "show number of file system blocks");
|
|
|
opts.optopt ("t", "time", "which timestamp to show for a file", "WORD");
|
|
opts.optopt ("t", "time", "which timestamp to show for a file", "WORD");
|
|
|
opts.optflag("T", "tree", "recurse into subdirectories in a tree view");
|
|
opts.optflag("T", "tree", "recurse into subdirectories in a tree view");
|
|
|
|
|
+ opts.optflag("u", "accessed", "display timestamp of last access for a file");
|
|
|
|
|
+ opts.optflag("U", "created", "display timestamp of creation for a file");
|
|
|
opts.optflag("x", "across", "sort multi-column view entries across");
|
|
opts.optflag("x", "across", "sort multi-column view entries across");
|
|
|
opts.optflag("?", "help", "show list of command-line options");
|
|
opts.optflag("?", "help", "show list of command-line options");
|
|
|
|
|
|
|
@@ -291,21 +294,56 @@ pub enum TimeType {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl TimeType {
|
|
impl TimeType {
|
|
|
|
|
+ pub fn header(&self) -> &'static str {
|
|
|
|
|
+ match *self {
|
|
|
|
|
+ TimeType::FileAccessed => "Date Accessed",
|
|
|
|
|
+ TimeType::FileModified => "Date Modified",
|
|
|
|
|
+ TimeType::FileCreated => "Date Created",
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#[derive(PartialEq, Debug, Copy)]
|
|
|
|
|
+pub struct TimeTypes {
|
|
|
|
|
+ accessed: bool,
|
|
|
|
|
+ modified: bool,
|
|
|
|
|
+ created: bool,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl TimeTypes {
|
|
|
|
|
|
|
|
/// Find which field to use based on a user-supplied word.
|
|
/// Find which field to use based on a user-supplied word.
|
|
|
- fn deduce(matches: &getopts::Matches) -> Result<TimeType, Misfire> {
|
|
|
|
|
|
|
+ fn deduce(matches: &getopts::Matches) -> Result<TimeTypes, Misfire> {
|
|
|
let possible_word = matches.opt_str("time");
|
|
let possible_word = matches.opt_str("time");
|
|
|
|
|
+ let modified = matches.opt_present("modified");
|
|
|
|
|
+ let created = matches.opt_present("created");
|
|
|
|
|
+ let accessed = matches.opt_present("accessed");
|
|
|
|
|
|
|
|
if let Some(word) = possible_word {
|
|
if let Some(word) = possible_word {
|
|
|
|
|
+ if modified {
|
|
|
|
|
+ return Err(Misfire::Useless("modified", true, "time"));
|
|
|
|
|
+ }
|
|
|
|
|
+ else if created {
|
|
|
|
|
+ return Err(Misfire::Useless("created", true, "time"));
|
|
|
|
|
+ }
|
|
|
|
|
+ else if accessed {
|
|
|
|
|
+ return Err(Misfire::Useless("accessed", true, "time"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
match word.as_slice() {
|
|
match word.as_slice() {
|
|
|
- "mod" | "modified" => Ok(TimeType::FileModified),
|
|
|
|
|
- "acc" | "accessed" => Ok(TimeType::FileAccessed),
|
|
|
|
|
- "cr" | "created" => Ok(TimeType::FileCreated),
|
|
|
|
|
- field => Err(TimeType::none(field)),
|
|
|
|
|
|
|
+ "mod" | "modified" => Ok(TimeTypes { accessed: false, modified: true, created: false }),
|
|
|
|
|
+ "acc" | "accessed" => Ok(TimeTypes { accessed: true, modified: false, created: false }),
|
|
|
|
|
+ "cr" | "created" => Ok(TimeTypes { accessed: false, modified: false, created: true }),
|
|
|
|
|
+ field => Err(TimeTypes::none(field)),
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
else {
|
|
else {
|
|
|
- Ok(TimeType::FileModified)
|
|
|
|
|
|
|
+ if modified || created || accessed {
|
|
|
|
|
+ Ok(TimeTypes { accessed: accessed, modified: modified, created: created })
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ Ok(TimeTypes { accessed: false, modified: true, created: false })
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -313,15 +351,8 @@ impl TimeType {
|
|
|
fn none(field: &str) -> Misfire {
|
|
fn none(field: &str) -> Misfire {
|
|
|
Misfire::InvalidOptions(getopts::Fail::UnrecognizedOption(format!("--time {}", field)))
|
|
Misfire::InvalidOptions(getopts::Fail::UnrecognizedOption(format!("--time {}", field)))
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- pub fn header(&self) -> &'static str {
|
|
|
|
|
- match *self {
|
|
|
|
|
- TimeType::FileAccessed => "Date Accessed",
|
|
|
|
|
- TimeType::FileModified => "Date Modified",
|
|
|
|
|
- TimeType::FileCreated => "Date Created",
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
/// What to do when encountering a directory?
|
|
/// What to do when encountering a directory?
|
|
|
#[derive(PartialEq, Debug, Copy)]
|
|
#[derive(PartialEq, Debug, Copy)]
|
|
|
pub enum DirAction {
|
|
pub enum DirAction {
|
|
@@ -347,7 +378,7 @@ impl DirAction {
|
|
|
#[derive(PartialEq, Copy, Debug)]
|
|
#[derive(PartialEq, Copy, Debug)]
|
|
|
pub struct Columns {
|
|
pub struct Columns {
|
|
|
size_format: SizeFormat,
|
|
size_format: SizeFormat,
|
|
|
- time_type: TimeType,
|
|
|
|
|
|
|
+ time_types: TimeTypes,
|
|
|
inode: bool,
|
|
inode: bool,
|
|
|
links: bool,
|
|
links: bool,
|
|
|
blocks: bool,
|
|
blocks: bool,
|
|
@@ -358,7 +389,7 @@ impl Columns {
|
|
|
pub fn deduce(matches: &getopts::Matches) -> Result<Columns, Misfire> {
|
|
pub fn deduce(matches: &getopts::Matches) -> Result<Columns, Misfire> {
|
|
|
Ok(Columns {
|
|
Ok(Columns {
|
|
|
size_format: try!(SizeFormat::deduce(matches)),
|
|
size_format: try!(SizeFormat::deduce(matches)),
|
|
|
- time_type: try!(TimeType::deduce(matches)),
|
|
|
|
|
|
|
+ time_types: try!(TimeTypes::deduce(matches)),
|
|
|
inode: matches.opt_present("inode"),
|
|
inode: matches.opt_present("inode"),
|
|
|
links: matches.opt_present("links"),
|
|
links: matches.opt_present("links"),
|
|
|
blocks: matches.opt_present("blocks"),
|
|
blocks: matches.opt_present("blocks"),
|
|
@@ -391,7 +422,17 @@ impl Columns {
|
|
|
columns.push(Group);
|
|
columns.push(Group);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- columns.push(Timestamp(self.time_type));
|
|
|
|
|
|
|
+ if self.time_types.modified {
|
|
|
|
|
+ columns.push(Timestamp(TimeType::FileModified));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if self.time_types.created {
|
|
|
|
|
+ columns.push(Timestamp(TimeType::FileCreated));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if self.time_types.accessed {
|
|
|
|
|
+ columns.push(Timestamp(TimeType::FileAccessed));
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if cfg!(feature="git") {
|
|
if cfg!(feature="git") {
|
|
|
if let Some(d) = dir {
|
|
if let Some(d) = dir {
|