From f534a1312cc0fe90f49e19733fc63a6c34594e4f Mon Sep 17 00:00:00 2001 From: Zakariyya Mughal Date: Thu, 13 Oct 2022 22:08:23 -0400 Subject: [PATCH 1/5] Refactor to use object --- lib/File/Which.pm | 106 +++++++++++++++++++++++++++++++--------------- t/file_which.t | 18 ++++---- 2 files changed, 82 insertions(+), 42 deletions(-) diff --git a/lib/File/Which.pm b/lib/File/Which.pm index 915f8a3..8c8c18e 100644 --- a/lib/File/Which.pm +++ b/lib/File/Which.pm @@ -99,33 +99,65 @@ me. our @EXPORT = 'which'; our @EXPORT_OK = 'where'; -use constant IS_VMS => ($^O eq 'VMS'); -use constant IS_MAC => ($^O eq 'MacOS'); -use constant IS_WIN => ($^O eq 'MSWin32' or $^O eq 'dos' or $^O eq 'os2'); -use constant IS_DOS => IS_WIN(); -use constant IS_CYG => ($^O eq 'cygwin' || $^O eq 'msys'); - -our $IMPLICIT_CURRENT_DIR = IS_WIN || IS_VMS || IS_MAC; - -# For Win32 systems, stores the extensions used for -# executable files -# For others, the empty string is used -# because 'perl' . '' eq 'perl' => easier -my @PATHEXT = (''); -if ( IS_WIN ) { - # WinNT. PATHEXT might be set on Cygwin, but not used. - if ( $ENV{PATHEXT} ) { - push @PATHEXT, split /;/, $ENV{PATHEXT}; - } else { - # Win9X or other: doesn't have PATHEXT, so needs hardcoded. - push @PATHEXT, qw{.com .exe .bat}; +sub _get_osname { @_ == 1 && ref $_[0] ? $_[0]->{osname} : $^O } + +sub IS_VMS { my $osname = &_get_osname; ($osname eq 'VMS'); } +sub IS_MAC { my $osname = &_get_osname; ($osname eq 'MacOS'); } +sub IS_WIN { my $osname = &_get_osname; ($osname eq 'MSWin32' or $osname eq 'dos' or $osname eq 'os2'); } +sub IS_DOS { IS_WIN(@_); } +sub IS_CYG { my $osname = &_get_osname; ($osname eq 'cygwin' || $osname eq 'msys'); } + +sub _default_IMPLICIT_CURRENT_DIR { + my $self = shift; + $self->IS_WIN || $self->IS_VMS || $self->IS_MAC; +} +our $IMPLICIT_CURRENT_DIR = do { + File::Which->new->_default_IMPLICIT_CURRENT_DIR; +}; + +sub new { + my ($class, %opts) = @_; + + my $osname = exists $opts{os} ? $opts{os} : $^O; + + my $self = bless { + osname => $osname, + }, $class; + + $self->{IMPLICIT_CURRENT_DIR} = + exists $opts{IMPLICIT_CURRENT_DIR} + ? $opts{IMPLICIT_CURRENT_DIR} + : $self->_default_IMPLICIT_CURRENT_DIR; + + $self->{PATHEXT} = $self->_default_pathext; + + $self; +} + +sub _default_pathext { + my $self = shift; + # For Win32 systems, stores the extensions used for + # executable files + # For others, the empty string is used + # because 'perl' . '' eq 'perl' => easier + my @PATHEXT = (''); + if ( $self->IS_WIN ) { + # WinNT. PATHEXT might be set on Cygwin, but not used. + if ( $ENV{PATHEXT} ) { + push @PATHEXT, split /;/, $ENV{PATHEXT}; + } else { + # Win9X or other: doesn't have PATHEXT, so needs hardcoded. + push @PATHEXT, qw{.com .exe .bat}; + } + } elsif ( $self->IS_VMS ) { + push @PATHEXT, qw{.exe .com}; + } elsif ( $self->IS_CYG ) { + # See this for more info + # http://cygwin.com/cygwin-ug-net/using-specialnames.html#pathnames-exe + push @PATHEXT, qw{.exe .com}; } -} elsif ( IS_VMS ) { - push @PATHEXT, qw{.exe .com}; -} elsif ( IS_CYG ) { - # See this for more info - # http://cygwin.com/cygwin-ug-net/using-specialnames.html#pathnames-exe - push @PATHEXT, qw{.exe .com}; + + \@PATHEXT; } =head1 FUNCTIONS @@ -152,6 +184,13 @@ matches. =cut sub which { + my $self = @_ == 1 + ? File::Which->new( + # Use global to retain compatibility, but only for the functional + # interface. + IMPLICIT_CURRENT_DIR => $IMPLICIT_CURRENT_DIR, + ) + : shift; my ($exec) = @_; return undef unless defined $exec; @@ -161,7 +200,7 @@ sub which { my @results = (); # check for aliases first - if ( IS_VMS ) { + if ( $self->IS_VMS ) { my $symbol = `SHOW SYMBOL $exec`; chomp($symbol); unless ( $? ) { @@ -169,7 +208,7 @@ sub which { push @results, $symbol; } } - if ( IS_MAC ) { + if ( $self->IS_MAC ) { my @aliases = split /\,/, $ENV{Aliases}; foreach my $alias ( @aliases ) { # This has not been tested!! @@ -188,10 +227,10 @@ sub which { } return $exec ## no critic (ValuesAndExpressions::ProhibitMixedBooleanOperators) - if !IS_VMS and !IS_MAC and !IS_WIN and $exec =~ /\// and -f $exec and -x $exec; + if !$self->IS_VMS and !$self->IS_MAC and !$self->IS_WIN and $exec =~ /\// and -f $exec and -x $exec; my @path; - if($^O eq 'MSWin32') { + if($self->{osname} eq 'MSWin32') { # File::Spec (at least recent versions) # add the implicit . for you on MSWin32, # but we may or may not want to include @@ -202,10 +241,11 @@ sub which { } else { @path = File::Spec->path; } - if ( $IMPLICIT_CURRENT_DIR ) { + if ( $self->{IMPLICIT_CURRENT_DIR} ) { unshift @path, File::Spec->curdir; } + my @PATHEXT = @{ $self->{PATHEXT} }; foreach my $base ( map { File::Spec->catfile($_, $exec) } @path ) { for my $ext ( @PATHEXT ) { my $file = $base.$ext; @@ -218,10 +258,10 @@ sub which { -x _ or ( # MacOS doesn't mark as executable so we check -e - IS_MAC ## no critic (ValuesAndExpressions::ProhibitMixedBooleanOperators) + $self->IS_MAC ## no critic (ValuesAndExpressions::ProhibitMixedBooleanOperators) || ( - ( IS_WIN or IS_CYG ) + ( $self->IS_WIN or $self->IS_CYG ) and grep { ## no critic (BuiltinFunctions::ProhibitBooleanGrep) $file =~ /$_\z/i diff --git a/t/file_which.t b/t/file_which.t index d398249..2b1937e 100644 --- a/t/file_which.t +++ b/t/file_which.t @@ -5,7 +5,7 @@ use Test::More tests => 19; use File::Spec (); use File::Which qw(which where); -unless (File::Which::IS_VMS or File::Which::IS_MAC or File::Which::IS_WIN ) { +unless (File::Which::IS_VMS() or File::Which::IS_MAC() or File::Which::IS_WIN() ) { foreach my $path (qw( corpus/test-bin-unix/test3 corpus/test-bin-unix/all @@ -35,24 +35,24 @@ unless (File::Which::IS_VMS or File::Which::IS_MAC or File::Which::IS_WIN ) { ); # Where is the test application - my $test_bin = File::Spec->catdir( 'corpus', File::Which::IS_WIN ? 'test-bin-win' : 'test-bin-unix' ); + my $test_bin = File::Spec->catdir( 'corpus', File::Which::IS_WIN() ? 'test-bin-win' : 'test-bin-unix' ); ok( -d $test_bin, 'Found test-bin' ); # Set up for running the test application @PATH = $test_bin; - push @PATH, File::Spec->catdir( 'corpus', 'test-bin-win' ) if File::Which::IS_CYG; + push @PATH, File::Spec->catdir( 'corpus', 'test-bin-win' ) if File::Which::IS_CYG(); SKIP: { - skip("Not on DOS-like filesystem", 3) unless File::Which::IS_WIN; + skip("Not on DOS-like filesystem", 3) unless File::Which::IS_WIN(); is( lc scalar which('test1'), 'corpus\test-bin-win\test1.exe', 'Looking for test1.exe' ); is( lc scalar which('test2'), 'corpus\test-bin-win\test2.bat', 'Looking for test2.bat' ); is( scalar which('test3'), undef, 'test3 returns undef' ); } SKIP: { - skip("Not on a UNIX filesystem", 1) if File::Which::IS_WIN; - skip("Not on a UNIX filesystem", 1) if File::Which::IS_MAC; - skip("Not on a UNIX filesystem", 1) if File::Which::IS_VMS; + skip("Not on a UNIX filesystem", 1) if File::Which::IS_WIN(); + skip("Not on a UNIX filesystem", 1) if File::Which::IS_MAC(); + skip("Not on a UNIX filesystem", 1) if File::Which::IS_VMS(); is( scalar(which('test3')), File::Spec->catfile( $test_bin, 'test3'), @@ -61,7 +61,7 @@ unless (File::Which::IS_VMS or File::Which::IS_MAC or File::Which::IS_WIN ) { } SKIP: { - skip("Not on a cygwin filesystem", 2) unless File::Which::IS_CYG; + skip("Not on a cygwin filesystem", 2) unless File::Which::IS_CYG(); # Cygwin: should make test1.exe transparent is( @@ -78,7 +78,7 @@ unless (File::Which::IS_VMS or File::Which::IS_MAC or File::Which::IS_WIN ) { # Make sure that .\ stuff works on DOSish, VMS, MacOS (. is in PATH implicitly). SKIP: { - unless ( File::Which::IS_WIN or File::Which::IS_VMS ) { + unless ( File::Which::IS_WIN() or File::Which::IS_VMS() ) { skip("Not on a DOS or VMS filesystem", 1); } From 28d08339779f40920f78ce24525c36179d3a7c67 Mon Sep 17 00:00:00 2001 From: Zakariyya Mughal Date: Fri, 14 Oct 2022 23:09:58 -0400 Subject: [PATCH 2/5] Rename OS-checking functions to lowercase with underscore prefix --- lib/File/Which.pm | 28 ++++++++++++++-------------- t/file_which.t | 18 +++++++++--------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lib/File/Which.pm b/lib/File/Which.pm index 8c8c18e..e7adf50 100644 --- a/lib/File/Which.pm +++ b/lib/File/Which.pm @@ -101,15 +101,15 @@ our @EXPORT_OK = 'where'; sub _get_osname { @_ == 1 && ref $_[0] ? $_[0]->{osname} : $^O } -sub IS_VMS { my $osname = &_get_osname; ($osname eq 'VMS'); } -sub IS_MAC { my $osname = &_get_osname; ($osname eq 'MacOS'); } -sub IS_WIN { my $osname = &_get_osname; ($osname eq 'MSWin32' or $osname eq 'dos' or $osname eq 'os2'); } -sub IS_DOS { IS_WIN(@_); } -sub IS_CYG { my $osname = &_get_osname; ($osname eq 'cygwin' || $osname eq 'msys'); } +sub _is_vms { my $osname = &_get_osname; ($osname eq 'VMS'); } +sub _is_mac { my $osname = &_get_osname; ($osname eq 'MacOS'); } +sub _is_win { my $osname = &_get_osname; ($osname eq 'MSWin32' or $osname eq 'dos' or $osname eq 'os2'); } +sub _is_dos { _is_win(@_); } +sub _is_cyg { my $osname = &_get_osname; ($osname eq 'cygwin' || $osname eq 'msys'); } sub _default_IMPLICIT_CURRENT_DIR { my $self = shift; - $self->IS_WIN || $self->IS_VMS || $self->IS_MAC; + $self->_is_win || $self->_is_vms || $self->_is_mac; } our $IMPLICIT_CURRENT_DIR = do { File::Which->new->_default_IMPLICIT_CURRENT_DIR; @@ -141,7 +141,7 @@ sub _default_pathext { # For others, the empty string is used # because 'perl' . '' eq 'perl' => easier my @PATHEXT = (''); - if ( $self->IS_WIN ) { + if ( $self->_is_win ) { # WinNT. PATHEXT might be set on Cygwin, but not used. if ( $ENV{PATHEXT} ) { push @PATHEXT, split /;/, $ENV{PATHEXT}; @@ -149,9 +149,9 @@ sub _default_pathext { # Win9X or other: doesn't have PATHEXT, so needs hardcoded. push @PATHEXT, qw{.com .exe .bat}; } - } elsif ( $self->IS_VMS ) { + } elsif ( $self->_is_vms ) { push @PATHEXT, qw{.exe .com}; - } elsif ( $self->IS_CYG ) { + } elsif ( $self->_is_cyg ) { # See this for more info # http://cygwin.com/cygwin-ug-net/using-specialnames.html#pathnames-exe push @PATHEXT, qw{.exe .com}; @@ -200,7 +200,7 @@ sub which { my @results = (); # check for aliases first - if ( $self->IS_VMS ) { + if ( $self->_is_vms ) { my $symbol = `SHOW SYMBOL $exec`; chomp($symbol); unless ( $? ) { @@ -208,7 +208,7 @@ sub which { push @results, $symbol; } } - if ( $self->IS_MAC ) { + if ( $self->_is_mac ) { my @aliases = split /\,/, $ENV{Aliases}; foreach my $alias ( @aliases ) { # This has not been tested!! @@ -227,7 +227,7 @@ sub which { } return $exec ## no critic (ValuesAndExpressions::ProhibitMixedBooleanOperators) - if !$self->IS_VMS and !$self->IS_MAC and !$self->IS_WIN and $exec =~ /\// and -f $exec and -x $exec; + if !$self->_is_vms and !$self->_is_mac and !$self->_is_win and $exec =~ /\// and -f $exec and -x $exec; my @path; if($self->{osname} eq 'MSWin32') { @@ -258,10 +258,10 @@ sub which { -x _ or ( # MacOS doesn't mark as executable so we check -e - $self->IS_MAC ## no critic (ValuesAndExpressions::ProhibitMixedBooleanOperators) + $self->_is_mac ## no critic (ValuesAndExpressions::ProhibitMixedBooleanOperators) || ( - ( $self->IS_WIN or $self->IS_CYG ) + ( $self->_is_win or $self->_is_cyg ) and grep { ## no critic (BuiltinFunctions::ProhibitBooleanGrep) $file =~ /$_\z/i diff --git a/t/file_which.t b/t/file_which.t index 2b1937e..5e2c29a 100644 --- a/t/file_which.t +++ b/t/file_which.t @@ -5,7 +5,7 @@ use Test::More tests => 19; use File::Spec (); use File::Which qw(which where); -unless (File::Which::IS_VMS() or File::Which::IS_MAC() or File::Which::IS_WIN() ) { +unless (File::Which::_is_vms() or File::Which::_is_mac() or File::Which::_is_win() ) { foreach my $path (qw( corpus/test-bin-unix/test3 corpus/test-bin-unix/all @@ -35,24 +35,24 @@ unless (File::Which::IS_VMS() or File::Which::IS_MAC() or File::Which::IS_WIN() ); # Where is the test application - my $test_bin = File::Spec->catdir( 'corpus', File::Which::IS_WIN() ? 'test-bin-win' : 'test-bin-unix' ); + my $test_bin = File::Spec->catdir( 'corpus', File::Which::_is_win() ? 'test-bin-win' : 'test-bin-unix' ); ok( -d $test_bin, 'Found test-bin' ); # Set up for running the test application @PATH = $test_bin; - push @PATH, File::Spec->catdir( 'corpus', 'test-bin-win' ) if File::Which::IS_CYG(); + push @PATH, File::Spec->catdir( 'corpus', 'test-bin-win' ) if File::Which::_is_cyg(); SKIP: { - skip("Not on DOS-like filesystem", 3) unless File::Which::IS_WIN(); + skip("Not on DOS-like filesystem", 3) unless File::Which::_is_win(); is( lc scalar which('test1'), 'corpus\test-bin-win\test1.exe', 'Looking for test1.exe' ); is( lc scalar which('test2'), 'corpus\test-bin-win\test2.bat', 'Looking for test2.bat' ); is( scalar which('test3'), undef, 'test3 returns undef' ); } SKIP: { - skip("Not on a UNIX filesystem", 1) if File::Which::IS_WIN(); - skip("Not on a UNIX filesystem", 1) if File::Which::IS_MAC(); - skip("Not on a UNIX filesystem", 1) if File::Which::IS_VMS(); + skip("Not on a UNIX filesystem", 1) if File::Which::_is_win(); + skip("Not on a UNIX filesystem", 1) if File::Which::_is_mac(); + skip("Not on a UNIX filesystem", 1) if File::Which::_is_vms(); is( scalar(which('test3')), File::Spec->catfile( $test_bin, 'test3'), @@ -61,7 +61,7 @@ unless (File::Which::IS_VMS() or File::Which::IS_MAC() or File::Which::IS_WIN() } SKIP: { - skip("Not on a cygwin filesystem", 2) unless File::Which::IS_CYG(); + skip("Not on a cygwin filesystem", 2) unless File::Which::_is_cyg(); # Cygwin: should make test1.exe transparent is( @@ -78,7 +78,7 @@ unless (File::Which::IS_VMS() or File::Which::IS_MAC() or File::Which::IS_WIN() # Make sure that .\ stuff works on DOSish, VMS, MacOS (. is in PATH implicitly). SKIP: { - unless ( File::Which::IS_WIN() or File::Which::IS_VMS() ) { + unless ( File::Which::_is_win() or File::Which::_is_vms() ) { skip("Not on a DOS or VMS filesystem", 1); } From 21ebfff77b7fe29a9f08259c8cf645a8d925cae1 Mon Sep 17 00:00:00 2001 From: Zakariyya Mughal Date: Fri, 14 Oct 2022 19:04:40 -0400 Subject: [PATCH 3/5] Extract getting path from $ENV{PATH} to method --- lib/File/Which.pm | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/lib/File/Which.pm b/lib/File/Which.pm index e7adf50..907b097 100644 --- a/lib/File/Which.pm +++ b/lib/File/Which.pm @@ -160,6 +160,23 @@ sub _default_pathext { \@PATHEXT; } +sub _default_path { + my $self = shift; + my @path; + if($self->{osname} eq 'MSWin32') { + # File::Spec (at least recent versions) + # add the implicit . for you on MSWin32, + # but we may or may not want to include + # that. + @path = split /;/, $ENV{PATH}; + s/"//g for @path; + @path = grep length, @path; + } else { + @path = File::Spec->path; + } + \@path; +} + =head1 FUNCTIONS =head2 which @@ -229,18 +246,8 @@ sub which { return $exec ## no critic (ValuesAndExpressions::ProhibitMixedBooleanOperators) if !$self->_is_vms and !$self->_is_mac and !$self->_is_win and $exec =~ /\// and -f $exec and -x $exec; - my @path; - if($self->{osname} eq 'MSWin32') { - # File::Spec (at least recent versions) - # add the implicit . for you on MSWin32, - # but we may or may not want to include - # that. - @path = split /;/, $ENV{PATH}; - s/"//g for @path; - @path = grep length, @path; - } else { - @path = File::Spec->path; - } + my @path = @{ $self->_default_path }; + if ( $self->{IMPLICIT_CURRENT_DIR} ) { unshift @path, File::Spec->curdir; } From 5b830c2bdc7246161ab85fedeb9c558002a4bcdd Mon Sep 17 00:00:00 2001 From: Zakariyya Mughal Date: Fri, 14 Oct 2022 19:08:22 -0400 Subject: [PATCH 4/5] Add constructor option `fixed_paths` The name indicates that it will remain the same despite changes to `$ENV{PATH}`. --- lib/File/Which.pm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/File/Which.pm b/lib/File/Which.pm index 907b097..d3af16a 100644 --- a/lib/File/Which.pm +++ b/lib/File/Which.pm @@ -131,6 +131,10 @@ sub new { $self->{PATHEXT} = $self->_default_pathext; + if( exists $opts{fixed_paths} ) { + $self->{fixed_paths} = $opts{fixed_paths}; + } + $self; } @@ -246,7 +250,9 @@ sub which { return $exec ## no critic (ValuesAndExpressions::ProhibitMixedBooleanOperators) if !$self->_is_vms and !$self->_is_mac and !$self->_is_win and $exec =~ /\// and -f $exec and -x $exec; - my @path = @{ $self->_default_path }; + my @path = exists $self->{fixed_paths} + ? @{ $self->{fixed_paths} } + : @{ $self->_default_path }; if ( $self->{IMPLICIT_CURRENT_DIR} ) { unshift @path, File::Spec->curdir; From a9b7707334881719c610a1635e8c08500e04dfe0 Mon Sep 17 00:00:00 2001 From: Zakariyya Mughal Date: Fri, 14 Oct 2022 20:05:18 -0400 Subject: [PATCH 5/5] Use lowercase for implicit_current_dir option --- lib/File/Which.pm | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/File/Which.pm b/lib/File/Which.pm index d3af16a..fde5d18 100644 --- a/lib/File/Which.pm +++ b/lib/File/Which.pm @@ -107,12 +107,12 @@ sub _is_win { my $osname = &_get_osname; ($osname eq 'MSWin32' or $osname eq 'do sub _is_dos { _is_win(@_); } sub _is_cyg { my $osname = &_get_osname; ($osname eq 'cygwin' || $osname eq 'msys'); } -sub _default_IMPLICIT_CURRENT_DIR { +sub _default_implicit_current_dir { my $self = shift; $self->_is_win || $self->_is_vms || $self->_is_mac; } our $IMPLICIT_CURRENT_DIR = do { - File::Which->new->_default_IMPLICIT_CURRENT_DIR; + File::Which->new->_default_implicit_current_dir; }; sub new { @@ -124,10 +124,10 @@ sub new { osname => $osname, }, $class; - $self->{IMPLICIT_CURRENT_DIR} = - exists $opts{IMPLICIT_CURRENT_DIR} - ? $opts{IMPLICIT_CURRENT_DIR} - : $self->_default_IMPLICIT_CURRENT_DIR; + $self->{implicit_current_dir} = + exists $opts{implicit_current_dir} + ? $opts{implicit_current_dir} + : $self->_default_implicit_current_dir; $self->{PATHEXT} = $self->_default_pathext; @@ -209,7 +209,7 @@ sub which { ? File::Which->new( # Use global to retain compatibility, but only for the functional # interface. - IMPLICIT_CURRENT_DIR => $IMPLICIT_CURRENT_DIR, + implicit_current_dir => $IMPLICIT_CURRENT_DIR, ) : shift; my ($exec) = @_; @@ -254,7 +254,7 @@ sub which { ? @{ $self->{fixed_paths} } : @{ $self->_default_path }; - if ( $self->{IMPLICIT_CURRENT_DIR} ) { + if ( $self->{implicit_current_dir} ) { unshift @path, File::Spec->curdir; }