develooper Front page | perl.perl5.porters | Postings from April 2017

weird do/require search behaviour with @INC refs or EACCES

Thread Next
Dave Mitchell
April 10, 2017 16:59
weird do/require search behaviour with @INC refs or EACCES
Message ID:
While working on

    RT #131098 Error message for require "./" is wrong

I was trying to understand the search logic in S_require_file() and have
provisionally concluded that it seems to make no sense. Or if it does, the
docs for do() and require() need expanding.

A bit of background.

In the Good Old Days before we supported refs in @INC, and before we added
the "stop searching @INC on EACCES" behaviour, the file-locating
logic for do/require looked roughly like:

    my $found;
    my $searchable = $file !~ m{^(  /  |  \./  |  \.\./  )}x;
    if (!$searchable) {
        $found = load_file($file);
    else {
        for my $prefix (@INC) {
            next unless load_file("$prefix/$file");
            $found = 1;
    croak(...) unless $found;

i.e. search @INC unless it's a special type of pathname.

However, once the facility for having refs in @INC was added, this
behaviour was changed; in particular, @INC is still searched (but non-refs
are ignored) for "non-searchable" filenames. I.e. the algorithm now looks

    my $found;
    my $searchable = $file !~ m{^(  /  |  \./  |  \.\./  )}x;
    if (!$searchable) {
        $found = load_file($file);

    if (!$found) { 
        for my $prefix (@INC) {
            if (ref($prefix)) {
                ... do stuff with the ref ...
            else if ($searchable) {
                next unless load_file("$prefix/$file");
                $found = 1;
    croak(...) unless $found;

This is somewhat weird. Ok, I can kind of understand that refs-in-@INC is
a "super" mechanism that overrides the normal "this filename isn't
searchable" behaviour,  but here we get *both* behaviours. For something
like require "./foo/", we try to load "./foo/", and only if
that fails, do we process the 'grep ref, @INC' subset of @INC. I would
expect that if we're going to search that subset, that we shouldn't try to
directly load "./foo/" first.

Now, the tests in t/op/inccode.t expect that loading "./"
will search refs in @INC; but nothing in the test suite fails if we skip
loading "./" and proceed directly to scanning @INC.
So that may be emergent behaviour.

The waters are the further muddied by the new 'stop scanning @INC if EACCES
is seen" behaviour. This works on the sensible principle that if we get
"permission denied" while trying to load a file when
"/dir_we_dont_have_perms_for" is in @INC, then we don't know whether the
file exists but we can't load it, or the file doesn't exist, but we
don't have read access to directory it's in to know that. So for safety we
stop rather than trying further @INC entries.

However, the EACCES feature was implemented by changing two of the lines
in the pseudo-code above to the equivalent of:

    - if (!$found) { 
    + if (!$found && !($! == EACCES && !$searchable)) { 

    - next unless load_file("$prefix/$file");
    + unless (load_file("$prefix/$file")) {
    +     last if $! == EACCES;
    +     next;
    + }

The second chunk of that diff is what I'd expect. The first just makes the
non-searchable filename behaviour with refs-in-@INC even weirder.  Now the
rules in their totality seem to be:

    if the filename is searchable:
        try against every entry in @INC (both plain and ref)
            stop if we get a match or EACCES;

    if the filename is non-searchable:
        try to load it.
            if EACCES, stop
        try against every ref entry in @INC

The non-searchable stop-on-EACCES behaviour isn't tested for.

Does that logic make any sense to anyone? If not, should we change it?
(And I'm not suggesting yet that we change it for 5.26).

Dave's first rule of Opera:
If something needs saying, say it: don't warble it.

Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About