Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[PATCH] prototype for subs accepting both a file name and a file handle

4 views
Skip to first unread message

Salvador Fandino

unread,
Mar 7, 2006, 8:03:16 PM3/7/06
to perl5-...@perl.org
Hi,

I was writing a replacement subroutine for CORE::GLOBAL::stat when I
noticed that there is no way to know if its argument is a file name or a
file handler. For instance:

use Devel::Peek;

CORE::GLOBAL::stat = sub ($) { Dump($_[0]) }

# these two calls dump similar SVs:
stat(Foo);
stat('Foo');

A simple way to remove that limitation is to use a new sub prototype to
handle that kind of argument.

The patch attached adds support for prototype '/'. It is similar to '$'
but automatically converts barewords to globs. For instance:

sub my_stat (/) { ... }

my_stat "foo"; # is equivalent to &my_stat("foo");
my_stat foo; # is equivalent to &my_stat(*foo);

I like '/' as the name of the prototype because it's easy to relate to
filenames (at least if you are a unix user!).

What do you thing? any possibility this could go into blead?

Cheers,

- Salva.

perl-filename-proto-0.patch

Salvador Fandiño)

unread,
Mar 8, 2006, 5:50:02 AM3/8/06
to Rafael Garcia-Suarez, perl5-...@perl.org

--- Rafael Garcia-Suarez <rgarci...@gmail.com> wrote:

> On 3/8/06, Salvador Fandino <sfan...@yahoo.com> wrote:
> > I was writing a replacement subroutine for CORE::GLOBAL::stat
> when I
> > noticed that there is no way to know if its argument is a file
> name or a
> > file handler. For instance:
> >
> > use Devel::Peek;
> >
> > CORE::GLOBAL::stat = sub ($) { Dump($_[0]) }
>

> What's the problem with (*) ? which is the prototype returned for
> this
> built-in, see :
> $ perl -le 'print prototype "CORE::stat"'
> *

with '*', it happens as with '$', barewords are interpreted as
barewords and there is no way to know if the caller wants to stat an
open file or a named file.

For instance,

CORE::GLOBAL::stat = sub (*) { CORE::stat($_[0]) };

open Foo, "myfile";
stat(Foo);

tries to stat a file named 'Foo' instead of 'myfile'.

Cheers,

- Salva.

__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com

Rafael Garcia-Suarez

unread,
Mar 8, 2006, 6:47:36 AM3/8/06
to perl5-...@perl.org
Salvador "FandiXo" wrote:
> with '*', it happens as with '$', barewords are interpreted as
> barewords and there is no way to know if the caller wants to stat an
> open file or a named file.
>
> For instance,
>
> CORE::GLOBAL::stat = sub (*) { CORE::stat($_[0]) };
>
> open Foo, "myfile";
> stat(Foo);
>
> tries to stat a file named 'Foo' instead of 'myfile'.

I think I'd rather fix (*) than introducing a new prototype character.
After all, overriden built-ins are supposed to behave like the real
built-ins.

--
A system is nothing more than the subordination of all aspects of
the universe to any one such aspect.
-- Borges

Salvador Fandino

unread,
Mar 8, 2006, 10:56:32 AM3/8/06
to perl5-...@perl.org
Rafael Garcia-Suarez wrote:
>
> I think I'd rather fix (*) than introducing a new prototype character.
> After all, overriden built-ins are supposed to behave like the real
> built-ins.

the problem I see with fixing '*' is that it would introduce backward
incompatibilities and break code expecting a glob name instead of a real
glob. Greping over my minicpan I have found that this is the most common
case. I have also found that it is very usual to employ '*' on
subroutines expecting a bareword ('require' replacements, for instance).

IMO, the best solution, would be to start calling '*' the bareword
prototype, and introduce '/' for file handles. The builtin 'prototype'
should be changed accordingly, so that it uses '/' for builtins
expecting a file handle.

Cheers,

- Salvador.

Rafael Garcia-Suarez

unread,
Mar 8, 2006, 11:53:53 AM3/8/06
to perl5-...@perl.org
Salvador Fandino wrote:
>
> the problem I see with fixing '*' is that it would introduce backward
> incompatibilities and break code expecting a glob name instead of a real
> glob. Greping over my minicpan I have found that this is the most common
> case.

Any examples ?

> I have also found that it is very usual to employ '*' on
> subroutines expecting a bareword ('require' replacements, for instance).

You don't need a prototype if you override require(), the prototype will
get ignored anyway since it's parsed specially.

(Moreover strictures are supposed to guard you against mixing barewords
and globs/handles.)

> IMO, the best solution, would be to start calling '*' the bareword
> prototype, and introduce '/' for file handles. The builtin 'prototype'
> should be changed accordingly, so that it uses '/' for builtins
> expecting a file handle.

I'm not convinced yet.

--
* What system had proved more effective?
* Indirect suggestion implicating selfinterest.
-- Ulysses

Salvador Fandino

unread,
Mar 8, 2006, 4:34:08 PM3/8/06
to perl5-...@perl.org
Rafael Garcia-Suarez wrote:
> Salvador Fandino wrote:
>
>>the problem I see with fixing '*' is that it would introduce backward
>>incompatibilities and break code expecting a glob name instead of a real
>>glob. Greping over my minicpan I have found that this is the most common
>>case.
>
>
> Any examples ?

after closer examination, I have found that most modules requiring a
file handle are still going to work when passed a GV because they do
something like that:

sub foo (*) {
my $ref = shift;
my $glob;
if (ref $ref) {
$glob = $ref;
else {
unless ($ref =~ /::/) { # a stringified GV matches /::/,
$ref = caller().'::'.$ref; # it is not modified here.
}
no strict 'refs';
$glob = *{$ref}; # if $ref is a GV this line
# is equivalent to $glob = $ref
}
whatever $glob;
...
}


but the problem with subroutines using '*' for syntactic sugar to get a
bareword still remains, and there are lots of modules doing it as PAR
for instance:

sub opt(*) {
my $opt = shift;
return exists($Options->{$opt}) && ($Options->{$opt} || 0);
}

Other modules doing the same are...

CPANPLUS-0.0499/lib/CPANPLUS/Tools/Load.pm
race-1.070/lib/Log/Trace.pm
Module-Find-0.05/Find.pm
Net_SSLeay.pm-1.30/SSLeay.pm
CPANPLUS-0.0562/lib/CPANPLUS/inc/Module/Load.pm
mod_perl-2.0.2/lib/Apache2/porting.pm
Module-Loaded-0.01/lib/Module/Loaded.pm
etc.


>>I have also found that it is very usual to employ '*' on
>>subroutines expecting a bareword ('require' replacements, for instance).
>
>
> You don't need a prototype if you override require(), the prototype will
> get ignored anyway since it's parsed specially.
>
> (Moreover strictures are supposed to guard you against mixing barewords
> and globs/handles.)

>>IMO, the best solution, would be to start calling '*' the bareword
>>prototype, and introduce '/' for file handles. The builtin 'prototype'
>>should be changed accordingly, so that it uses '/' for builtins
>>expecting a file handle.
>
>
> I'm not convinced yet.
>

well, it is an ugly solution but I can not see any other alternative
that does not break backwards compatibility.

And after all, what's the problem with introducing a new prototype? it's
not likely to cause incompatibilities and there are plenty of symbols
available for future use (!?-+="'`:,{}^~|.#).

Cheers,

- Salvador.

Nick Ing-Simmons

unread,
Mar 9, 2006, 1:33:20 PM3/9/06
to sfan...@yahoo.com, perl5-...@perl.org
Salvador Fandino <sfan...@yahoo.com> writes:
>Hi,
>
>I was writing a replacement subroutine for CORE::GLOBAL::stat when I
>noticed that there is no way to know if its argument is a file name or a
>file handler. For instance:
>
> use Devel::Peek;
>
> CORE::GLOBAL::stat = sub ($) { Dump($_[0]) }
>
> # these two calls dump similar SVs:
> stat(Foo);
> stat('Foo');
>
>A simple way to remove that limitation is to use a new sub prototype to
>handle that kind of argument.
>
>The patch attached adds support for prototype '/'. It is similar to '$'
>but automatically converts barewords to globs.

I thought there was already a prototype for that - '*' or is that _too_
strong?

>For instance:
>
> sub my_stat (/) { ... }
>
> my_stat "foo"; # is equivalent to &my_stat("foo");
> my_stat foo; # is equivalent to &my_stat(*foo);
>
>I like '/' as the name of the prototype because it's easy to relate to
>filenames (at least if you are a unix user!).
>
>What do you thing? any possibility this could go into blead?
>
>Cheers,
>
> - Salva.

>diff -ru orig-perl-current/op.c my-perl-current/op.c
>--- orig-perl-current/op.c 2006-03-07 18:11:24.000000000 +0100
>+++ my-perl-current/op.c 2006-03-08 00:12:29.000000000 +0100
>@@ -6621,6 +6621,21 @@
> arg == 1 ? "block or sub {}" : "sub {}",
> gv_ename(namegv), o2);
> break;
>+ case '/':
>+ proto++;
>+ arg++;
>+ if (o2->op_type == OP_CONST && (o2->op_private & OPpCONST_BARE)) {
>+ GV * gv = gv_fetchsv(cSVOPx(o2)->op_sv, GV_ADD, 0);
>+ OP * kid = newGVOP(OP_GV, 0, gv);
>+ OP * const sib = o2->op_sibling;
>+ o2->op_sibling = 0;
>+ op_free(o2);
>+ o2 = newUNOP(OP_RV2GV, OPf_REF, scalar(kid));
>+ o2->op_sibling = sib;
>+ prev->op_sibling = o2;
>+ }
>+ scalar(o2);
>+ break;
> case '*':
> /* '*' allows any scalar type, including bareword */
> proto++;
>diff -ru orig-perl-current/toke.c my-perl-current/toke.c
>--- orig-perl-current/toke.c 2006-02-27 12:06:56.000000000 +0100
>+++ my-perl-current/toke.c 2006-03-08 00:11:56.000000000 +0100
>@@ -2146,7 +2146,7 @@
> if (proto) {
> if (*proto == ';')
> proto++;
>- if (*proto == '*')
>+ if (*proto == '*' || *proto == '/')
> return 0;
> }
> }
>@@ -5506,7 +5506,7 @@
> for (p = d; *p; ++p) {
> if (!isSPACE(*p)) {
> d[tmp++] = *p;
>- if (!strchr("$@%*;[]&\\", *p))
>+ if (!strchr("$@%*;[]&\\/", *p))
> bad_proto = TRUE;
> }
> }

Nick Ing-Simmons

unread,
Mar 9, 2006, 1:35:44 PM3/9/06
to sfan...@yahoo.com, perl5-...@perl.org, Rafael Garcia-Suarez
Salvador "Fandiño <sfan...@yahoo.com> writes:
>--- Rafael Garcia-Suarez <rgarci...@gmail.com> wrote:
>
>> On 3/8/06, Salvador Fandino <sfan...@yahoo.com> wrote:
>> > I was writing a replacement subroutine for CORE::GLOBAL::stat
>> when I
>> > noticed that there is no way to know if its argument is a file
>> name or a
>> > file handler. For instance:
>> >
>> > use Devel::Peek;
>> >
>> > CORE::GLOBAL::stat = sub ($) { Dump($_[0]) }
>>
>> What's the problem with (*) ? which is the prototype returned for
>> this
>> built-in, see :
>> $ perl -le 'print prototype "CORE::stat"'
>> *
>
>with '*', it happens as with '$', barewords are interpreted as
>barewords and there is no way to know if the caller wants to stat an
>open file or a named file.

But perl's stat() does what '*' proto does.
So having the overriden stat() do something else would be confusing.


Nick Ing-Simmons

unread,
Mar 9, 2006, 1:38:31 PM3/9/06
to sfan...@yahoo.com, perl5-...@perl.org

But REAL stat gets passed the string doesn't it.
So the sub could do what stat does - test if the glob exists.

>
>Cheers,
>
> - Salvador.

Salvador Fandino

unread,
Mar 9, 2006, 3:04:34 PM3/9/06
to perl5-...@perl.org
Nick Ing-Simmons wrote:

> Salvador Fandino <sfan...@yahoo.com> writes:
>>the problem I see with fixing '*' is that it would introduce backward
>>incompatibilities and break code expecting a glob name instead of a real
>>glob. Greping over my minicpan I have found that this is the most common
>>case. I have also found that it is very usual to employ '*' on
>>subroutines expecting a bareword ('require' replacements, for instance).
>>
>>IMO, the best solution, would be to start calling '*' the bareword
>>prototype, and introduce '/' for file handles. The builtin 'prototype'
>>should be changed accordingly, so that it uses '/' for builtins
>>expecting a file handle.
>
>
> But REAL stat gets passed the string doesn't it.

no, in Perl_ck_ftst, this code

if (kid->op_type == OP_CONST &&
(kid->op_private & OPpCONST_BARE)) {
OP * const newop = newGVOP(type, OPf_REF,
gv_fetchsv(kid->op_sv, GV_ADD, SVt_PVIO));
op_free(o);
o = newop;
return o;
}

checks if the argument is a bareword and if so, converts it to a glob.
Same is done for file test operators in Perl_ck_ftst. And I believe that
at some point inside Perl_ck_fun, the same change is performed for other
ops like chdir or truncate.

Whit out that conversion, it is impossible to know if the caller wants
to perform an operation over the file named "foo" or over the file
handle *foo.

> So the sub could do what stat does - test if the glob exists.

Checking for the existence of the glob is a very bad heuristic. It
doesn't really mean anything because file names and glob names can
collide easily.


Cheers,

- Salva.

Nick Ing-Simmons

unread,
Mar 9, 2006, 3:32:14 PM3/9/06
to sfan...@yahoo.com, perl5-...@perl.org
Salvador Fandino <sfan...@yahoo.com> writes:
>>
>>
>> But REAL stat gets passed the string doesn't it.
>
>no, in Perl_ck_ftst, this code
>
> if (kid->op_type == OP_CONST &&
> (kid->op_private & OPpCONST_BARE)) {
> OP * const newop = newGVOP(type, OPf_REF,
> gv_fetchsv(kid->op_sv, GV_ADD, SVt_PVIO));
> op_free(o);
> o = newop;
> return o;
> }
>
>checks if the argument is a bareword and if so, converts it to a glob.
>Same is done for file test operators in Perl_ck_ftst. And I believe that
>at some point inside Perl_ck_fun, the same change is performed for other
>ops like chdir or truncate.
>
>Whit out that conversion, it is impossible to know if the caller wants
>to perform an operation over the file named "foo" or over the file
>handle *foo.
>
>> So the sub could do what stat does - test if the glob exists.
>
>Checking for the existence of the glob is a very bad heuristic. It
>doesn't really mean anything because file names and glob names can
>collide easily.

I thought so too, and didn't know about the op hackery.

None the less the _intent_ of the '*' prototype was to make the perl sub
behave like the builtin. So if '*' proto caused the op hackery to be done
so that

stat(Foo)

Passed a glob and stat('Foo') passed a string to the sub it would be
like real stat().


Salvador Fandino

unread,
Mar 9, 2006, 4:14:43 PM3/9/06
to perl5-...@perl.org
Nick Ing-Simmons wrote:

> None the less the _intent_ of the '*' prototype was to make the perl sub
> behave like the builtin. So if '*' proto caused the op hackery to be done
> so that
>
> stat(Foo)
>
> Passed a glob and stat('Foo') passed a string to the sub it would be
> like real stat().

yes, but the problem is that changing '*' now will break lots of code
using it to accept a bareword under "use strict". Code as this:

use strict;
sub option (*);
print "option t:" , option(t) , "\n";

can be found in several CPAN modules.

I see three different ways to handle that limitation:

- left it as is, some builtins can not be properly overridden but it has
been broken for a long time and nobody else has complained so, it
shouldn't be something important!

- change '*' to work as built-ins, and break old code doing funny things

- use a new prototype that works as '*' should.

IMO, the last one is the way to go. It is not going to break anything
and would let do interesting things, like implementing a VFS layer.

Cheers,

- Salva


Nick Ing-Simmons

unread,
Mar 10, 2006, 6:00:55 PM3/10/06
to sfan...@yahoo.com, perl5-...@perl.org
Salvador Fandino <sfan...@yahoo.com> writes:
>Nick Ing-Simmons wrote:
>
>> None the less the _intent_ of the '*' prototype was to make the perl sub
>> behave like the builtin. So if '*' proto caused the op hackery to be done
>> so that
>>
>> stat(Foo)
>>
>> Passed a glob and stat('Foo') passed a string to the sub it would be
>> like real stat().
>
>yes, but the problem is that changing '*' now will break lots of code
>using it to accept a bareword under "use strict". Code as this:
>
> use strict;
> sub option (*);
> print "option t:" , option(t) , "\n";
>
>can be found in several CPAN modules.
>
>I see three different ways to handle that limitation:
>
>- left it as is, some builtins can not be properly overridden but it has
>been broken for a long time and nobody else has complained so, it
>shouldn't be something important!
>
>- change '*' to work as built-ins, and break old code doing funny things
>
>- use a new prototype that works as '*' should.

I agree those are the options and maybe even that third looks
best. I don't like '/' as choice of character.

>
>IMO, the last one is the way to go. It is not going to break anything
>and would let do interesting things,


>like implementing a VFS layer.

I/we already did that ;-)

>
>Cheers,
>
> - Salva

sfan...@yahoo.com

unread,
Mar 11, 2006, 6:21:01 AM3/11/06
to perl5-...@perl.org
Nick Ing-Simmons wrote:

> Salvador Fandino <sfan...@yahoo.com> writes:
>> I see three different ways to handle that limitation:
>>
>> - left it as is, some builtins can not be properly overridden but it has
>> been broken for a long time and nobody else has complained so, it
>> shouldn't be something important!
>>
>> - change '*' to work as built-ins, and break old code doing funny things
>>
>> - use a new prototype that works as '*' should.
>
> I agree those are the options and maybe even that third looks
> best. I don't like '/' as choice of character.

Which one would be your choice?

Symbols still available are: !?-+="'`:,{}^~|.#

>> IMO, the last one is the way to go. It is not going to break anything
>> and would let do interesting things,
>
>
>> like implementing a VFS layer.
>
> I/we already did that ;-)

PerlIO is not a full VFS layer, it is missing all the directory handling
functionality, or are you talking about something else?

Cheers,

- Salva

Nick Ing-Simmons

unread,
Mar 11, 2006, 11:20:35 AM3/11/06
to sfan...@yahoo.com, perl5-...@perl.org
=?UTF-8?B?U2FsdmFkb3IgRmFuZGnDsW8=?= <sfan...@yahoo.com> writes:
>
>Which one would be your choice?
>
>Symbols still available are: !?-+="'`:,{}^~|.#

Not sure, but as it relates to barewords as strings then ' would seem

>
>>> IMO, the last one is the way to go. It is not going to break anything
>>> and would let do interesting things,
>>
>>
>>> like implementing a VFS layer.
>>
>> I/we already did that ;-)
>
>PerlIO is not a full VFS layer,

True - but then neither is tie nor is
override-every-file-touching-operator-to-a-sub

H.Merijn Brand

unread,
Mar 11, 2006, 12:26:28 PM3/11/06
to perl5-...@perl.org
On Sat, 11 Mar 2006 12:21:01 +0100, Salvador Fandiño <sfan...@yahoo.com>
wrote:

> Nick Ing-Simmons wrote:
>
> > Salvador Fandino <sfan...@yahoo.com> writes:
> >> I see three different ways to handle that limitation:
> >>
> >> - left it as is, some builtins can not be properly overridden but it has
> >> been broken for a long time and nobody else has complained so, it
> >> shouldn't be something important!
> >>
> >> - change '*' to work as built-ins, and break old code doing funny things
> >>
> >> - use a new prototype that works as '*' should.
> >
> > I agree those are the options and maybe even that third looks
> > best. I don't like '/' as choice of character.
>
> Which one would be your choice?
>
> Symbols still available are: !?-+="'`:,{}^~|.#

note that ", ', `, and # will most likely cause much trouble to syntax
highlighters as text editors. Not that this should control our decision
process, but it might be something to consider

> >> IMO, the last one is the way to go. It is not going to break anything
> >> and would let do interesting things,
> >
> >
> >> like implementing a VFS layer.
> >
> > I/we already did that ;-)
>
> PerlIO is not a full VFS layer, it is missing all the directory handling
> functionality, or are you talking about something else?

--
H.Merijn Brand Amsterdam Perl Mongers (http://amsterdam.pm.org/)
using & porting perl 5.6.2, 5.8.x, 5.9.x on HP-UX 10.20, 11.00, 11.11,
& 11.23, SuSE 10.0, AIX 4.3 & 5.2, and Cygwin. http://qa.perl.org
http://mirrors.develooper.com/hpux/ http://www.test-smoke.org
http://www.goldmark.org/jeff/stupid-disclaimers/

Nick Ing-Simmons

unread,
Mar 11, 2006, 1:57:43 PM3/11/06
to h.m....@xs4all.nl, perl5-...@perl.org
H.Merijn Brand <h.m....@xs4all.nl> writes:
>>
>> Which one would be your choice?
>>
>> Symbols still available are: !?-+="'`:,{}^~|.#
>
>note that ", ', `, and # will most likely cause much trouble to syntax
>highlighters as text editors. Not that this should control our decision
>process, but it might be something to consider

And Humans now you mention it.

Unpaired {} are probably dubious as well.

So that leaves us with !?-+=:,^~|.

I am torn between '+' (looks most like *) and '?' as what happens
is conditional.


sfan...@yahoo.com

unread,
Mar 11, 2006, 2:25:34 PM3/11/06
to perl5-...@perl.org
Nick Ing-Simmons wrote:

>> Symbols still available are: !?-+="'`:,{}^~|.#
>
> Not sure, but as it relates to barewords as strings then ' would seem

emm... no, the prototype converting barewords to strings is the current
'*' that we want to maintain for backward compatibility. The new
operator, should convert barewords to file handlers.

Cheers,

- Salva

0 new messages