File Coverage

blib/lib/IO/Util.pm
Criterion Covered Total %
statement 155 182 85.1
branch 60 86 69.7
condition 23 40 57.5
subroutine 25 39 64.1
pod 7 10 70.0
total 270 357 75.6


line stmt bran cond sub pod time code
1             package IO::Util ;
2             $VERSION = 1.5 ;
3 7     7   218728 use 5.006_001 ;
  7         28  
  7         294  
4 7     7   41 use strict ;
  7         13  
  7         261  
5              
6             # This file uses the "Perlish" coding style
7             # please read http://perl.4pro.net/perlish_coding_style.html
8              
9             ; use Carp
10 7     7   46 ; $Carp::Internal{+__PACKAGE__}++
  7         14  
  7         702  
11             ; use File::Spec
12              
13             ############# slurping files #############
14              
15 7     7   40 ; sub slurp
  7         11  
  7         16321  
16 10   100 10 1 5178 { my %a = map {ref||path => $_} @_
  11         65  
17 10 100 66     215 ; local $_ = defined $a{path} ? $a{path}
    100          
    100          
18             : defined $a{GLOB} ? $a{GLOB}
19             : (defined && length) ? $_
20             : croak 'Wrong file argument, died'
21 9   100     29 ; $a{SCALAR} ||= \ my $content
22 9 100 100     48 ; if ( ref eq 'GLOB'
23             || ref \$_ eq 'GLOB'
24             )
25 3         4 { ${$a{SCALAR}} = do { local $/; <$_> }
  3         6  
  3         10  
  3         57  
26             }
27             else
28 6 100       326 { open _ or croak "$^E, died"
29 5         9 ; ${$a{SCALAR}} = do { local $/; <_> }
  5         11  
  5         12  
  5         93  
30 5         46 ; close _
31             }
32 8         26 ; $a{SCALAR}
33             }
34              
35              
36             ############# unique ids #############
37              
38             ; my %charset = ( base34 => [ 1..9, 'A'..'N', 'P'..'Z' ]
39             , base62 => [ 0..9, 'a'..'z', 'A'..'Z' ]
40             )
41             ; my $separator = '_'
42              
43             ; sub import
44             { return unless @_
45 6 50   6   66 ; require Exporter
46 6         30 ; our @ISA = 'Exporter'
  6         105  
47 6         28 ; our @EXPORT_OK = qw| capture
48             slurp
49             Tid Lid Uid
50             load_mml
51             |
52 6         15 ; my ($pkg, @subs) = @_
53 6 100       2198 ; require Time::HiRes if grep /id$/ , @subs
54 6 100       3264 ; require Sys::Hostname if grep /^Uid$/, @subs
55 6         14726 ; $pkg->export_to_level(1, @_)
56             }
57            
58             ; sub _convert
59 54     54   155 { my ( $num, $args ) = @_
60 54 50       191 ; my $chars = defined $$args{chars}
    100          
    100          
61             ? ref($$args{chars}) eq 'ARRAY'
62             ? $$args{chars}
63             : exists $charset{$$args{chars}}
64             ? $charset{$$args{chars}}
65             : croak 'Invalid chars, died'
66             : $charset{base34}
67 54         62 ; my $result = ''
68 54         67 ; my $dnum = @$chars
69 54         116 ; while ( $num > 0 )
70 278         444 { substr($result, 0, 0) = $$chars[ $num % $dnum]
71 278         585 ; $num = int($num/$dnum)
72             }
73 54         228 ; $result
74             }
75              
76             ; sub Tid
77             { require Time::HiRes
78 18     18 1 13703 ; my %args = @_
  18         2674  
79 18 50       44 ; my $sep = defined $args{separator} ? $args{separator} : $separator
80 18         60 ; my($sec, $usec) = Time::HiRes::gettimeofday()
81 18         24 ; my($new_sec, $new_usec)
82 18   33     20 ; do{ ($new_sec, $new_usec) = Time::HiRes::gettimeofday() }
  18         96  
83             until ( $new_usec != $usec || $new_sec != $sec )
84 18         48 ; join $sep, map _convert($_, \%args), $sec , $usec
85             }
86            
87             ; sub Lid
88 12     12 1 76 { my %args = @_
89 12 50       31 ; my $sep = defined $args{separator} ? $args{separator} : $separator
90 12         26 ; join $sep, _convert($$, \%args), Tid(@_)
91             }
92              
93             ; sub Uid
94             { require Sys::Hostname
95 6     6 1 5019 ; my %args = @_
  6         1464  
96 6 50       18 ; my $sep = defined $args{separator} ? $args{separator} : $separator
97 0         0 ; my $ip = sprintf '1%03d%03d%03d%03d'
98             , $args{IP}
99 6 50       29 ? @{$args{IP}}
100             : unpack( "C4", (gethostbyname(Sys::Hostname::hostname()))[4] )
101 6         2206 ; join $sep, _convert($ip, \%args), Lid(@_)
102             }
103              
104             ############# parsing cache #############
105              
106             ; our %PARSING_CACHE
107              
108             ; sub _path_mtime
109 0     0   0 { my $path = File::Spec->rel2abs($_[0])
110 0 0       0 ; my $mtime = ( stat($path) )[9] or croak "$^E, died"
111 0         0 ; $path, $mtime
112             }
113              
114             ; sub _set_parsing_cache
115 0     0   0 { my $type = shift
116 0         0 ; my ($path, $mtime) = &_path_mtime
117 0         0 ; $PARSING_CACHE{$type}{$path}{mtime} = $mtime
118 0         0 ; $PARSING_CACHE{$type}{$path}{value} = $_[1]
119             }
120            
121             ; sub _get_parsing_cache
122 0     0   0 { my $type = shift
123 0         0 ; my ($path, $mtime) = &_path_mtime
124 0 0 0     0 ; exists $PARSING_CACHE{$type}{$path} # if it is cached
125             &&! $mtime > $PARSING_CACHE{$type}{$path}{mtime} # and not old
126             ? $PARSING_CACHE{$type}{$path}{value}
127             : undef
128             }
129              
130             ; sub _purge_parsing_cache
131 0     0   0 { my $type = shift
132 0 0       0 ; @_ || return delete $PARSING_CACHE{$type}
133 0         0 ; map delete $PARSING_CACHE{$type}{$_}
134             , map File::Spec->rel2abs($_)
135             , @_
136             }
137              
138             ############# loading MML #############
139              
140              
141             ; my %parser_re = ( '<>' =>
142             qr/ \G(.*?) # elements and text outside blocks
143             (?
144             (\w+)([^>]*?) # id + attributes
145             (? # not escaped '>'
146             (.*?) # content
147             <\/\2> # end
148             /xs
149             , '[]' =>
150             qr/ \G(.*?) # elements and text outside blocks
151             (?
152             (\w+)([^\]]*?) # id + attributes
153             (?
154             (.*?) # content
155             \[\/\2\] # end
156             /xs
157             , '{}' =>
158             qr/ \G(.*?) # elements and text outside blocks
159             (?
160             (\w+)([^\}]*?) # id + attributes
161             (?'
162             (.*?) # content
163             \{\/\2\} # end
164             /xs
165             )
166            
167             ; my %not_escaped_re = ( '<>' => qr/( (? )/xs
168             , '[]' => qr/( (?
169             , '{}' => qr/( (?
170             )
171            
172             ; my %comment_re = ( '<>' => qr//xs
173             , '[]' => qr/\[!--.*?--\]/xs
174             , '{}' => qr/\{!--.*?--\}/xs
175             )
176            
177             ; sub load_mml
178 40     40 1 30428 { my $mml = shift
179 40 50       185 ; my $opt = ref $_[0] eq 'HASH' ? $_[0] : { @_ }
180 40 50 33     136 ; if ( $$opt{optional} && not ref $mml )
181 0 0       0 { return unless -f $mml
182             }
183 40         50 ; my $struct
184 40 50       123 ; defined $$opt{cache} or $$opt{cache} = 1
185 40 50 33     224 ; if ( $$opt{cache} && not ref $mml )
186 0         0 { $struct = _get_parsing_cache 'mml_struct', $mml
187 0 0       0 ; return $struct if $struct
188             }
189 40 100       247 ; defined $$opt{strict} or $$opt{strict} = 1
190 40 50       99 ; my $content = ref $mml eq 'SCALAR' ? $mml : slurp $mml
191 40   100     129 ; $$opt{markers} ||= '<>'
192 40         250 ; $$content =~ s/$comment_re{$$opt{markers}}//g
193 34         81 ; $struct = parse_mml( '', $content, $opt )
194 34 100       104 ; unless ( $$opt{keep_root} )
195 32 50       89 { ref $struct eq 'HASH'
196             or croak 'Parsed structure is not a HASH reference: '
197             . 'check your MML or set keep_root, died'
198 32         97 ; $struct = $$struct{(keys %$struct)[0]}
199             }
200 34 50 33     201 ; $$opt{cache} &&! ref($mml)
201             && _set_parsing_cache 'mml_struct', $mml, $struct
202 34         143 ; $struct
203             }
204            
205             ; sub parse_mml
206 236     236 1 389 { my ($id, $mml, $opt) = @_
207 236         252 ; my ($node, $control, $no_data)
208 236         1570 ; while ( $$mml =~ /$parser_re{$$opt{markers}}/g )
209 204         246 { $no_data = 1
210 204         643 ; my ( $garb, $child_id, $attr, $child_mml ) = ($1, $2, $3, $4)
211 204 100       505 ; if ( $$opt{strict} )
212 168 50       378 { $garb =~ /\S/ && croak
213             "Garbage '$garb' found parsing element '$child_id', died"
214 168 50       342 ; length $attr && croak
215             "Attributes '$attr' found parsing element '$child_id', died"
216             }
217 204         584 ; my ($k) = grep $child_id =~ /$_/
218 204         272 , keys %{$$opt{handler}}
219 204 100       478 ; my $parser_sub = defined $k
220             ? $$opt{handler}{$k}
221             : \&parse_mml
222 7         1852 ; my $child = do{ no strict 'refs'
  204         226  
223 7     7   59 ; &$parser_sub($child_id, \$child_mml, $opt)
  7         14  
  204         443  
224             }
225 204 100       568 ; if ( defined $child )
226 202 100       399 { if ( defined $$control{$child_id} )
227 20 100       42 { if ( $$control{$child_id} > 1 )
228 8         14 { push @{$$node{$child_id}}, $child
  8         25  
229             }
230             else
231 12         39 { $$node{$child_id} = [ $$node{$child_id}, $child ]
232             }
233             }
234             else
235 182         413 { $$node{$child_id} = $child
236             }
237 202         1640 ; $$control{$child_id} ++
238             }
239             }
240 236 100       590 ; return $node if $no_data
241 142 50 66     1065 ; $$opt{strict} && ( $$mml =~ $not_escaped_re{$$opt{markers}} )
242             and croak "Not escaped '$1' found in '$id' data, died"
243 142         253 ; $$mml =~ s/\\(.)/$1/g # unescape
244 142         590 ; my ($k) = grep $id =~ /$_/
245 142         160 , keys %{$$opt{filter}}
246 142 100       350 ; my $filter_sub = $$opt{filter}{$k} if defined $k
247             ; $filter_sub
248 142 100       396 ? do{ local $_ = $$mml
  46         82  
249 7         2420 ; no strict 'refs'
250 7     7   37 ; &{$filter_sub}($id, $mml, $opt)
  7         13  
  46         61  
  46         121  
251             }
252             : $$mml
253             }
254              
255             ; sub TRIM_BLANKS # filter
256 32     32 0 153 { s/^\s+//gm
257 32         87 ; s/\s+$//gm
258 32         99 ; $_
259             }
260              
261             ; sub ONE_LINE # filter
262 30     30 0 98 { s/\n+/ /g
263 30         99 ; $_
264             }
265              
266             ; sub SPLIT_LINES # handler
267 2     2 0 8 { [ split /\n+/, parse_mml @_ ]
268             }
269              
270             ############## capturing output #############
271              
272             ; sub capture (&;@)
273 9     9 1 3079 { my %a = map {ref, $_} @_
  14         49  
274 9   66     46 ; $a{GLOB} ||= select
275 9   100     39 ; local $IO::Util::WriteHandle::out_ref
276             = $a{SCALAR} || \ my $scalar
277 7         1014 ; no strict 'refs'
278 7 100   7   38 ; if ( my $tied = tied *{$a{GLOB}} )
  7         13  
  9         12  
  9         34  
279 2         4 { my $tied_class = ref $tied
280 2         4 ; bless $tied, 'IO::Util::WriteHandle'
281 2         5 ; $a{CODE}->()
282 2         4 ; bless $tied, $tied_class
283             }
284             else
285 7         9 { tie *{$a{GLOB}}, 'IO::Util::WriteHandle'
  7         35  
286 7         21 ; $a{CODE}->()
287 7         289 ; untie *{$a{GLOB}}
  7         25  
288             }
289 9         29 ; $IO::Util::WriteHandle::out_ref
290             }
291              
292              
293             ###############################
294             ; package IO::Util::WriteHandle
295             ; use strict
296 7     7   40 ; our $out_ref
  7         12  
  7         2888  
297              
298             ; sub TIEHANDLE
299 7     7   25 { bless \ my $dummy, $_[0]
300             }
301              
302             ; sub WRITE
303             { shift
304 2     2   12 ; my( $scalar, $len, $offset ) = @_
  2         3  
305 2   50     12 ; my $data = substr $scalar
      33        
306             , $offset || 0
307             , $len || length $scalar
308 2         16 ; $$out_ref .= $data
309 2         4 ; length $data
310             }
311              
312             ; sub PRINT
313             { shift
314 13 100   13   50 ; $$out_ref .= join defined $, ? $, : '', @_
  13         40  
315 13 100       32 ; $$out_ref .= $\ if defined $\
316 13         27 ; 1
317             }
318              
319             ; sub PRINTF
320             { shift
321 1     1   12 ; $$out_ref .= sprintf shift, @_
  1         5  
322 1         3 ; 1
323             }
324              
325 0     0   0 ; sub OPEN { 1 }
326 0     0   0 ; sub CLOSE { 1 }
327 0     0   0 ; sub FILENO { 1 }
328 0     0   0 ; sub BINMODE { 1 }
329 7     7   36 ; sub UNTIE { 1 }
330              
331             ###### not supported #######
332 0     0     ; sub READ { 0 }
333 0     0     ; sub READLINE { undef }
334 0     0     ; sub GETC { undef }
335 0     0     ; sub TELL {-1 }
336 0     0     ; sub SEEK { 0 }
337 0     0     ; sub EOF { 1 }
338              
339              
340             ; 1
341             __END__