| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Mason::Util; | 
| 2 |  |  |  |  |  |  | $Mason::Util::VERSION = '2.23'; | 
| 3 | 21 |  |  | 21 |  | 18254 | use Carp; | 
|  | 21 |  |  |  |  | 33 |  | 
|  | 21 |  |  |  |  | 1551 |  | 
| 4 | 21 |  |  | 21 |  | 10009 | use Class::Unload; | 
|  | 21 |  |  |  |  | 78292 |  | 
|  | 21 |  |  |  |  | 731 |  | 
| 5 | 21 |  |  | 21 |  | 585 | use Class::Load; | 
|  | 21 |  |  |  |  | 21166 |  | 
|  | 21 |  |  |  |  | 930 |  | 
| 6 | 21 |  |  | 21 |  | 1768 | use Data::Dumper; | 
|  | 21 |  |  |  |  | 15316 |  | 
|  | 21 |  |  |  |  | 1531 |  | 
| 7 | 21 |  |  | 21 |  | 110 | use Fcntl qw( :DEFAULT :seek ); | 
|  | 21 |  |  |  |  | 25 |  | 
|  | 21 |  |  |  |  | 7841 |  | 
| 8 | 21 |  |  | 21 |  | 123 | use File::Find; | 
|  | 21 |  |  |  |  | 28 |  | 
|  | 21 |  |  |  |  | 1118 |  | 
| 9 | 21 |  |  | 21 |  | 10906 | use File::Spec::Functions (); | 
|  | 21 |  |  |  |  | 14088 |  | 
|  | 21 |  |  |  |  | 482 |  | 
| 10 | 21 |  |  | 21 |  | 14112 | use JSON; | 
|  | 21 |  |  |  |  | 186027 |  | 
|  | 21 |  |  |  |  | 105 |  | 
| 11 | 21 |  |  | 21 |  | 2944 | use Try::Tiny; | 
|  | 21 |  |  |  |  | 48 |  | 
|  | 21 |  |  |  |  | 1158 |  | 
| 12 | 21 |  |  | 21 |  | 103 | use strict; | 
|  | 21 |  |  |  |  | 28 |  | 
|  | 21 |  |  |  |  | 541 |  | 
| 13 | 21 |  |  | 21 |  | 84 | use warnings; | 
|  | 21 |  |  |  |  | 31 |  | 
|  | 21 |  |  |  |  | 675 |  | 
| 14 | 21 |  |  | 21 |  | 92 | use base qw(Exporter); | 
|  | 21 |  |  |  |  | 30 |  | 
|  | 21 |  |  |  |  | 31136 |  | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | our @EXPORT_OK = | 
| 17 |  |  |  |  |  |  | qw(can_load catdir catfile checksum combine_similar_paths dump_one_line find_wanted first_index is_absolute json_encode json_decode mason_canon_path read_file taint_is_on touch_file trim uniq write_file); | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | my $Fetch_Flags          = O_RDONLY | O_BINARY; | 
| 20 |  |  |  |  |  |  | my $Store_Flags          = O_WRONLY | O_CREAT | O_BINARY; | 
| 21 |  |  |  |  |  |  | my $File_Spec_Using_Unix = $File::Spec::ISA[0] eq 'File::Spec::Unix'; | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | # Map null, true and false to real Perl values | 
| 24 |  |  |  |  |  |  | if ( JSON->VERSION < 2 ) { | 
| 25 |  |  |  |  |  |  | $JSON::UnMapping = 1; | 
| 26 |  |  |  |  |  |  | } | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | sub can_load { | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | # Load $class_name if possible. Return 1 if successful, 0 if it could not be | 
| 31 |  |  |  |  |  |  | # found, and rethrow load error (other than not found). | 
| 32 |  |  |  |  |  |  | # | 
| 33 | 1624 |  |  | 1624 | 0 | 2323 | my ($class_name) = @_; | 
| 34 |  |  |  |  |  |  |  | 
| 35 | 1624 |  |  |  |  | 1984 | my $result; | 
| 36 |  |  |  |  |  |  | try { | 
| 37 | 1624 |  |  | 1624 |  | 53548 | Class::Load::load_class($class_name); | 
| 38 | 1181 |  |  |  |  | 34307 | $result = 1; | 
| 39 |  |  |  |  |  |  | } | 
| 40 |  |  |  |  |  |  | catch { | 
| 41 | 443 | 50 | 33 | 443 |  | 312138 | if ( /Can\'t locate .* in \@INC/ && !/Compilation failed/ ) { | 
| 42 | 443 |  |  |  |  | 1510 | $result = 0; | 
| 43 |  |  |  |  |  |  | } | 
| 44 |  |  |  |  |  |  | else { | 
| 45 | 0 |  |  |  |  | 0 | die $_; | 
| 46 |  |  |  |  |  |  | } | 
| 47 | 1624 |  |  |  |  | 11708 | }; | 
| 48 | 1624 |  |  |  |  | 24378 | return $result; | 
| 49 |  |  |  |  |  |  | } | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | sub catdir { | 
| 52 | 270 | 50 |  | 270 | 0 | 2224 | return $File_Spec_Using_Unix ? join( "/", @_ ) : File::Spec::Functions::catdir(@_); | 
| 53 |  |  |  |  |  |  | } | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | sub catfile { | 
| 56 | 270 | 50 |  | 270 | 0 | 8144 | return $File_Spec_Using_Unix ? join( "/", @_ ) : File::Spec::Functions::catfile(@_); | 
| 57 |  |  |  |  |  |  | } | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | sub checksum { | 
| 60 | 0 |  |  | 0 | 0 | 0 | my ($str) = @_; | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | # Adler32 algorithm | 
| 63 | 0 |  |  |  |  | 0 | my $s1 = 1; | 
| 64 | 0 |  |  |  |  | 0 | my $s2 = 1; | 
| 65 | 0 |  |  |  |  | 0 | for my $c ( unpack( "C*", $str ) ) { | 
| 66 | 0 |  |  |  |  | 0 | $s1 = ( $s1 + $c ) % 65521; | 
| 67 | 0 |  |  |  |  | 0 | $s2 = ( $s2 + $s1 ) % 65521; | 
| 68 |  |  |  |  |  |  | } | 
| 69 | 0 |  |  |  |  | 0 | return ( $s2 << 16 ) + $s1; | 
| 70 |  |  |  |  |  |  | } | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | # Convert /foo/bar.m, /foo/bar.pm, /foo.m, /foo.pm to | 
| 73 |  |  |  |  |  |  | # /foo/bar.{m,pm}, /foo.{m,pm}. I have no idea why this takes | 
| 74 |  |  |  |  |  |  | # so much code. | 
| 75 |  |  |  |  |  |  | # | 
| 76 |  |  |  |  |  |  | sub combine_similar_paths { | 
| 77 | 18 |  |  | 18 | 0 | 82 | my @paths = @_; | 
| 78 | 18 |  |  |  |  | 33 | my ( @final, $current_base, @current_exts ); | 
| 79 | 18 |  |  |  |  | 37 | foreach my $path (@paths) { | 
| 80 | 220 | 100 |  |  |  | 726 | if ( my ( $base, $ext ) = ( $path =~ /^(.*)\.(.*)$/ ) ) { | 
| 81 | 211 | 100 | 100 |  |  | 624 | if ( defined($current_base) && $current_base ne $base ) { | 
| 82 | 91 | 100 |  |  |  | 323 | push( | 
| 83 |  |  |  |  |  |  | @final, | 
| 84 |  |  |  |  |  |  | "$current_base." | 
| 85 |  |  |  |  |  |  | . ( | 
| 86 |  |  |  |  |  |  | ( @current_exts == 1 ) | 
| 87 |  |  |  |  |  |  | ? $current_exts[0] | 
| 88 |  |  |  |  |  |  | : sprintf( '{%s}', join( ',', @current_exts ) ) | 
| 89 |  |  |  |  |  |  | ) | 
| 90 |  |  |  |  |  |  | ); | 
| 91 | 91 |  |  |  |  | 133 | @current_exts = ($ext); | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  | else { | 
| 94 | 120 |  |  |  |  | 188 | push( @current_exts, $ext ); | 
| 95 |  |  |  |  |  |  | } | 
| 96 | 211 |  |  |  |  | 266 | $current_base = $base; | 
| 97 |  |  |  |  |  |  | } | 
| 98 |  |  |  |  |  |  | else { | 
| 99 | 9 |  |  |  |  | 16 | push( @final, $path ); | 
| 100 |  |  |  |  |  |  | } | 
| 101 |  |  |  |  |  |  | } | 
| 102 | 18 | 50 |  |  |  | 50 | if ( defined($current_base) ) { | 
| 103 | 18 | 100 |  |  |  | 109 | push( | 
| 104 |  |  |  |  |  |  | @final, | 
| 105 |  |  |  |  |  |  | "$current_base." | 
| 106 |  |  |  |  |  |  | . ( | 
| 107 |  |  |  |  |  |  | ( @current_exts == 1 ) | 
| 108 |  |  |  |  |  |  | ? $current_exts[0] | 
| 109 |  |  |  |  |  |  | : sprintf( '{%s}', join( ',', @current_exts ) ) | 
| 110 |  |  |  |  |  |  | ) | 
| 111 |  |  |  |  |  |  | ); | 
| 112 |  |  |  |  |  |  | } | 
| 113 | 18 |  |  |  |  | 145 | return @final; | 
| 114 |  |  |  |  |  |  | } | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | sub delete_package { | 
| 117 | 225 |  |  | 225 | 0 | 401 | my $pkg = shift; | 
| 118 | 225 |  |  |  |  | 1214 | Class::Unload->unload($pkg); | 
| 119 |  |  |  |  |  |  | } | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | sub dump_one_line { | 
| 122 | 13 |  |  | 13 | 0 | 22 | my ($value) = @_; | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 13 |  |  |  |  | 103 | return Data::Dumper->new( [$value] )->Indent(0)->Sortkeys(1)->Quotekeys(0)->Terse(1)->Dump(); | 
| 125 |  |  |  |  |  |  | } | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | # From File::Find::Wanted | 
| 128 |  |  |  |  |  |  | sub find_wanted { | 
| 129 | 6 |  |  | 6 | 0 | 7 | my $func = shift; | 
| 130 | 6 |  |  |  |  | 8 | my @files; | 
| 131 |  |  |  |  |  |  |  | 
| 132 | 6 |  |  |  |  | 8 | local $_; | 
| 133 | 6 | 100 |  | 24 |  | 330 | find( sub { push @files, $File::Find::name if &$func }, @_ ); | 
|  | 24 |  |  |  |  | 44 |  | 
| 134 |  |  |  |  |  |  |  | 
| 135 | 6 |  |  |  |  | 36 | return @files; | 
| 136 |  |  |  |  |  |  | } | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | # From List::MoreUtils | 
| 139 |  |  |  |  |  |  | sub first_index (&@) { | 
| 140 | 466 |  |  | 466 | 0 | 690 | my $f = shift; | 
| 141 | 466 |  |  |  |  | 1295 | for my $i ( 0 .. $#_ ) { | 
| 142 | 881 |  |  |  |  | 1414 | local *_ = \$_[$i]; | 
| 143 | 881 | 100 |  |  |  | 1590 | return $i if $f->(); | 
| 144 |  |  |  |  |  |  | } | 
| 145 | 419 |  |  |  |  | 1428 | return -1; | 
| 146 |  |  |  |  |  |  | } | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | sub is_absolute { | 
| 149 | 1470 |  |  | 1470 | 0 | 1616 | my ($path) = @_; | 
| 150 |  |  |  |  |  |  |  | 
| 151 | 1470 |  |  |  |  | 5670 | return substr( $path, 0, 1 ) eq '/'; | 
| 152 |  |  |  |  |  |  | } | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | # Maintain compatibility with both JSON 1 and 2. Borrowed from Data::Serializer::JSON. | 
| 155 |  |  |  |  |  |  | # | 
| 156 |  |  |  |  |  |  | sub json_decode { | 
| 157 | 13 |  |  | 13 | 0 | 19 | my ($text) = @_; | 
| 158 | 13 | 50 |  |  |  | 299 | return JSON->VERSION < 2 ? JSON->new->jsonToObj($text) : JSON->new->decode($text); | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | sub json_encode { | 
| 162 | 6 |  |  | 6 | 0 | 10 | my ($data) = @_; | 
| 163 | 6 | 50 |  |  |  | 289 | return JSON->VERSION < 2 ? JSON->new->objToJson($data) : JSON->new->utf8->encode($data); | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | sub mason_canon_path { | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | # Like File::Spec::canonpath but with a few fixes. | 
| 169 |  |  |  |  |  |  | # | 
| 170 | 1289 |  |  | 1289 | 0 | 1486 | my $path = shift; | 
| 171 | 1289 |  |  |  |  | 5289 | $path =~ s|/+|/|g;           # xx////yy  -> xx/yy | 
| 172 | 1289 |  |  |  |  | 2002 | $path =~ s|(?:/\.)+/|/|g;    # xx/././yy -> xx/yy | 
| 173 |  |  |  |  |  |  | { | 
| 174 | 1289 | 50 |  |  |  | 1284 | $path =~ s|^(?:\./)+||s unless $path eq "./";    # ./xx      -> xx | 
|  | 1291 |  |  |  |  | 3021 |  | 
| 175 | 1291 |  |  |  |  | 1644 | $path =~ s|^/(?:\.\./)+|/|s;                     # /../../xx -> xx | 
| 176 | 1291 | 50 |  |  |  | 2848 | $path =~ s|/\Z(?!\n)|| unless $path eq "/";      # xx/       -> xx | 
| 177 | 1291 | 50 |  |  |  | 2605 | $path =~ s|/[^/]+/\.\.$|| && redo;               # /xx/..    -> / | 
| 178 | 1291 | 100 |  |  |  | 2828 | $path =~ s|[^/]+/\.\./||  && redo;               # /xx/../yy -> /yy | 
| 179 |  |  |  |  |  |  | } | 
| 180 | 1289 |  |  |  |  | 2465 | return $path; | 
| 181 |  |  |  |  |  |  | } | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | sub read_file { | 
| 184 | 237 |  |  | 237 | 0 | 386 | my ($file) = @_; | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | # Fast slurp, adapted from File::Slurp::read, with unnecessary options removed | 
| 187 |  |  |  |  |  |  | # | 
| 188 | 237 |  |  |  |  | 452 | my $buf = ""; | 
| 189 | 237 |  |  |  |  | 1097 | my $read_fh; | 
| 190 | 237 | 50 |  |  |  | 9791 | unless ( sysopen( $read_fh, $file, $Fetch_Flags ) ) { | 
| 191 | 0 |  |  |  |  | 0 | croak "read_file '$file' - sysopen: $!"; | 
| 192 |  |  |  |  |  |  | } | 
| 193 | 237 |  |  |  |  | 1530 | my $size_left = -s $read_fh; | 
| 194 | 237 |  |  |  |  | 415 | while (1) { | 
| 195 | 237 |  |  |  |  | 1828 | my $read_cnt = sysread( $read_fh, $buf, $size_left, length $buf ); | 
| 196 | 237 | 50 |  |  |  | 767 | if ( defined $read_cnt ) { | 
| 197 | 237 | 50 |  |  |  | 760 | last if $read_cnt == 0; | 
| 198 | 237 |  |  |  |  | 570 | $size_left -= $read_cnt; | 
| 199 | 237 | 50 |  |  |  | 969 | last if $size_left <= 0; | 
| 200 |  |  |  |  |  |  | } | 
| 201 |  |  |  |  |  |  | else { | 
| 202 | 0 |  |  |  |  | 0 | croak "read_file '$file' - sysread: $!"; | 
| 203 |  |  |  |  |  |  | } | 
| 204 |  |  |  |  |  |  | } | 
| 205 | 237 |  |  |  |  | 3238 | return $buf; | 
| 206 |  |  |  |  |  |  | } | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | sub taint_is_on { | 
| 209 | 1036 | 50 |  | 1036 | 0 | 5189 | return ${^TAINT} ? 1 : 0; | 
| 210 |  |  |  |  |  |  | } | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  | sub touch_file { | 
| 213 | 2 |  |  | 2 | 0 | 5 | my ($file) = @_; | 
| 214 | 2 | 50 |  |  |  | 35 | if ( -f $file ) { | 
| 215 | 0 |  |  |  |  | 0 | my $time = time; | 
| 216 | 0 |  |  |  |  | 0 | utime( $time, $time, $file ); | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  | else { | 
| 219 | 2 |  |  |  |  | 7 | write_file( $file, "" ); | 
| 220 |  |  |  |  |  |  | } | 
| 221 |  |  |  |  |  |  | } | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | sub trim { | 
| 224 | 872 |  |  | 872 | 0 | 1105 | my ($str) = @_; | 
| 225 | 872 | 100 |  |  |  | 1731 | if ( defined($str) ) { | 
| 226 | 835 |  |  |  |  | 1258 | for ($str) { s/^\s+//; s/\s+$// } | 
|  | 835 |  |  |  |  | 1582 |  | 
|  | 835 |  |  |  |  | 5250 |  | 
| 227 |  |  |  |  |  |  | } | 
| 228 | 872 |  |  |  |  | 9745 | return $str; | 
| 229 |  |  |  |  |  |  | } | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | # From List::MoreUtils | 
| 232 |  |  |  |  |  |  | sub uniq (@) { | 
| 233 | 1342 |  |  | 1342 | 0 | 1646 | my %h; | 
| 234 | 1342 | 100 |  |  |  | 6179 | map { $h{$_}++ == 0 ? $_ : () } @_; | 
|  | 2009 |  |  |  |  | 7800 |  | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | sub write_file { | 
| 238 | 524 |  |  | 524 | 0 | 1751 | my ( $file, $data, $file_create_mode ) = @_; | 
| 239 |  |  |  |  |  |  |  | 
| 240 | 524 | 50 |  |  |  | 1266 | ($file) = $file =~ /^(.*)/s if taint_is_on();    # Untaint blindly | 
| 241 | 524 | 50 |  |  |  | 1427 | $file_create_mode = oct(666) if !defined($file_create_mode); | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | # Fast spew, adapted from File::Slurp::write, with unnecessary options removed | 
| 244 |  |  |  |  |  |  | # | 
| 245 |  |  |  |  |  |  | { | 
| 246 | 524 |  |  |  |  | 681 | my $write_fh; | 
|  | 524 |  |  |  |  | 637 |  | 
| 247 | 524 | 50 |  |  |  | 41730 | unless ( sysopen( $write_fh, $file, $Store_Flags, $file_create_mode ) ) { | 
| 248 | 0 |  |  |  |  | 0 | croak "write_file '$file' - sysopen: $!"; | 
| 249 |  |  |  |  |  |  | } | 
| 250 | 524 |  |  |  |  | 1446 | my $size_left = length($data); | 
| 251 | 524 |  |  |  |  | 774 | my $offset    = 0; | 
| 252 | 524 |  |  |  |  | 787 | do { | 
| 253 | 524 |  |  |  |  | 16508 | my $write_cnt = syswrite( $write_fh, $data, $size_left, $offset ); | 
| 254 | 524 | 50 |  |  |  | 1571 | unless ( defined $write_cnt ) { | 
| 255 | 0 |  |  |  |  | 0 | croak "write_file '$file' - syswrite: $!"; | 
| 256 |  |  |  |  |  |  | } | 
| 257 | 524 |  |  |  |  | 910 | $size_left -= $write_cnt; | 
| 258 | 524 |  |  |  |  | 2018 | $offset += $write_cnt; | 
| 259 |  |  |  |  |  |  | } while ( $size_left > 0 ); | 
| 260 | 524 |  |  |  |  | 17595 | truncate( $write_fh, sysseek( $write_fh, 0, SEEK_CUR ) ) | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  | } | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | 1; |