in reply to Re^8: Best way to check if something is a file handle?
in thread Best way to check if something is a file handle?

A filename may be the name of a file that does not (yet) exist.

I've been sitting on this reply for a day or so trying to decide if it contributes anything. I can't make up my mind, so I'll let you dismiss it or not.

If the sole purpose of the module is to determine if a passed parameter is an IO handle, then why does it have is_filename()?

Surely all that is needed, at most is:

... unless( is_filehandle( $arg ) { open $arg, '...', $arg or die $!; } ...

And what does is_filename() mean when it reports true? For example, will it consider fred"bill"smith as valid? Which it possibly is on *nix, but not on NTFS.

Now with respect to making the required determination of whether the passed parameter represents a "legitimate" IO target, you dismissed my earlier suggestion of using fileno because it didn't work for ram files (IO::Scalar & open $fh, '>', \$ramfile) , (and IO:All object, but I'll get back to that!).

Running it against a simplified version of your test suite confirms that is the only failure:

#! perl -slw package main; use strict; use FileHandle; use IO::File; use IO::Socket::INET; sub ok{ my( $code, $wanted, $label, $value ) = @_; my $bool = $code->( $value ); return $label . ':' . ( $bool == $wanted ? 'Ack' : 'Nak' ); } sub isIO { my $arg = shift; my $p = eval{ fileno( $arg ) }; return defined( $p ) && $p >=0 ? 1 : 0; } my @handles = ( [0, "1", 1], [0, "hashref", {}], [0, "arrayref", []], [0, "globref", \*INC], [1, "in-memory", do {{ my $buf; open my $fh, "<", \$buf; $fh }}] +, [1, "FH1 glob", do {{ open FH1, "<", "nul"; *FH1 }}], [1, "FH2 globref", do {{ open FH2, "<", "nul"; \*FH2 }}], [1, "FH3 string", do {{ no strict; $FH3 = 'nul'; open FH3; FH3 }} +], [1, "STDIN glob", \*STDIN], [1, "STDOUT glob", \*STDOUT], [1, "plain read", do {{ open my $fh, "<", "nul"; $fh }}], [1, "plain write", do {{ open my $fh, ">", "nul"; $fh }}], [1, "FH read", FileHandle->new("< nul")], [1, "FH write", FileHandle->new("> nul")], [1, "I::F read", IO::File->new("< nul")], [1, "I::F write", IO::File->new("> nul")], [1, "pipe read", do {{ open my $fh, "ver |"; $fh }}], [1, "pipe write", do {{ open my $fh, "| more"; $fh }}], [1, "socket", IO::Socket::INET->new(LocalAddr => sprintf('loc +alhost:%d', 10000 + rand 20000))], ); print ok( \&isIO, @$_ ) for @handles; __END__ 1:Ack hashref:Ack arrayref:Ack globref:Ack in-memory:Nak FH1 glob:Ack FH2 globref:Ack FH3 string:Ack STDIN glob:Ack STDOUT glob:Ack plain read:Ack plain write:Ack FH read:Ack FH write:Ack I::F read:Ack I::F write:Ack pipe read:Ack pipe write:Ack socket:Ack

I was aware of this, but I also contend that it is a bug in the implementation of ramfiles.

Why should I not be able to duplicate a ramfile handle using open $dup, '&=', fileno( $ramfh ) ... as I can with any other filehandle?

And I suspect that limitation -- the failure of an IO object to respond to fileno in an appropriate and consistent way -- is the cause of the IO::All object failure also.

Whilst I believe that to be a bug that needs fixing, it is a legitimate position to take to say that your module -- with its mechanism that detects a ramfile handle (and I assume an IO::All object) as a suitable target for many IO operations -- fulfills a useful function, now, that is otherwise not easily available unless and until those IO-like thingies do respond to fileno correctly.

When this thread came up, I knew that I had solved this problem (for ramfiles) sometime in the past. I did do a search of my system and PM, but failed to find the code. This morning I got around to checking my backups from my previous system and found it. This is how I did it:

#! perl -slw package main; use strict; use FileHandle; use IO::File; use IO::Socket::INET; sub ok{ my( $code, $wanted, $label, $value ) = @_; my $bool = $code->( $value ); return $label . ':' . ( $bool == $wanted ? 'Ack' : 'Nak' ); } sub isIO{ local $^W; my $arg = shift; my $p = eval{ tell( $arg ) }; return defined( $p ) && $p >=0 ? 1 : 0; } my @handles = ( [0, "1", 1], [0, "hashref", {}], [0, "arrayref", []], [0, "globref", \*INC], [1, "in-memory", do {{ my $buf; open my $fh, "<", \$buf; $fh }}] +, [1, "FH1 glob", do {{ open FH1, "<", "nul"; *FH1 }}], [1, "FH2 globref", do {{ open FH2, "<", "nul"; \*FH2 }}], [1, "FH3 string", do {{ no strict; $FH3 = 'nul'; open FH3; FH3 }} +], [1, "STDIN glob", \*STDIN], [1, "STDOUT glob", \*STDOUT], [1, "plain read", do {{ open my $fh, "<", "nul"; $fh }}], [1, "plain write", do {{ open my $fh, ">", "nul"; $fh }}], [1, "FH read", FileHandle->new("< nul")], [1, "FH write", FileHandle->new("> nul")], [1, "I::F read", IO::File->new("< nul")], [1, "I::F write", IO::File->new("> nul")], [1, "pipe read", do {{ open my $fh, "ver |"; $fh }}], [1, "pipe write", do {{ open my $fh, "| more"; $fh }}], [1, "socket", IO::Socket::INET->new(LocalAddr => sprintf('loc +alhost:%d', 10000 + rand 20000))], ); print ok( \&isIO, @$_ ) for @handles; __END__ [ 6:39:21.75] C:\test>junk6 1:Ack hashref:Ack arrayref:Ack globref:Ack in-memory:Ack FH1 glob:Ack FH2 globref:Ack FH3 string:Ack STDIN glob:Ack STDOUT glob:Ack plain read:Ack plain write:Ack FH read:Ack FH write:Ack I::F read:Ack I::F write:Ack pipe read:Ack pipe write:Ack socket:Ack

I haven't tested it against IO:All as I don't use it, but maybe there is something there that is useful to you.


With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.

The start of some sanity?