Skip to content

Commit b633a46

Browse files
committed
pidwait: Use common process_matcher
Its matching functionality should be a superset of what pidwait can currently do.
1 parent 74a2ffd commit b633a46

File tree

1 file changed

+12
-219
lines changed

1 file changed

+12
-219
lines changed

src/uu/pidwait/src/pidwait.rs

Lines changed: 12 additions & 219 deletions
Original file line numberDiff line numberDiff line change
@@ -3,91 +3,34 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55

6-
use clap::{arg, crate_version, value_parser, Arg, ArgAction, ArgMatches, Command};
7-
use regex::Regex;
8-
use std::{collections::HashSet, env, sync::OnceLock};
9-
use uu_pgrep::process::{walk_process, ProcessInformation, RunState, Teletype};
10-
use uucore::{
11-
error::{UResult, USimpleError},
12-
format_usage, help_about, help_usage,
13-
};
6+
use clap::{arg, crate_version, Command};
7+
use uu_pgrep::process_matcher;
8+
use uucore::{error::UResult, format_usage, help_about, help_usage};
149
use wait::wait;
1510

1611
mod wait;
1712

1813
const ABOUT: &str = help_about!("pidwait.md");
1914
const USAGE: &str = help_usage!("pidwait.md");
2015

21-
static REGEX: OnceLock<Regex> = OnceLock::new();
22-
23-
#[derive(Debug)]
24-
struct Settings {
25-
echo: bool,
26-
count: bool,
27-
full: bool,
28-
ignore_case: bool,
29-
newest: bool,
30-
oldest: bool,
31-
older: Option<u64>,
32-
terminal: Option<HashSet<Teletype>>,
33-
exact: bool,
34-
runstates: Option<HashSet<RunState>>,
35-
}
36-
3716
#[uucore::main]
3817
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
3918
let matches = uu_app().try_get_matches_from(args)?;
4019

41-
let settings = Settings {
42-
echo: matches.get_flag("echo"),
43-
count: matches.get_flag("count"),
44-
full: matches.get_flag("full"),
45-
ignore_case: matches.get_flag("ignore-case"),
46-
newest: matches.get_flag("newest"),
47-
oldest: matches.get_flag("oldest"),
48-
older: matches.get_one::<u64>("older").copied(),
49-
terminal: matches.get_many::<String>("terminal").map(|ttys| {
50-
ttys.cloned()
51-
.flat_map(Teletype::try_from)
52-
.collect::<HashSet<_>>()
53-
}),
54-
exact: matches.get_flag("exact"),
55-
runstates: matches
56-
.get_many::<String>("runstates")
57-
.map(|it| it.cloned().flat_map(RunState::try_from).collect()),
58-
};
59-
60-
let pattern = initialize_pattern(&matches, &settings)?;
61-
REGEX
62-
.set(Regex::new(&pattern).map_err(|e| USimpleError::new(2, e.to_string()))?)
63-
.unwrap();
64-
65-
if (!settings.newest
66-
&& !settings.oldest
67-
&& settings.runstates.is_none()
68-
&& settings.older.is_none()
69-
&& settings.terminal.is_none())
70-
&& pattern.is_empty()
71-
{
72-
return Err(USimpleError::new(
73-
2,
74-
"no matching criteria specified\nTry `pidwait --help' for more information.",
75-
));
76-
}
77-
78-
let mut proc_infos = collect_proc_infos(&settings);
20+
let settings = process_matcher::get_match_settings(&matches)?;
21+
let mut proc_infos = process_matcher::find_matching_pids(&settings);
7922

8023
// For empty result
8124
if proc_infos.is_empty() {
8225
uucore::error::set_exit_code(1);
8326
}
8427

8528
// Process outputs
86-
if settings.count {
29+
if matches.get_flag("count") {
8730
println!("{}", proc_infos.len());
8831
}
8932

90-
if settings.echo {
33+
if matches.get_flag("echo") {
9134
if settings.newest || settings.oldest {
9235
for ele in &proc_infos {
9336
println!("waiting for (pid {})", ele.pid);
@@ -104,165 +47,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
10447
Ok(())
10548
}
10649

107-
fn initialize_pattern(matches: &ArgMatches, settings: &Settings) -> UResult<String> {
108-
let pattern = match matches.get_many::<String>("pattern") {
109-
Some(patterns) if patterns.len() > 1 => {
110-
return Err(USimpleError::new(
111-
2,
112-
"only one pattern can be provided\nTry `pidwait --help' for more information.",
113-
))
114-
}
115-
Some(mut patterns) => patterns.next().unwrap(),
116-
None => return Ok(String::new()),
117-
};
118-
119-
let pattern = if settings.ignore_case {
120-
&pattern.to_lowercase()
121-
} else {
122-
pattern
123-
};
124-
125-
let pattern = if settings.exact {
126-
&format!("^{}$", pattern)
127-
} else {
128-
pattern
129-
};
130-
131-
if !settings.full && pattern.len() >= 15 {
132-
const MSG_0: &str= "pidwait: pattern that searches for process name longer than 15 characters will result in zero matches";
133-
const MSG_1: &str = "Try `pidwait -f' option to match against the complete command line.";
134-
return Err(USimpleError::new(1, format!("{MSG_0}\n{MSG_1}")));
135-
}
136-
137-
Ok(pattern.to_string())
138-
}
139-
140-
fn collect_proc_infos(settings: &Settings) -> Vec<ProcessInformation> {
141-
// Process pattern
142-
let proc_infos = {
143-
let mut temp = Vec::new();
144-
for mut it in walk_process() {
145-
let matched = {
146-
let binding = it.status();
147-
let name = binding.get("Name").unwrap();
148-
let name = if settings.ignore_case {
149-
name.to_lowercase()
150-
} else {
151-
name.into()
152-
};
153-
154-
let want = if settings.exact {
155-
&name
156-
} else if settings.full {
157-
&it.cmdline
158-
} else {
159-
&it.proc_stat()[..15]
160-
};
161-
162-
REGEX.get().unwrap().is_match(want)
163-
};
164-
if matched {
165-
temp.push(it);
166-
}
167-
}
168-
temp
169-
};
170-
171-
// Process `-O`
172-
let proc_infos = {
173-
let mut temp: Vec<ProcessInformation> = Vec::new();
174-
let older = settings.older.unwrap_or_default();
175-
for mut proc_info in proc_infos {
176-
if proc_info.start_time().unwrap() >= older {
177-
temp.push(proc_info);
178-
}
179-
}
180-
temp
181-
};
182-
183-
let mut proc_infos = {
184-
if let Some(terminals) = &settings.terminal {
185-
proc_infos
186-
.into_iter()
187-
.filter(|it| terminals.contains(&it.tty()))
188-
.collect()
189-
} else {
190-
proc_infos
191-
}
192-
};
193-
194-
if proc_infos.is_empty() {
195-
return proc_infos;
196-
}
197-
198-
// Sorting oldest and newest
199-
let proc_infos = if settings.oldest || settings.newest {
200-
proc_infos.sort_by(|a, b| {
201-
b.clone()
202-
.start_time()
203-
.unwrap()
204-
.cmp(&a.clone().start_time().unwrap())
205-
});
206-
207-
let start_time = if settings.newest {
208-
proc_infos.first().cloned().unwrap().start_time().unwrap()
209-
} else {
210-
proc_infos.last().cloned().unwrap().start_time().unwrap()
211-
};
212-
213-
// There might be some process start at same time, so need to be filtered.
214-
let mut filtered = proc_infos
215-
.iter()
216-
.filter(|it| (*it).clone().start_time().unwrap() == start_time)
217-
.collect::<Vec<_>>();
218-
219-
if settings.newest {
220-
filtered.sort_by(|a, b| b.pid.cmp(&a.pid));
221-
} else {
222-
filtered.sort_by(|a, b| a.pid.cmp(&b.pid));
223-
}
224-
225-
vec![filtered.first().cloned().unwrap().clone()]
226-
} else {
227-
proc_infos
228-
};
229-
230-
proc_infos
231-
}
232-
233-
#[allow(clippy::cognitive_complexity)]
23450
pub fn uu_app() -> Command {
23551
Command::new(env!("CARGO_PKG_NAME"))
23652
.version(crate_version!())
23753
.about(ABOUT)
23854
.override_usage(format_usage(USAGE))
23955
.infer_long_args(true)
240-
.args([
241-
arg!(-e --echo "display PIDs before waiting"),
242-
arg!(-c --count "count of matching processes"),
243-
arg!(-f --full "use full process name to match"),
244-
// arg!(-g --pgroup <PGID> "match listed process group IDs"),
245-
// arg!(-G --group <GID> "match real group IDs"),
246-
arg!(-i --"ignore-case" "match case insensitively"),
247-
arg!(-n --newest "select most recently started"),
248-
arg!(-o --oldest "select least recently started"),
249-
arg!(-O --older <seconds> "select where older than seconds")
250-
.value_parser(value_parser!(u64)),
251-
// arg!(-P --parent <PPID> "match only child processes of the given parent"),
252-
// arg!(-s --session <SID> "match session IDs"),
253-
arg!(-t --terminal <tty> "match by controlling terminal"),
254-
// arg!(-u --euid <ID> "match by effective IDs"),
255-
// arg!(-U --uid <ID> "match by real IDs"),
256-
arg!(-x --exact "match exactly with the command name"),
257-
// arg!(-F --pidfile <file> "read PIDs from file"),
258-
// arg!(-L --logpidfile "fail if PID file is not locked"),
259-
arg!(-r --runstates <state> "match runstates [D,S,Z,...]"),
260-
// arg!(-A --"ignore-ancestors" "exclude our ancestors from results"),
261-
])
262-
.arg(
263-
Arg::new("pattern")
264-
.help("Name of the program to find the PID of")
265-
.action(ArgAction::Append)
266-
.index(1),
267-
)
56+
.args([arg!(-e --echo "display PIDs before waiting")])
57+
.args(process_matcher::clap_args(
58+
"Name of the program to wait for",
59+
true,
60+
))
26861
}

0 commit comments

Comments
 (0)