File Coverage

blib/lib/No/Worries/File.pm
Criterion Covered Total %
statement 99 99 100.0
branch 34 44 77.2
condition 2 6 33.3
subroutine 12 12 100.0
pod 2 2 100.0
total 149 163 91.4


line stmt bran cond sub pod time code
1             #+##############################################################################
2             # #
3             # File: No/Worries/File.pm #
4             # #
5             # Description: file handling without worries #
6             # #
7             #-##############################################################################
8              
9             #
10             # module definition
11             #
12              
13             package No::Worries::File;
14 15     15   1102772 use strict;
  15         44  
  15         557  
15 15     15   105 use warnings;
  15         44  
  15         1791  
16             our $VERSION = "1.5";
17             our $REVISION = sprintf("%d.%02d", q$Revision: 1.18 $ =~ /(\d+)\.(\d+)/);
18              
19             #
20             # used modules
21             #
22              
23 15     15   4102 use No::Worries qw($_IntegerRegexp);
  15         64  
  15         95  
24 15     15   4432 use No::Worries::Die qw(dief);
  15         70  
  15         124  
25 15     15   148 use No::Worries::Export qw(export_control);
  15         45  
  15         127  
26 15     15   122 use Params::Validate qw(validate :types);
  15         43  
  15         23546  
27              
28             #
29             # global variables
30             #
31              
32             our($DefaultBufSize);
33              
34             #
35             # open() helper
36             #
37              
38             sub _open ($$$) {
39 48     48   1275 my($path, $mode, $opt) = @_;
40 48         111 my($fh);
41              
42 48 100       170 return($opt->{handle}) if $opt->{handle};
43             ## no critic 'InputOutput::RequireBriefOpen'
44 46 100       2403 open($fh, $mode, $path) or dief("cannot open(%s): %s", $path, $!);
45 45 100       288 if ($opt->{binmode}) {
    100          
46             binmode($fh, $opt->{binmode})
47 2 50       15 or dief("cannot binmode(%s, %s): %s", $path, $opt->{binmode}, $!);
48             } elsif ($opt->{binary}) {
49 2 50       7 binmode($fh)
50             or dief("cannot binmode(%s): %s", $path, $!);
51             }
52 45         173 return($fh);
53             }
54              
55             #
56             # sysread() helper
57             #
58              
59             sub _read ($$$$) {
60 22     22   135 my($path, $fh, $data, $bufsize) = @_;
61 22         92 my($done, $ref, $result);
62              
63 22         60 $done = -1;
64 22 100       130 $ref = $data ? ref($data) : "";
65 22 100       152 if ($ref eq "SCALAR") {
    100          
66             # by reference
67 1         2 ${ $data } = "";
  1         2  
68 1         4 while ($done) {
69 2         3 $done = sysread($fh, ${ $data }, $bufsize, length(${ $data }));
  2         5  
  2         8  
70 2 50       7 dief("cannot sysread(%s): %s", $path, $!)
71             unless defined($done);
72             }
73 1         1 $result = $data;
74             } elsif ($ref eq "CODE") {
75             # by code
76 1         6 while ($done) {
77 2         5 $result = "";
78 2         9 $done = sysread($fh, $result, $bufsize);
79 2 50       5 dief("cannot sysread(%s): %s", $path, $!)
80             unless defined($done);
81 2 100       7 $data->($result) if $done;
82             }
83 1         3 $result = $data->("");
84             } else {
85             # normal
86 20         112 $result = "";
87 20         75 while ($done) {
88 39         1315 $done = sysread($fh, $result, $bufsize, length($result));
89 39 50       179 dief("cannot sysread(%s): %s", $path, $!)
90             unless defined($done);
91             }
92             }
93 22         89 return(\$result);
94             }
95              
96             #
97             # syswrite() helper
98             #
99              
100             sub _write ($$$$) {
101 25     25   114 my($path, $fh, $data, $bufsize) = @_;
102 25         54 my($ref, $offset, $length, $done, $chunk);
103              
104 25         53 $offset = 0;
105 25         61 $ref = ref($data);
106 25 100       114 if ($ref eq "SCALAR") {
    100          
107             # by reference
108 2         3 $length = length(${ $data });
  2         4  
109 2         6 while ($length) {
110 2         4 $done = syswrite($fh, ${ $data }, $bufsize, $offset);
  2         41  
111 2 50       7 dief("cannot syswrite(%s): %s", $path, $!)
112             unless defined($done);
113 2         4 $length -= $done;
114 2         5 $offset += $done;
115             }
116             } elsif ($ref eq "CODE") {
117             # by code
118 1         2 while (1) {
119 4         9 $chunk = $data->();
120 4         21 $length = length($chunk);
121 4 100       10 last unless $length;
122 3         3 $offset = 0;
123 3         6 while ($length) {
124 3         25 $done = syswrite($fh, $chunk, $bufsize, $offset);
125 3 50       7 dief("cannot syswrite(%s): %s", $path, $!)
126             unless defined($done);
127 3         5 $length -= $done;
128 3         6 $offset += $done;
129             }
130             }
131             } else {
132             # normal
133 22         47 $length = length($data);
134 22         69 while ($length) {
135 21         754 $done = syswrite($fh, $data, $bufsize, $offset);
136 21 50       97 dief("cannot syswrite(%s): %s", $path, $!)
137             unless defined($done);
138 21         31 $length -= $done;
139 21         95 $offset += $done;
140             }
141             }
142             }
143              
144             #
145             # common read/write options
146             #
147              
148             my %file_rw_options = (
149             binary => { optional => 1, type => BOOLEAN },
150             binmode => { optional => 1, type => SCALAR },
151             bufsize => { optional => 1, type => SCALAR, regex => $_IntegerRegexp },
152             handle => { optional => 1, type => HANDLE },
153             );
154              
155             #
156             # read from a file
157             #
158              
159             my %file_read_options = (%file_rw_options,
160             data => { optional => 1, type => SCALARREF | CODEREF },
161             );
162              
163             sub file_read ($@) {
164 23     23 1 24956 my($path, %option, $fh, $result);
165              
166 23         101 $path = shift(@_);
167 23 100       217 %option = validate(@_, \%file_read_options) if @_;
168 23   33     248 $option{bufsize} ||= $DefaultBufSize;
169 23         138 $fh = _open($path, "<", \%option);
170 22         196 $result = _read($path, $fh, $option{data}, $option{bufsize});
171 22 50       235 close($fh) or dief("cannot close(%s): %s", $path, $!);
172 22         334 return(${ $result });
  22         431  
173             }
174              
175             #
176             # write to a file
177             #
178              
179             my %file_write_options = (%file_rw_options,
180             data => { optional => 0, type => SCALAR | SCALARREF | CODEREF },
181             );
182              
183             sub file_write ($@) {
184 25     25 1 41174 my($path, %option, $fh);
185              
186 25         74 $path = shift(@_);
187 25         754 %option = validate(@_, \%file_write_options);
188 25   33     292 $option{bufsize} ||= $DefaultBufSize;
189 25         93 $fh = _open($path, ">", \%option);
190 25         124 _write($path, $fh, $option{data}, $option{bufsize});
191 25 50       291 close($fh) or dief("cannot close(%s): %s", $path, $!);
192             }
193              
194             #
195             # module initialization
196             #
197              
198             $DefaultBufSize = 1_048_576; # 1MB
199              
200             #
201             # export control
202             #
203              
204             sub import : method {
205 15     15   185 my($pkg, %exported);
206              
207 15         54 $pkg = shift(@_);
208 15         131 grep($exported{$_}++, map("file_$_", qw(read write)));
209 15         112 export_control(scalar(caller()), $pkg, \%exported, @_);
210             }
211              
212             1;
213              
214             __DATA__