Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 96 additions & 43 deletions lib/File/Which.pm
Original file line number Diff line number Diff line change
Expand Up @@ -112,33 +112,86 @@ is essentially unmaintained.
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};
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;

if( exists $opts{fixed_paths} ) {
$self->{fixed_paths} = $opts{fixed_paths};
}

$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};
}

\@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 {
# Win9X or other: doesn't have PATHEXT, so needs hardcoded.
push @PATHEXT, qw{.com .exe .bat};
@path = File::Spec->path;
}
} 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};
\@path;
}

=head1 FUNCTIONS
Expand All @@ -165,6 +218,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;
Expand All @@ -174,15 +234,15 @@ sub which {
my @results = ();

# check for aliases first
if ( IS_VMS ) {
if ( $self->_is_vms ) {
my $symbol = `SHOW SYMBOL $exec`;
chomp($symbol);
unless ( $? ) {
return $symbol unless $all;
push @results, $symbol;
}
}
if ( IS_MAC ) {
if ( $self->_is_mac ) {
my @aliases = split /\,/, $ENV{Aliases};
foreach my $alias ( @aliases ) {
# This has not been tested!!
Expand All @@ -201,24 +261,17 @@ 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') {
# 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;
}
if ( $IMPLICIT_CURRENT_DIR ) {
my @path = exists $self->{fixed_paths}
? @{ $self->{fixed_paths} }
: @{ $self->_default_path };

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;
Expand All @@ -231,10 +284,10 @@ sub which {
-x _
or (
# OS9 (classic Mac OS) doesn't mark files as executable, so we check -e instead
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
Expand Down
18 changes: 9 additions & 9 deletions t/file_which.t
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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'),
Expand All @@ -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(
Expand All @@ -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, OS9 (classic Mac OS) (. 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);
}

Expand Down
Loading