@@ -334,6 +334,8 @@ fn human_readable_size(size: u64) -> String {
334334/// Options for find command
335335struct FindOptions {
336336 name_pattern : Option < String > ,
337+ /// -path pattern: match against the full display path
338+ path_pattern : Option < String > ,
337339 type_filter : Option < char > ,
338340 max_depth : Option < usize > ,
339341 min_depth : Option < usize > ,
@@ -342,6 +344,10 @@ struct FindOptions {
342344 exec_args : Vec < String > ,
343345 /// true if -exec uses + (batch mode), false for \; (per-file mode)
344346 exec_batch : bool ,
347+ /// Negate the -name predicate
348+ negate_name : bool ,
349+ /// Negate the -path predicate
350+ negate_path : bool ,
345351}
346352
347353/// The find builtin - search for files.
@@ -366,13 +372,17 @@ fn parse_find_args(args: &[String]) -> std::result::Result<(Vec<String>, FindOpt
366372 let mut paths: Vec < String > = Vec :: new ( ) ;
367373 let mut opts = FindOptions {
368374 name_pattern : None ,
375+ path_pattern : None ,
369376 type_filter : None ,
370377 max_depth : None ,
371378 min_depth : None ,
372379 printf_format : None ,
373380 exec_args : Vec :: new ( ) ,
374381 exec_batch : false ,
382+ negate_name : false ,
383+ negate_path : false ,
375384 } ;
385+ let mut negate_next = false ;
376386
377387 let mut i = 0 ;
378388 while i < args. len ( ) {
@@ -387,6 +397,24 @@ fn parse_find_args(args: &[String]) -> std::result::Result<(Vec<String>, FindOpt
387397 ) ) ;
388398 }
389399 opts. name_pattern = Some ( args[ i] . clone ( ) ) ;
400+ if negate_next {
401+ opts. negate_name = true ;
402+ negate_next = false ;
403+ }
404+ }
405+ "-path" => {
406+ i += 1 ;
407+ if i >= args. len ( ) {
408+ return Err ( ExecResult :: err (
409+ "find: missing argument to '-path'\n " . to_string ( ) ,
410+ 1 ,
411+ ) ) ;
412+ }
413+ opts. path_pattern = Some ( args[ i] . clone ( ) ) ;
414+ if negate_next {
415+ opts. negate_path = true ;
416+ negate_next = false ;
417+ }
390418 }
391419 "-type" => {
392420 i += 1 ;
@@ -469,7 +497,7 @@ fn parse_find_args(args: &[String]) -> std::result::Result<(Vec<String>, FindOpt
469497 }
470498 }
471499 "-not" | "!" => {
472- // Negation - skip (not fully supported)
500+ negate_next = true ;
473501 }
474502 s if s. starts_with ( '-' ) => {
475503 return Err ( ExecResult :: err (
@@ -501,12 +529,15 @@ async fn collect_find_paths(
501529 // Reuse find_recursive but with a temporary output buffer
502530 let temp_opts = FindOptions {
503531 name_pattern : opts. name_pattern . clone ( ) ,
532+ path_pattern : opts. path_pattern . clone ( ) ,
504533 type_filter : opts. type_filter ,
505534 max_depth : opts. max_depth ,
506535 min_depth : opts. min_depth ,
507536 printf_format : None , // Don't format, just collect paths
508537 exec_args : Vec :: new ( ) ,
509538 exec_batch : false ,
539+ negate_name : opts. negate_name ,
540+ negate_path : opts. negate_path ,
510541 } ;
511542 let mut output = String :: new ( ) ;
512543 for path_str in search_paths {
@@ -672,7 +703,19 @@ fn find_recursive<'a>(
672703
673704 // Check name pattern
674705 let name_matches = match & opts. name_pattern {
675- Some ( pattern) => glob_match ( & entry_name, pattern) ,
706+ Some ( pattern) => {
707+ let m = glob_match ( & entry_name, pattern) ;
708+ if opts. negate_name { !m } else { m }
709+ }
710+ None => true ,
711+ } ;
712+
713+ // Check path pattern
714+ let path_matches = match & opts. path_pattern {
715+ Some ( pattern) => {
716+ let m = glob_match ( display_path, pattern) ;
717+ if opts. negate_path { !m } else { m }
718+ }
676719 None => true ,
677720 } ;
678721
@@ -683,7 +726,7 @@ fn find_recursive<'a>(
683726 } ;
684727
685728 // Output if matches (or if no filters, show everything)
686- if type_matches && name_matches && above_min_depth {
729+ if type_matches && name_matches && path_matches && above_min_depth {
687730 if let Some ( ref fmt) = opts. printf_format {
688731 output. push_str ( & find_printf_format ( fmt, display_path, & metadata) ) ;
689732 } else {
0 commit comments