mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
id: implement '--zero' flag
* add tests for '--zero' flag * add a bunch of requires/conflicts rules for flags (incl. tests)
This commit is contained in:
parent
74a7da7b52
commit
98225105af
2 changed files with 157 additions and 22 deletions
|
@ -13,8 +13,10 @@
|
||||||
// This is not based on coreutils (8.32) GNU's `id`.
|
// This is not based on coreutils (8.32) GNU's `id`.
|
||||||
// This is based on BSD's `id` (noticeable in functionality, usage text, options text, etc.)
|
// This is based on BSD's `id` (noticeable in functionality, usage text, options text, etc.)
|
||||||
//
|
//
|
||||||
|
// Option '--zero' does not exist for BSD's `id`, therefor '--zero' is only allowed together
|
||||||
|
// with other options that are available on GNU's `id`.
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) asid auditid auditinfo auid cstr egid emod euid getaudit getlogin gflag nflag pline rflag termid uflag
|
// spell-checker:ignore (ToDO) asid auditid auditinfo auid cstr egid emod euid getaudit getlogin gflag nflag pline rflag termid uflag gsflag zflag
|
||||||
|
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
@ -85,6 +87,7 @@ mod options {
|
||||||
pub const OPT_NAME: &str = "name";
|
pub const OPT_NAME: &str = "name";
|
||||||
pub const OPT_PASSWORD: &str = "password"; // GNU's id does not have this
|
pub const OPT_PASSWORD: &str = "password"; // GNU's id does not have this
|
||||||
pub const OPT_REAL_ID: &str = "real";
|
pub const OPT_REAL_ID: &str = "real";
|
||||||
|
pub const OPT_ZERO: &str = "zero"; // BSD's id does not have this
|
||||||
pub const ARG_USERS: &str = "USER";
|
pub const ARG_USERS: &str = "USER";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,26 +105,28 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::OPT_AUDIT)
|
Arg::with_name(options::OPT_AUDIT)
|
||||||
.short("A")
|
.short("A")
|
||||||
|
.conflicts_with_all(&[options::OPT_GROUP, options::OPT_EFFECTIVE_USER, options::OPT_HUMAN_READABLE, options::OPT_PASSWORD, options::OPT_GROUPS, options::OPT_ZERO])
|
||||||
.help("Display the process audit user ID and other process audit properties, which requires privilege (not available on Linux)."),
|
.help("Display the process audit user ID and other process audit properties, which requires privilege (not available on Linux)."),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::OPT_EFFECTIVE_USER)
|
Arg::with_name(options::OPT_EFFECTIVE_USER)
|
||||||
.short("u")
|
.short("u")
|
||||||
.long(options::OPT_EFFECTIVE_USER)
|
.long(options::OPT_EFFECTIVE_USER)
|
||||||
.help("Display the effective user ID as a number."),
|
.conflicts_with(options::OPT_GROUP)
|
||||||
|
.help("Display only the effective user ID as a number."),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::OPT_GROUP)
|
Arg::with_name(options::OPT_GROUP)
|
||||||
.short("g")
|
.short("g")
|
||||||
.long(options::OPT_GROUP)
|
.long(options::OPT_GROUP)
|
||||||
.help("Display the effective group ID as a number"),
|
.help("Display only the effective group ID as a number"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::OPT_GROUPS)
|
Arg::with_name(options::OPT_GROUPS)
|
||||||
.short("G")
|
.short("G")
|
||||||
.long(options::OPT_GROUPS)
|
.long(options::OPT_GROUPS)
|
||||||
.conflicts_with_all(&[options::OPT_GROUP, options::OPT_EFFECTIVE_USER, options::OPT_HUMAN_READABLE, options::OPT_PASSWORD, options::OPT_AUDIT])
|
.conflicts_with_all(&[options::OPT_GROUP, options::OPT_EFFECTIVE_USER, options::OPT_HUMAN_READABLE, options::OPT_PASSWORD, options::OPT_AUDIT])
|
||||||
.help("Display the different group IDs as white-space separated numbers, in no particular order."),
|
.help("Display only the different group IDs as white-space separated numbers, in no particular order."),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::OPT_HUMAN_READABLE)
|
Arg::with_name(options::OPT_HUMAN_READABLE)
|
||||||
|
@ -145,6 +150,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.long(options::OPT_REAL_ID)
|
.long(options::OPT_REAL_ID)
|
||||||
.help("Display the real ID for the -g and -u options instead of the effective ID."),
|
.help("Display the real ID for the -g and -u options instead of the effective ID."),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OPT_ZERO)
|
||||||
|
.short("z")
|
||||||
|
.long(options::OPT_ZERO)
|
||||||
|
.help("delimit entries with NUL characters, not whitespace;\nnot permitted in default format"),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(options::ARG_USERS)
|
Arg::with_name(options::ARG_USERS)
|
||||||
.multiple(true)
|
.multiple(true)
|
||||||
|
@ -158,11 +169,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let gflag = matches.is_present(options::OPT_GROUP);
|
let gflag = matches.is_present(options::OPT_GROUP);
|
||||||
let gsflag = matches.is_present(options::OPT_GROUPS);
|
let gsflag = matches.is_present(options::OPT_GROUPS);
|
||||||
let rflag = matches.is_present(options::OPT_REAL_ID);
|
let rflag = matches.is_present(options::OPT_REAL_ID);
|
||||||
|
let zflag = matches.is_present(options::OPT_ZERO);
|
||||||
|
|
||||||
// -ugG
|
// "default format" is when none of '-ugG' was used
|
||||||
|
// could not implement these "required" rules with just clap
|
||||||
if (nflag || rflag) && !(uflag || gflag || gsflag) {
|
if (nflag || rflag) && !(uflag || gflag || gsflag) {
|
||||||
crash!(1, "cannot print only names or real IDs in default format");
|
crash!(1, "cannot print only names or real IDs in default format");
|
||||||
}
|
}
|
||||||
|
if (zflag) && !(uflag || gflag || gsflag) {
|
||||||
|
crash!(1, "option --zero not permitted in default format");
|
||||||
|
}
|
||||||
|
|
||||||
let users: Vec<String> = matches
|
let users: Vec<String> = matches
|
||||||
.values_of(options::ARG_USERS)
|
.values_of(options::ARG_USERS)
|
||||||
|
@ -183,17 +199,20 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let line_ending = if zflag { '\0' } else { '\n' };
|
||||||
|
|
||||||
if gflag {
|
if gflag {
|
||||||
let id = possible_pw
|
let id = possible_pw
|
||||||
.map(|p| p.gid())
|
.map(|p| p.gid())
|
||||||
.unwrap_or(if rflag { getgid() } else { getegid() });
|
.unwrap_or(if rflag { getgid() } else { getegid() });
|
||||||
println!(
|
print!(
|
||||||
"{}",
|
"{}{}",
|
||||||
if nflag {
|
if nflag {
|
||||||
entries::gid2grp(id).unwrap_or_else(|_| id.to_string())
|
entries::gid2grp(id).unwrap_or_else(|_| id.to_string())
|
||||||
} else {
|
} else {
|
||||||
id.to_string()
|
id.to_string()
|
||||||
}
|
},
|
||||||
|
line_ending
|
||||||
);
|
);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -202,20 +221,22 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let id = possible_pw
|
let id = possible_pw
|
||||||
.map(|p| p.uid())
|
.map(|p| p.uid())
|
||||||
.unwrap_or(if rflag { getuid() } else { geteuid() });
|
.unwrap_or(if rflag { getuid() } else { geteuid() });
|
||||||
println!(
|
print!(
|
||||||
"{}",
|
"{}{}",
|
||||||
if nflag {
|
if nflag {
|
||||||
entries::uid2usr(id).unwrap_or_else(|_| id.to_string())
|
entries::uid2usr(id).unwrap_or_else(|_| id.to_string())
|
||||||
} else {
|
} else {
|
||||||
id.to_string()
|
id.to_string()
|
||||||
}
|
},
|
||||||
|
line_ending
|
||||||
);
|
);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if gsflag {
|
if gsflag {
|
||||||
println!(
|
let delimiter = if zflag { "" } else { " " };
|
||||||
"{}",
|
print!(
|
||||||
|
"{}{}",
|
||||||
if nflag {
|
if nflag {
|
||||||
possible_pw
|
possible_pw
|
||||||
.map(|p| p.belongs_to())
|
.map(|p| p.belongs_to())
|
||||||
|
@ -223,7 +244,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&id| entries::gid2grp(id).unwrap())
|
.map(|&id| entries::gid2grp(id).unwrap())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" ")
|
.join(delimiter)
|
||||||
} else {
|
} else {
|
||||||
possible_pw
|
possible_pw
|
||||||
.map(|p| p.belongs_to())
|
.map(|p| p.belongs_to())
|
||||||
|
@ -231,8 +252,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&id| id.to_string())
|
.map(|&id| id.to_string())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" ")
|
.join(delimiter)
|
||||||
}
|
},
|
||||||
|
line_ending
|
||||||
);
|
);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -398,3 +420,5 @@ fn id_print(possible_pw: Option<Passwd>, p_euid: bool, p_egid: bool) {
|
||||||
.join(",")
|
.join(",")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_groups() ->
|
||||||
|
|
|
@ -96,11 +96,19 @@ fn test_id_group() {
|
||||||
|
|
||||||
let mut result = scene.ucmd().arg("-g").succeeds();
|
let mut result = scene.ucmd().arg("-g").succeeds();
|
||||||
let s1 = result.stdout_str().trim();
|
let s1 = result.stdout_str().trim();
|
||||||
assert!(s1.parse::<f64>().is_ok());
|
assert!(s1.parse::<u64>().is_ok());
|
||||||
|
|
||||||
result = scene.ucmd().arg("--group").succeeds();
|
result = scene.ucmd().arg("--group").succeeds();
|
||||||
let s1 = result.stdout_str().trim();
|
let s1 = result.stdout_str().trim();
|
||||||
assert!(s1.parse::<f64>().is_ok());
|
assert!(s1.parse::<u64>().is_ok());
|
||||||
|
|
||||||
|
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
|
||||||
|
for flag in &["-g", "--group"] {
|
||||||
|
new_ucmd!()
|
||||||
|
.arg(flag)
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is(expected_result(&[flag], false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -110,13 +118,22 @@ fn test_id_groups() {
|
||||||
let result = scene.ucmd().arg("-G").succeeds();
|
let result = scene.ucmd().arg("-G").succeeds();
|
||||||
let groups = result.stdout_str().trim().split_whitespace();
|
let groups = result.stdout_str().trim().split_whitespace();
|
||||||
for s in groups {
|
for s in groups {
|
||||||
assert!(s.parse::<f64>().is_ok());
|
assert!(s.parse::<u64>().is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = scene.ucmd().arg("--groups").succeeds();
|
let result = scene.ucmd().arg("--groups").succeeds();
|
||||||
let groups = result.stdout_str().trim().split_whitespace();
|
let groups = result.stdout_str().trim().split_whitespace();
|
||||||
for s in groups {
|
for s in groups {
|
||||||
assert!(s.parse::<f64>().is_ok());
|
assert!(s.parse::<u64>().is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
|
||||||
|
for args in &["-G", "--groups"] {
|
||||||
|
let expect = expected_result(&[args], false);
|
||||||
|
let actual = new_ucmd!().arg(&args).succeeds().stdout_move_str();
|
||||||
|
let mut v_actual: Vec<&str> = actual.split_whitespace().collect();
|
||||||
|
let mut v_expect: Vec<&str> = expect.split_whitespace().collect();
|
||||||
|
assert_eq!(v_actual.sort_unstable(), v_expect.sort_unstable());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,11 +143,19 @@ fn test_id_user() {
|
||||||
|
|
||||||
let result = scene.ucmd().arg("-u").succeeds();
|
let result = scene.ucmd().arg("-u").succeeds();
|
||||||
let s1 = result.stdout_str().trim();
|
let s1 = result.stdout_str().trim();
|
||||||
assert!(s1.parse::<f64>().is_ok());
|
assert!(s1.parse::<u64>().is_ok());
|
||||||
|
|
||||||
let result = scene.ucmd().arg("--user").succeeds();
|
let result = scene.ucmd().arg("--user").succeeds();
|
||||||
let s1 = result.stdout_str().trim();
|
let s1 = result.stdout_str().trim();
|
||||||
assert!(s1.parse::<f64>().is_ok());
|
assert!(s1.parse::<u64>().is_ok());
|
||||||
|
|
||||||
|
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
|
||||||
|
for flag in &["-u", "--user"] {
|
||||||
|
new_ucmd!()
|
||||||
|
.arg(flag)
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is(expected_result(&[flag], false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -167,3 +192,89 @@ fn test_id_password_style() {
|
||||||
|
|
||||||
assert!(result.stdout_str().starts_with(&username));
|
assert!(result.stdout_str().starts_with(&username));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
|
||||||
|
fn test_id_default_format() {
|
||||||
|
// -ugG
|
||||||
|
for flag in &["--name", "--real"] {
|
||||||
|
new_ucmd!()
|
||||||
|
.arg(flag)
|
||||||
|
.fails()
|
||||||
|
.stderr_is(expected_result(&[flag], true));
|
||||||
|
for &opt in &["--user", "--group", "--groups"] {
|
||||||
|
if is_ci() && *flag == "--name" {
|
||||||
|
// '--name' does not work in CI:
|
||||||
|
// id: cannot find name for user ID 1001
|
||||||
|
// id: cannot find name for group ID 116
|
||||||
|
println!("test skipped:");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let args = [opt, flag];
|
||||||
|
let expect = expected_result(&args, false);
|
||||||
|
let actual = new_ucmd!().args(&args).succeeds().stdout_move_str();
|
||||||
|
let mut v_actual: Vec<&str> = actual.split_whitespace().collect();
|
||||||
|
let mut v_expect: Vec<&str> = expect.split_whitespace().collect();
|
||||||
|
assert_eq!(v_actual.sort_unstable(), v_expect.sort_unstable());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
|
||||||
|
fn test_id_zero() {
|
||||||
|
for z_flag in &["-z", "--zero"] {
|
||||||
|
for &opt in &["-n", "--name", "-r", "--real"] {
|
||||||
|
let args = [opt, z_flag];
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&args)
|
||||||
|
.fails()
|
||||||
|
.stderr_is(expected_result(&args, true));
|
||||||
|
}
|
||||||
|
for &opt in &["-u", "--user", "-g", "--group"] {
|
||||||
|
let args = [opt, z_flag];
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&args)
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is(expected_result(&args, false));
|
||||||
|
}
|
||||||
|
// '--groups' ids are in no particular order and when paired with '--zero' there's no
|
||||||
|
// delimiter which makes the split_whitespace-collect-into-vector comparison impossible.
|
||||||
|
for opt in &["-G", "--groups"] {
|
||||||
|
let args = [opt, z_flag];
|
||||||
|
let result = new_ucmd!().args(&args).succeeds().stdout_move_str();
|
||||||
|
assert!(!result.contains(" "));
|
||||||
|
assert!(result.ends_with('\0'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
|
||||||
|
fn expected_result(args: &[&str], exp_fail: bool) -> String {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
let util_name = util_name!();
|
||||||
|
#[cfg(target_vendor = "apple")]
|
||||||
|
let util_name = format!("g{}", util_name!());
|
||||||
|
|
||||||
|
let result = if !exp_fail {
|
||||||
|
TestScenario::new(&util_name)
|
||||||
|
.cmd_keepenv(util_name)
|
||||||
|
.env("LANGUAGE", "C")
|
||||||
|
.args(args)
|
||||||
|
.succeeds()
|
||||||
|
.stdout_move_str()
|
||||||
|
} else {
|
||||||
|
TestScenario::new(&util_name)
|
||||||
|
.cmd_keepenv(util_name)
|
||||||
|
.env("LANGUAGE", "C")
|
||||||
|
.args(args)
|
||||||
|
.fails()
|
||||||
|
.stderr_move_str()
|
||||||
|
};
|
||||||
|
// #[cfg(target_vendor = "apple")]
|
||||||
|
return if cfg!(target_os = "macos") && result.starts_with("gid") {
|
||||||
|
result[1..].to_string()
|
||||||
|
} else {
|
||||||
|
result
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue