xref: /openbsd-src/gnu/usr.bin/perl/ext/FileCache/lib/FileCache.pm (revision 5759b3d249badf144a6240f7eec4dcf9df003e6b)
1b39c5158Smillertpackage FileCache;
2b39c5158Smillert
3*5759b3d2Safresh1our $VERSION = '1.10';
4b39c5158Smillert
5b39c5158Smillert=head1 NAME
6b39c5158Smillert
7b39c5158SmillertFileCache - keep more files open than the system permits
8b39c5158Smillert
9b39c5158Smillert=head1 SYNOPSIS
10b39c5158Smillert
11b39c5158Smillert    no strict 'refs';
12b39c5158Smillert
13b39c5158Smillert    use FileCache;
14b39c5158Smillert    # or
15b39c5158Smillert    use FileCache maxopen => 16;
16b39c5158Smillert
17b39c5158Smillert    cacheout $mode, $path;
18b39c5158Smillert    # or
19b39c5158Smillert    cacheout $path;
20b39c5158Smillert    print $path @data;
21b39c5158Smillert
22b39c5158Smillert    $fh = cacheout $mode, $path;
23b39c5158Smillert    # or
24b39c5158Smillert    $fh = cacheout $path;
25b39c5158Smillert    print $fh @data;
26b39c5158Smillert
27b39c5158Smillert=head1 DESCRIPTION
28b39c5158Smillert
29b39c5158SmillertThe C<cacheout> function will make sure that there's a filehandle open
30b39c5158Smillertfor reading or writing available as the pathname you give it. It
31b39c5158Smillertautomatically closes and re-opens files if you exceed your system's
32b39c5158Smillertmaximum number of file descriptors, or the suggested maximum I<maxopen>.
33b39c5158Smillert
34b39c5158Smillert=over
35b39c5158Smillert
36b39c5158Smillert=item cacheout EXPR
37b39c5158Smillert
38b39c5158SmillertThe 1-argument form of cacheout will open a file for writing (C<< '>' >>)
39b39c5158Smillerton it's first use, and appending (C<<< '>>' >>>) thereafter.
40b39c5158Smillert
41b39c5158SmillertReturns EXPR on success for convenience. You may neglect the
42b39c5158Smillertreturn value and manipulate EXPR as the filehandle directly if you prefer.
43b39c5158Smillert
44b39c5158Smillert=item cacheout MODE, EXPR
45b39c5158Smillert
46b39c5158SmillertThe 2-argument form of cacheout will use the supplied mode for the initial
47b39c5158Smillertand subsequent openings. Most valid modes for 3-argument C<open> are supported
48b39c5158Smillertnamely; C<< '>' >>, C<< '+>' >>, C<< '<' >>, C<< '<+' >>, C<<< '>>' >>>,
49b39c5158SmillertC< '|-' > and C< '-|' >
50b39c5158Smillert
51b39c5158SmillertTo pass supplemental arguments to a program opened with C< '|-' > or C< '-|' >
52b39c5158Smillertappend them to the command string as you would system EXPR.
53b39c5158Smillert
54b39c5158SmillertReturns EXPR on success for convenience. You may neglect the
55b39c5158Smillertreturn value and manipulate EXPR as the filehandle directly if you prefer.
56b39c5158Smillert
57b39c5158Smillert=back
58b39c5158Smillert
59b39c5158Smillert=head1 CAVEATS
60b39c5158Smillert
61b39c5158SmillertWhile it is permissible to C<close> a FileCache managed file,
62b39c5158Smillertdo not do so if you are calling C<FileCache::cacheout> from a package other
63b39c5158Smillertthan which it was imported, or with another module which overrides C<close>.
64b39c5158SmillertIf you must, use C<FileCache::cacheout_close>.
65b39c5158Smillert
66b39c5158SmillertAlthough FileCache can be used with piped opens ('-|' or '|-') doing so is
67b39c5158Smillertstrongly discouraged.  If FileCache finds it necessary to close and then reopen
68b39c5158Smillerta pipe, the command at the far end of the pipe will be reexecuted - the results
69b39c5158Smillertof performing IO on FileCache'd pipes is unlikely to be what you expect.  The
70b39c5158Smillertability to use FileCache on pipes may be removed in a future release.
71b39c5158Smillert
72b39c5158SmillertFileCache does not store the current file offset if it finds it necessary to
73b39c5158Smillertclose a file.  When the file is reopened, the offset will be as specified by the
74b39c5158Smillertoriginal C<open> file mode.  This could be construed to be a bug.
75b39c5158Smillert
76b39c5158SmillertThe module functionality relies on symbolic references, so things will break
77b39c5158Smillertunder 'use strict' unless 'no strict "refs"' is also specified.
78b39c5158Smillert
79b39c5158Smillert=head1 BUGS
80b39c5158Smillert
81b39c5158SmillertF<sys/param.h> lies with its C<NOFILE> define on some systems,
82b39c5158Smillertso you may have to set I<maxopen> yourself.
83b39c5158Smillert
84b39c5158Smillert=cut
85b39c5158Smillert
86b39c5158Smillertrequire 5.006;
87b39c5158Smillertuse Carp;
88b39c5158Smillertuse strict;
89b39c5158Smillertno strict 'refs';
90b39c5158Smillert
91b39c5158Smillert# These are not C<my> for legacy reasons.
92b39c5158Smillert# Previous versions requested the user set $cacheout_maxopen by hand.
93b39c5158Smillert# Some authors fiddled with %saw to overcome the clobber on initial open.
94*5759b3d2Safresh1our %saw;
95*5759b3d2Safresh1our $cacheout_maxopen = 16;
96b39c5158Smillert
976fb12b70Safresh1use parent 'Exporter';
98b39c5158Smillertour @EXPORT = qw[cacheout cacheout_close];
99b39c5158Smillert
100b39c5158Smillert
101b39c5158Smillertmy %isopen;
102b39c5158Smillertmy $cacheout_seq = 0;
103b39c5158Smillert
104b39c5158Smillertsub import {
105b39c5158Smillert    my ($pkg,%args) = @_;
106b39c5158Smillert
107b39c5158Smillert    # Use Exporter. %args are for us, not Exporter.
108b39c5158Smillert    # Make sure to up export_to_level, or we will import into ourselves,
109b39c5158Smillert    # rather than our calling package;
110b39c5158Smillert
111b39c5158Smillert    __PACKAGE__->export_to_level(1);
112b39c5158Smillert    Exporter::import( $pkg );
113b39c5158Smillert
114b39c5158Smillert    # Truth is okay here because setting maxopen to 0 would be bad
115b39c5158Smillert    return $cacheout_maxopen = $args{maxopen} if $args{maxopen};
116b39c5158Smillert
117b39c5158Smillert    # XXX This code is crazy.  Why is it a one element foreach loop?
118b39c5158Smillert    # Why is it using $param both as a filename and filehandle?
119b39c5158Smillert    foreach my $param ( '/usr/include/sys/param.h' ){
120b39c5158Smillert      if (open($param, '<', $param)) {
121b39c5158Smillert	local ($_, $.);
122b39c5158Smillert	while (<$param>) {
123b39c5158Smillert	  if( /^\s*#\s*define\s+NOFILE\s+(\d+)/ ){
124b39c5158Smillert	    $cacheout_maxopen = $1 - 4;
125b39c5158Smillert	    close($param);
126b39c5158Smillert	    last;
127b39c5158Smillert	  }
128b39c5158Smillert	}
129b39c5158Smillert	close $param;
130b39c5158Smillert      }
131b39c5158Smillert    }
132b39c5158Smillert    $cacheout_maxopen ||= 16;
133b39c5158Smillert}
134b39c5158Smillert
135b39c5158Smillert# Open in their package.
136b39c5158Smillertsub cacheout_open {
137b39c5158Smillert  return open(*{caller(1) . '::' . $_[1]}, $_[0], $_[1]) && $_[1];
138b39c5158Smillert}
139b39c5158Smillert
140b39c5158Smillert# Close in their package.
141b39c5158Smillertsub cacheout_close {
142b39c5158Smillert  # Short-circuit in case the filehandle disappeared
143b39c5158Smillert  my $pkg = caller($_[1]||0);
144b39c5158Smillert  defined fileno(*{$pkg . '::' . $_[0]}) &&
145b39c5158Smillert    CORE::close(*{$pkg . '::' . $_[0]});
146b39c5158Smillert  delete $isopen{$_[0]};
147b39c5158Smillert}
148b39c5158Smillert
149b39c5158Smillert# But only this sub name is visible to them.
150b39c5158Smillertsub cacheout {
151b39c5158Smillert    my($mode, $file, $class, $ret, $ref, $narg);
152b39c5158Smillert    croak "Not enough arguments for cacheout"  unless $narg = scalar @_;
153b39c5158Smillert    croak "Too many arguments for cacheout"    if $narg > 2;
154b39c5158Smillert
155b39c5158Smillert    ($mode, $file) = @_;
156b39c5158Smillert    ($file, $mode) = ($mode, $file) if $narg == 1;
157b39c5158Smillert    croak "Invalid mode for cacheout" if $mode &&
158b39c5158Smillert      ( $mode !~ /^\s*(?:>>|\+?>|\+?<|\|\-|)|\-\|\s*$/ );
159b39c5158Smillert
160b39c5158Smillert    # Mode changed?
161b39c5158Smillert    if( $isopen{$file} && ($mode||'>') ne $isopen{$file}->[1] ){
162b39c5158Smillert      &cacheout_close($file, 1);
163b39c5158Smillert    }
164b39c5158Smillert
165b39c5158Smillert    if( $isopen{$file}) {
166b39c5158Smillert      $ret = $file;
167b39c5158Smillert      $isopen{$file}->[0]++;
168b39c5158Smillert    }
169b39c5158Smillert    else{
170b39c5158Smillert      if( scalar keys(%isopen) > $cacheout_maxopen -1 ) {
171b39c5158Smillert	my @lru = sort{ $isopen{$a}->[0] <=> $isopen{$b}->[0] } keys(%isopen);
172b39c5158Smillert	$cacheout_seq = 0;
173b39c5158Smillert	$isopen{$_}->[0] = $cacheout_seq++ for
174b39c5158Smillert	  splice(@lru, int($cacheout_maxopen / 3)||$cacheout_maxopen);
175b39c5158Smillert	&cacheout_close($_, 1) for @lru;
176b39c5158Smillert      }
177b39c5158Smillert
178b39c5158Smillert      unless( $ref ){
179b39c5158Smillert	$mode ||= $saw{$file} ? '>>' : ($saw{$file}=1, '>');
180b39c5158Smillert      }
181b39c5158Smillert      #XXX should we just return the value from cacheout_open, no croak?
182b39c5158Smillert      $ret = cacheout_open($mode, $file) or croak("Can't create $file: $!");
183b39c5158Smillert
184b39c5158Smillert      $isopen{$file} = [++$cacheout_seq, $mode];
185b39c5158Smillert    }
186b39c5158Smillert    return $ret;
187b39c5158Smillert}
188b39c5158Smillert1;
189