mirror of
https://github.com/uutils/coreutils
synced 2024-11-16 01:38:04 +00:00
Merge branch 'master' into refactoring_parse_size
This commit is contained in:
commit
39f5f70f2b
63 changed files with 537 additions and 683 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -6,16 +6,6 @@ version = "0.11.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
||||
|
||||
[[package]]
|
||||
name = "advapi32-sys"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a"
|
||||
dependencies = [
|
||||
"winapi 0.2.8",
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
|
@ -1772,6 +1762,7 @@ dependencies = [
|
|||
name = "uu_cat"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"clap",
|
||||
"nix 0.20.0",
|
||||
"thiserror",
|
||||
|
@ -1872,6 +1863,7 @@ dependencies = [
|
|||
name = "uu_cut"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bstr",
|
||||
"clap",
|
||||
"memchr 2.4.0",
|
||||
|
@ -2262,6 +2254,7 @@ dependencies = [
|
|||
name = "uu_nohup"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"clap",
|
||||
"libc",
|
||||
"uucore",
|
||||
|
@ -2602,7 +2595,6 @@ name = "uu_timeout"
|
|||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"getopts",
|
||||
"libc",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
|
@ -2660,6 +2652,7 @@ dependencies = [
|
|||
name = "uu_tty"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"clap",
|
||||
"libc",
|
||||
"uucore",
|
||||
|
@ -2751,7 +2744,6 @@ dependencies = [
|
|||
name = "uu_whoami"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"advapi32-sys",
|
||||
"clap",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
|
|
|
@ -351,7 +351,7 @@ time = "0.1"
|
|||
unindent = "0.1"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="src/uucore", features=["entries", "process"] }
|
||||
walkdir = "2.2"
|
||||
atty = "0.2.14"
|
||||
atty = "0.2"
|
||||
|
||||
[target.'cfg(unix)'.dev-dependencies]
|
||||
rlimit = "0.4.0"
|
||||
|
|
|
@ -38,18 +38,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
let config_result: Result<base_common::Config, String> =
|
||||
base_common::parse_base_cmd_args(args, name, VERSION, ABOUT, &usage);
|
||||
|
||||
if config_result.is_err() {
|
||||
match config_result {
|
||||
Ok(_) => panic!(),
|
||||
Err(s) => crash!(BASE_CMD_PARSE_ERROR, "{}", s),
|
||||
}
|
||||
}
|
||||
let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s));
|
||||
|
||||
// Create a reference to stdin so we can return a locked stdin from
|
||||
// parse_base_cmd_args
|
||||
let stdin_raw = stdin();
|
||||
let config = config_result.unwrap();
|
||||
let mut input: Box<dyn Read> = base_common::get_input(&config, &stdin_raw);
|
||||
|
||||
base_common::handle_input(
|
||||
|
|
|
@ -54,15 +54,13 @@ impl Config {
|
|||
None => None,
|
||||
};
|
||||
|
||||
let cols = match options.value_of(options::WRAP) {
|
||||
Some(num) => match num.parse::<usize>() {
|
||||
Ok(n) => Some(n),
|
||||
Err(e) => {
|
||||
return Err(format!("Invalid wrap size: ‘{}’: {}", num, e));
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
let cols = options
|
||||
.value_of(options::WRAP)
|
||||
.map(|num| {
|
||||
num.parse::<usize>()
|
||||
.map_err(|e| format!("Invalid wrap size: ‘{}’: {}", num, e))
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
Ok(Config {
|
||||
decode: options.is_present(options::DECODE),
|
||||
|
|
|
@ -38,18 +38,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
let name = executable!();
|
||||
let config_result: Result<base_common::Config, String> =
|
||||
base_common::parse_base_cmd_args(args, name, VERSION, ABOUT, &usage);
|
||||
|
||||
if config_result.is_err() {
|
||||
match config_result {
|
||||
Ok(_) => panic!(),
|
||||
Err(s) => crash!(BASE_CMD_PARSE_ERROR, "{}", s),
|
||||
}
|
||||
}
|
||||
let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s));
|
||||
|
||||
// Create a reference to stdin so we can return a locked stdin from
|
||||
// parse_base_cmd_args
|
||||
let stdin_raw = stdin();
|
||||
let config = config_result.unwrap();
|
||||
let mut input: Box<dyn Read> = base_common::get_input(&config, &stdin_raw);
|
||||
|
||||
base_common::handle_input(
|
||||
|
|
|
@ -118,14 +118,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
fn basename(fullname: &str, suffix: &str) -> String {
|
||||
// Remove all platform-specific path separators from the end
|
||||
let mut path: String = fullname
|
||||
.chars()
|
||||
.rev()
|
||||
.skip_while(|&ch| is_separator(ch))
|
||||
.collect();
|
||||
|
||||
// Undo reverse
|
||||
path = path.chars().rev().collect();
|
||||
let path = fullname.trim_end_matches(is_separator);
|
||||
|
||||
// Convert to path buffer and get last path component
|
||||
let pb = PathBuf::from(path);
|
||||
|
|
|
@ -17,6 +17,7 @@ path = "src/cat.rs"
|
|||
[dependencies]
|
||||
clap = "2.33"
|
||||
thiserror = "1.0"
|
||||
atty = "0.2"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ use clap::{crate_version, App, Arg};
|
|||
use std::fs::{metadata, File};
|
||||
use std::io::{self, Read, Write};
|
||||
use thiserror::Error;
|
||||
use uucore::fs::is_stdin_interactive;
|
||||
|
||||
/// Linux splice support
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
|
@ -306,7 +305,7 @@ fn cat_path(path: &str, options: &OutputOptions, state: &mut OutputState) -> Cat
|
|||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
file_descriptor: stdin.as_raw_fd(),
|
||||
reader: stdin,
|
||||
is_interactive: is_stdin_interactive(),
|
||||
is_interactive: atty::is(atty::Stream::Stdin),
|
||||
};
|
||||
return cat_handle(&mut handle, options, state);
|
||||
}
|
||||
|
|
|
@ -134,23 +134,25 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
Err(err) => crash!(1, "cannot stat attributes of '{}': {}", fref, err),
|
||||
});
|
||||
let modes = matches.value_of(options::MODE).unwrap(); // should always be Some because required
|
||||
let mut cmode = if mode_had_minus_prefix {
|
||||
let cmode = if mode_had_minus_prefix {
|
||||
// clap parsing is finished, now put prefix back
|
||||
Some(format!("-{}", modes))
|
||||
format!("-{}", modes)
|
||||
} else {
|
||||
Some(modes.to_string())
|
||||
modes.to_string()
|
||||
};
|
||||
let mut files: Vec<String> = matches
|
||||
.values_of(options::FILE)
|
||||
.map(|v| v.map(ToString::to_string).collect())
|
||||
.unwrap_or_default();
|
||||
if fmode.is_some() {
|
||||
let cmode = if fmode.is_some() {
|
||||
// "--reference" and MODE are mutually exclusive
|
||||
// if "--reference" was used MODE needs to be interpreted as another FILE
|
||||
// it wasn't possible to implement this behavior directly with clap
|
||||
files.push(cmode.unwrap());
|
||||
cmode = None;
|
||||
}
|
||||
files.push(cmode);
|
||||
None
|
||||
} else {
|
||||
Some(cmode)
|
||||
};
|
||||
|
||||
let chmoder = Chmoder {
|
||||
changes,
|
||||
|
|
|
@ -278,37 +278,25 @@ fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
|
|||
let usr_only = args.len() == 1 && !args[0].is_empty();
|
||||
let grp_only = args.len() == 2 && args[0].is_empty();
|
||||
let usr_grp = args.len() == 2 && !args[0].is_empty() && !args[1].is_empty();
|
||||
|
||||
if usr_only {
|
||||
Ok((
|
||||
Some(match Passwd::locate(args[0]) {
|
||||
Ok(v) => v.uid(),
|
||||
_ => return Err(format!("invalid user: ‘{}’", spec)),
|
||||
}),
|
||||
None,
|
||||
))
|
||||
} else if grp_only {
|
||||
Ok((
|
||||
None,
|
||||
Some(match Group::locate(args[1]) {
|
||||
Ok(v) => v.gid(),
|
||||
_ => return Err(format!("invalid group: ‘{}’", spec)),
|
||||
}),
|
||||
))
|
||||
} else if usr_grp {
|
||||
Ok((
|
||||
Some(match Passwd::locate(args[0]) {
|
||||
Ok(v) => v.uid(),
|
||||
_ => return Err(format!("invalid user: ‘{}’", spec)),
|
||||
}),
|
||||
Some(match Group::locate(args[1]) {
|
||||
Ok(v) => v.gid(),
|
||||
_ => return Err(format!("invalid group: ‘{}’", spec)),
|
||||
}),
|
||||
))
|
||||
let uid = if usr_only || usr_grp {
|
||||
Some(
|
||||
Passwd::locate(args[0])
|
||||
.map_err(|_| format!("invalid user: ‘{}’", spec))?
|
||||
.uid(),
|
||||
)
|
||||
} else {
|
||||
Ok((None, None))
|
||||
}
|
||||
None
|
||||
};
|
||||
let gid = if grp_only || usr_grp {
|
||||
Some(
|
||||
Group::locate(args[1])
|
||||
.map_err(|_| format!("invalid group: ‘{}’", spec))?
|
||||
.gid(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok((uid, gid))
|
||||
}
|
||||
|
||||
enum IfFrom {
|
||||
|
@ -497,3 +485,17 @@ impl Chowner {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_spec() {
|
||||
assert_eq!(parse_spec(":"), Ok((None, None)));
|
||||
assert!(parse_spec("::")
|
||||
.err()
|
||||
.unwrap()
|
||||
.starts_with("invalid group: "));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ mod options {
|
|||
pub const GROUP: &str = "group";
|
||||
pub const GROUPS: &str = "groups";
|
||||
pub const USERSPEC: &str = "userspec";
|
||||
pub const COMMAND: &str = "command";
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
|
@ -39,7 +40,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.version(crate_version!())
|
||||
.about(ABOUT)
|
||||
.usage(SYNTAX)
|
||||
.arg(Arg::with_name(options::NEWROOT).hidden(true).required(true))
|
||||
.arg(
|
||||
Arg::with_name(options::NEWROOT)
|
||||
.hidden(true)
|
||||
.required(true)
|
||||
.index(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::USER)
|
||||
.short("u")
|
||||
|
@ -71,6 +77,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
)
|
||||
.value_name("USER:GROUP"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::COMMAND)
|
||||
.hidden(true)
|
||||
.multiple(true)
|
||||
.index(2),
|
||||
)
|
||||
.get_matches_from(args);
|
||||
|
||||
let default_shell: &'static str = "/bin/sh";
|
||||
|
@ -94,7 +106,14 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
);
|
||||
}
|
||||
|
||||
let command: Vec<&str> = match matches.args.len() {
|
||||
let commands = match matches.values_of(options::COMMAND) {
|
||||
Some(v) => v.collect(),
|
||||
None => vec![],
|
||||
};
|
||||
|
||||
// TODO: refactor the args and command matching
|
||||
// See: https://github.com/uutils/coreutils/pull/2365#discussion_r647849967
|
||||
let command: Vec<&str> = match commands.len() {
|
||||
1 => {
|
||||
let shell: &str = match user_shell {
|
||||
Err(_) => default_shell,
|
||||
|
@ -102,14 +121,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
};
|
||||
vec![shell, default_option]
|
||||
}
|
||||
_ => {
|
||||
let mut vector: Vec<&str> = Vec::new();
|
||||
for (&k, v) in matches.args.iter() {
|
||||
vector.push(k);
|
||||
vector.push(v.vals[0].to_str().unwrap());
|
||||
}
|
||||
vector
|
||||
}
|
||||
_ => commands,
|
||||
};
|
||||
|
||||
set_context(newroot, &matches);
|
||||
|
|
|
@ -160,18 +160,14 @@ fn cksum(fname: &str) -> io::Result<(u32, usize)> {
|
|||
|
||||
let mut bytes = init_byte_array();
|
||||
loop {
|
||||
match rd.read(&mut bytes) {
|
||||
Ok(num_bytes) => {
|
||||
if num_bytes == 0 {
|
||||
return Ok((crc_final(crc, size), size));
|
||||
}
|
||||
for &b in bytes[..num_bytes].iter() {
|
||||
crc = crc_update(crc, b);
|
||||
}
|
||||
size += num_bytes;
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
let num_bytes = rd.read(&mut bytes)?;
|
||||
if num_bytes == 0 {
|
||||
return Ok((crc_final(crc, size), size));
|
||||
}
|
||||
for &b in bytes[..num_bytes].iter() {
|
||||
crc = crc_update(crc, b);
|
||||
}
|
||||
size += num_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,9 +50,8 @@ fn mkdelim(col: usize, opts: &ArgMatches) -> String {
|
|||
}
|
||||
|
||||
fn ensure_nl(line: &mut String) {
|
||||
match line.chars().last() {
|
||||
Some('\n') => (),
|
||||
_ => line.push('\n'),
|
||||
if !line.ends_with('\n') {
|
||||
line.push('\n');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -709,27 +709,26 @@ fn parse_path_args(path_args: &[String], options: &Options) -> CopyResult<(Vec<S
|
|||
return Err(format!("extra operand {:?}", paths[2]).into());
|
||||
}
|
||||
|
||||
let (mut sources, target) = match options.target_dir {
|
||||
let target = match options.target_dir {
|
||||
Some(ref target) => {
|
||||
// All path args are sources, and the target dir was
|
||||
// specified separately
|
||||
(paths, PathBuf::from(target))
|
||||
PathBuf::from(target)
|
||||
}
|
||||
None => {
|
||||
// If there was no explicit target-dir, then use the last
|
||||
// path_arg
|
||||
let target = paths.pop().unwrap();
|
||||
(paths, target)
|
||||
paths.pop().unwrap()
|
||||
}
|
||||
};
|
||||
|
||||
if options.strip_trailing_slashes {
|
||||
for source in sources.iter_mut() {
|
||||
for source in paths.iter_mut() {
|
||||
*source = source.components().as_path().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
Ok((sources, target))
|
||||
Ok((paths, target))
|
||||
}
|
||||
|
||||
fn preserve_hardlinks(
|
||||
|
@ -1271,15 +1270,15 @@ fn copy_on_write_linux(source: &Path, dest: &Path, mode: ReflinkMode) -> CopyRes
|
|||
ReflinkMode::Always => unsafe {
|
||||
let result = ficlone(dst_file.as_raw_fd(), src_file.as_raw_fd() as *const i32);
|
||||
if result != 0 {
|
||||
return Err(format!(
|
||||
Err(format!(
|
||||
"failed to clone {:?} from {:?}: {}",
|
||||
source,
|
||||
dest,
|
||||
std::io::Error::last_os_error()
|
||||
)
|
||||
.into());
|
||||
.into())
|
||||
} else {
|
||||
return Ok(());
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
ReflinkMode::Auto => unsafe {
|
||||
|
@ -1287,11 +1286,10 @@ fn copy_on_write_linux(source: &Path, dest: &Path, mode: ReflinkMode) -> CopyRes
|
|||
if result != 0 {
|
||||
fs::copy(source, dest).context(&*context_for(source, dest))?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
ReflinkMode::Never => unreachable!(),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Copies `source` to `dest` using copy-on-write if possible.
|
||||
|
|
|
@ -133,20 +133,12 @@ fn extract_patterns(args: &[String]) -> Result<Vec<Pattern>, CsplitError> {
|
|||
Some(m) => m.as_str().parse().unwrap(),
|
||||
};
|
||||
if let Some(up_to_match) = captures.name("UPTO") {
|
||||
let pattern = match Regex::new(up_to_match.as_str()) {
|
||||
Err(_) => {
|
||||
return Err(CsplitError::InvalidPattern(arg.to_string()));
|
||||
}
|
||||
Ok(reg) => reg,
|
||||
};
|
||||
let pattern = Regex::new(up_to_match.as_str())
|
||||
.map_err(|_| CsplitError::InvalidPattern(arg.to_string()))?;
|
||||
patterns.push(Pattern::UpToMatch(pattern, offset, execute_ntimes));
|
||||
} else if let Some(skip_to_match) = captures.name("SKIPTO") {
|
||||
let pattern = match Regex::new(skip_to_match.as_str()) {
|
||||
Err(_) => {
|
||||
return Err(CsplitError::InvalidPattern(arg.to_string()));
|
||||
}
|
||||
Ok(reg) => reg,
|
||||
};
|
||||
let pattern = Regex::new(skip_to_match.as_str())
|
||||
.map_err(|_| CsplitError::InvalidPattern(arg.to_string()))?;
|
||||
patterns.push(Pattern::SkipToMatch(pattern, offset, execute_ntimes));
|
||||
}
|
||||
} else if let Ok(line_number) = arg.parse::<usize>() {
|
||||
|
|
|
@ -33,13 +33,13 @@ impl SplitName {
|
|||
// get the prefix
|
||||
let prefix = prefix_opt.unwrap_or_else(|| "xx".to_string());
|
||||
// the width for the split offset
|
||||
let n_digits = match n_digits_opt {
|
||||
None => 2,
|
||||
Some(opt) => match opt.parse::<usize>() {
|
||||
Ok(digits) => digits,
|
||||
Err(_) => return Err(CsplitError::InvalidNumber(opt)),
|
||||
},
|
||||
};
|
||||
let n_digits = n_digits_opt
|
||||
.map(|opt| {
|
||||
opt.parse::<usize>()
|
||||
.map_err(|_| CsplitError::InvalidNumber(opt))
|
||||
})
|
||||
.transpose()?
|
||||
.unwrap_or(2);
|
||||
// translate the custom format into a function
|
||||
let fn_split_name: Box<dyn Fn(usize) -> String> = match format_opt {
|
||||
None => Box::new(move |n: usize| -> String {
|
||||
|
|
|
@ -20,6 +20,7 @@ uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
|||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
memchr = "2"
|
||||
bstr = "0.2"
|
||||
atty = "0.2"
|
||||
|
||||
[[bin]]
|
||||
name = "cut"
|
||||
|
|
|
@ -17,7 +17,6 @@ use std::io::{stdin, stdout, BufReader, BufWriter, Read, Write};
|
|||
use std::path::Path;
|
||||
|
||||
use self::searcher::Searcher;
|
||||
use uucore::fs::is_stdout_interactive;
|
||||
use uucore::ranges::Range;
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
|
@ -127,7 +126,7 @@ enum Mode {
|
|||
}
|
||||
|
||||
fn stdout_writer() -> Box<dyn Write> {
|
||||
if is_stdout_interactive() {
|
||||
if atty::is(atty::Stream::Stdout) {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(BufWriter::new(stdout())) as Box<dyn Write>
|
||||
|
|
|
@ -19,6 +19,8 @@ clap = "2.33"
|
|||
chrono = "0.4"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = { version="0.3", features=[] }
|
||||
|
||||
[[bin]]
|
||||
|
|
|
@ -59,6 +59,7 @@ mod options {
|
|||
pub const SI: &str = "si";
|
||||
pub const TIME: &str = "time";
|
||||
pub const TIME_STYLE: &str = "time-style";
|
||||
pub const ONE_FILE_SYSTEM: &str = "one-file-system";
|
||||
pub const FILE: &str = "FILE";
|
||||
}
|
||||
|
||||
|
@ -83,6 +84,7 @@ struct Options {
|
|||
max_depth: Option<usize>,
|
||||
total: bool,
|
||||
separate_dirs: bool,
|
||||
one_file_system: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
||||
|
@ -278,10 +280,18 @@ fn du(
|
|||
Ok(entry) => match Stat::new(entry.path()) {
|
||||
Ok(this_stat) => {
|
||||
if this_stat.is_dir {
|
||||
if options.one_file_system {
|
||||
if let (Some(this_inode), Some(my_inode)) =
|
||||
(this_stat.inode, my_stat.inode)
|
||||
{
|
||||
if this_inode.dev_id != my_inode.dev_id {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
futures.push(du(this_stat, options, depth + 1, inodes));
|
||||
} else {
|
||||
if this_stat.inode.is_some() {
|
||||
let inode = this_stat.inode.unwrap();
|
||||
if let Some(inode) = this_stat.inode {
|
||||
if inodes.contains(&inode) {
|
||||
continue;
|
||||
}
|
||||
|
@ -320,7 +330,9 @@ fn du(
|
|||
my_stat.size += stat.size;
|
||||
my_stat.blocks += stat.blocks;
|
||||
}
|
||||
options.max_depth == None || depth < options.max_depth.unwrap()
|
||||
options
|
||||
.max_depth
|
||||
.map_or(true, |max_depth| depth < max_depth)
|
||||
}));
|
||||
stats.push(my_stat);
|
||||
Box::new(stats.into_iter())
|
||||
|
@ -492,12 +504,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.long(options::SI)
|
||||
.help("like -h, but use powers of 1000 not 1024")
|
||||
)
|
||||
// .arg(
|
||||
// Arg::with_name("one-file-system")
|
||||
// .short("x")
|
||||
// .long("one-file-system")
|
||||
// .help("skip directories on different file systems")
|
||||
// )
|
||||
.arg(
|
||||
Arg::with_name(options::ONE_FILE_SYSTEM)
|
||||
.short("x")
|
||||
.long(options::ONE_FILE_SYSTEM)
|
||||
.help("skip directories on different file systems")
|
||||
)
|
||||
// .arg(
|
||||
// Arg::with_name("")
|
||||
// .short("x")
|
||||
|
@ -562,6 +574,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
max_depth,
|
||||
total: matches.is_present(options::TOTAL),
|
||||
separate_dirs: matches.is_present(options::SEPARATE_DIRS),
|
||||
one_file_system: matches.is_present(options::ONE_FILE_SYSTEM),
|
||||
};
|
||||
|
||||
let files = match matches.value_of(options::FILE) {
|
||||
|
|
18
src/uu/env/src/env.rs
vendored
18
src/uu/env/src/env.rs
vendored
|
@ -82,13 +82,10 @@ fn load_config_file(opts: &mut Options) -> Result<(), i32> {
|
|||
Ini::load_from_file(file)
|
||||
};
|
||||
|
||||
let conf = match conf {
|
||||
Ok(config) => config,
|
||||
Err(error) => {
|
||||
eprintln!("env: error: \"{}\": {}", file, error);
|
||||
return Err(1);
|
||||
}
|
||||
};
|
||||
let conf = conf.map_err(|error| {
|
||||
eprintln!("env: error: \"{}\": {}", file, error);
|
||||
1
|
||||
})?;
|
||||
|
||||
for (_, prop) in &conf {
|
||||
// ignore all INI section lines (treat them as comments)
|
||||
|
@ -256,13 +253,10 @@ fn run_env(args: impl uucore::Args) -> Result<(), i32> {
|
|||
|
||||
// FIXME: this should just use execvp() (no fork()) on Unix-like systems
|
||||
match Command::new(&*prog).args(args).status() {
|
||||
Ok(exit) => {
|
||||
if !exit.success() {
|
||||
return Err(exit.code().unwrap());
|
||||
}
|
||||
}
|
||||
Ok(exit) if !exit.success() => return Err(exit.code().unwrap()),
|
||||
Err(ref err) if err.kind() == io::ErrorKind::NotFound => return Err(127),
|
||||
Err(_) => return Err(126),
|
||||
Ok(_) => (),
|
||||
}
|
||||
} else {
|
||||
// no program provided, so just dump all env vars to stdout
|
||||
|
|
|
@ -56,11 +56,7 @@ fn print_expr_error(expr_error: &str) -> ! {
|
|||
}
|
||||
|
||||
fn evaluate_ast(maybe_ast: Result<Box<syntax_tree::AstNode>, String>) -> Result<String, String> {
|
||||
if maybe_ast.is_err() {
|
||||
Err(maybe_ast.err().unwrap())
|
||||
} else {
|
||||
maybe_ast.ok().unwrap().evaluate()
|
||||
}
|
||||
maybe_ast.and_then(|ast| ast.evaluate())
|
||||
}
|
||||
|
||||
fn maybe_handle_help_or_version(args: &[String]) -> bool {
|
||||
|
|
|
@ -160,10 +160,8 @@ impl AstNode {
|
|||
if let AstNode::Node { operands, .. } = self {
|
||||
let mut out = Vec::with_capacity(operands.len());
|
||||
for operand in operands {
|
||||
match operand.evaluate() {
|
||||
Ok(value) => out.push(value),
|
||||
Err(reason) => return Err(reason),
|
||||
}
|
||||
let value = operand.evaluate()?;
|
||||
out.push(value);
|
||||
}
|
||||
Ok(out)
|
||||
} else {
|
||||
|
@ -175,23 +173,14 @@ impl AstNode {
|
|||
pub fn tokens_to_ast(
|
||||
maybe_tokens: Result<Vec<(usize, Token)>, String>,
|
||||
) -> Result<Box<AstNode>, String> {
|
||||
if maybe_tokens.is_err() {
|
||||
Err(maybe_tokens.err().unwrap())
|
||||
} else {
|
||||
let tokens = maybe_tokens.ok().unwrap();
|
||||
maybe_tokens.and_then(|tokens| {
|
||||
let mut out_stack: TokenStack = Vec::new();
|
||||
let mut op_stack: TokenStack = Vec::new();
|
||||
|
||||
for (token_idx, token) in tokens {
|
||||
if let Err(reason) =
|
||||
push_token_to_either_stack(token_idx, &token, &mut out_stack, &mut op_stack)
|
||||
{
|
||||
return Err(reason);
|
||||
}
|
||||
}
|
||||
if let Err(reason) = move_rest_of_ops_to_out(&mut out_stack, &mut op_stack) {
|
||||
return Err(reason);
|
||||
push_token_to_either_stack(token_idx, &token, &mut out_stack, &mut op_stack)?;
|
||||
}
|
||||
move_rest_of_ops_to_out(&mut out_stack, &mut op_stack)?;
|
||||
assert!(op_stack.is_empty());
|
||||
|
||||
maybe_dump_rpn(&out_stack);
|
||||
|
@ -205,7 +194,7 @@ pub fn tokens_to_ast(
|
|||
maybe_dump_ast(&result);
|
||||
result
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn maybe_dump_ast(result: &Result<Box<AstNode>, String>) {
|
||||
|
@ -261,10 +250,8 @@ fn maybe_ast_node(
|
|||
) -> Result<Box<AstNode>, String> {
|
||||
let mut operands = Vec::with_capacity(arity);
|
||||
for _ in 0..arity {
|
||||
match ast_from_rpn(rpn) {
|
||||
Err(reason) => return Err(reason),
|
||||
Ok(operand) => operands.push(operand),
|
||||
}
|
||||
let operand = ast_from_rpn(rpn)?;
|
||||
operands.push(operand);
|
||||
}
|
||||
operands.reverse();
|
||||
Ok(AstNode::new_node(token_idx, op_type, operands))
|
||||
|
@ -408,10 +395,12 @@ fn move_till_match_paren(
|
|||
op_stack: &mut TokenStack,
|
||||
) -> Result<(), String> {
|
||||
loop {
|
||||
match op_stack.pop() {
|
||||
None => return Err("syntax error (Mismatched close-parenthesis)".to_string()),
|
||||
Some((_, Token::ParOpen)) => return Ok(()),
|
||||
Some(other) => out_stack.push(other),
|
||||
let op = op_stack
|
||||
.pop()
|
||||
.ok_or_else(|| "syntax error (Mismatched close-parenthesis)".to_string())?;
|
||||
match op {
|
||||
(_, Token::ParOpen) => return Ok(()),
|
||||
other => out_stack.push(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -471,22 +460,17 @@ fn infix_operator_and(values: &[String]) -> String {
|
|||
|
||||
fn operator_match(values: &[String]) -> Result<String, String> {
|
||||
assert!(values.len() == 2);
|
||||
let re = match Regex::with_options(&values[1], RegexOptions::REGEX_OPTION_NONE, Syntax::grep())
|
||||
{
|
||||
Ok(m) => m,
|
||||
Err(err) => return Err(err.description().to_string()),
|
||||
};
|
||||
if re.captures_len() > 0 {
|
||||
Ok(match re.captures(&values[0]) {
|
||||
Some(captures) => captures.at(1).unwrap().to_string(),
|
||||
None => "".to_string(),
|
||||
})
|
||||
let re = Regex::with_options(&values[1], RegexOptions::REGEX_OPTION_NONE, Syntax::grep())
|
||||
.map_err(|err| err.description().to_string())?;
|
||||
Ok(if re.captures_len() > 0 {
|
||||
re.captures(&values[0])
|
||||
.map(|captures| captures.at(1).unwrap())
|
||||
.unwrap_or("")
|
||||
.to_string()
|
||||
} else {
|
||||
Ok(match re.find(&values[0]) {
|
||||
Some((start, end)) => (end - start).to_string(),
|
||||
None => "0".to_string(),
|
||||
})
|
||||
}
|
||||
re.find(&values[0])
|
||||
.map_or("0".to_string(), |(start, end)| (end - start).to_string())
|
||||
})
|
||||
}
|
||||
|
||||
fn prefix_operator_length(values: &[String]) -> String {
|
||||
|
|
|
@ -98,7 +98,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) {
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
let slice = &arg;
|
||||
if slice.starts_with('-') && slice.len() > 1 && slice.chars().nth(1).unwrap().is_digit(10) {
|
||||
if slice.starts_with('-') && slice.chars().nth(1).map_or(false, |c| c.is_digit(10)) {
|
||||
let mut v = args.to_vec();
|
||||
v.remove(i);
|
||||
return (v, Some(slice[1..].to_owned()));
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/groups.rs"
|
||||
|
||||
[dependencies]
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "process"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
clap = "2.33"
|
||||
|
||||
|
|
|
@ -255,10 +255,8 @@ fn detect_algo<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
if alg.is_none() {
|
||||
crash!(1, "You must specify hash algorithm!")
|
||||
};
|
||||
(name, alg.unwrap(), output_bits)
|
||||
let alg = alg.unwrap_or_else(|| crash!(1, "You must specify hash algorithm!"));
|
||||
(name, alg, output_bits)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -176,19 +176,11 @@ impl HeadOptions {
|
|||
options.zeroed = matches.is_present(options::ZERO_NAME);
|
||||
|
||||
let mode_and_from_end = if let Some(v) = matches.value_of(options::BYTES_NAME) {
|
||||
match parse_mode(v, Modes::Bytes) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
return Err(format!("invalid number of bytes: {}", err));
|
||||
}
|
||||
}
|
||||
parse_mode(v, Modes::Bytes)
|
||||
.map_err(|err| format!("invalid number of bytes: {}", err))?
|
||||
} else if let Some(v) = matches.value_of(options::LINES_NAME) {
|
||||
match parse_mode(v, Modes::Lines) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
return Err(format!("invalid number of lines: {}", err));
|
||||
}
|
||||
}
|
||||
parse_mode(v, Modes::Lines)
|
||||
.map_err(|err| format!("invalid number of lines: {}", err))?
|
||||
} else {
|
||||
(Modes::Lines(10), false)
|
||||
};
|
||||
|
|
|
@ -299,29 +299,17 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
|
|||
let considering_dir: bool = MainFunction::Directory == main_function;
|
||||
|
||||
let specified_mode: Option<u32> = if matches.is_present(OPT_MODE) {
|
||||
match matches.value_of(OPT_MODE) {
|
||||
Some(x) => match mode::parse(x, considering_dir) {
|
||||
Ok(y) => Some(y),
|
||||
Err(err) => {
|
||||
show_error!("Invalid mode string: {}", err);
|
||||
return Err(1);
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(1);
|
||||
}
|
||||
}
|
||||
let x = matches.value_of(OPT_MODE).ok_or(1)?;
|
||||
Some(mode::parse(x, considering_dir).map_err(|err| {
|
||||
show_error!("Invalid mode string: {}", err);
|
||||
1
|
||||
})?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let backup_suffix = if matches.is_present(OPT_SUFFIX) {
|
||||
match matches.value_of(OPT_SUFFIX) {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
return Err(1);
|
||||
}
|
||||
}
|
||||
matches.value_of(OPT_SUFFIX).ok_or(1)?
|
||||
} else {
|
||||
"~"
|
||||
};
|
||||
|
|
|
@ -111,7 +111,7 @@ fn handle_obsolete(mut args: Vec<String>) -> (Vec<String>, Option<String>) {
|
|||
while i < args.len() {
|
||||
// this is safe because slice is valid when it is referenced
|
||||
let slice = &args[i].clone();
|
||||
if slice.starts_with('-') && slice.len() > 1 && slice.chars().nth(1).unwrap().is_digit(10) {
|
||||
if slice.starts_with('-') && slice.chars().nth(1).map_or(false, |c| c.is_digit(10)) {
|
||||
let val = &slice[1..];
|
||||
match val.parse() {
|
||||
Ok(num) => {
|
||||
|
|
|
@ -78,17 +78,19 @@ fn get_long_usage() -> String {
|
|||
|
||||
static ABOUT: &str = "change file owner and group";
|
||||
|
||||
static OPT_B: &str = "b";
|
||||
static OPT_BACKUP: &str = "backup";
|
||||
static OPT_FORCE: &str = "force";
|
||||
static OPT_INTERACTIVE: &str = "interactive";
|
||||
static OPT_NO_DEREFERENCE: &str = "no-dereference";
|
||||
static OPT_SYMBOLIC: &str = "symbolic";
|
||||
static OPT_SUFFIX: &str = "suffix";
|
||||
static OPT_TARGET_DIRECTORY: &str = "target-directory";
|
||||
static OPT_NO_TARGET_DIRECTORY: &str = "no-target-directory";
|
||||
static OPT_RELATIVE: &str = "relative";
|
||||
static OPT_VERBOSE: &str = "verbose";
|
||||
mod options {
|
||||
pub const B: &str = "b";
|
||||
pub const BACKUP: &str = "backup";
|
||||
pub const FORCE: &str = "force";
|
||||
pub const INTERACTIVE: &str = "interactive";
|
||||
pub const NO_DEREFERENCE: &str = "no-dereference";
|
||||
pub const SYMBOLIC: &str = "symbolic";
|
||||
pub const SUFFIX: &str = "suffix";
|
||||
pub const TARGET_DIRECTORY: &str = "target-directory";
|
||||
pub const NO_TARGET_DIRECTORY: &str = "no-target-directory";
|
||||
pub const RELATIVE: &str = "relative";
|
||||
pub const VERBOSE: &str = "verbose";
|
||||
}
|
||||
|
||||
static ARG_FILES: &str = "files";
|
||||
|
||||
|
@ -101,47 +103,42 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.about(ABOUT)
|
||||
.usage(&usage[..])
|
||||
.after_help(&long_usage[..])
|
||||
.arg(Arg::with_name(OPT_B).short(OPT_B).help(
|
||||
.arg(Arg::with_name(options::B).short(options::B).help(
|
||||
"make a backup of each file that would otherwise be overwritten or \
|
||||
removed",
|
||||
))
|
||||
.arg(
|
||||
Arg::with_name(OPT_BACKUP)
|
||||
.long(OPT_BACKUP)
|
||||
Arg::with_name(options::BACKUP)
|
||||
.long(options::BACKUP)
|
||||
.help(
|
||||
"make a backup of each file that would otherwise be overwritten \
|
||||
or removed",
|
||||
)
|
||||
.takes_value(true)
|
||||
.possible_value("simple")
|
||||
.possible_value("never")
|
||||
.possible_value("numbered")
|
||||
.possible_value("t")
|
||||
.possible_value("existing")
|
||||
.possible_value("nil")
|
||||
.possible_value("none")
|
||||
.possible_value("off")
|
||||
.possible_values(&[
|
||||
"simple", "never", "numbered", "t", "existing", "nil", "none", "off",
|
||||
])
|
||||
.value_name("METHOD"),
|
||||
)
|
||||
// TODO: opts.arg(
|
||||
// Arg::with_name(("d", "directory", "allow users with appropriate privileges to attempt \
|
||||
// to make hard links to directories");
|
||||
.arg(
|
||||
Arg::with_name(OPT_FORCE)
|
||||
Arg::with_name(options::FORCE)
|
||||
.short("f")
|
||||
.long(OPT_FORCE)
|
||||
.long(options::FORCE)
|
||||
.help("remove existing destination files"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_INTERACTIVE)
|
||||
Arg::with_name(options::INTERACTIVE)
|
||||
.short("i")
|
||||
.long(OPT_INTERACTIVE)
|
||||
.long(options::INTERACTIVE)
|
||||
.help("prompt whether to remove existing destination files"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_NO_DEREFERENCE)
|
||||
Arg::with_name(options::NO_DEREFERENCE)
|
||||
.short("n")
|
||||
.long(OPT_NO_DEREFERENCE)
|
||||
.long(options::NO_DEREFERENCE)
|
||||
.help(
|
||||
"treat LINK_executable!() as a normal file if it is a \
|
||||
symbolic link to a directory",
|
||||
|
@ -153,43 +150,45 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
// TODO: opts.arg(
|
||||
// Arg::with_name(("P", "physical", "make hard links directly to symbolic links");
|
||||
.arg(
|
||||
Arg::with_name(OPT_SYMBOLIC)
|
||||
Arg::with_name(options::SYMBOLIC)
|
||||
.short("s")
|
||||
.long("symbolic")
|
||||
.help("make symbolic links instead of hard links"),
|
||||
.help("make symbolic links instead of hard links")
|
||||
// override added for https://github.com/uutils/coreutils/issues/2359
|
||||
.overrides_with(options::SYMBOLIC),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_SUFFIX)
|
||||
Arg::with_name(options::SUFFIX)
|
||||
.short("S")
|
||||
.long(OPT_SUFFIX)
|
||||
.long(options::SUFFIX)
|
||||
.help("override the usual backup suffix")
|
||||
.value_name("SUFFIX")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_TARGET_DIRECTORY)
|
||||
Arg::with_name(options::TARGET_DIRECTORY)
|
||||
.short("t")
|
||||
.long(OPT_TARGET_DIRECTORY)
|
||||
.long(options::TARGET_DIRECTORY)
|
||||
.help("specify the DIRECTORY in which to create the links")
|
||||
.value_name("DIRECTORY")
|
||||
.conflicts_with(OPT_NO_TARGET_DIRECTORY),
|
||||
.conflicts_with(options::NO_TARGET_DIRECTORY),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_NO_TARGET_DIRECTORY)
|
||||
Arg::with_name(options::NO_TARGET_DIRECTORY)
|
||||
.short("T")
|
||||
.long(OPT_NO_TARGET_DIRECTORY)
|
||||
.long(options::NO_TARGET_DIRECTORY)
|
||||
.help("treat LINK_executable!() as a normal file always"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_RELATIVE)
|
||||
Arg::with_name(options::RELATIVE)
|
||||
.short("r")
|
||||
.long(OPT_RELATIVE)
|
||||
.long(options::RELATIVE)
|
||||
.help("create symbolic links relative to link location"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_VERBOSE)
|
||||
Arg::with_name(options::VERBOSE)
|
||||
.short("v")
|
||||
.long(OPT_VERBOSE)
|
||||
.long(options::VERBOSE)
|
||||
.help("print name of each linked file"),
|
||||
)
|
||||
.arg(
|
||||
|
@ -209,18 +208,18 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.map(PathBuf::from)
|
||||
.collect();
|
||||
|
||||
let overwrite_mode = if matches.is_present(OPT_FORCE) {
|
||||
let overwrite_mode = if matches.is_present(options::FORCE) {
|
||||
OverwriteMode::Force
|
||||
} else if matches.is_present(OPT_INTERACTIVE) {
|
||||
} else if matches.is_present(options::INTERACTIVE) {
|
||||
OverwriteMode::Interactive
|
||||
} else {
|
||||
OverwriteMode::NoClobber
|
||||
};
|
||||
|
||||
let backup_mode = if matches.is_present(OPT_B) {
|
||||
let backup_mode = if matches.is_present(options::B) {
|
||||
BackupMode::ExistingBackup
|
||||
} else if matches.is_present(OPT_BACKUP) {
|
||||
match matches.value_of(OPT_BACKUP) {
|
||||
} else if matches.is_present(options::BACKUP) {
|
||||
match matches.value_of(options::BACKUP) {
|
||||
None => BackupMode::ExistingBackup,
|
||||
Some(mode) => match mode {
|
||||
"simple" | "never" => BackupMode::SimpleBackup,
|
||||
|
@ -234,8 +233,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
BackupMode::NoBackup
|
||||
};
|
||||
|
||||
let backup_suffix = if matches.is_present(OPT_SUFFIX) {
|
||||
matches.value_of(OPT_SUFFIX).unwrap()
|
||||
let backup_suffix = if matches.is_present(options::SUFFIX) {
|
||||
matches.value_of(options::SUFFIX).unwrap()
|
||||
} else {
|
||||
"~"
|
||||
};
|
||||
|
@ -243,14 +242,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
let settings = Settings {
|
||||
overwrite: overwrite_mode,
|
||||
backup: backup_mode,
|
||||
force: matches.is_present(OPT_FORCE),
|
||||
force: matches.is_present(options::FORCE),
|
||||
suffix: backup_suffix.to_string(),
|
||||
symbolic: matches.is_present(OPT_SYMBOLIC),
|
||||
relative: matches.is_present(OPT_RELATIVE),
|
||||
target_dir: matches.value_of(OPT_TARGET_DIRECTORY).map(String::from),
|
||||
no_target_dir: matches.is_present(OPT_NO_TARGET_DIRECTORY),
|
||||
no_dereference: matches.is_present(OPT_NO_DEREFERENCE),
|
||||
verbose: matches.is_present(OPT_VERBOSE),
|
||||
symbolic: matches.is_present(options::SYMBOLIC),
|
||||
relative: matches.is_present(options::RELATIVE),
|
||||
target_dir: matches
|
||||
.value_of(options::TARGET_DIRECTORY)
|
||||
.map(String::from),
|
||||
no_target_dir: matches.is_present(options::NO_TARGET_DIRECTORY),
|
||||
no_dereference: matches.is_present(options::NO_DEREFERENCE),
|
||||
verbose: matches.is_present(options::VERBOSE),
|
||||
};
|
||||
|
||||
exec(&paths[..], &settings)
|
||||
|
|
|
@ -19,7 +19,7 @@ clap = "2.33"
|
|||
uucore = { version = ">=0.0.7", package = "uucore", path = "../../uucore" }
|
||||
uucore_procs = { version = ">=0.0.5", package = "uucore_procs", path = "../../uucore_procs" }
|
||||
crossterm = ">=0.19"
|
||||
atty = "0.2.14"
|
||||
atty = "0.2"
|
||||
unicode-width = "0.1.7"
|
||||
unicode-segmentation = "1.7.1"
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ use crossterm::{
|
|||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
const BELL: &str = "\x07";
|
||||
|
||||
pub mod options {
|
||||
pub const SILENT: &str = "silent";
|
||||
pub const LOGICAL: &str = "logical";
|
||||
|
@ -53,14 +55,14 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
let matches = App::new(executable!())
|
||||
.about("A file perusal filter for CRT viewing.")
|
||||
.version(crate_version!())
|
||||
// The commented arguments below are unimplemented:
|
||||
/*
|
||||
.arg(
|
||||
Arg::with_name(options::SILENT)
|
||||
.short("d")
|
||||
.long(options::SILENT)
|
||||
.help("Display help instead of ringing bell"),
|
||||
)
|
||||
// The commented arguments below are unimplemented:
|
||||
/*
|
||||
.arg(
|
||||
Arg::with_name(options::LOGICAL)
|
||||
.short("f")
|
||||
|
@ -140,6 +142,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.get_matches_from(args);
|
||||
|
||||
let mut buff = String::new();
|
||||
let silent = matches.is_present(options::SILENT);
|
||||
if let Some(files) = matches.values_of(options::FILES) {
|
||||
let mut stdout = setup_term();
|
||||
let length = files.len();
|
||||
|
@ -162,14 +165,14 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
}
|
||||
let mut reader = BufReader::new(File::open(file).unwrap());
|
||||
reader.read_to_string(&mut buff).unwrap();
|
||||
more(&buff, &mut stdout, next_file.copied());
|
||||
more(&buff, &mut stdout, next_file.copied(), silent);
|
||||
buff.clear();
|
||||
}
|
||||
reset_term(&mut stdout);
|
||||
} else if atty::isnt(atty::Stream::Stdin) {
|
||||
stdin().read_to_string(&mut buff).unwrap();
|
||||
let mut stdout = setup_term();
|
||||
more(&buff, &mut stdout, None);
|
||||
more(&buff, &mut stdout, None, silent);
|
||||
reset_term(&mut stdout);
|
||||
} else {
|
||||
show_usage_error!("bad usage");
|
||||
|
@ -204,13 +207,14 @@ fn reset_term(stdout: &mut std::io::Stdout) {
|
|||
#[inline(always)]
|
||||
fn reset_term(_: &mut usize) {}
|
||||
|
||||
fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>) {
|
||||
fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>, silent: bool) {
|
||||
let (cols, rows) = terminal::size().unwrap();
|
||||
let lines = break_buff(buff, usize::from(cols));
|
||||
let line_count: u16 = lines.len().try_into().unwrap();
|
||||
|
||||
let mut upper_mark = 0;
|
||||
let mut lines_left = line_count.saturating_sub(upper_mark + rows);
|
||||
let mut wrong_key = false;
|
||||
|
||||
draw(
|
||||
&mut upper_mark,
|
||||
|
@ -219,6 +223,8 @@ fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>) {
|
|||
lines.clone(),
|
||||
line_count,
|
||||
next_file,
|
||||
silent,
|
||||
wrong_key,
|
||||
);
|
||||
|
||||
let is_last = next_file.is_none();
|
||||
|
@ -237,6 +243,7 @@ fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>) {
|
|||
|
||||
loop {
|
||||
if event::poll(Duration::from_millis(10)).unwrap() {
|
||||
wrong_key = false;
|
||||
match event::read().unwrap() {
|
||||
Event::Key(KeyEvent {
|
||||
code: KeyCode::Char('q'),
|
||||
|
@ -265,7 +272,9 @@ fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>) {
|
|||
}) => {
|
||||
upper_mark = upper_mark.saturating_sub(rows.saturating_sub(1));
|
||||
}
|
||||
_ => continue,
|
||||
_ => {
|
||||
wrong_key = true;
|
||||
}
|
||||
}
|
||||
lines_left = line_count.saturating_sub(upper_mark + rows);
|
||||
draw(
|
||||
|
@ -275,6 +284,8 @@ fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>) {
|
|||
lines.clone(),
|
||||
line_count,
|
||||
next_file,
|
||||
silent,
|
||||
wrong_key,
|
||||
);
|
||||
|
||||
if lines_left == 0 {
|
||||
|
@ -287,6 +298,7 @@ fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>) {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn draw(
|
||||
upper_mark: &mut u16,
|
||||
rows: u16,
|
||||
|
@ -294,6 +306,8 @@ fn draw(
|
|||
lines: Vec<String>,
|
||||
lc: u16,
|
||||
next_file: Option<&str>,
|
||||
silent: bool,
|
||||
wrong_key: bool,
|
||||
) {
|
||||
execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
|
||||
let (up_mark, lower_mark) = calc_range(*upper_mark, rows, lc);
|
||||
|
@ -308,7 +322,7 @@ fn draw(
|
|||
.write_all(format!("\r{}\n", line).as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
make_prompt_and_flush(&mut stdout, lower_mark, lc, next_file);
|
||||
make_prompt_and_flush(&mut stdout, lower_mark, lc, next_file, silent, wrong_key);
|
||||
*upper_mark = up_mark;
|
||||
}
|
||||
|
||||
|
@ -364,8 +378,15 @@ fn calc_range(mut upper_mark: u16, rows: u16, line_count: u16) -> (u16, u16) {
|
|||
}
|
||||
|
||||
// Make a prompt similar to original more
|
||||
fn make_prompt_and_flush(stdout: &mut Stdout, lower_mark: u16, lc: u16, next_file: Option<&str>) {
|
||||
let status = if lower_mark == lc {
|
||||
fn make_prompt_and_flush(
|
||||
stdout: &mut Stdout,
|
||||
lower_mark: u16,
|
||||
lc: u16,
|
||||
next_file: Option<&str>,
|
||||
silent: bool,
|
||||
wrong_key: bool,
|
||||
) {
|
||||
let status_inner = if lower_mark == lc {
|
||||
format!("Next file: {}", next_file.unwrap_or_default())
|
||||
} else {
|
||||
format!(
|
||||
|
@ -373,11 +394,21 @@ fn make_prompt_and_flush(stdout: &mut Stdout, lower_mark: u16, lc: u16, next_fil
|
|||
(lower_mark as f64 / lc as f64 * 100.0).round() as u16
|
||||
)
|
||||
};
|
||||
|
||||
let status = format!("--More--({})", status_inner);
|
||||
|
||||
let banner = match (silent, wrong_key) {
|
||||
(true, true) => "[Press 'h' for instructions. (unimplemented)]".to_string(),
|
||||
(true, false) => format!("{}[Press space to continue, 'q' to quit.]", status),
|
||||
(false, true) => format!("{}{}", status, BELL),
|
||||
(false, false) => status,
|
||||
};
|
||||
|
||||
write!(
|
||||
stdout,
|
||||
"\r{}--More--({}){}",
|
||||
"\r{}{}{}",
|
||||
Attribute::Reverse,
|
||||
status,
|
||||
banner,
|
||||
Attribute::Reset
|
||||
)
|
||||
.unwrap();
|
||||
|
|
|
@ -247,7 +247,7 @@ fn nl<T: Read>(reader: &mut BufReader<T>, settings: &Settings) {
|
|||
let mut line_filter: fn(&str, ®ex::Regex) -> bool = pass_regex;
|
||||
for mut l in reader.lines().map(|r| r.unwrap()) {
|
||||
// Sanitize the string. We want to print the newline ourselves.
|
||||
if !l.is_empty() && l.chars().rev().next().unwrap() == '\n' {
|
||||
if l.ends_with('\n') {
|
||||
l.pop();
|
||||
}
|
||||
// Next we iterate through the individual chars to see if this
|
||||
|
|
|
@ -17,6 +17,7 @@ path = "src/nohup.rs"
|
|||
[dependencies]
|
||||
clap = "2.33"
|
||||
libc = "0.2.42"
|
||||
atty = "0.2"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ use std::fs::{File, OpenOptions};
|
|||
use std::io::Error;
|
||||
use std::os::unix::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
use uucore::fs::{is_stderr_interactive, is_stdin_interactive, is_stdout_interactive};
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
static ABOUT: &str = "Run COMMAND ignoring hangup signals.";
|
||||
|
@ -84,7 +83,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
}
|
||||
|
||||
fn replace_fds() {
|
||||
if is_stdin_interactive() {
|
||||
if atty::is(atty::Stream::Stdin) {
|
||||
let new_stdin = match File::open(Path::new("/dev/null")) {
|
||||
Ok(t) => t,
|
||||
Err(e) => crash!(2, "Cannot replace STDIN: {}", e),
|
||||
|
@ -94,7 +93,7 @@ fn replace_fds() {
|
|||
}
|
||||
}
|
||||
|
||||
if is_stdout_interactive() {
|
||||
if atty::is(atty::Stream::Stdout) {
|
||||
let new_stdout = find_stdout();
|
||||
let fd = new_stdout.as_raw_fd();
|
||||
|
||||
|
@ -103,7 +102,7 @@ fn replace_fds() {
|
|||
}
|
||||
}
|
||||
|
||||
if is_stderr_interactive() && unsafe { dup2(1, 2) } != 2 {
|
||||
if atty::is(atty::Stream::Stderr) && unsafe { dup2(1, 2) } != 2 {
|
||||
crash!(2, "Cannot replace STDERR: {}", Error::last_os_error())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,24 +137,17 @@ impl OdOptions {
|
|||
|
||||
let mut label: Option<usize> = None;
|
||||
|
||||
let input_strings = match parse_inputs(&matches) {
|
||||
Ok(CommandLineInputs::FileNames(v)) => v,
|
||||
Ok(CommandLineInputs::FileAndOffset((f, s, l))) => {
|
||||
let parsed_input = parse_inputs(&matches).map_err(|e| format!("Invalid inputs: {}", e))?;
|
||||
let input_strings = match parsed_input {
|
||||
CommandLineInputs::FileNames(v) => v,
|
||||
CommandLineInputs::FileAndOffset((f, s, l)) => {
|
||||
skip_bytes = s;
|
||||
label = l;
|
||||
vec![f]
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("Invalid inputs: {}", e));
|
||||
}
|
||||
};
|
||||
|
||||
let formats = match parse_format_flags(&args) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
let formats = parse_format_flags(&args)?;
|
||||
|
||||
let mut line_bytes = matches.value_of(options::WIDTH).map_or(16, |s| {
|
||||
if matches.occurrences_of(options::WIDTH) == 0 {
|
||||
|
|
|
@ -108,10 +108,8 @@ pub fn parse_format_flags(args: &[String]) -> Result<Vec<ParsedFormatterItemInfo
|
|||
|
||||
for arg in arg_iter {
|
||||
if expect_type_string {
|
||||
match parse_type_string(arg) {
|
||||
Ok(v) => formats.extend(v.into_iter()),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
let v = parse_type_string(arg)?;
|
||||
formats.extend(v.into_iter());
|
||||
expect_type_string = false;
|
||||
} else if arg.starts_with("--") {
|
||||
if arg.len() == 2 {
|
||||
|
@ -119,10 +117,8 @@ pub fn parse_format_flags(args: &[String]) -> Result<Vec<ParsedFormatterItemInfo
|
|||
}
|
||||
if arg.starts_with("--format=") {
|
||||
let params: String = arg.chars().skip_while(|c| *c != '=').skip(1).collect();
|
||||
match parse_type_string(¶ms) {
|
||||
Ok(v) => formats.extend(v.into_iter()),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
let v = parse_type_string(¶ms)?;
|
||||
formats.extend(v.into_iter());
|
||||
}
|
||||
if arg == "--format" {
|
||||
expect_type_string = true;
|
||||
|
@ -145,10 +141,8 @@ pub fn parse_format_flags(args: &[String]) -> Result<Vec<ParsedFormatterItemInfo
|
|||
}
|
||||
}
|
||||
if !format_spec.is_empty() {
|
||||
match parse_type_string(&format_spec) {
|
||||
Ok(v) => formats.extend(v.into_iter()),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
let v = parse_type_string(&format_spec)?;
|
||||
formats.extend(v.into_iter());
|
||||
expect_type_string = false;
|
||||
}
|
||||
}
|
||||
|
@ -275,17 +269,13 @@ fn parse_type_string(params: &str) -> Result<Vec<ParsedFormatterItemInfo>, Strin
|
|||
let mut chars = params.chars();
|
||||
let mut ch = chars.next();
|
||||
|
||||
while ch.is_some() {
|
||||
let type_char = ch.unwrap();
|
||||
let type_char = match format_type(type_char) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
return Err(format!(
|
||||
"unexpected char '{}' in format specification '{}'",
|
||||
type_char, params
|
||||
));
|
||||
}
|
||||
};
|
||||
while let Some(type_char) = ch {
|
||||
let type_char = format_type(type_char).ok_or_else(|| {
|
||||
format!(
|
||||
"unexpected char '{}' in format specification '{}'",
|
||||
type_char, params
|
||||
)
|
||||
})?;
|
||||
|
||||
let type_cat = format_type_category(type_char);
|
||||
|
||||
|
@ -301,30 +291,25 @@ fn parse_type_string(params: &str) -> Result<Vec<ParsedFormatterItemInfo>, Strin
|
|||
ch = chars.next();
|
||||
}
|
||||
if !decimal_size.is_empty() {
|
||||
byte_size = match decimal_size.parse() {
|
||||
Err(_) => {
|
||||
return Err(format!(
|
||||
"invalid number '{}' in format specification '{}'",
|
||||
decimal_size, params
|
||||
))
|
||||
}
|
||||
Ok(n) => n,
|
||||
}
|
||||
byte_size = decimal_size.parse().map_err(|_| {
|
||||
format!(
|
||||
"invalid number '{}' in format specification '{}'",
|
||||
decimal_size, params
|
||||
)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
if is_format_dump_char(ch, &mut show_ascii_dump) {
|
||||
ch = chars.next();
|
||||
}
|
||||
|
||||
match od_format_type(type_char, byte_size) {
|
||||
Some(ft) => formats.push(ParsedFormatterItemInfo::new(ft, show_ascii_dump)),
|
||||
None => {
|
||||
return Err(format!(
|
||||
"invalid size '{}' in format specification '{}'",
|
||||
byte_size, params
|
||||
))
|
||||
}
|
||||
}
|
||||
let ft = od_format_type(type_char, byte_size).ok_or_else(|| {
|
||||
format!(
|
||||
"invalid size '{}' in format specification '{}'",
|
||||
byte_size, params
|
||||
)
|
||||
})?;
|
||||
formats.push(ParsedFormatterItemInfo::new(ft, show_ascii_dump));
|
||||
}
|
||||
|
||||
Ok(formats)
|
||||
|
@ -335,16 +320,13 @@ pub fn parse_format_flags_str(
|
|||
args_str: &Vec<&'static str>,
|
||||
) -> Result<Vec<FormatterItemInfo>, String> {
|
||||
let args: Vec<String> = args_str.iter().map(|s| s.to_string()).collect();
|
||||
match parse_format_flags(&args) {
|
||||
Err(e) => Err(e),
|
||||
Ok(v) => {
|
||||
// tests using this function assume add_ascii_dump is not set
|
||||
Ok(v.into_iter()
|
||||
.inspect(|f| assert!(!f.add_ascii_dump))
|
||||
.map(|f| f.formatter_item_info)
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
parse_format_flags(&args).map(|v| {
|
||||
// tests using this function assume add_ascii_dump is not set
|
||||
v.into_iter()
|
||||
.inspect(|f| assert!(!f.add_ascii_dump))
|
||||
.map(|f| f.formatter_item_info)
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -36,16 +36,15 @@ impl<R: Read> Read for PartialReader<R> {
|
|||
while self.skip > 0 {
|
||||
let skip_count = cmp::min(self.skip, MAX_SKIP_BUFFER);
|
||||
|
||||
match self.inner.read(&mut bytes[..skip_count]) {
|
||||
Ok(0) => {
|
||||
match self.inner.read(&mut bytes[..skip_count])? {
|
||||
0 => {
|
||||
// this is an error as we still have more to skip
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::UnexpectedEof,
|
||||
"tried to skip past end of input",
|
||||
));
|
||||
}
|
||||
Ok(n) => self.skip -= n,
|
||||
Err(e) => return Err(e),
|
||||
n => self.skip -= n,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -241,13 +241,14 @@ fn no_leading_hyphen(path_segment: &str) -> bool {
|
|||
|
||||
// check whether a path segment contains only valid (read: portable) characters
|
||||
fn check_portable_chars(path_segment: &str) -> bool {
|
||||
let valid_str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-".to_string();
|
||||
for ch in path_segment.chars() {
|
||||
if !valid_str.contains(ch) {
|
||||
const VALID_CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-";
|
||||
for (i, ch) in path_segment.as_bytes().iter().enumerate() {
|
||||
if !VALID_CHARS.contains(ch) {
|
||||
let invalid = path_segment[i..].chars().next().unwrap();
|
||||
writeln!(
|
||||
&mut std::io::stderr(),
|
||||
"nonportable character '{}' in file name component '{}'",
|
||||
ch,
|
||||
invalid,
|
||||
path_segment
|
||||
);
|
||||
return false;
|
||||
|
|
|
@ -401,18 +401,18 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
for file_group in file_groups {
|
||||
let result_options = build_options(&matches, &file_group, args.join(" "));
|
||||
let options = match result_options {
|
||||
Ok(options) => options,
|
||||
Err(err) => {
|
||||
print_error(&matches, err);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
if result_options.is_err() {
|
||||
print_error(&matches, result_options.err().unwrap());
|
||||
return 1;
|
||||
}
|
||||
|
||||
let options = &result_options.unwrap();
|
||||
|
||||
let cmd_result = if file_group.len() == 1 {
|
||||
pr(file_group.get(0).unwrap(), options)
|
||||
let cmd_result = if let Ok(group) = file_group.iter().exactly_one() {
|
||||
pr(group, &options)
|
||||
} else {
|
||||
mpr(&file_group, options)
|
||||
mpr(&file_group, &options)
|
||||
};
|
||||
|
||||
let status = match cmd_result {
|
||||
|
@ -442,11 +442,12 @@ fn recreate_arguments(args: &[String]) -> Vec<String> {
|
|||
let mut arguments = args.to_owned();
|
||||
let num_option = args.iter().find_position(|x| n_regex.is_match(x.trim()));
|
||||
if let Some((pos, _value)) = num_option {
|
||||
let num_val_opt = args.get(pos + 1);
|
||||
if num_val_opt.is_some() && !num_regex.is_match(num_val_opt.unwrap()) {
|
||||
let could_be_file = arguments.remove(pos + 1);
|
||||
arguments.insert(pos + 1, format!("{}", NumberingMode::default().width));
|
||||
arguments.insert(pos + 2, could_be_file);
|
||||
if let Some(num_val_opt) = args.get(pos + 1) {
|
||||
if !num_regex.is_match(num_val_opt) {
|
||||
let could_be_file = arguments.remove(pos + 1);
|
||||
arguments.insert(pos + 1, format!("{}", NumberingMode::default().width));
|
||||
arguments.insert(pos + 2, could_be_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -666,12 +667,13 @@ fn build_options(
|
|||
None => end_page_in_plus_option,
|
||||
};
|
||||
|
||||
if end_page.is_some() && start_page > end_page.unwrap() {
|
||||
return Err(PrError::EncounteredErrors(format!(
|
||||
"invalid --pages argument '{}:{}'",
|
||||
start_page,
|
||||
end_page.unwrap()
|
||||
)));
|
||||
if let Some(end_page) = end_page {
|
||||
if start_page > end_page {
|
||||
return Err(PrError::EncounteredErrors(format!(
|
||||
"invalid --pages argument '{}:{}'",
|
||||
start_page, end_page
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
let default_lines_per_page = if form_feed_used {
|
||||
|
@ -947,7 +949,7 @@ fn read_stream_and_create_pages(
|
|||
let current_page = x + 1;
|
||||
|
||||
current_page >= start_page
|
||||
&& (last_page.is_none() || current_page <= last_page.unwrap())
|
||||
&& last_page.map_or(true, |last_page| current_page <= last_page)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
@ -996,8 +998,8 @@ fn mpr(paths: &[String], options: &OutputOptions) -> Result<i32, PrError> {
|
|||
|
||||
for (_key, file_line_group) in file_line_groups.into_iter() {
|
||||
for file_line in file_line_group {
|
||||
if file_line.line_content.is_err() {
|
||||
return Err(file_line.line_content.unwrap_err().into());
|
||||
if let Err(e) = file_line.line_content {
|
||||
return Err(e.into());
|
||||
}
|
||||
let new_page_number = file_line.page_number;
|
||||
if page_counter != new_page_number {
|
||||
|
@ -1030,8 +1032,7 @@ fn print_page(lines: &[FileLine], options: &OutputOptions, page: usize) -> Resul
|
|||
|
||||
let lines_written = write_columns(lines, options, out)?;
|
||||
|
||||
for index in 0..trailer_content.len() {
|
||||
let x = trailer_content.get(index).unwrap();
|
||||
for (index, x) in trailer_content.iter().enumerate() {
|
||||
out.write_all(x.as_bytes())?;
|
||||
if index + 1 != trailer_content.len() {
|
||||
out.write_all(line_separator)?;
|
||||
|
@ -1074,8 +1075,7 @@ fn write_columns(
|
|||
let mut offset = 0;
|
||||
for col in 0..columns {
|
||||
let mut inserted = 0;
|
||||
for i in offset..lines.len() {
|
||||
let line = lines.get(i).unwrap();
|
||||
for line in &lines[offset..] {
|
||||
if line.file_id != col {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -55,18 +55,9 @@ impl Formatter for Decf {
|
|||
);
|
||||
// strip trailing zeroes
|
||||
if let Some(ref post_dec) = f_sci.post_decimal {
|
||||
let mut i = post_dec.len();
|
||||
{
|
||||
let mut it = post_dec.chars();
|
||||
while let Some(c) = it.next_back() {
|
||||
if c != '0' {
|
||||
break;
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
if i != post_dec.len() {
|
||||
f_sci.post_decimal = Some(String::from(&post_dec[0..i]));
|
||||
let trimmed = post_dec.trim_end_matches('0');
|
||||
if trimmed.len() != post_dec.len() {
|
||||
f_sci.post_decimal = Some(trimmed.to_owned());
|
||||
}
|
||||
}
|
||||
let f_fl = get_primitive_dec(
|
||||
|
|
|
@ -247,8 +247,12 @@ pub fn get_primitive_dec(
|
|||
first_segment.len() as isize - 1,
|
||||
)
|
||||
} else {
|
||||
match first_segment.chars().next() {
|
||||
Some('0') => {
|
||||
match first_segment
|
||||
.chars()
|
||||
.next()
|
||||
.expect("float_common: no chars in first segment.")
|
||||
{
|
||||
'0' => {
|
||||
let it = second_segment.chars().enumerate();
|
||||
let mut m: isize = 0;
|
||||
let mut pre = String::from("0");
|
||||
|
@ -266,10 +270,7 @@ pub fn get_primitive_dec(
|
|||
}
|
||||
(pre, post, m)
|
||||
}
|
||||
Some(_) => (first_segment, second_segment, 0),
|
||||
None => {
|
||||
panic!("float_common: no chars in first segment.");
|
||||
}
|
||||
_ => (first_segment, second_segment, 0),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -109,17 +109,14 @@ fn remove(dirs: Vec<String>, ignore: bool, parents: bool, verbose: bool) -> Resu
|
|||
}
|
||||
|
||||
fn remove_dir(path: &Path, ignore: bool, verbose: bool) -> Result<(), i32> {
|
||||
let mut read_dir = match fs::read_dir(path) {
|
||||
Ok(m) => m,
|
||||
Err(e) if e.raw_os_error() == Some(ENOTDIR) => {
|
||||
let mut read_dir = fs::read_dir(path).map_err(|e| {
|
||||
if e.raw_os_error() == Some(ENOTDIR) {
|
||||
show_error!("failed to remove '{}': Not a directory", path.display());
|
||||
return Err(1);
|
||||
}
|
||||
Err(e) => {
|
||||
} else {
|
||||
show_error!("reading directory '{}': {}", path.display(), e);
|
||||
return Err(1);
|
||||
}
|
||||
};
|
||||
1
|
||||
})?;
|
||||
|
||||
let mut r = Ok(());
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ extern crate uucore;
|
|||
|
||||
static NAME: &str = "shred";
|
||||
const BLOCK_SIZE: usize = 512;
|
||||
const NAME_CHARSET: &str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_.";
|
||||
const NAME_CHARSET: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_.";
|
||||
|
||||
// Patterns as shown in the GNU coreutils shred implementation
|
||||
const PATTERNS: [&[u8]; 22] = [
|
||||
|
@ -89,7 +89,7 @@ impl Iterator for FilenameGenerator {
|
|||
// Make the return value, then increment
|
||||
let mut ret = String::new();
|
||||
for i in name_charset_indices.iter() {
|
||||
let c: char = NAME_CHARSET.chars().nth(*i).unwrap();
|
||||
let c = char::from(NAME_CHARSET[*i]);
|
||||
ret.push(c);
|
||||
}
|
||||
|
||||
|
@ -163,16 +163,14 @@ impl<'a> BytesGenerator<'a> {
|
|||
return None;
|
||||
}
|
||||
|
||||
let this_block_size = {
|
||||
if !self.exact {
|
||||
let this_block_size = if !self.exact {
|
||||
self.block_size
|
||||
} else {
|
||||
let bytes_left = self.total_bytes - self.bytes_generated.get();
|
||||
if bytes_left >= self.block_size as u64 {
|
||||
self.block_size
|
||||
} else {
|
||||
let bytes_left = self.total_bytes - self.bytes_generated.get();
|
||||
if bytes_left >= self.block_size as u64 {
|
||||
self.block_size
|
||||
} else {
|
||||
(bytes_left % self.block_size as u64) as usize
|
||||
}
|
||||
(bytes_left % self.block_size as u64) as usize
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -184,12 +182,10 @@ impl<'a> BytesGenerator<'a> {
|
|||
rng.fill(bytes);
|
||||
}
|
||||
PassType::Pattern(pattern) => {
|
||||
let skip = {
|
||||
if self.bytes_generated.get() == 0 {
|
||||
0
|
||||
} else {
|
||||
(pattern.len() as u64 % self.bytes_generated.get()) as usize
|
||||
}
|
||||
let skip = if self.bytes_generated.get() == 0 {
|
||||
0
|
||||
} else {
|
||||
(pattern.len() as u64 % self.bytes_generated.get()) as usize
|
||||
};
|
||||
|
||||
// Copy the pattern in chunks rather than simply one byte at a time
|
||||
|
|
|
@ -285,14 +285,12 @@ fn parse_range(input_range: &str) -> Result<(usize, usize), String> {
|
|||
if split.len() != 2 {
|
||||
Err(format!("invalid input range: '{}'", input_range))
|
||||
} else {
|
||||
let begin = match split[0].parse::<usize>() {
|
||||
Ok(m) => m,
|
||||
Err(_) => return Err(format!("invalid input range: '{}'", split[0])),
|
||||
};
|
||||
let end = match split[1].parse::<usize>() {
|
||||
Ok(m) => m,
|
||||
Err(_) => return Err(format!("invalid input range: '{}'", split[1])),
|
||||
};
|
||||
let begin = split[0]
|
||||
.parse::<usize>()
|
||||
.map_err(|_| format!("invalid input range: '{}'", split[0]))?;
|
||||
let end = split[1]
|
||||
.parse::<usize>()
|
||||
.map_err(|_| format!("invalid input range: '{}'", split[1]))?;
|
||||
Ok((begin, end + 1))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -175,9 +175,7 @@ impl GlobalSettings {
|
|||
];
|
||||
let mut size_string = input.trim().to_string();
|
||||
|
||||
if size_string.ends_with(|c: char| ALLOW_LIST.contains(&c))
|
||||
|| size_string.ends_with(|c: char| c.is_digit(10))
|
||||
{
|
||||
if size_string.ends_with(|c: char| ALLOW_LIST.contains(&c) || c.is_digit(10)) {
|
||||
// b 1, K 1024 (default)
|
||||
if size_string.ends_with(|c: char| c.is_digit(10)) {
|
||||
size_string.push('K');
|
||||
|
|
|
@ -16,7 +16,6 @@ path = "src/timeout.rs"
|
|||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
getopts = "0.2.18"
|
||||
libc = "0.2.42"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["process", "signals"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
|
|
@ -22,14 +22,15 @@ use std::ops::RangeInclusive;
|
|||
/// character; octal escape sequences consume 1 to 3 octal digits.
|
||||
#[inline]
|
||||
fn parse_sequence(s: &str) -> (char, usize) {
|
||||
let c = s.chars().next().expect("invalid escape: empty string");
|
||||
let mut s = s.chars();
|
||||
let c = s.next().expect("invalid escape: empty string");
|
||||
|
||||
if ('0'..='7').contains(&c) {
|
||||
let mut v = c.to_digit(8).unwrap();
|
||||
let mut consumed = 1;
|
||||
let bits_per_digit = 3;
|
||||
|
||||
for c in s.chars().skip(1).take(2) {
|
||||
for c in s.take(2) {
|
||||
match c.to_digit(8) {
|
||||
Some(c) => {
|
||||
v = (v << bits_per_digit) | c;
|
||||
|
|
|
@ -126,7 +126,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified")
|
||||
.value_name("SIZE")
|
||||
)
|
||||
.arg(Arg::with_name(options::ARG_FILES).value_name("FILE").multiple(true).takes_value(true).required(true).min_values(1))
|
||||
.arg(Arg::with_name(options::ARG_FILES)
|
||||
.value_name("FILE")
|
||||
.multiple(true)
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.min_values(1))
|
||||
.get_matches_from(args);
|
||||
|
||||
let files: Vec<String> = matches
|
||||
|
|
|
@ -17,6 +17,7 @@ path = "src/tty.rs"
|
|||
[dependencies]
|
||||
clap = "2.33"
|
||||
libc = "0.2.42"
|
||||
atty = "0.2"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ extern crate uucore;
|
|||
|
||||
use clap::{crate_version, App, Arg};
|
||||
use std::ffi::CStr;
|
||||
use uucore::fs::is_stdin_interactive;
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
static ABOUT: &str = "Print the file name of the terminal connected to standard input.";
|
||||
|
@ -67,7 +66,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
}
|
||||
}
|
||||
|
||||
if is_stdin_interactive() {
|
||||
if atty::is(atty::Stream::Stdin) {
|
||||
libc::EXIT_SUCCESS
|
||||
} else {
|
||||
libc::EXIT_FAILURE
|
||||
|
|
|
@ -179,124 +179,58 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
// Ignored for 'who am i'.
|
||||
let short_list = matches.is_present(options::COUNT);
|
||||
|
||||
// If true, display only name, line, and time fields.
|
||||
let mut short_output = false;
|
||||
|
||||
// If true, display the hours:minutes since each user has touched
|
||||
// the keyboard, or "." if within the last minute, or "old" if
|
||||
// not within the last day.
|
||||
let mut include_idle = false;
|
||||
let all = matches.is_present(options::ALL);
|
||||
|
||||
// If true, display a line at the top describing each field.
|
||||
let include_heading = matches.is_present(options::HEADING);
|
||||
|
||||
// If true, display a '+' for each user if mesg y, a '-' if mesg n,
|
||||
// or a '?' if their tty cannot be statted.
|
||||
let include_mesg = matches.is_present(options::ALL)
|
||||
|| matches.is_present(options::MESG)
|
||||
|| matches.is_present("w");
|
||||
|
||||
// If true, display process termination & exit status.
|
||||
let mut include_exit = false;
|
||||
let include_mesg = all || matches.is_present(options::MESG) || matches.is_present("w");
|
||||
|
||||
// If true, display the last boot time.
|
||||
let mut need_boottime = false;
|
||||
let need_boottime = all || matches.is_present(options::BOOT);
|
||||
|
||||
// If true, display dead processes.
|
||||
let mut need_deadprocs = false;
|
||||
let need_deadprocs = all || matches.is_present(options::DEAD);
|
||||
|
||||
// If true, display processes waiting for user login.
|
||||
let mut need_login = false;
|
||||
let need_login = all || matches.is_present(options::LOGIN);
|
||||
|
||||
// If true, display processes started by init.
|
||||
let mut need_initspawn = false;
|
||||
let need_initspawn = all || matches.is_present(options::PROCESS);
|
||||
|
||||
// If true, display the last clock change.
|
||||
let mut need_clockchange = false;
|
||||
let need_clockchange = all || matches.is_present(options::TIME);
|
||||
|
||||
// If true, display the current runlevel.
|
||||
let mut need_runlevel = false;
|
||||
let need_runlevel = all || matches.is_present(options::RUNLEVEL);
|
||||
|
||||
let use_defaults = !(all
|
||||
|| need_boottime
|
||||
|| need_deadprocs
|
||||
|| need_login
|
||||
|| need_initspawn
|
||||
|| need_runlevel
|
||||
|| need_clockchange
|
||||
|| matches.is_present(options::USERS));
|
||||
|
||||
// If true, display user processes.
|
||||
let mut need_users = false;
|
||||
let need_users = all || matches.is_present(options::USERS) || use_defaults;
|
||||
|
||||
// If true, display the hours:minutes since each user has touched
|
||||
// the keyboard, or "." if within the last minute, or "old" if
|
||||
// not within the last day.
|
||||
let include_idle = need_deadprocs || need_login || need_runlevel || need_users;
|
||||
|
||||
// If true, display process termination & exit status.
|
||||
let include_exit = need_deadprocs;
|
||||
|
||||
// If true, display only name, line, and time fields.
|
||||
let short_output = !include_exit && use_defaults;
|
||||
|
||||
// If true, display info only for the controlling tty.
|
||||
let mut my_line_only = false;
|
||||
|
||||
let mut assumptions = true;
|
||||
|
||||
#[allow(clippy::useless_let_if_seq)]
|
||||
{
|
||||
if matches.is_present(options::ALL) {
|
||||
need_boottime = true;
|
||||
need_deadprocs = true;
|
||||
need_login = true;
|
||||
need_initspawn = true;
|
||||
need_runlevel = true;
|
||||
need_clockchange = true;
|
||||
need_users = true;
|
||||
include_idle = true;
|
||||
include_exit = true;
|
||||
assumptions = false;
|
||||
}
|
||||
|
||||
if matches.is_present(options::BOOT) {
|
||||
need_boottime = true;
|
||||
assumptions = false;
|
||||
}
|
||||
|
||||
if matches.is_present(options::DEAD) {
|
||||
need_deadprocs = true;
|
||||
include_idle = true;
|
||||
include_exit = true;
|
||||
assumptions = false;
|
||||
}
|
||||
|
||||
if matches.is_present(options::LOGIN) {
|
||||
need_login = true;
|
||||
include_idle = true;
|
||||
assumptions = false;
|
||||
}
|
||||
|
||||
if matches.is_present(options::ONLY_HOSTNAME_USER) || files.len() == 2 {
|
||||
my_line_only = true;
|
||||
}
|
||||
|
||||
if matches.is_present(options::PROCESS) {
|
||||
need_initspawn = true;
|
||||
assumptions = false;
|
||||
}
|
||||
|
||||
if matches.is_present(options::RUNLEVEL) {
|
||||
need_runlevel = true;
|
||||
include_idle = true;
|
||||
assumptions = false;
|
||||
}
|
||||
|
||||
if matches.is_present(options::SHORT) {
|
||||
short_output = true;
|
||||
}
|
||||
|
||||
if matches.is_present(options::TIME) {
|
||||
need_clockchange = true;
|
||||
assumptions = false;
|
||||
}
|
||||
|
||||
if matches.is_present(options::USERS) {
|
||||
need_users = true;
|
||||
include_idle = true;
|
||||
assumptions = false;
|
||||
}
|
||||
|
||||
if assumptions {
|
||||
need_users = true;
|
||||
short_output = true;
|
||||
}
|
||||
|
||||
if include_exit {
|
||||
short_output = false;
|
||||
}
|
||||
}
|
||||
let my_line_only = matches.is_present(options::ONLY_HOSTNAME_USER) || files.len() == 2;
|
||||
|
||||
let mut who = Who {
|
||||
do_lookup,
|
||||
|
|
|
@ -20,7 +20,6 @@ uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["
|
|||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
advapi32-sys = "0.2.0"
|
||||
winapi = { version = "0.3", features = ["lmcons"] }
|
||||
|
||||
[[bin]]
|
||||
|
|
|
@ -11,7 +11,7 @@ extern crate winapi;
|
|||
|
||||
use self::winapi::shared::lmcons;
|
||||
use self::winapi::shared::minwindef;
|
||||
use self::winapi::um::winnt;
|
||||
use self::winapi::um::{winbase, winnt};
|
||||
use std::io::{Error, Result};
|
||||
use std::mem;
|
||||
use uucore::wide::FromWide;
|
||||
|
@ -20,7 +20,7 @@ pub unsafe fn get_username() -> Result<String> {
|
|||
#[allow(deprecated)]
|
||||
let mut buffer: [winnt::WCHAR; lmcons::UNLEN as usize + 1] = mem::uninitialized();
|
||||
let mut len = buffer.len() as minwindef::DWORD;
|
||||
if advapi32::GetUserNameW(buffer.as_mut_ptr(), &mut len) == 0 {
|
||||
if winbase::GetUserNameW(buffer.as_mut_ptr(), &mut len) == 0 {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
let username = String::from_wide(&buffer[..len as usize - 1]);
|
||||
|
|
|
@ -94,16 +94,21 @@ pub fn get_groups() -> IOResult<Vec<gid_t>> {
|
|||
/// groups is the same (in the mathematical sense of ``set''). (The
|
||||
/// history of a process and its parents could affect the details of
|
||||
/// the result.)
|
||||
#[cfg(all(unix, feature = "process"))]
|
||||
pub fn get_groups_gnu(arg_id: Option<u32>) -> IOResult<Vec<gid_t>> {
|
||||
let mut groups = get_groups()?;
|
||||
let groups = get_groups()?;
|
||||
let egid = arg_id.unwrap_or_else(crate::features::process::getegid);
|
||||
if !groups.is_empty() && *groups.first().unwrap() == egid {
|
||||
return Ok(groups);
|
||||
} else if let Some(index) = groups.iter().position(|&x| x == egid) {
|
||||
groups.remove(index);
|
||||
Ok(sort_groups(groups, egid))
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "process"))]
|
||||
fn sort_groups(mut groups: Vec<gid_t>, egid: gid_t) -> Vec<gid_t> {
|
||||
if let Some(index) = groups.iter().position(|&x| x == egid) {
|
||||
groups[..=index].rotate_right(1);
|
||||
} else {
|
||||
groups.insert(0, egid);
|
||||
}
|
||||
groups.insert(0, egid);
|
||||
Ok(groups)
|
||||
groups
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -308,6 +313,15 @@ pub fn grp2gid(name: &str) -> IOResult<gid_t> {
|
|||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_sort_groups() {
|
||||
assert_eq!(sort_groups(vec![1, 2, 3], 4), vec![4, 1, 2, 3]);
|
||||
assert_eq!(sort_groups(vec![1, 2, 3], 3), vec![3, 1, 2]);
|
||||
assert_eq!(sort_groups(vec![1, 2, 3], 2), vec![2, 1, 3]);
|
||||
assert_eq!(sort_groups(vec![1, 2, 3], 1), vec![1, 2, 3]);
|
||||
assert_eq!(sort_groups(vec![1, 2, 3], 0), vec![0, 1, 2, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_entries_get_groups_gnu() {
|
||||
if let Ok(mut groups) = get_groups() {
|
||||
|
|
|
@ -113,22 +113,14 @@ fn resolve<P: AsRef<Path>>(original: P) -> IOResult<PathBuf> {
|
|||
));
|
||||
}
|
||||
|
||||
match fs::symlink_metadata(&result) {
|
||||
Err(e) => return Err(e),
|
||||
Ok(ref m) if !m.file_type().is_symlink() => break,
|
||||
Ok(..) => {
|
||||
followed += 1;
|
||||
match fs::read_link(&result) {
|
||||
Ok(path) => {
|
||||
result.pop();
|
||||
result.push(path);
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !fs::symlink_metadata(&result)?.file_type().is_symlink() {
|
||||
break;
|
||||
}
|
||||
|
||||
followed += 1;
|
||||
let path = fs::read_link(&result)?;
|
||||
result.pop();
|
||||
result.push(path);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
@ -193,10 +185,8 @@ pub fn canonicalize<P: AsRef<Path>>(original: P, can_mode: CanonicalizeMode) ->
|
|||
}
|
||||
|
||||
match resolve(&result) {
|
||||
Err(e) => match can_mode {
|
||||
CanonicalizeMode::Missing => continue,
|
||||
_ => return Err(e),
|
||||
},
|
||||
Err(_) if can_mode == CanonicalizeMode::Missing => continue,
|
||||
Err(e) => return Err(e),
|
||||
Ok(path) => {
|
||||
result.pop();
|
||||
result.push(path);
|
||||
|
@ -211,65 +201,19 @@ pub fn canonicalize<P: AsRef<Path>>(original: P, can_mode: CanonicalizeMode) ->
|
|||
}
|
||||
|
||||
match resolve(&result) {
|
||||
Err(e) => {
|
||||
if can_mode == CanonicalizeMode::Existing {
|
||||
return Err(e);
|
||||
}
|
||||
Err(e) if can_mode == CanonicalizeMode::Existing => {
|
||||
return Err(e);
|
||||
}
|
||||
Ok(path) => {
|
||||
result.pop();
|
||||
result.push(path);
|
||||
}
|
||||
Err(_) => (),
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn is_stdin_interactive() -> bool {
|
||||
unsafe { libc::isatty(libc::STDIN_FILENO) == 1 }
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn is_stdin_interactive() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
pub fn is_stdin_interactive() -> bool {
|
||||
termion::is_tty(&io::stdin())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn is_stdout_interactive() -> bool {
|
||||
unsafe { libc::isatty(libc::STDOUT_FILENO) == 1 }
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn is_stdout_interactive() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
pub fn is_stdout_interactive() -> bool {
|
||||
termion::is_tty(&io::stdout())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn is_stderr_interactive() -> bool {
|
||||
unsafe { libc::isatty(libc::STDERR_FILENO) == 1 }
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn is_stderr_interactive() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
pub fn is_stderr_interactive() -> bool {
|
||||
termion::is_tty(&io::stderr())
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
#[allow(unused_variables)]
|
||||
pub fn display_permissions(metadata: &fs::Metadata, display_file_type: bool) -> String {
|
||||
|
|
|
@ -89,19 +89,19 @@ fn parse_levels(mode: &str) -> (u32, usize) {
|
|||
}
|
||||
|
||||
fn parse_op(mode: &str, default: Option<char>) -> Result<(char, usize), String> {
|
||||
match mode.chars().next() {
|
||||
Some(ch) => match ch {
|
||||
'+' | '-' | '=' => Ok((ch, 1)),
|
||||
_ => match default {
|
||||
Some(ch) => Ok((ch, 0)),
|
||||
None => Err(format!(
|
||||
"invalid operator (expected +, -, or =, but found {})",
|
||||
ch
|
||||
)),
|
||||
},
|
||||
},
|
||||
None => Err("unexpected end of mode".to_owned()),
|
||||
}
|
||||
let ch = mode
|
||||
.chars()
|
||||
.next()
|
||||
.ok_or_else(|| "unexpected end of mode".to_owned())?;
|
||||
Ok(match ch {
|
||||
'+' | '-' | '=' => (ch, 1),
|
||||
_ => {
|
||||
let ch = default.ok_or_else(|| {
|
||||
format!("invalid operator (expected +, -, or =, but found {})", ch)
|
||||
})?;
|
||||
(ch, 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_change(mode: &str, fperm: u32, considering_dir: bool) -> (u32, usize) {
|
||||
|
|
|
@ -85,10 +85,9 @@ impl Range {
|
|||
let mut ranges: Vec<Range> = vec![];
|
||||
|
||||
for item in list.split(',') {
|
||||
match FromStr::from_str(item) {
|
||||
Ok(range_item) => ranges.push(range_item),
|
||||
Err(e) => return Err(format!("range '{}' was invalid: {}", item, e)),
|
||||
}
|
||||
let range_item = FromStr::from_str(item)
|
||||
.map_err(|e| format!("range '{}' was invalid: {}", item, e))?;
|
||||
ranges.push(range_item);
|
||||
}
|
||||
|
||||
ranges.sort();
|
||||
|
|
|
@ -20,20 +20,18 @@ pub fn from_str(string: &str) -> Result<Duration, String> {
|
|||
'm' | 'M' => (slice, 60),
|
||||
'h' | 'H' => (slice, 60 * 60),
|
||||
'd' | 'D' => (slice, 60 * 60 * 24),
|
||||
val => {
|
||||
if !val.is_alphabetic() {
|
||||
(string, 1)
|
||||
} else if string == "inf" || string == "infinity" {
|
||||
val if !val.is_alphabetic() => (string, 1),
|
||||
_ => {
|
||||
if string == "inf" || string == "infinity" {
|
||||
("inf", 1)
|
||||
} else {
|
||||
return Err(format!("invalid time interval '{}'", string));
|
||||
}
|
||||
}
|
||||
};
|
||||
let num = match numstr.parse::<f64>() {
|
||||
Ok(m) => m,
|
||||
Err(e) => return Err(format!("invalid time interval '{}': {}", string, e)),
|
||||
};
|
||||
let num = numstr
|
||||
.parse::<f64>()
|
||||
.map_err(|e| format!("invalid time interval '{}': {}", string, e))?;
|
||||
|
||||
const NANOS_PER_SEC: u32 = 1_000_000_000;
|
||||
let whole_secs = num.trunc();
|
||||
|
|
|
@ -334,3 +334,20 @@ fn _du_no_permission(s: &str) {
|
|||
fn _du_no_permission(s: &str) {
|
||||
assert_eq!(s, "4\tsubdir/links\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_du_one_file_system() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
let result = scene.ucmd().arg("-x").arg(SUB_DIR).succeeds();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let result_reference = scene.cmd("du").arg("-x").arg(SUB_DIR).run();
|
||||
if result_reference.succeeded() {
|
||||
assert_eq!(result.stdout_str(), result_reference.stdout_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
_du_basics_subdir(result.stdout_str());
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::common::util::*;
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
|
||||
#[cfg(unix)]
|
||||
fn test_groups() {
|
||||
if !is_ci() {
|
||||
new_ucmd!().succeeds().stdout_is(expected_result(&[]));
|
||||
|
@ -13,7 +13,7 @@ fn test_groups() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux"))]
|
||||
#[cfg(unix)]
|
||||
#[ignore = "fixme: 'groups USERNAME' needs more debugging"]
|
||||
fn test_groups_username() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
@ -37,17 +37,20 @@ fn test_groups_username() {
|
|||
.stdout_is(expected_result(&[&username]));
|
||||
}
|
||||
|
||||
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
|
||||
#[cfg(unix)]
|
||||
fn expected_result(args: &[&str]) -> String {
|
||||
#[cfg(target_os = "linux")]
|
||||
let util_name = util_name!();
|
||||
#[cfg(target_vendor = "apple")]
|
||||
let util_name = format!("g{}", util_name!());
|
||||
// We want to use GNU id. On most linux systems, this is "id", but on
|
||||
// bsd-like systems (e.g. FreeBSD, MacOS), it is commonly "gid".
|
||||
#[cfg(any(target_os = "linux"))]
|
||||
let util_name = "id";
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let util_name = "gid";
|
||||
|
||||
TestScenario::new(&util_name)
|
||||
TestScenario::new(util_name)
|
||||
.cmd_keepenv(util_name)
|
||||
.env("LANGUAGE", "C")
|
||||
.args(args)
|
||||
.args(&["-Gn"])
|
||||
.succeeds()
|
||||
.stdout_move_str()
|
||||
}
|
||||
|
|
|
@ -69,6 +69,22 @@ pub struct CmdResult {
|
|||
}
|
||||
|
||||
impl CmdResult {
|
||||
pub fn new(
|
||||
tmpd: Option<Rc<TempDir>>,
|
||||
code: Option<i32>,
|
||||
success: bool,
|
||||
stdout: &[u8],
|
||||
stderr: &[u8],
|
||||
) -> CmdResult {
|
||||
CmdResult {
|
||||
tmpd,
|
||||
code,
|
||||
success,
|
||||
stdout: stdout.to_vec(),
|
||||
stderr: stderr.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the program's standard output as a slice of bytes
|
||||
pub fn stdout(&self) -> &[u8] {
|
||||
&self.stdout
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
set -e
|
||||
if test ! -d ../gnu; then
|
||||
echo "Could not find ../gnu"
|
||||
echo "git clone git@github.com:coreutils/coreutils.git ../gnu"
|
||||
echo "git clone git@github.com:coreutils/coreutils.git gnu"
|
||||
exit 1
|
||||
fi
|
||||
if test ! -d ../gnulib; then
|
||||
echo "Could not find ../gnulib"
|
||||
echo "git clone git@github.com:coreutils/gnulib.git ../gnulib"
|
||||
echo "git clone git@github.com:coreutils/gnulib.git gnulib"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
Loading…
Reference in a new issue