Skip to content

Commit 15b9428

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

3 files changed

Lines changed: 197 additions & 2 deletions

File tree

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

0 commit comments

Comments
 (0)