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} ;
149use wait:: wait;
1510
1611mod wait;
1712
1813const ABOUT : & str = help_about ! ( "pidwait.md" ) ;
1914const 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]
3817pub 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\n Try `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\n Try `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) ]
23450pub 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