Skip to content

Commit 201fbd9

Browse files
committed
ps: Implement bunch of field selection flags
1 parent 831878a commit 201fbd9

File tree

3 files changed

+198
-2
lines changed

3 files changed

+198
-2
lines changed

src/uu/ps/src/mapping.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,63 @@ pub(crate) fn default_codes() -> Vec<String> {
2626
["pid", "tname", "time", "ucmd"].map(Into::into).to_vec()
2727
}
2828

29+
/// Returns the full format codes (for -f flag).
30+
pub(crate) fn full_format_codes() -> Vec<String> {
31+
["user", "pid", "ppid", "c", "stime", "tname", "time", "cmd"]
32+
.map(Into::into)
33+
.to_vec()
34+
}
35+
36+
/// Returns the extra full format codes (for -F flag).
37+
pub(crate) fn extra_full_format_codes() -> Vec<String> {
38+
[
39+
"uid", "pid", "ppid", "c", "sz", "rss", "psr", "stime", "tname", "time", "ucmd",
40+
]
41+
.map(Into::into)
42+
.to_vec()
43+
}
44+
45+
/// Returns the job format codes (for -j flag).
46+
pub(crate) fn job_format_codes() -> Vec<String> {
47+
["pid", "pgid", "sid", "tname", "time", "ucmd"]
48+
.map(Into::into)
49+
.to_vec()
50+
}
51+
52+
/// Returns the default codes with PSR column (for -P flag).
53+
pub(crate) fn default_with_psr_codes() -> Vec<String> {
54+
["pid", "psr", "tname", "time", "ucmd"]
55+
.map(Into::into)
56+
.to_vec()
57+
}
58+
59+
/// Returns the signal format codes (for -s flag).
60+
pub(crate) fn signal_format_codes() -> Vec<String> {
61+
[
62+
"uid", "pid", "pending", "blocked", "ignored", "caught", "stat", "tname", "time", "command",
63+
]
64+
.map(Into::into)
65+
.to_vec()
66+
}
67+
68+
/// Returns the user format codes (for -u flag).
69+
pub(crate) fn user_format_codes() -> Vec<String> {
70+
[
71+
"user", "pid", "%cpu", "%mem", "vsz", "rss", "tname", "stat", "bsdstart", "time", "command",
72+
]
73+
.map(Into::into)
74+
.to_vec()
75+
}
76+
77+
/// Returns the virtual memory format codes (for -v flag).
78+
pub(crate) fn vm_format_codes() -> Vec<String> {
79+
[
80+
"pid", "tname", "stat", "time", "maj_flt", "trs", "drs", "rss", "%mem", "command",
81+
]
82+
.map(Into::into)
83+
.to_vec()
84+
}
85+
2986
/// Collect mapping from argument
3087
pub(crate) fn default_mapping() -> HashMap<String, String> {
3188
let mut mapping = HashMap::new();

src/uu/ps/src/ps.rs

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ mod sorting;
1111

1212
use clap::crate_version;
1313
use clap::{Arg, ArgAction, ArgMatches, Command};
14-
use mapping::{collect_code_mapping, default_codes, default_mapping};
14+
use mapping::{
15+
collect_code_mapping, default_codes, default_mapping, default_with_psr_codes,
16+
extra_full_format_codes, full_format_codes, job_format_codes, signal_format_codes,
17+
user_format_codes, vm_format_codes,
18+
};
1519
use parser::{parser, OptionalKeyValue};
1620
use prettytable::{format::consts::FORMAT_CLEAN, Row, Table};
1721
use std::{cell::RefCell, rc::Rc};
@@ -47,7 +51,21 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
4751
};
4852

4953
// Collect codes with order
50-
let codes = if arg_formats.is_empty() {
54+
let codes = if matches.get_flag("f") {
55+
full_format_codes()
56+
} else if matches.get_flag("F") {
57+
extra_full_format_codes()
58+
} else if matches.get_flag("j") {
59+
job_format_codes()
60+
} else if matches.get_flag("P") {
61+
default_with_psr_codes()
62+
} else if matches.get_flag("s") {
63+
signal_format_codes()
64+
} else if matches.get_flag("u") {
65+
user_format_codes()
66+
} else if matches.get_flag("v") {
67+
vm_format_codes()
68+
} else if arg_formats.is_empty() {
5169
default_codes()
5270
} else {
5371
arg_formats.iter().map(|it| it.key().to_owned()).collect()
@@ -164,6 +182,49 @@ pub fn uu_app() -> Command {
164182
// .help("processes without controlling ttys")
165183
// .allow_hyphen_values(true),
166184
])
185+
.arg(
186+
Arg::new("f")
187+
.short('f')
188+
.action(ArgAction::SetTrue)
189+
.help("full format listing"),
190+
)
191+
.arg(
192+
Arg::new("F")
193+
.short('F')
194+
.action(ArgAction::SetTrue)
195+
.help("extra full format listing"),
196+
)
197+
.arg(
198+
Arg::new("j")
199+
.short('j')
200+
.action(ArgAction::SetTrue)
201+
.help("job format"),
202+
)
203+
.arg(
204+
Arg::new("P")
205+
.short('P')
206+
.action(ArgAction::SetTrue)
207+
.help("add psr column"),
208+
)
209+
.arg(
210+
Arg::new("s")
211+
.short('s')
212+
.action(ArgAction::SetTrue)
213+
.help("signal format"),
214+
)
215+
// TODO: this can also be used with argument to filter by uid
216+
.arg(
217+
Arg::new("u")
218+
.short('u')
219+
.action(ArgAction::SetTrue)
220+
.help("user format"),
221+
)
222+
.arg(
223+
Arg::new("v")
224+
.short('v')
225+
.action(ArgAction::SetTrue)
226+
.help("virtual memory format"),
227+
)
167228
.arg(
168229
Arg::new("format")
169230
.short('o')

tests/by-util/test_ps.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,84 @@ fn test_invalid_arg() {
2121
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
2222
}
2323

24+
/// Helper function to check that ps output has the correct headers in the correct order
25+
#[cfg(target_os = "linux")]
26+
fn check_header(flag: &str, expected_headers: &[&str]) {
27+
let result = new_ucmd!().arg(flag).succeeds();
28+
let lines: Vec<&str> = result.stdout_str().lines().collect();
29+
let headers: Vec<&str> = lines[0].split_whitespace().collect();
30+
31+
assert_eq!(headers, expected_headers);
32+
}
33+
34+
#[test]
35+
#[cfg(target_os = "linux")]
36+
fn test_full_format_listing() {
37+
// TODO: Upstream `ps -f` shows username but UID in the header
38+
check_header(
39+
"-f",
40+
&["USER", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"],
41+
);
42+
}
43+
44+
#[test]
45+
#[cfg(target_os = "linux")]
46+
fn test_extra_full_format() {
47+
check_header(
48+
"-F",
49+
&[
50+
"UID", "PID", "PPID", "C", "SZ", "RSS", "PSR", "STIME", "TTY", "TIME", "CMD",
51+
],
52+
);
53+
}
54+
55+
#[test]
56+
#[cfg(target_os = "linux")]
57+
fn test_job_format() {
58+
check_header("-j", &["PID", "PGID", "SID", "TTY", "TIME", "CMD"]);
59+
}
60+
61+
#[test]
62+
#[cfg(target_os = "linux")]
63+
fn test_psr_format() {
64+
check_header("-P", &["PID", "PSR", "TTY", "TIME", "CMD"]);
65+
}
66+
67+
#[test]
68+
#[cfg(target_os = "linux")]
69+
fn test_signal_format() {
70+
check_header(
71+
"-s",
72+
&[
73+
"UID", "PID", "PENDING", "BLOCKED", "IGNORED", "CAUGHT", "STAT", "TTY", "TIME",
74+
"COMMAND",
75+
],
76+
);
77+
}
78+
79+
#[test]
80+
#[cfg(target_os = "linux")]
81+
fn test_user_format() {
82+
check_header(
83+
"-u",
84+
&[
85+
"USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", "STAT", "START", "TIME", "COMMAND",
86+
],
87+
);
88+
}
89+
90+
#[test]
91+
#[cfg(target_os = "linux")]
92+
fn test_virtual_memory_format() {
93+
// TODO: Upstream `ps -v` shows MAJFL instead of MAJFLT
94+
check_header(
95+
"-v",
96+
&[
97+
"PID", "TTY", "STAT", "TIME", "MAJFLT", "TRS", "DRS", "RSS", "%MEM", "COMMAND",
98+
],
99+
);
100+
}
101+
24102
#[test]
25103
#[cfg(target_os = "linux")]
26104
fn test_code_mapping() {

0 commit comments

Comments
 (0)