File Coverage

blib/lib/Dunce/Files.pm
Criterion Covered Total %
statement 18 18 100.0
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 24 24 100.0


line stmt bran cond sub pod time code
1             package Dunce::Files;
2              
3 1     1   37470 use strict;
  1         1  
  1         112  
4 1     1   6 use vars qw($VERSION @EXPORT);
  1         3  
  1         74  
5             $VERSION = '0.04';
6              
7 1     1   5 use base qw(Exporter);
  1         6  
  1         140  
8              
9             =pod
10              
11             =head1 NAME
12              
13             Dunce::Files - Protects against sloppy use of files.
14              
15              
16             =head1 SYNOPSIS
17              
18             use Dunce::Files;
19              
20             # open() warns you that you forgot to check if it worked.
21             open(FILE, $filename);
22             while( ) {
23             chop; # chop() warns you to use chomp() instead
24             print;
25             }
26             exit;
27              
28             # *FILE will warn you that you forgot to close it.
29              
30              
31             =head1 DESCRIPTION
32              
33             One of the most common programming mistakes is failing to check if an
34             open() worked. Same goes for other file and system operations. The
35             world outside your program is a scary, unreliable place, and things
36             you try to do with it might not always work.
37              
38             Dunce::Files makes trick versions of all file functions which do some
39             basic sanity checking.
40              
41             If used in void context (ie. you didn't check to see if it worked),
42             they will throw a warning. If the function returns a filehandle (like
43             open() and readdir()) that filehandle will complain if its never
44             closed, or if its never used.
45              
46             This module is useful for automated code auditing. Its also useful as
47             a dunce cap to place on junior programmers, make sure they're not
48             making silly mistakes.
49              
50             The list of overridden functions is:
51              
52             chdir
53             chmod
54             chop
55             chown
56             chroot
57             dbmopen
58             flock
59             link
60             mkdir
61             open
62             opendir
63             read
64             rename
65             rmdir
66             seek
67             seekdir
68             symlink
69             syscall
70             sysseek
71             system
72             syswrite
73             truncate
74             unlink
75             write
76              
77             =cut
78              
79             # Commonly abused file functions.
80 1     1   18 use vars qw(@File_Functions);
  1         1  
  1         85  
81             @File_Functions= qw(
82             chdir
83             chmod
84             chown
85             chroot
86             dbmopen
87             flock
88             link
89             mkdir
90             open
91             opendir
92             read
93             rename
94             rmdir
95             seek
96             seekdir
97             symlink
98             syscall
99             sysseek
100             syswrite
101             truncate
102             unlink
103             write
104             );
105             @EXPORT = (@File_Functions, 'chop');
106              
107 1     1   926 use Function::Override;
  1         1631  
  1         50  
108 1     1   7 use Carp;
  1         1  
  1         455  
109             foreach my $func (@File_Functions) {
110             override($func, sub {
111             my $wantarray = (caller(1))[5];
112             carp "You didn't check if $func() succeeded"
113             unless defined $wantarray;
114             }
115             );
116             }
117              
118              
119             =pod
120              
121             A few functions have some additional warnings:
122              
123             =over 4
124              
125             =item B
126              
127             Often, people will gratuitiously grant files more permissions than
128             they really need causing unnecessary security problems. Making
129             non-program files executable is a common mistake. Unnecessarily
130             giving world write permission is another. Dunce::Files will throw a
131             warning if either is detected.
132              
133             I
134              
135             =cut
136              
137             override('chmod',
138             sub {
139             my $mode = $_[0];
140             carp "Don't make files executable without a good reason"
141             if $mode & 0111;
142             carp "Don't make files writable by others without a good reason"
143             if $mode & 0003;
144              
145             my $wantarray = (caller(1))[5];
146             carp "You didn't check if chmod() succeeded"
147             unless defined $wantarray;
148             }
149             );
150              
151             =pod
152              
153             =item B
154              
155             chop() works a little differently. Using it in void context is fine,
156             but if it looks like you're using it to strip newlines it will throw a
157             warning reminding you about chomp().
158              
159             B chop() was non-overridable before 5.7.0, so this feature will
160             only work on that perl or newer.
161              
162             =cut
163              
164             # Alas, chop often isn't overridable.
165             if( prototype("CORE::chop") ) {
166             override('chop',
167             sub {
168             # Hmm, should this be \n or (\012|\015)?
169             if( grep { /\n$/s } @_ ? @_ : $_ ) {
170             carp "Looks like you're using chop() to strip newlines. ".
171             "Use chomp() instead.\n";
172             }
173             }
174             );
175             }
176              
177             =pod
178              
179             =item B
180              
181             dbmopen() will warn you if the hash argument you gave it already
182             contains data.
183              
184             =cut
185              
186             override('dbmopen',
187             sub {
188             my $hash = $_[0];
189             carp "Hash given to dbmopen() already contains data"
190             if keys %$hash;
191              
192             my $wantarray = (caller(1))[5];
193             carp "You didn't check if chmod() succeeded"
194             unless defined $wantarray;
195             }
196             );
197              
198             =pod
199              
200             =item B
201              
202             I
203              
204             open() will warn you if you don't close its filehandle explicitly
205             before the program ends. It will also warn if you give it an already
206             open filehandle.
207              
208             XXX I'd also like to have made sure $! is checked, but $! can't be
209             usefully tied. :(
210              
211             =pod
212              
213             #'#
214             # Waiting on postamble callbacks in Function::Override.
215              
216             =cut
217              
218             =item B
219              
220             I
221              
222             Same as open().
223              
224             =back
225              
226              
227             =head1 CAVEATS
228              
229             Because of the way perl compiles, the following code will produce a
230             'Name main::FILE used only once: possible typo' where it shouldn't.
231              
232             use Dunce::Files;
233             open(FILE, $filename) or die $!;
234             print ;
235              
236             Because open() is really Dunce::Files::open() and not the real open,
237             Perl doesn't realize that FILE is the filehandle *FILE, so it thinks
238             its only being used once.
239              
240             Turns out this is a useful feature. If you close FILE the warning
241             will go away, and you should have closed it in the first place.
242              
243              
244             =head1 TODO
245              
246             Make a flag to have Dunce::Files die instead of just warning.
247              
248             Complete Function::Override so I can finish open() and opendir().
249              
250              
251             =head1 AUTHOR
252              
253             Michael G Schwern with help from crysflame and
254             Simon Cozens. Thanks to Jay A. Kreibich for the chop() idea.
255              
256              
257             =head1 SEE ALSO
258              
259             L
260              
261             =cut
262              
263             1;