From 70863620e3949474b77521a79ef8c2a00e91c0a2 Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 4 Nov 2025 10:08:10 -0500 Subject: [PATCH 1/3] Add dirmap --- lib/LUCCDC/Jiujitsu/Util/Linux/Files.pm | 55 ++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/lib/LUCCDC/Jiujitsu/Util/Linux/Files.pm b/lib/LUCCDC/Jiujitsu/Util/Linux/Files.pm index d5e0f0a..e715960 100644 --- a/lib/LUCCDC/Jiujitsu/Util/Linux/Files.pm +++ b/lib/LUCCDC/Jiujitsu/Util/Linux/Files.pm @@ -2,10 +2,13 @@ package LUCCDC::Jiujitsu::Util::Linux::Files; use strictures 2; use parent qw(Exporter); use Symbol qw( gensym ); +use Carp qw(croak); +use Cwd qw(abs_path); +use File::Spec; use vars qw($VERSION @EXPORT_OK %EXPORT_TAGS); $VERSION = 1.00; -@EXPORT_OK = qw(fgrep_flat fgrep slurp_to_array); +@EXPORT_OK = qw(fgrep_flat fgrep slurp_to_array dirmap); %EXPORT_TAGS = ( DEFAULT => \@EXPORT_OK, @@ -123,4 +126,54 @@ sub slurp_to_array { return @array; } +sub dirmap { + my ( $start_dir, $filter_func, $p_recurse, $p_max_depth ) = @_; + + croak "Starting directory not provided" unless defined $start_dir; + croak "Filter function not provided" unless defined $filter_func; + croak "Filter function is not a code reference" if ref($filter_func) ne 'CODE'; + + my $recurse = $p_recurse // 1; + my $max_depth = $p_max_depth // -1; + + my $abs_start_dir = abs_path($start_dir); + croak "Directory '$start_dir' does not exist or is not a directory" unless defined $abs_start_dir && -d $abs_start_dir; + + my @found_files; + _traverse( $abs_start_dir, $filter_func, $recurse, 0, $max_depth, \@found_files ); + + return @found_files; +} + +sub _traverse { + my ( $current_dir, $filter_func, $recurse, $current_depth, $max_depth, $results_ref ) = @_; + + return if ( $max_depth != -1 && $current_depth > $max_depth ); + + my $dh; + unless ( opendir( $dh, $current_dir ) ) { + warn "Could not open directory '$current_dir' : $!"; + return; + } + + while ( my $entry = readdir($dh) ) { + next if $entry eq '.' or $entry eq '..'; + + my $full_path = File::Spec->catfile( $current_dir, $entry ); + + if ( -d $full_path ) { + if ($recurse) { + _traverse( $full_path, $filter_func, $recurse, $current_depth + 1, $max_depth, $results_ref ); + } + } + elsif ( -f $full_path ) { + if ( $filter_func->($full_path) ) { + push @{$results_ref}, $full_path; + } + } + } + + closedir($dh); +} + 1; From 2cb68c5ae1bd874b847cbe7a3919646f1c43d656 Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 4 Nov 2025 10:59:29 -0500 Subject: [PATCH 2/3] Handle symlinks --- lib/LUCCDC/Jiujitsu/Util/Linux/Files.pm | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/LUCCDC/Jiujitsu/Util/Linux/Files.pm b/lib/LUCCDC/Jiujitsu/Util/Linux/Files.pm index e715960..ce155a8 100644 --- a/lib/LUCCDC/Jiujitsu/Util/Linux/Files.pm +++ b/lib/LUCCDC/Jiujitsu/Util/Linux/Files.pm @@ -127,7 +127,7 @@ sub slurp_to_array { } sub dirmap { - my ( $start_dir, $filter_func, $p_recurse, $p_max_depth ) = @_; + my ( $start_dir, $filter_func, $p_recurse, $p_max_depth, $p_follow_symlinks) = @_; croak "Starting directory not provided" unless defined $start_dir; croak "Filter function not provided" unless defined $filter_func; @@ -135,18 +135,19 @@ sub dirmap { my $recurse = $p_recurse // 1; my $max_depth = $p_max_depth // -1; + my $follow_symlinks = $p_follow_symlinks // 0; my $abs_start_dir = abs_path($start_dir); croak "Directory '$start_dir' does not exist or is not a directory" unless defined $abs_start_dir && -d $abs_start_dir; my @found_files; - _traverse( $abs_start_dir, $filter_func, $recurse, 0, $max_depth, \@found_files ); + _traverse( $abs_start_dir, $filter_func, $recurse, 0, $max_depth, $follow_symlinks, \@found_files ); return @found_files; } sub _traverse { - my ( $current_dir, $filter_func, $recurse, $current_depth, $max_depth, $results_ref ) = @_; + my ( $current_dir, $filter_func, $recurse, $current_depth, $max_depth, $follow_symlinks, $results_ref ) = @_; return if ( $max_depth != -1 && $current_depth > $max_depth ); @@ -161,14 +162,16 @@ sub _traverse { my $full_path = File::Spec->catfile( $current_dir, $entry ); - if ( -d $full_path ) { - if ($recurse) { - _traverse( $full_path, $filter_func, $recurse, $current_depth + 1, $max_depth, $results_ref ); + if ( !( -l $full_path ) || $follow_symlinks ) { + if ( -d $full_path ) { + if ($recurse) { + _traverse( $full_path, $filter_func, $recurse, $current_depth + 1, $max_depth, $follow_symlinks, $results_ref ); + } } - } - elsif ( -f $full_path ) { - if ( $filter_func->($full_path) ) { - push @{$results_ref}, $full_path; + elsif ( -f $full_path ) { + if ( $filter_func->($full_path) ) { + push @{$results_ref}, $full_path; + } } } } From 3f26e2bc2d6c40dde691533cb1b5c3b855bb7f74 Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 4 Nov 2025 11:32:52 -0500 Subject: [PATCH 3/3] Make perlcritic happy --- lib/LUCCDC/Jiujitsu/Util/Linux/Files.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/LUCCDC/Jiujitsu/Util/Linux/Files.pm b/lib/LUCCDC/Jiujitsu/Util/Linux/Files.pm index ce155a8..9491d27 100644 --- a/lib/LUCCDC/Jiujitsu/Util/Linux/Files.pm +++ b/lib/LUCCDC/Jiujitsu/Util/Linux/Files.pm @@ -177,6 +177,7 @@ sub _traverse { } closedir($dh); + return; } 1;