1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

Merge pull request #1621 from sylvestre/clap-stat

refactor(stat): Move to clap
This commit is contained in:
Sylvestre Ledru 2020-11-03 08:37:39 +01:00 committed by GitHub
commit 38e90b9a03
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 99 additions and 88 deletions

2
Cargo.lock generated
View file

@ -2054,7 +2054,7 @@ dependencies = [
name = "uu_stat"
version = "0.0.1"
dependencies = [
"getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"uucore 0.0.4 (git+https://github.com/uutils/uucore.git?branch=canary)",
"uucore_procs 0.0.4 (git+https://github.com/uutils/uucore.git?branch=canary)",

View file

@ -15,7 +15,7 @@ edition = "2018"
path = "src/stat.rs"
[dependencies]
getopts = "0.2.18"
clap = "2.33"
time = "0.1.40"
uucore = { version="0.0.4", package="uucore", git="https://github.com/uutils/uucore.git", branch="canary", features=["entries", "libc"] }
uucore_procs = { version="0.0.4", package="uucore_procs", git="https://github.com/uutils/uucore.git", branch="canary" }

View file

@ -7,8 +7,7 @@
// spell-checker:ignore (ToDO) mtab fsext showfs otype fmtstr prec ftype blocksize nlink rdev fnodes fsid namelen blksize inodes fstype iosize statfs gnulib NBLOCKSIZE
extern crate getopts;
use getopts::Options;
extern crate clap;
#[macro_use]
mod fsext;
@ -18,6 +17,7 @@ pub use crate::fsext::*;
extern crate uucore;
use uucore::entries;
use clap::{App, Arg, ArgMatches};
use std::borrow::Cow;
use std::convert::AsRef;
use std::fs::File;
@ -86,9 +86,17 @@ macro_rules! print_adjusted {
};
}
static NAME: &str = "stat";
static ABOUT: &str = "Display file or file system status.";
static VERSION: &str = env!("CARGO_PKG_VERSION");
static OPT_DEREFERENCE: &str = "dereference";
static OPT_FILE_SYSTEM: &str = "file-system";
static OPT_FORMAT: &str = "format";
static OPT_PRINTF: &str = "printf";
static OPT_TERSE: &str = "terse";
static ARG_FILES: &str = "files";
const MOUNT_INFO: &str = "/etc/mtab";
pub const F_ALTER: u8 = 1;
pub const F_ZERO: u8 = 1 << 1;
@ -332,7 +340,6 @@ impl Stater {
let mut tokens = Vec::new();
let bound = fmtstr.len();
let chars = fmtstr.chars().collect::<Vec<char>>();
let mut i = 0_usize;
while i < bound {
match chars[i] {
@ -453,16 +460,21 @@ impl Stater {
Ok(tokens)
}
fn new(matches: getopts::Matches) -> Result<Stater, String> {
let fmtstr = if matches.opt_present("printf") {
matches.opt_str("printf").expect("Invalid format string")
fn new(matches: ArgMatches) -> Result<Stater, String> {
let files: Vec<String> = matches
.values_of(ARG_FILES)
.map(|v| v.map(ToString::to_string).collect())
.unwrap_or_default();
let fmtstr = if matches.is_present(OPT_PRINTF) {
matches.value_of(OPT_PRINTF).expect("Invalid format string")
} else {
matches.opt_str("format").unwrap_or_else(|| "".to_owned())
matches.value_of(OPT_FORMAT).unwrap_or_else(|| "")
};
let use_printf = matches.opt_present("printf");
let terse = matches.opt_present("terse");
let showfs = matches.opt_present("file-system");
let use_printf = matches.is_present(OPT_PRINTF);
let terse = matches.is_present(OPT_TERSE);
let showfs = matches.is_present(OPT_FILE_SYSTEM);
let default_tokens = if fmtstr.is_empty() {
Stater::generate_tokens(&Stater::default_fmt(showfs, terse, false), use_printf).unwrap()
@ -491,10 +503,10 @@ impl Stater {
};
Ok(Stater {
follow: matches.opt_present("dereference"),
follow: matches.is_present(OPT_DEREFERENCE),
showfs,
from_user: !fmtstr.is_empty(),
files: matches.free,
files,
default_tokens,
default_dev_tokens,
mount_list,
@ -873,76 +885,13 @@ impl Stater {
}
}
pub fn uumain(args: impl uucore::Args) -> i32 {
let args = args.collect_str();
let mut opts = Options::new();
opts.optflag("h", "help", "display this help and exit");
opts.optflag("", "version", "output version information and exit");
opts.optflag("L", "dereference", "follow links");
opts.optflag(
"f",
"file-system",
"display file system status instead of file status",
);
opts.optflag("t", "terse", "print the information in terse form");
// Omit the unused description as they are too long
opts.optopt("c", "format", "", "FORMAT");
opts.optopt("", "printf", "", "FORMAT");
let matches = match opts.parse(&args[1..]) {
Ok(m) => m,
Err(f) => {
show_usage_error!("{}", f);
return 1;
}
};
if matches.opt_present("help") {
return help();
} else if matches.opt_present("version") {
return version();
}
if matches.free.is_empty() {
show_usage_error!("missing operand");
return 1;
}
match Stater::new(matches) {
Ok(stater) => stater.exec(),
Err(e) => {
show_info!("{}", e);
1
}
}
fn get_usage() -> String {
format!("{0} [OPTION]... FILE...", executable!())
}
fn version() -> i32 {
println!("{} {}", NAME, VERSION);
0
}
fn help() -> i32 {
let msg = format!(
r#"Usage: {} [OPTION]... FILE...
Display file or file system status.
Mandatory arguments to long options are mandatory for short options too.
-L, --dereference follow links
-f, --file-system display file system status instead of file status
-c --format=FORMAT use the specified FORMAT instead of the default;
output a newline after each use of FORMAT
--printf=FORMAT like --format, but interpret backslash escapes,
and do not output a mandatory trailing newline;
if you want a newline, include \n in FORMAT
-t, --terse print the information in terse form
--help display this help and exit
--version output version information and exit
fn get_long_usage() -> String {
String::from(
"
The valid format sequences for files (without --file-system):
%a access rights in octal (note '#' and '0' printf flags)
@ -993,9 +942,71 @@ Valid format sequences for file systems:
NOTE: your shell may have its own version of stat, which usually supersedes
the version described here. Please refer to your shell's documentation
for details about the options it supports."#,
NAME
);
println!("{}", msg);
0
for details about the options it supports.
",
)
}
pub fn uumain(args: impl uucore::Args) -> i32 {
let usage = get_usage();
let long_usage = get_long_usage();
let matches = App::new(executable!())
.version(VERSION)
.about(ABOUT)
.usage(&usage[..])
.after_help(&long_usage[..])
.arg(
Arg::with_name(OPT_DEREFERENCE)
.short("L")
.long(OPT_DEREFERENCE)
.help("follow links"),
)
.arg(
Arg::with_name(OPT_FILE_SYSTEM)
.short("f")
.long(OPT_FILE_SYSTEM)
.help("display file system status instead of file status"),
)
.arg(
Arg::with_name(OPT_TERSE)
.short("t")
.long(OPT_TERSE)
.help("print the information in terse form"),
)
.arg(
Arg::with_name(OPT_FORMAT)
.short("c")
.long(OPT_FORMAT)
.help(
"use the specified FORMAT instead of the default;
output a newline after each use of FORMAT",
)
.value_name("FORMAT"),
)
.arg(
Arg::with_name(OPT_PRINTF)
.long(OPT_PRINTF)
.value_name("FORMAT")
.help(
"like --format, but interpret backslash escapes,
and do not output a mandatory trailing newline;
if you want a newline, include \n in FORMAT",
),
)
.arg(
Arg::with_name(ARG_FILES)
.multiple(true)
.takes_value(true)
.min_values(1),
)
.get_matches_from(args);
match Stater::new(matches) {
Ok(stater) => stater.exec(),
Err(e) => {
show_info!("{}", e);
1
}
}
}