diff options
Diffstat (limited to 'lib/FileHandle.pm')
-rw-r--r-- | lib/FileHandle.pm | 390 |
1 files changed, 265 insertions, 125 deletions
diff --git a/lib/FileHandle.pm b/lib/FileHandle.pm index cbc6efbc6c..93a3088886 100644 --- a/lib/FileHandle.pm +++ b/lib/FileHandle.pm @@ -1,25 +1,80 @@ package FileHandle; -# Note that some additional FileHandle methods are defined in POSIX.pm. - =head1 NAME FileHandle - supply object methods for filehandles -cacheout - keep more files open than the system permits - =head1 SYNOPSIS use FileHandle; - autoflush STDOUT 1; - cacheout($path); - print $path @data; + $fh = new FileHandle; + if ($fh->open "< file") { + print <$fh>; + $fh->close; + } + + $fh = new FileHandle "> FOO"; + if (defined $fh) { + print $fh "bar\n"; + $fh->close; + } + + $fh = new FileHandle "file", "r"; + if (defined $fh) { + print <$fh>; + undef $fh; # automatically closes the file + } + + $fh = new FileHandle "file", O_WRONLY|O_APPEND; + if (defined $fh) { + print $fh "corge\n"; + undef $fh; # automatically closes the file + } + + ($readfh, $writefh) = FileHandle::pipe; + autoflush STDOUT 1; + =head1 DESCRIPTION -See L<perlvar> for complete descriptions of each of the following supported C<FileHandle> -methods: +C<FileHandle::new> creates a C<FileHandle>, which is a reference to a +newly created symbol (see the C<Symbol> package). If it receives any +parameters, they are passed to C<FileHandle::open>; if the open fails, +the C<FileHandle> object is destroyed. Otherwise, it is returned to +the caller. + +C<FileHandle::new_from_fd> creates a C<FileHandle> like C<new> does. +It requires two parameters, which are passed to C<FileHandle::fdopen>; +if the fdopen fails, the C<FileHandle> object is destroyed. +Otherwise, it is returned to the caller. + +C<FileHandle::open> accepts one parameter or two. With one parameter, +it is just a front end for the built-in C<open> function. With two +parameters, the first parameter is a filename that may include +whitespace or other special characters, and the second parameter is +the open mode in either Perl form (">", "+<", etc.) or POSIX form +("w", "r+", etc.). + +C<FileHandle::fdopen> is like C<open> except that its first parameter +is not a filename but rather a file handle name, a FileHandle object, +or a file descriptor number. + +See L<perlfunc> for complete descriptions of each of the following +supported C<FileHandle> methods, which are just front ends for the +corresponding built-in functions: + + close + fileno + getc + gets + eof + clearerr + seek + tell + +See L<perlvar> for complete descriptions of each of the following +supported C<FileHandle> methods: autoflush output_field_separator @@ -48,9 +103,9 @@ See L<perlfunc/printf>. =item $fh->getline -This works like <$fh> described in L<perlop/"I/O Operators"> except that it's more readable -and can be safely called in an array context but still -returns just one line. +This works like <$fh> described in L<perlop/"I/O Operators"> +except that it's more readable and can be safely called in an +array context but still returns just one line. =item $fh->getlines @@ -60,12 +115,6 @@ It will also croak() if accidentally called in a scalar context. =back -=head2 The cacheout() Library - -The cacheout() function will make sure that there's a filehandle -open for writing available as the pathname you give it. It automatically -closes and re-opens files if you exceed your system file descriptor maximum. - =head1 SEE ALSO L<perlfunc>, @@ -74,15 +123,6 @@ L<POSIX/"FileHandle"> =head1 BUGS -F<sys/param.h> lies with its C<NOFILE> define on some systems, -so you may have to set $cacheout::maxopen yourself. - -Some of the methods that set variables (like format_name()) don't -seem to work. - -The POSIX functions that create FileHandle methods should be -in this module instead. - Due to backwards compatibility, all filehandles resemble objects of class C<FileHandle>, or actually classes derived from that class. They actually aren't. Which means you can't derive your own @@ -91,12 +131,20 @@ class from C<FileHandle> and inherit those methods. =cut require 5.000; -use English; use Carp; -use Exporter; +use Fcntl; +use Symbol; +use English; +use SelectSaver; + +require Exporter; +require DynaLoader; +@ISA = qw(Exporter DynaLoader); + +@EXPORT = (@Fcntl::EXPORT, + qw(_IOFBF _IOLBF _IONBF)); -@ISA = qw(Exporter); -@EXPORT = qw( +@EXPORT_OK = qw( autoflush output_field_separator output_record_separator @@ -114,173 +162,265 @@ use Exporter; printf getline getlines - - cacheout ); + +################################################ +## Interaction with the XS. +## + +bootstrap FileHandle; + +sub AUTOLOAD { + if ($AUTOLOAD =~ /::(_?[a-z])/) { + $AutoLoader::AUTOLOAD = $AUTOLOAD; + goto &AutoLoader::AUTOLOAD + } + my $constname = $AUTOLOAD; + $constname =~ s/.*:://; + my $val = constant($constname); + defined $val or croak "$constname is not a valid FileHandle macro"; + *$AUTOLOAD = sub { $val }; + goto &$AUTOLOAD; +} + + +################################################ +## Constructors, destructors. +## + +sub new { + @_ >= 1 && @_ <= 3 or croak 'usage: new FileHandle [FILENAME [,MODE]]'; + my $class = shift; + my $fh = gensym; + if (@_) { + FileHandle::open($fh, @_) + or return undef; + } + bless $fh, $class; +} + +sub new_from_fd { + @_ == 3 or croak 'usage: new_from_fd FileHandle FD, MODE'; + my $class = shift; + my $fh = gensym; + FileHandle::fdopen($fh, @_) + or return undef; + bless $fh, $class; +} + +sub DESTROY { + my ($fh) = @_; + close($fh); +} + +################################################ +## Open and close. +## + +sub pipe { + @_ and croak 'usage: FileHandle::pipe()'; + my $readfh = new FileHandle; + my $writefh = new FileHandle; + pipe($readfh, $writefh) + or return undef; + ($readfh, $writefh); +} + +sub _open_mode_string { + my ($mode) = @_; + $mode =~ /^\+?(<|>>?)$/ + or $mode =~ s/^r(\+?)$/$1</ + or $mode =~ s/^w(\+?)$/$1>/ + or $mode =~ s/^a(\+?)$/$1>>/ + or croak "FileHandle: bad open mode: $mode"; + $mode; +} + +sub open { + @_ >= 2 && @_ <= 4 or croak 'usage: $fh->open(FILENAME [,MODE [,PERMS]])'; + my ($fh, $file) = @_; + if (@_ > 2) { + my ($mode, $perms) = @_[2, 3]; + if ($mode =~ /^\d+$/) { + defined $perms or $perms = 0666; + return sysopen($fh, $file, $mode, $perms); + } + $file = "./" . $file unless $file =~ m#^/#; + $file = _open_mode_string($mode) . " $file\0"; + } + open($fh, $file); +} + +sub fdopen { + @_ == 3 or croak 'usage: $fh->fdopen(FD, MODE)'; + my ($fh, $fd, $mode) = @_; + if (ref($fd) =~ /GLOB\(/) { + # It's a glob reference; remove the star from its name. + ($fd = "".$$fd) =~ s/^\*//; + } elsif ($fd =~ m#^\d+$#) { + # It's an FD number; prefix with "=". + $fd = "=$fd"; + } + open($fh, _open_mode_string($mode) . '&' . $fd); +} + +sub close { + @_ == 1 or croak 'usage: $fh->close()'; + close($_[0]); +} + +################################################ +## Normal I/O functions. +## + +sub fileno { + @_ == 1 or croak 'usage: $fh->fileno()'; + fileno($_[0]); +} + +sub getc { + @_ == 1 or croak 'usage: $fh->getc()'; + getc($_[0]); +} + +sub gets { + @_ == 1 or croak 'usage: $fh->gets()'; + my ($handle) = @_; + scalar <$handle>; +} + +sub eof { + @_ == 1 or croak 'usage: $fh->eof()'; + eof($_[0]); +} + +sub clearerr { + @_ == 1 or croak 'usage: $fh->clearerr()'; + seek($_[0], 0, 1); +} + +sub seek { + @_ == 3 or croak 'usage: $fh->seek(POS, WHENCE)'; + seek($_[0], $_[1], $_[2]); +} + +sub tell { + @_ == 1 or croak 'usage: $fh->tell()'; + tell($_[0]); +} + sub print { - local($this) = shift; + @_ or croak 'usage: $fh->print([ARGS])'; + my $this = shift; print $this @_; } sub printf { - local($this) = shift; + @_ or croak 'usage: $fh->printf([ARGS])'; + my $this = shift; printf $this @_; } sub getline { - local($this) = shift; - croak "usage: FileHandle::getline()" if @_; + @_ == 1 or croak 'usage: $fh->getline'; + my $this = shift; return scalar <$this>; } sub getlines { - local($this) = shift; - croak "usage: FileHandle::getline()" if @_; - croak "can't call FileHandle::getlines in a scalar context" if wantarray; + @_ == 1 or croak 'usage: $fh->getline()'; + my $this = shift; + wantarray or croak "Can't call FileHandle::getlines in a scalar context"; return <$this>; -} +} + +################################################ +## State modification functions. +## sub autoflush { - local($old) = select($_[0]); - local($prev) = $OUTPUT_AUTOFLUSH; + my $old = new SelectSaver qualify($_[0], caller); + my $prev = $OUTPUT_AUTOFLUSH; $OUTPUT_AUTOFLUSH = @_ > 1 ? $_[1] : 1; - select($old); $prev; } sub output_field_separator { - local($old) = select($_[0]); - local($prev) = $OUTPUT_FIELD_SEPARATOR; + my $old = new SelectSaver qualify($_[0], caller); + my $prev = $OUTPUT_FIELD_SEPARATOR; $OUTPUT_FIELD_SEPARATOR = $_[1] if @_ > 1; - select($old); $prev; } sub output_record_separator { - local($old) = select($_[0]); - local($prev) = $OUTPUT_RECORD_SEPARATOR; + my $old = new SelectSaver qualify($_[0], caller); + my $prev = $OUTPUT_RECORD_SEPARATOR; $OUTPUT_RECORD_SEPARATOR = $_[1] if @_ > 1; - select($old); $prev; } sub input_record_separator { - local($old) = select($_[0]); - local($prev) = $INPUT_RECORD_SEPARATOR; + my $old = new SelectSaver qualify($_[0], caller); + my $prev = $INPUT_RECORD_SEPARATOR; $INPUT_RECORD_SEPARATOR = $_[1] if @_ > 1; - select($old); $prev; } sub input_line_number { - local($old) = select($_[0]); - local($prev) = $INPUT_LINE_NUMBER; + my $old = new SelectSaver qualify($_[0], caller); + my $prev = $INPUT_LINE_NUMBER; $INPUT_LINE_NUMBER = $_[1] if @_ > 1; - select($old); $prev; } sub format_page_number { - local($old) = select($_[0]); - local($prev) = $FORMAT_PAGE_NUMBER; + my $old = new SelectSaver qualify($_[0], caller); + my $prev = $FORMAT_PAGE_NUMBER; $FORMAT_PAGE_NUMBER = $_[1] if @_ > 1; - select($old); $prev; } sub format_lines_per_page { - local($old) = select($_[0]); - local($prev) = $FORMAT_LINES_PER_PAGE; + my $old = new SelectSaver qualify($_[0], caller); + my $prev = $FORMAT_LINES_PER_PAGE; $FORMAT_LINES_PER_PAGE = $_[1] if @_ > 1; - select($old); $prev; } sub format_lines_left { - local($old) = select($_[0]); - local($prev) = $FORMAT_LINES_LEFT; + my $old = new SelectSaver qualify($_[0], caller); + my $prev = $FORMAT_LINES_LEFT; $FORMAT_LINES_LEFT = $_[1] if @_ > 1; - select($old); $prev; } sub format_name { - local($old) = select($_[0]); - local($prev) = $FORMAT_NAME; - $FORMAT_NAME = $_[1] if @_ > 1; - select($old); + my $old = new SelectSaver qualify($_[0], caller); + my $prev = $FORMAT_NAME; + $FORMAT_NAME = qualify($_[1], caller) if @_ > 1; $prev; } sub format_top_name { - local($old) = select($_[0]); - local($prev) = $FORMAT_TOP_NAME; - $FORMAT_TOP_NAME = $_[1] if @_ > 1; - select($old); + my $old = new SelectSaver qualify($_[0], caller); + my $prev = $FORMAT_TOP_NAME; + $FORMAT_TOP_NAME = qualify($_[1], caller) if @_ > 1; $prev; } sub format_line_break_characters { - local($old) = select($_[0]); - local($prev) = $FORMAT_LINE_BREAK_CHARACTERS; + my $old = new SelectSaver qualify($_[0], caller); + my $prev = $FORMAT_LINE_BREAK_CHARACTERS; $FORMAT_LINE_BREAK_CHARACTERS = $_[1] if @_ > 1; - select($old); $prev; } sub format_formfeed { - local($old) = select($_[0]); - local($prev) = $FORMAT_FORMFEED; + my $old = new SelectSaver qualify($_[0], caller); + my $prev = $FORMAT_FORMFEED; $FORMAT_FORMFEED = $_[1] if @_ > 1; - select($old); $prev; } - -# --- cacheout functions --- - -# Open in their package. - -sub cacheout_open { - my $pack = caller(1); - open(*{$pack . '::' . $_[0]}, $_[1]); -} - -sub cacheout_close { - my $pack = caller(1); - close(*{$pack . '::' . $_[0]}); -} - -# But only this sub name is visible to them. - -sub cacheout { - ($file) = @_; - if (!$cacheout_maxopen){ - if (open(PARAM,'/usr/include/sys/param.h')) { - local($.); - while (<PARAM>) { - $cacheout_maxopen = $1 - 4 - if /^\s*#\s*define\s+NOFILE\s+(\d+)/; - } - close PARAM; - } - $cacheout_maxopen = 16 unless $cacheout_maxopen; - } - if (!$isopen{$file}) { - if (++$cacheout_numopen > $cacheout_maxopen) { - local(@lru) = sort {$isopen{$a} <=> $isopen{$b};} keys(%isopen); - splice(@lru, $cacheout_maxopen / 3); - $cacheout_numopen -= @lru; - for (@lru) { &cacheout_close($_); delete $isopen{$_}; } - } - &cacheout_open($file, ($saw{$file}++ ? '>>' : '>') . $file) - || croak("Can't create $file: $!"); - } - $isopen{$file} = ++$cacheout_seq; -} - -$cacheout_seq = 0; -$cacheout_numopen = 0; - 1; |