File Coverage

blib/lib/File/Read.pm
Criterion Covered Total %
statement 54 62 87.1
branch 29 38 76.3
condition 0 4 0.0
subroutine 7 8 87.5
pod 1 1 100.0
total 91 113 80.5


line stmt bran cond sub pod time code
1             package File::Read;
2 2     2   50528 use strict;
  2         5  
  2         76  
3 2     2   11 use Carp;
  2         5  
  2         157  
4 2     2   1893 use File::Slurp ();
  2         32527  
  2         53  
5             require Exporter;
6              
7 2     2   20 { no strict;
  2         3  
  2         1643  
8             $VERSION = '0.0801';
9             @ISA = qw(Exporter);
10             @EXPORT = qw(read_file read_files);
11             }
12              
13             *read_files = \&read_file;
14              
15             =head1 NAME
16              
17             File::Read - Unique interface for reading one or more files
18              
19             =head1 VERSION
20              
21             Version 0.0801
22              
23             =head1 SYNOPSIS
24              
25             use File::Read;
26              
27             # read a file
28             $file = read_file($path);
29              
30             # read several files
31             @files = read_files(@paths);
32              
33             # aggregate several files
34             $file = read_files(@paths);
35              
36             # read a file as root, skip comments and blank lines
37             $file = read_file({ as_root => 1, skip_comments => 1, skip_blanks => 1 }, $path);
38              
39              
40             =head1 DESCRIPTION
41              
42             This module mainly proposes functions for reading one or more files,
43             with different options. See below for more details and examples.
44              
45             =head2 Rationale
46              
47             This module was created to address a quite specific need: reading many
48             files, some as a normal user and others as root, and eventually do a
49             little more processing, all while being at the same time compatible
50             with Perl 5.004. C<File::Slurp> addresses the first point, but not the
51             others, hence the creation of C<File::Read>. If you don't need reading
52             files as root or the post-processing features, then it's faster to
53             directly use C<File::Slurp>.
54              
55             =head1 EXPORT
56              
57             By default, this module exports all the functions documented afterhand.
58             It also recognizes import options. For example
59              
60             use File::Read 'err_mode=quiet';
61              
62             set C<read_file()>'s C<err_mode> option default value to C<"quiet">.
63              
64             =head1 FUNCTIONS
65              
66             =over
67              
68             =item B<read_file()>
69              
70             Read the files given in argument and return their content,
71             as as list, one element per file, when called in list context,
72             or as one big chunk of text when called in scalar context.
73             Options can be set using a hashref as first parameter.
74              
75             B<Options>
76              
77             =over
78              
79             =item *
80              
81             C<aggregate> controls how the function returns the content of the files
82             that were successfully read. By default, When set to true (default),
83             the function returns the content as a scalar; when set to false, the
84             content is returned as a list.
85              
86             =item *
87              
88             C<as_root> tells the function to read the given file(s) as root using
89             the command indicated by the C<cmd> option.
90              
91             =item *
92              
93             C<cmd> sets the shell command used for reading files as root. Default
94             is C<"sudo cat">. Therefore you need B<sudo(8)> and B<cat(1)> on your
95             system, and F<sudoers(5)> must be set so the user can execute B<cat(1)>.
96              
97             =item *
98              
99             C<err_mode> controls how the function behaves when an error occurs.
100             Available values are C<"croak">, C<"carp"> and C<"quiet">.
101             Default value is C<"croak">.
102              
103             =item *
104              
105             C<skip_comments> tells the functions to remove all comment lines from
106             the read files.
107              
108             =item *
109              
110             C<skip_blanks> tells the functions to remove all blank lines from
111             the read files.
112              
113             =item *
114              
115             C<to_ascii> tells the functions to convert the text to US-ASCII using
116             C<Text::Unidecode>. If this module is not available, non-ASCII data
117             are deleted.
118              
119             =back
120              
121             B<Examples>
122              
123             Just read a file:
124              
125             my $file = read_file($path);
126              
127             Read a file, returning it as list:
128              
129             my @file = read_file({ aggregate => 0 }, $path);
130              
131             Read a file, skipping comments:
132              
133             my $file = read_file({ skip_comments => 1 }, $path);
134              
135             Read several files, skipping blank lines and comments:
136              
137             my @files = read_file({ skip_comments => 1, skip_blanks => 1 }, @paths);
138              
139             =item B<read_files()>
140              
141             C<read_files()> is just an alias for C<read_file()> so that it look more
142             sane when reading several files.
143              
144             =cut
145              
146             my %defaults = (
147             aggregate => 1,
148             cmd => "sudo cat",
149             err_mode => 'croak',
150             skip_comments => 0,
151             skip_blanks => 0,
152             to_ascii => 0,
153             );
154              
155             sub import {
156 2     2   22 my ($module, @args) = @_;
157 2         4 my @new = ();
158              
159             # parse arguments
160 2         5 for my $arg (@args) {
161 1 50       5 if (index($arg, '=') >= 0) {
162 1         9 my ($opt, $val) = split '=', $arg;
163 1 50       8 $defaults{$opt} = $val if exists $defaults{$opt};
164             }
165             else {
166 0         0 push @new, $arg
167             }
168             }
169              
170 2         207 $module->export_to_level(1, $module, @new);
171             }
172              
173             sub read_file {
174 20 100   20 1 35037 my %opts = ref $_[0] eq 'HASH' ? %{+shift} : ();
  7         26  
175 20         46 my @paths = @_;
176 20         29 my @files = ();
177              
178             # check options
179 20         72 for my $opt (keys %defaults) {
180 120 100       371 $opts{$opt} = $defaults{$opt} unless defined $opts{$opt}
181             }
182              
183             # define error handler
184 20 100       215 $opts{err_mode} =~ /^(?:carp|croak|quiet)$/
185             or croak "error: Bad value '$opts{err_mode}' for option 'err_mode'";
186              
187             my %err_with = (
188             'carp' => \&carp,
189             'croak' => \&croak,
190 0     0   0 'quiet' => sub{},
191 19         135 );
192 19         45 my $err_sub = $err_with{$opts{err_mode}};
193              
194 19 100       402 $err_sub->("error: This function needs at least one path") unless @paths;
195              
196 17         26 for my $path (@paths) {
197 21         29 my @lines = ();
198 21         31 my $error = '';
199            
200             # first, read the file
201 21 100       40 if ($opts{as_root}) { # ... as root
202 1 50       5 my $redir = $opts{err_mode} eq 'quiet' ? '2>&1' : '';
203 1         6474 @lines = `$opts{cmd} $path $redir`;
204              
205 1 50       45 if ($?) {
206 0 0       0 if (not -f $path) {
    0          
207 0   0     0 $! = eval { require Errno; Errno->import(":POSIX"); ENOENT() } || 2
208             }
209             elsif (not -r $path) {
210 0   0     0 $! = eval { require Errno; Errno->import(":POSIX"); EACCES() } || 13
211             }
212             else {
213 0         0 $! = 1024
214             }
215 0         0 ($error = "$!") =~ s/ 1024//;
216             }
217             }
218             else { # ... as a normal user
219 20         26 @lines = eval { File::Slurp::read_file($path) };
  20         65  
220 20         3179 $error = $@;
221             }
222              
223             # if there's an error
224 21 100       152 $error and $err_sub->("error: $error");
225              
226             # if there's any content at all...
227 20 100       56 if (@lines) {
228             # ... then do some filtering work if asked so
229 18 100       51 @lines = grep { ! /^$/ } @lines if $opts{skip_blanks};
  16         33  
230 18 100       46 @lines = grep { ! /^\s*#/ } @lines if $opts{skip_comments};
  14         39  
231 18 100       52 @lines = map { _to_ascii($_) } @lines if $opts{to_ascii};
  2         6  
232             }
233              
234 20 100       122 push @files, $opts{aggregate} ? join('', @lines) : @lines;
235             }
236              
237             # how to return the content(s)?
238 16 100       208 return wantarray ? @files : join '', @files
239             }
240              
241              
242             # Text::Unidecode doesn't work on Perl 5.6
243             my $has_unidecode = eval "require 5.008; require Text::Unidecode; 1"; $@ = "";
244              
245             sub _to_ascii {
246             # use Text::Unidecode if available
247 2 50   2   8 if ($has_unidecode) {
248 0         0 return Text::Unidecode::unidecode(@_)
249             }
250             else { # use a simple s///
251 2         27 my @text = @_;
252 2         5 map { s/[^\x00-\x7f]//g } @text;
  2         22  
253             return @text
254 2         9 }
255             }
256              
257             =back
258              
259             =head1 DIAGNOSTICS
260              
261             =over
262              
263             =item C<Bad value '%s' for option '%s'>
264              
265             B<(E)> You gave a bad value for the indicated option. Please check the
266             documentation for the valid values.
267              
268             =item C<This function needs at least one path>
269              
270             B<(E)> You called a function without giving it argument.
271              
272             =back
273              
274             =head1 SEE ALSO
275              
276             L<File::Slurp>
277              
278             L<IO::All>
279              
280             =head1 AUTHOR
281              
282             SE<eacute>bastien Aperghis-Tramoni, C<< <sebastien at aperghis.net> >>
283              
284             =head1 BUGS
285              
286             Please report any bugs or feature requests to
287             C<bug-file-read at rt.cpan.org>, or through the web interface at
288             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=File-Read>.
289             I will be notified, and then you'll automatically be notified of progress on
290             your bug as I make changes.
291              
292             =head1 SUPPORT
293              
294             You can find documentation for this module with the perldoc command.
295              
296             perldoc File::Read
297              
298             You can also look for information at:
299              
300             =over 4
301              
302             =item *
303              
304             AnnoCPAN: Annotated CPAN documentation -
305             L<http://annocpan.org/dist/File-Read>
306              
307             =item *
308              
309             CPAN Ratings -
310             L<http://cpanratings.perl.org/d/File-Read>
311              
312             =item *
313              
314             RT: CPAN's request tracker -
315             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=File-Read>
316              
317             =item *
318              
319             Search CPAN -
320             L<http://search.cpan.org/dist/File-Read>
321              
322             =back
323              
324             =head1 COPYRIGHT & LICENSE
325              
326             Copyright (C) 2006, 2007 SE<eacute>bastien Aperghis-Tramoni, all rights reserved.
327              
328             This program is free software; you can redistribute it and/or modify it
329             under the same terms as Perl itself.
330              
331             =cut
332              
333             1; # End of File::Read