14

You are given either an IO::File object or a typeglob (\*STDOUT or Symbol::symbol_to_ref("main::FH")); how would you go about determining if it is a read or write handle? The interface cannot be extended to pass this information (I am overriding close to add calls to flush and sync before the actual close).

Currently I am attempting to flush and sync the filehandle and ignoring the error "Invalid argument" (which is what I get when I attempt to flush or sync a read filehandle):

eval { $fh->flush; 1 } or do {
        #this seems to exclude flushes on read handles
        unless ($! =~ /Invalid argument/) {
                croak "could not flush $fh: $!";
        }
};

eval { $fh->sync; 1 } or do {
        #this seems to exclude syncs on read handles
        unless ($! =~ /Invalid argument/) {
                croak "could not sync $fh: $!";
        }
};
Chas. Owens
  • 64,182
  • 22
  • 135
  • 226
  • +1 Fantastic question! I have no idea when I would ever use this, but I'm fascinated to know someone needs this. – Shalom Craimer Mar 23 '09 at 06:05
  • Ext4 has brought up problems that Ext3 had kept hidden for a while. See http://thunk.org/tytso/blog/2009/03/15/dont-fear-the-fsync/ and the ongoing p5p thread (http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2009-03/msg00322.html) for background. – Chas. Owens Mar 23 '09 at 06:18

1 Answers1

8

Have a look at the fcntl options. Maybe F_GETFL with O_ACCMODE.

Edit: I did a little googling and playing over lunch and here is some probably non-portable code but it works for my Linux box, and probably any Posix system (perhaps even Cygwin, who knows?).

use strict;
use Fcntl;
use IO::File;

my $file;
my %modes = ( 0 => 'Read only', 1 => 'Write only', 2 => 'Read / Write' );

sub open_type {
    my $fh = shift;
    my $mode = fcntl($fh, F_GETFL, 0);
    print "File is: " . $modes{$mode & 3} . "\n";
}

print "out\n";
$file = new IO::File();
$file->open('> /tmp/out');
open_type($file);

print "\n";

print "in\n";
$file = new IO::File();
$file->open('< /etc/passwd');
open_type($file);

print "\n";

print "both\n";
$file = new IO::File();
$file->open('+< /tmp/out');
open_type($file);

Example output:

$ perl test.pl 
out
File is: Write only

in
File is: Read only

both
File is: Read / Write
JasonSmith
  • 72,674
  • 22
  • 123
  • 149
  • It looks like fcntl is OS specific, but if it works for a given OS, I might just build a dispatch hash based on the OS and fall back to my current code if the current OS is not in the dispatch hash. – Chas. Owens Mar 23 '09 at 05:47
  • 1
    Instead of hard coding '3' you could do `O_RDONLY | O_RDWR | O_WRONLY` but those constants are very unlikely to change since they haven't changed for like 20 years. Still, it makes the code more readable. – JasonSmith Mar 23 '09 at 06:21
  • I have a access to OS X, Linux, FreeBSD, and WinXP; if it works on all of those then this might just be the answer. – Chas. Owens Mar 23 '09 at 06:57
  • It seems to work for IO::File objects, but not typeglobs, time to see how to upgrade a typeglob to IO::File. – Chas. Owens Mar 23 '09 at 07:28
  • 1
    Apparently I am on crack, it seems to work just fine for typeglobs, STDIN and STDOUT just seem to be marked as Read / Write. When I use a file I open it just works. – Chas. Owens Mar 23 '09 at 07:44