Skip to content

Commit 1cb6145

Browse files
committed
ps: flexible output field selection (WIP)
1 parent c5274c5 commit 1cb6145

File tree

1 file changed

+88
-8
lines changed

1 file changed

+88
-8
lines changed

sys/ps.rs

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod psmacos;
1414
mod pslinux;
1515

1616
use clap::Parser;
17+
use std::collections::HashMap;
1718

1819
#[cfg(target_os = "macos")]
1920
mod platform {
@@ -44,6 +45,57 @@ struct Args {
4445
/// Exclude session leaders
4546
#[arg(short = 'd', long)]
4647
exclude_session_leaders: bool,
48+
49+
/// Full output format (-f)
50+
#[arg(short = 'f', long)]
51+
full_format: bool,
52+
53+
/// Long output format (-l)
54+
#[arg(short = 'l', long)]
55+
long_format: bool,
56+
57+
/// Custom output format (-o)
58+
#[arg(short = 'o', long, value_parser = clap::builder::NonEmptyStringValueParser::new())]
59+
output_format: Option<String>,
60+
}
61+
62+
// Parse the -o format option into a list of fields
63+
fn parse_output_format<'a>(
64+
format: &'a str,
65+
posix_fields: &'a HashMap<&'a str, (&'a str, &'a str)>,
66+
) -> Vec<&'a str> {
67+
format
68+
.split(|c| c == ' ' || c == ',')
69+
.map(|s| {
70+
let field = s.split('=').next().unwrap_or("").trim();
71+
if posix_fields.contains_key(field) {
72+
field
73+
} else {
74+
panic!("Invalid field specified in -o option: {}", field);
75+
}
76+
})
77+
.collect()
78+
}
79+
80+
// Lookup table for POSIX-compliant output fields
81+
fn posix_field_map() -> HashMap<&'static str, (&'static str, &'static str)> {
82+
HashMap::from([
83+
("ruser", ("uid", "RUSER")),
84+
("user", ("uid", "USER")),
85+
("rgroup", ("gid", "RGROUP")),
86+
("group", ("gid", "GROUP")),
87+
("pid", ("pid", "PID")),
88+
("ppid", ("ppid", "PPID")),
89+
("pgid", ("pgid", "PGID")),
90+
("pcpu", ("pcpu", "%CPU")),
91+
("vsz", ("vsz", "VSZ")),
92+
("nice", ("nice", "NI")),
93+
("etime", ("etime", "ELAPSED")),
94+
("time", ("time", "TIME")),
95+
("tty", ("tty", "TTY")),
96+
("comm", ("comm", "COMMAND")),
97+
("args", ("args", "COMMAND")),
98+
])
4799
}
48100

49101
fn main() {
@@ -80,14 +132,42 @@ fn main() {
80132
processes
81133
};
82134

83-
println!(
84-
"{:<5} {:<5} {:<5} {:<5} {}",
85-
"PID", "PPID", "UID", "GID", "COMMAND"
86-
);
135+
// Define a lookup table for POSIX-compliant fields
136+
let posix_fields = posix_field_map();
137+
138+
// Build output based on -o, -f, -l, or default
139+
let output_fields = if let Some(ref format) = args.output_format {
140+
parse_output_format(format, &posix_fields)
141+
} else if args.full_format {
142+
vec!["uid", "pid", "ppid", "C", "time", "comm"]
143+
} else if args.long_format {
144+
vec!["nice", "vsz", "WCHAN", "tty", "comm"]
145+
} else {
146+
vec!["pid", "ppid", "tty", "time", "comm"] // Default format
147+
};
148+
149+
// Print the header
150+
for field in &output_fields {
151+
let header = posix_fields.get(*field).unwrap_or(&(&field, &field)).1;
152+
print!("{:<10} ", header);
153+
}
154+
println!();
155+
156+
// Print each process
87157
for proc in filtered_processes {
88-
println!(
89-
"{:<5} {:<5} {:<5} {:<5} {}",
90-
proc.pid, proc.ppid, proc.uid, proc.gid, proc.path
91-
);
158+
for field in &output_fields {
159+
match *field {
160+
"pid" => print!("{:<10} ", proc.pid),
161+
"ppid" => print!("{:<10} ", proc.ppid),
162+
"group" => print!("{:<10} ", proc.gid),
163+
"tty" => print!("{:<10} ", proc.tty.as_deref().unwrap_or("-")),
164+
// "time" => print!("{:<10} ", proc.time),
165+
"comm" => print!("{:<10} ", proc.path),
166+
"user" => print!("{:<10} ", proc.uid), // Example for user field, would need to resolve UID -> username
167+
// Add cases for more fields as needed...
168+
_ => print!("{:<10} ", "-"),
169+
}
170+
}
171+
println!();
92172
}
93173
}

0 commit comments

Comments
 (0)