File Coverage

blib/lib/Mojo/Util.pm
Criterion Covered Total %
statement 338 357 94.6
branch 127 136 93.3
condition 46 58 79.3
subroutine 75 77 97.4
pod 36 36 100.0
total 622 664 93.6


line stmt bran cond sub pod time code
1             package Mojo::Util;
2 98     98   100333 use Mojo::Base -strict;
  98         190  
  98         3152  
3              
4 98     98   918 use Carp qw(carp croak);
  98         278  
  98         6625  
5 98     98   68864 use Data::Dumper ();
  98         1002681  
  98         4435  
6 98     98   768 use Digest::MD5 qw(md5 md5_hex);
  98         240  
  98         9290  
7 98     98   79455 use Digest::SHA qw(hmac_sha1_hex sha1 sha1_hex);
  98         364377  
  98         12143  
8 98     98   60926 use Encode qw(find_encoding);
  98         1798538  
  98         11352  
9 98     98   879 use Exporter qw(import);
  98         190  
  98         4030  
10 98     98   603 use File::Basename qw(dirname);
  98         215  
  98         8760  
11 98     98   77566 use Getopt::Long qw(GetOptionsFromArray);
  98         1405055  
  98         676  
12 98     98   90975 use IO::Compress::Gzip;
  98         4233493  
  98         9056  
13 98     98   54911 use IO::Poll qw(POLLIN POLLPRI);
  98         98310  
  98         8280  
14 98     98   63639 use IO::Uncompress::Gunzip;
  98         1803285  
  98         7574  
15 98     98   968 use List::Util qw(min);
  98         239  
  98         8774  
16 98     98   56184 use MIME::Base64 qw(decode_base64 encode_base64);
  98         81885  
  98         8684  
17 98     98   874 use Mojo::BaseUtil qw(class_to_path monkey_patch);
  98         276  
  98         6944  
18 98     98   61587 use Pod::Usage qw(pod2usage);
  98         5465137  
  98         9855  
19 98     98   42276 use Socket qw(inet_pton AF_INET6 AF_INET);
  98         304951  
  98         18707  
20 98     98   873 use Symbol qw(delete_package);
  98         292  
  98         4832  
21 98     98   1250 use Time::HiRes ();
  98         1652  
  98         2000  
22 98     98   61374 use Unicode::Normalize ();
  98         320044  
  98         15421  
23              
24             # Encryption support requires CryptX 0.080+
25 98 50       613 use constant CRYPTX => $ENV{MOJO_NO_CRYPTX} ? 0 : !!(eval {
26 98         28010 require CryptX;
27 0         0 require Crypt::AuthEnc::ChaCha20Poly1305;
28 0         0 require Crypt::KeyDerivation;
29 0         0 require Crypt::Misc;
30 0         0 require Crypt::PRNG;
31 0         0 CryptX->VERSION('0.080');
32 0         0 1;
33 98     98   865 });
  98         340  
34              
35             # Check for monotonic clock support
36 98     98   571 use constant MONOTONIC => !!eval { Time::HiRes::clock_gettime(Time::HiRes::CLOCK_MONOTONIC()) };
  98         226  
  98         236  
  98         893  
37              
38             # Punycode bootstring parameters
39             use constant {
40 98         388571 PC_BASE => 36,
41             PC_TMIN => 1,
42             PC_TMAX => 26,
43             PC_SKEW => 38,
44             PC_DAMP => 700,
45             PC_INITIAL_BIAS => 72,
46             PC_INITIAL_N => 128
47 98     98   12344 };
  98         224  
48              
49             # To generate a new HTML entity table run this command
50             # perl examples/entities.pl > lib/Mojo/resources/html_entities.txt
51             my %ENTITIES;
52             {
53             # Don't use Mojo::File here due to circular dependencies
54             my $path = File::Spec->catfile(dirname(__FILE__), 'resources', 'html_entities.txt');
55              
56             open my $file, '<', $path or croak "Unable to open html entities file ($path): $!";
57             my $lines = do { local $/; <$file> };
58              
59             for my $line (split /\n/, $lines) {
60             next unless $line =~ /^(\S+)\s+U\+(\S+)(?:\s+U\+(\S+))?/;
61             $ENTITIES{$1} = defined $3 ? (chr(hex $2) . chr(hex $3)) : chr(hex $2);
62             }
63             }
64              
65             # Characters that should be escaped in XML
66             my %XML = ('&' => '&', '<' => '<', '>' => '>', '"' => '"', '\'' => ''');
67              
68             # "Sun, 06 Nov 1994 08:49:37 GMT" and "Sunday, 06-Nov-94 08:49:37 GMT"
69             my $EXPIRES_RE = qr/(\w+\W+\d+\W+\w+\W+\d+\W+\d+:\d+:\d+\W*\w+)/;
70              
71             # Header key/value pairs
72             my $QUOTED_VALUE_RE = qr/\G=\s*("(?:\\\\|\\"|[^"])*")/;
73             my $UNQUOTED_VALUE_RE = qr/\G=\s*([^;, ]*)/;
74              
75             # HTML entities
76             my $ENTITY_RE = qr/&(?:\#((?:[0-9]{1,7}|x[0-9a-fA-F]{1,6}));|(\w+[;=]?))/;
77              
78             # Encoding, encryption and pattern caches
79             my (%ENCODING, %ENCRYPTION, %PATTERN);
80              
81             our @EXPORT_OK = (
82             qw(b64_decode b64_encode camelize class_to_file class_to_path decamelize decode decrypt_cookie deprecated dumper),
83             qw(encode encrypt_cookie extract_usage generate_secret getopt gunzip gzip header_params hmac_sha1_sum),
84             qw(html_attr_unescape html_unescape humanize_bytes md5_bytes md5_sum monkey_patch network_contains punycode_decode),
85             qw(punycode_encode quote scope_guard secure_compare sha1_bytes sha1_sum slugify split_cookie_header split_header),
86             qw(steady_time tablify term_escape trim unindent unquote url_escape url_unescape xml_escape xor_encode)
87             );
88              
89             # Aliases
90             monkey_patch(__PACKAGE__, 'b64_decode', \&decode_base64);
91             monkey_patch(__PACKAGE__, 'b64_encode', \&encode_base64);
92             monkey_patch(__PACKAGE__, 'hmac_sha1_sum', \&hmac_sha1_hex);
93             monkey_patch(__PACKAGE__, 'md5_bytes', \&md5);
94             monkey_patch(__PACKAGE__, 'md5_sum', \&md5_hex);
95             monkey_patch(__PACKAGE__, 'sha1_bytes', \&sha1);
96             monkey_patch(__PACKAGE__, 'sha1_sum', \&sha1_hex);
97              
98             # Use a monotonic clock if possible
99             monkey_patch(__PACKAGE__, 'steady_time',
100 111664     111664   247737 MONOTONIC ? sub () { Time::HiRes::clock_gettime(Time::HiRes::CLOCK_MONOTONIC()) } : \&Time::HiRes::time);
101              
102             sub camelize {
103 42     42 1 262366 my $str = shift;
104 42 100       335 return $str if $str =~ /^[A-Z]/;
105              
106             # CamelCase words
107             return join '::', map {
108 39         229 join('', map { ucfirst lc } split /_/)
  53         177  
  81         502  
109             } split /-/, $str;
110             }
111              
112             sub class_to_file {
113 10     10 1 4332 my $class = shift;
114 10         75 $class =~ s/::|'//g;
115 10         66 $class =~ s/([A-Z])([A-Z]*)/$1 . lc $2/ge;
  18         94  
116 10         34 return decamelize($class);
117             }
118              
119             sub decamelize {
120 28     28 1 3761 my $str = shift;
121 28 100       229 return $str if $str !~ /^[A-Z]/;
122              
123             # snake_case words
124             return join '-', map {
125 23         124 join('_', map {lc} grep {length} split /([A-Z]{1}[^A-Z]*)/)
  26         200  
  49         359  
  98         176  
126             } split /::/, $str;
127             }
128              
129             sub decrypt_cookie {
130 0     0 1 0 my ($value, $key, $salt) = @_;
131 0         0 croak 'CryptX 0.080+ required for encrypted cookie support' unless CRYPTX;
132              
133 0 0       0 return undef unless $value =~ /^([^-]+)-([^-]+)-([^-]+)$/;
134 0         0 my ($ct, $iv, $tag) = ($1, $2, $3);
135 0         0 ($ct, $iv, $tag) = (Crypt::Misc::decode_b64($ct), Crypt::Misc::decode_b64($iv), Crypt::Misc::decode_b64($tag));
136              
137 0   0     0 my $dk = $ENCRYPTION{$key}{$salt} ||= Crypt::KeyDerivation::pbkdf2($key, $salt);
138 0         0 return Crypt::AuthEnc::ChaCha20Poly1305::chacha20poly1305_decrypt_verify($dk, $iv, '', $ct, $tag);
139             }
140              
141             sub decode {
142 7825     7825 1 33268 my ($encoding, $bytes) = @_;
143 7825 100       13462 return undef unless eval { $bytes = _encoding($encoding)->decode("$bytes", 1); 1 };
  7825         22134  
  7738         77544  
144 7738         33674 return $bytes;
145             }
146              
147             sub deprecated {
148 2     2 1 8628 local $Carp::CarpLevel = 1;
149 2 100       355 $ENV{MOJO_FATAL_DEPRECATIONS} ? croak @_ : carp @_;
150             }
151              
152 303     303 1 8354 sub dumper { Data::Dumper->new([@_])->Indent(1)->Sortkeys(1)->Terse(1)->Useqq(1)->Dump }
153              
154 11021     11021 1 64195 sub encode { _encoding($_[0])->encode("$_[1]", 0) }
155              
156             sub encrypt_cookie {
157 0     0 1 0 my ($value, $key, $salt) = @_;
158 0         0 croak 'CryptX 0.080+ required for encrypted cookie support' unless CRYPTX;
159              
160 0   0     0 my $dk = $ENCRYPTION{$key}{$salt} ||= Crypt::KeyDerivation::pbkdf2($key, $salt);
161 0         0 my $iv = Crypt::PRNG::random_bytes(12);
162 0         0 my ($ct, $tag) = Crypt::AuthEnc::ChaCha20Poly1305::chacha20poly1305_encrypt_authenticate($dk, $iv, '', $value);
163              
164 0         0 return join '-', Crypt::Misc::encode_b64($ct), Crypt::Misc::encode_b64($iv), Crypt::Misc::encode_b64($tag);
165             }
166              
167             sub extract_usage {
168 26 100   26 1 4376 my $file = @_ ? "$_[0]" : (caller)[1];
169              
170 26         605 open my $handle, '>', \my $output;
171 26         238 pod2usage -exitval => 'noexit', -input => $file, -output => $handle;
172 26         957397 $output =~ s/^.*\n|\n$//;
173 26         1444 $output =~ s/\n$//;
174              
175 26         168 return unindent($output);
176             }
177              
178             sub generate_secret {
179 2     2 1 9301 return Crypt::Misc::encode_b64u(Crypt::PRNG::random_bytes(128)) if CRYPTX;
180 2         91 srand;
181 2         14 return sha1_sum($$ . steady_time() . rand);
182             }
183              
184             sub getopt {
185 120 100   120 1 17390 my ($array, $opts) = map { ref $_[0] eq 'ARRAY' ? shift : $_ } \@ARGV, [];
  240         1267  
186              
187 120         1130 my $save = Getopt::Long::Configure(qw(default no_auto_abbrev no_ignore_case), @$opts);
188 120         13857 my $result = GetOptionsFromArray $array, @_;
189 120         86500 Getopt::Long::Configure($save);
190              
191 120         14939 return $result;
192             }
193              
194             sub gunzip {
195 2     2 1 1760 my $compressed = shift;
196 2 50       17 IO::Uncompress::Gunzip::gunzip \$compressed, \my $uncompressed
197             or croak "Couldn't gunzip: $IO::Uncompress::Gunzip::GzipError";
198 2         5451 return $uncompressed;
199             }
200              
201             sub gzip {
202 59     59 1 25096 my $uncompressed = shift;
203 59 50       590 IO::Compress::Gzip::gzip \$uncompressed, \my $compressed or croak "Couldn't gzip: $IO::Compress::Gzip::GzipError";
204 59         218095 return $compressed;
205             }
206              
207             sub header_params {
208 16     16 1 4985 my $value = shift;
209              
210 16         33 my $params = {};
211 16         144 while ($value =~ /\G[;\s]*([^=;, ]+)\s*/gc) {
212 20         47 my $name = $1;
213              
214             # Quoted value
215 20 100 66     164 if ($value =~ /$QUOTED_VALUE_RE/gco) { $params->{$name} //= unquote($1) }
  4 100       29  
216              
217             # Unquoted value
218 15   66     113 elsif ($value =~ /$UNQUOTED_VALUE_RE/gco) { $params->{$name} //= $1 }
219             }
220              
221 16   100     131 return ($params, substr($value, pos($value) // 0));
222             }
223              
224 34130     34130 1 61831 sub html_attr_unescape { _html(shift, 1) }
225 3226     3226 1 27678 sub html_unescape { _html(shift, 0) }
226              
227             sub humanize_bytes {
228 19     19 1 4636 my $size = shift;
229              
230 19 100       67 my $prefix = $size < 0 ? '-' : '';
231              
232 19 100       81 return "$prefix${size}B" if ($size = abs $size) < 1024;
233 16 100       66 return $prefix . _round($size) . 'KiB' if ($size /= 1024) < 1024;
234 11 100       53 return $prefix . _round($size) . 'MiB' if ($size /= 1024) < 1024;
235 8 100       36 return $prefix . _round($size) . 'GiB' if ($size /= 1024) < 1024;
236 2         9 return $prefix . _round($size /= 1024) . 'TiB';
237             }
238              
239             sub network_contains {
240 99     99 1 14869 my ($cidr, $addr) = @_;
241 99 100 100     711 return undef unless length $cidr && length $addr;
242              
243             # Parse inputs
244 93         390 my ($net, $mask) = split m!/!, $cidr, 2;
245 93         289 my $v6 = $net =~ /:/;
246 93 100 100     436 return undef if $v6 xor $addr =~ /:/;
247              
248             # Convert addresses to binary
249 91 100       528 return undef unless $net = inet_pton($v6 ? AF_INET6 : AF_INET, $net);
    100          
250 89 100       374 return undef unless $addr = inet_pton($v6 ? AF_INET6 : AF_INET, $addr);
    100          
251 87 100       194 my $length = $v6 ? 128 : 32;
252              
253             # Apply mask if given
254 87 100       453 $addr &= pack "B$length", '1' x $mask if defined $mask;
255              
256             # Compare
257 87         846 return 0 == unpack "B$length", ($net ^ $addr);
258             }
259              
260             # Direct translation of RFC 3492
261             sub punycode_decode {
262 25     25 1 2580 my $input = shift;
263 98     98   1049 use integer;
  98         204  
  98         959  
264              
265 25         69 my ($n, $i, $bias, @output) = (PC_INITIAL_N, 0, PC_INITIAL_BIAS);
266              
267             # Consume all code points before the last delimiter
268 25 100       220 push @output, split(//, $1) if $input =~ s/(.*)\x2d//s;
269              
270 25         67 while (length $input) {
271 223         246 my ($oldi, $w) = ($i, 1);
272              
273             # Base to infinity in steps of base
274 223         254 for (my $k = PC_BASE; 1; $k += PC_BASE) {
275 472         526 my $digit = ord substr $input, 0, 1, '';
276 472 100       556 $digit = $digit < 0x40 ? $digit + (26 - 0x30) : ($digit & 0x1f) - 1;
277 472         423 $i += $digit * $w;
278 472         447 my $t = $k - $bias;
279 472 100       562 $t = $t < PC_TMIN ? PC_TMIN : $t > PC_TMAX ? PC_TMAX : $t;
    100          
280 472 100       607 last if $digit < $t;
281 249         257 $w *= PC_BASE - $t;
282             }
283              
284 223         331 $bias = _adapt($i - $oldi, @output + 1, $oldi == 0);
285 223         244 $n += $i / (@output + 1);
286 223         205 $i = $i % (@output + 1);
287 223         445 splice @output, $i++, 0, chr $n;
288             }
289              
290 25         154 return join '', @output;
291             }
292              
293             # Direct translation of RFC 3492
294             sub punycode_encode {
295 66     66 1 21014 my $output = shift;
296 98     98   67966 use integer;
  98         224  
  98         495  
297              
298 66         141 my ($n, $delta, $bias) = (PC_INITIAL_N, 0, PC_INITIAL_BIAS);
299              
300             # Extract basic code points
301 66         262 my @input = map {ord} split //, $output;
  558         761  
302 66         472 $output =~ s/[^\x00-\x7f]+//gs;
303 66         217 my $h = my $basic = length $output;
304 66 100       195 $output .= "\x2d" if $basic > 0;
305              
306 66         119 for my $m (sort { $a <=> $b } grep { $_ >= PC_INITIAL_N } @input) {
  613         596  
  558         826  
307 264 100       410 next if $m < $n;
308 222         341 $delta += ($m - $n) * ($h + 1);
309 222         256 $n = $m;
310              
311 222         264 for my $c (@input) {
312              
313 3643 100       4383 if ($c < $n) { $delta++ }
  2039 100       1871  
314             elsif ($c == $n) {
315 264         285 my $q = $delta;
316              
317             # Base to infinity in steps of base
318 264         305 for (my $k = PC_BASE; 1; $k += PC_BASE) {
319 595         624 my $t = $k - $bias;
320 595 100       892 $t = $t < PC_TMIN ? PC_TMIN : $t > PC_TMAX ? PC_TMAX : $t;
    100          
321 595 100       822 last if $q < $t;
322 331         363 my $o = $t + (($q - $t) % (PC_BASE - $t));
323 331 100       561 $output .= chr $o + ($o < 26 ? 0x61 : 0x30 - 26);
324 331         389 $q = ($q - $t) / (PC_BASE - $t);
325             }
326              
327 264 50       390 $output .= chr $q + ($q < 26 ? 0x61 : 0x30 - 26);
328 264         417 $bias = _adapt($delta, $h + 1, $h == $basic);
329 264         295 $delta = 0;
330 264         287 $h++;
331             }
332             }
333              
334 222         206 $delta++;
335 222         278 $n++;
336             }
337              
338 66         347 return $output;
339             }
340              
341             sub quote {
342 21     21 1 4110 my $str = shift;
343 21         178 $str =~ s/(["\\])/\\$1/g;
344 21         135 return qq{"$str"};
345             }
346              
347 18     18 1 9964 sub scope_guard { Mojo::Util::_Guard->new(cb => shift) }
348              
349             sub secure_compare {
350 97     97 1 24239 my ($one, $two) = @_;
351 97         252 my $r = length $one != length $two;
352 97 100       317 $two = $one if $r;
353 97         2590 $r |= ord(substr $one, $_) ^ ord(substr $two, $_) for 0 .. length($one) - 1;
354 97         617 return $r == 0;
355             }
356              
357             sub slugify {
358 14     14 1 5093 my ($value, $allow_unicode) = @_;
359              
360 14 100       126 if ($allow_unicode) {
361              
362             # Force unicode semantics by upgrading string
363 6         155 utf8::upgrade($value = Unicode::Normalize::NFKC($value));
364 6         44 $value =~ s/[^\w\s-]+//g;
365             }
366             else {
367 8         81 $value = Unicode::Normalize::NFKD($value);
368 8         111 $value =~ s/[^a-zA-Z0-9_\p{PosixSpace}-]+//g;
369             }
370 14         52 (my $new = lc trim($value)) =~ s/[-\s]+/-/g;
371              
372 14         92 return $new;
373             }
374              
375 1072     1072 1 15969 sub split_cookie_header { _header(shift, 1) }
376 225     225 1 5750 sub split_header { _header(shift, 0) }
377              
378             sub tablify {
379 18     18 1 9149 my $rows = shift;
380              
381 18         39 my @spec;
382 18         109 for my $row (@$rows) {
383 87         231 for my $i (0 .. $#$row) {
384 176   100     463 ($row->[$i] //= '') =~ y/\r\n//d;
385 176         274 my $len = length $row->[$i];
386 176 100 100     619 $spec[$i] = $len if $len >= ($spec[$i] // 0);
387             }
388             }
389              
390 18         80 my @fm = (map({"\%-${_}s"} @spec[0 .. $#spec - 1]), '%s');
  23         119  
391 18         48 return join '', map { sprintf join(' ', @fm[0 .. $#$_]) . "\n", @$_ } @$rows;
  87         541  
392             }
393              
394             sub term_escape {
395 4     4 1 5069 my $str = shift;
396 4         30 $str =~ s/([\x00-\x09\x0b-\x1f\x7f\x80-\x9f])/sprintf '\\x%02x', ord $1/ge;
  16         72  
397 4         28 return $str;
398             }
399              
400             sub trim {
401 1397     1397 1 5050 my $str = shift;
402 1397         5204 $str =~ s/^\s+//;
403 1397         4108 $str =~ s/\s+$//;
404 1397         4350 return $str;
405             }
406              
407             sub unindent {
408 37     37 1 4627 my $str = shift;
409 37 100       340 my $min = min map { m/^([ \t]*)/; length $1 || () } split /\n/, $str;
  426         999  
  426         1471  
410 37 100       1230 $str =~ s/^[ \t]{0,$min}//gm if $min;
411 37         1129 return $str;
412             }
413              
414             sub unquote {
415 48     48 1 2712 my $str = shift;
416 48 50       384 return $str unless $str =~ s/^"(.*)"$/$1/g;
417 48         167 $str =~ s/\\\\/\\/g;
418 48         152 $str =~ s/\\"/"/g;
419 48         149 return $str;
420             }
421              
422             sub url_escape {
423 6302     6302 1 26401 my ($str, $pattern) = @_;
424              
425 6302 100       13310 if ($pattern) {
426 6287 100       16768 unless (exists $PATTERN{$pattern}) {
427 142         1784 (my $quoted = $pattern) =~ s!([/\$\[])!\\$1!g;
428 142 50       42848 $PATTERN{$pattern} = eval "sub { \$_[0] =~ s/([$quoted])/sprintf '%%%02X', ord \$1/ge }" or croak $@;
429             }
430 6287         190045 $PATTERN{$pattern}->($str);
431             }
432 15         127 else { $str =~ s/([^A-Za-z0-9\-._~])/sprintf '%%%02X', ord $1/ge }
  22         114  
433              
434 6302         28480 return $str;
435             }
436              
437             sub url_unescape {
438 8145     8145 1 20935 my $str = shift;
439 8145         18877 $str =~ s/%([0-9a-fA-F]{2})/chr hex $1/ge;
  793         3072  
440 8145         20281 return $str;
441             }
442              
443             sub xml_escape {
444 494 100 66 494 1 8825 return $_[0] if ref $_[0] && ref $_[0] eq 'Mojo::ByteStream';
  8715 100 100 8715   85041  
445 493   50     875 my $str = shift // '';
  6705   100     14748  
446 493         894 $str =~ s/([&<>"'])/$XML{$1}/ge;
  49         201  
  6705         16425  
  7644         27568  
447 493         1639 return $str;
  6705         18443  
448             }
449              
450             sub xor_encode {
451 295     295 1 6424 my ($input, $key) = @_;
452              
453             # Encode with variable key length
454 295         455 my $len = length $key;
455 295         694 my $buffer = my $output = '';
456 295         2324 $output .= $buffer ^ $key while length($buffer = substr($input, 0, $len, '')) == $len;
457 295         1916 return $output .= $buffer ^ substr($key, 0, length $buffer, '');
458             }
459              
460             sub _adapt {
461 487     487   676 my ($delta, $numpoints, $firsttime) = @_;
462 98     98   300552 use integer;
  98         318  
  98         596  
463              
464 487 100       599 $delta = $firsttime ? $delta / PC_DAMP : $delta / 2;
465 487         481 $delta += $delta / $numpoints;
466 487         448 my $k = 0;
467 487         636 while ($delta > ((PC_BASE - PC_TMIN) * PC_TMAX) / 2) {
468 116         100 $delta /= PC_BASE - PC_TMIN;
469 116         157 $k += PC_BASE;
470             }
471              
472 487         607 return $k + (((PC_BASE - PC_TMIN + 1) * $delta) / ($delta + PC_SKEW));
473             }
474              
475 18846   66 18846   185078 sub _encoding { $ENCODING{$_[0]} //= find_encoding($_[0]) // croak "Unknown encoding '$_[0]'" }
      66        
476              
477             sub _entity {
478 1003     1003   2722 my ($point, $name, $attr) = @_;
479              
480             # Code point
481 1003 100       2435 return chr($point !~ /^x/ ? $point : hex $point) unless defined $name;
    100          
482              
483             # Named character reference
484 962         1626 my $rest = my $last = '';
485 962         2015 while (length $name) {
486             return $ENTITIES{$name} . reverse $rest
487 1000 100 100     6321 if exists $ENTITIES{$name} && (!$attr || $name =~ /;$/ || $last !~ /[A-Za-z0-9=]/);
      100        
488 48         83 $rest .= $last = chop $name;
489             }
490 10         44 return '&' . reverse $rest;
491             }
492              
493             sub _header {
494 1297     1297   3762 my ($str, $cookie) = @_;
495              
496 1297         2825 my (@tree, @part);
497 1297         5605 while ($str =~ /\G[,;\s]*([^=;, ]+)\s*/gc) {
498 833         2562 push @part, $1, undef;
499 833   100     3603 my $expires = $cookie && @part > 2 && lc $1 eq 'expires';
500              
501             # Special "expires" value
502 833 100 100     6489 if ($expires && $str =~ /\G=\s*$EXPIRES_RE/gco) { $part[-1] = $1 }
  125 100       402  
    100          
503              
504             # Quoted value
505 42         184 elsif ($str =~ /$QUOTED_VALUE_RE/gco) { $part[-1] = unquote $1 }
506              
507             # Unquoted value
508 563         1283 elsif ($str =~ /$UNQUOTED_VALUE_RE/gco) { $part[-1] = $1 }
509              
510             # Separator
511 833 100       3652 next unless $str =~ /\G[;\s]*,\s*/gc;
512 107         385 push @tree, [@part];
513 107         436 @part = ();
514             }
515              
516             # Take care of final part
517 1297 100       6362 return [@part ? (@tree, \@part) : @tree];
518             }
519              
520             sub _html {
521 37356     37356   63377 my ($str, $attr) = @_;
522 37356         48240 $str =~ s/$ENTITY_RE/_entity($1, $2, $attr)/geo;
  1003         1965  
523 37356         149471 return $str;
524             }
525              
526             sub _options {
527              
528             # Hash or name (one)
529 1817 100   1817   25824 return ref $_[0] eq 'HASH' ? (undef, %{shift()}) : @_ if @_ == 1;
  1012 100       3800  
530              
531             # Name and values (odd)
532 349 100       1326 return shift, @_ if @_ % 2;
533              
534             # Name and hash or just values (even)
535 269 100       1800 return ref $_[1] eq 'HASH' ? (shift, %{shift()}) : (undef, @_);
  18         97  
536             }
537              
538             # This may break in the future, but is worth it for performance
539 796     796   14327 sub _readable { !!(IO::Poll::_poll(@_[0, 1], my $m = POLLIN | POLLPRI) > 0) }
540              
541 16 100   16   196 sub _round { $_[0] < 10 ? int($_[0] * 10 + 0.5) / 10 : int($_[0] + 0.5) }
542              
543             sub _stash {
544 22413     22413   47598 my ($name, $object) = (shift, shift);
545              
546             # Hash
547 22413 100 100     137890 return $object->{$name} //= {} unless @_;
548              
549             # Get
550 1405 100 100     6734 return $object->{$name}{$_[0]} unless @_ > 1 || ref $_[0];
551              
552             # Set
553 1165 100       5724 my $values = ref $_[0] ? $_[0] : {@_};
554 1165         4556 @{$object->{$name}}{keys %$values} = values %$values;
  1165         3811  
555              
556 1165         13103 return $object;
557             }
558              
559             sub _teardown {
560 826 50   826   40626 return unless my $class = shift;
561              
562             # @ISA has to be cleared first because of circular references
563 98     98   169235 no strict 'refs';
  98         214  
  98         15488  
564 826         1296 @{"${class}::ISA"} = ();
  826         10236  
565 826         3716 delete_package $class;
566             }
567              
568             package Mojo::Util::_Guard;
569 98     98   764 use Mojo::Base -base;
  98         218  
  98         1169  
570              
571 18     18   1898 sub DESTROY { shift->{cb}() }
572              
573             1;
574              
575             =encoding utf8
576              
577             =head1 NAME
578              
579             Mojo::Util - Portable utility functions
580              
581             =head1 SYNOPSIS
582              
583             use Mojo::Util qw(b64_encode url_escape url_unescape);
584              
585             my $str = 'test=23';
586             my $escaped = url_escape $str;
587             say url_unescape $escaped;
588             say b64_encode $escaped, '';
589              
590             =head1 DESCRIPTION
591              
592             L provides portable utility functions for L.
593              
594             =head1 FUNCTIONS
595              
596             L implements the following functions, which can be imported individually.
597              
598             =head2 b64_decode
599              
600             my $bytes = b64_decode $b64;
601              
602             Base64 decode bytes with L.
603              
604             =head2 b64_encode
605              
606             my $b64 = b64_encode $bytes;
607             my $b64 = b64_encode $bytes, "\n";
608              
609             Base64 encode bytes with L, the line ending defaults to a newline.
610              
611             =head2 camelize
612              
613             my $camelcase = camelize $snakecase;
614              
615             Convert C string to C and replace C<-> with C<::>.
616              
617             # "FooBar"
618             camelize 'foo_bar';
619              
620             # "FooBar::Baz"
621             camelize 'foo_bar-baz';
622              
623             # "FooBar::Baz"
624             camelize 'FooBar::Baz';
625              
626             =head2 class_to_file
627              
628             my $file = class_to_file 'Foo::Bar';
629              
630             Convert a class name to a file.
631              
632             # "foo_bar"
633             class_to_file 'Foo::Bar';
634              
635             # "foobar"
636             class_to_file 'FOO::Bar';
637              
638             # "foo_bar"
639             class_to_file 'FooBar';
640              
641             # "foobar"
642             class_to_file 'FOOBar';
643              
644             =head2 class_to_path
645              
646             my $path = class_to_path 'Foo::Bar';
647              
648             Convert class name to path, as used by C<%INC>.
649              
650             # "Foo/Bar.pm"
651             class_to_path 'Foo::Bar';
652              
653             # "FooBar.pm"
654             class_to_path 'FooBar';
655              
656             =head2 decamelize
657              
658             my $snakecase = decamelize $camelcase;
659              
660             Convert C string to C and replace C<::> with C<->.
661              
662             # "foo_bar"
663             decamelize 'FooBar';
664              
665             # "foo_bar-baz"
666             decamelize 'FooBar::Baz';
667              
668             # "foo_bar-baz"
669             decamelize 'foo_bar-baz';
670              
671             =head2 decode
672              
673             my $chars = decode 'UTF-8', $bytes;
674              
675             Decode bytes to characters with L, or return C if decoding failed.
676              
677             =head2 decrypt_cookie
678              
679             my $value = decrypt_cookie $encrypted, 'passw0rd', 'salt';
680              
681             Decrypt cookie value encrypted with L, returns the decrypted value or C.
682              
683             =head2 deprecated
684              
685             deprecated 'foo is DEPRECATED in favor of bar';
686              
687             Warn about deprecated feature from perspective of caller. You can also set the C environment
688             variable to make them die instead with L.
689              
690             =head2 dumper
691              
692             my $perl = dumper {some => 'data'};
693              
694             Dump a Perl data structure with L.
695              
696             =head2 encode
697              
698             my $bytes = encode 'UTF-8', $chars;
699              
700             Encode characters to bytes with L.
701              
702             =head2 encrypt_cookie
703              
704             my $encrypted = encrypt_cookie $value, 'passw0rd', 'salt';
705              
706             Encrypt cookie value.
707              
708             =head2 extract_usage
709              
710             my $usage = extract_usage;
711             my $usage = extract_usage '/home/sri/foo.pod';
712              
713             Extract usage message from the SYNOPSIS section of a file containing POD documentation, defaults to using the file this
714             function was called from.
715              
716             # "Usage: APPLICATION test [OPTIONS]\n"
717             extract_usage;
718              
719             =head1 SYNOPSIS
720              
721             Usage: APPLICATION test [OPTIONS]
722              
723             =cut
724              
725             =head2 generate_secret
726              
727             my $secret = generate_secret;
728              
729             Generate a random secret with a cryptographically secure random number generator if available, and a less secure
730             fallback if not.
731              
732             =head2 getopt
733              
734             getopt
735             'H|headers=s' => \my @headers,
736             't|timeout=i' => \my $timeout,
737             'v|verbose' => \my $verbose;
738             getopt $array,
739             'H|headers=s' => \my @headers,
740             't|timeout=i' => \my $timeout,
741             'v|verbose' => \my $verbose;
742             getopt $array, ['pass_through'],
743             'H|headers=s' => \my @headers,
744             't|timeout=i' => \my $timeout,
745             'v|verbose' => \my $verbose;
746              
747             Extract options from an array reference with L, but without changing its global configuration, defaults
748             to using C<@ARGV>. The configuration options C and C are enabled by default.
749              
750             # Extract "charset" option
751             getopt ['--charset', 'UTF-8'], 'charset=s' => \my $charset;
752             say $charset;
753              
754             =head2 gunzip
755              
756             my $uncompressed = gunzip $compressed;
757              
758             Uncompress bytes with L.
759              
760             =head2 gzip
761              
762             my $compressed = gzip $uncompressed;
763              
764             Compress bytes with L.
765              
766             =head2 header_params
767              
768             my ($params, $remainder) = header_params 'one=foo; two="bar", three=baz';
769              
770             Extract HTTP header field parameters until the first comma according to L.
771              
772             =head2 hmac_sha1_sum
773              
774             my $checksum = hmac_sha1_sum $bytes, 'passw0rd';
775              
776             Generate HMAC-SHA1 checksum for bytes with L.
777              
778             # "11cedfd5ec11adc0ec234466d8a0f2a83736aa68"
779             hmac_sha1_sum 'foo', 'passw0rd';
780              
781             =head2 html_attr_unescape
782              
783             my $str = html_attr_unescape $escaped;
784              
785             Same as L, but handles special rules from the L
786             for HTML attributes.
787              
788             # "foo=bar<est=baz"
789             html_attr_unescape 'foo=bar<est=baz';
790              
791             # "foo=bar
792             html_attr_unescape 'foo=bar<est=baz';
793              
794             =head2 html_unescape
795              
796             my $str = html_unescape $escaped;
797              
798             Unescape all HTML entities in string.
799              
800             # "
"
801             html_unescape '<div>';
802              
803             =head2 humanize_bytes
804              
805             my $str = humanize_bytes 1234;
806              
807             Turn number of bytes into a simplified human readable format.
808              
809             # "1B"
810             humanize_bytes 1;
811              
812             # "7.5GiB"
813             humanize_bytes 8007188480;
814              
815             # "13GiB"
816             humanize_bytes 13443399680;
817              
818             # "-685MiB"
819             humanize_bytes -717946880;
820              
821             =head2 md5_bytes
822              
823             my $checksum = md5_bytes $bytes;
824              
825             Generate binary MD5 checksum for bytes with L.
826              
827             =head2 md5_sum
828              
829             my $checksum = md5_sum $bytes;
830              
831             Generate MD5 checksum for bytes with L.
832              
833             # "acbd18db4cc2f85cedef654fccc4a4d8"
834             md5_sum 'foo';
835              
836             =head2 monkey_patch
837              
838             monkey_patch $package, foo => sub {...};
839             monkey_patch $package, foo => sub {...}, bar => sub {...};
840              
841             Monkey patch functions into package.
842              
843             monkey_patch 'MyApp',
844             one => sub { say 'One!' },
845             two => sub { say 'Two!' },
846             three => sub { say 'Three!' };
847              
848             =head2 network_contains
849              
850             my $bool = network_contains $network, $address;
851              
852             Check that a given address is contained within a network in CIDR form. If the network is a single address, the
853             addresses must be equivalent.
854              
855             # True
856             network_contains('10.0.0.0/8', '10.10.10.10');
857             network_contains('10.10.10.10', '10.10.10.10');
858             network_contains('fc00::/7', 'fc::c0:ff:ee');
859              
860             # False
861             network_contains('10.0.0.0/29', '10.10.10.10');
862             network_contains('10.10.10.12', '10.10.10.10');
863             network_contains('fc00::/7', '::1');
864            
865             =head2 punycode_decode
866              
867             my $str = punycode_decode $punycode;
868              
869             Punycode decode string as described in L.
870              
871             # "bücher"
872             punycode_decode 'bcher-kva';
873              
874             =head2 punycode_encode
875              
876             my $punycode = punycode_encode $str;
877              
878             Punycode encode string as described in L.
879              
880             # "bcher-kva"
881             punycode_encode 'bücher';
882              
883             =head2 quote
884              
885             my $quoted = quote $str;
886              
887             Quote string.
888              
889             =head2 scope_guard
890              
891             my $guard = scope_guard sub {...};
892              
893             Create anonymous scope guard object that will execute the passed callback when the object is destroyed.
894              
895             # Execute closure at end of scope
896             {
897             my $guard = scope_guard sub { say "Mojo!" };
898             say "Hello";
899             }
900              
901             =head2 secure_compare
902              
903             my $bool = secure_compare $str1, $str2;
904              
905             Constant time comparison algorithm to prevent timing attacks. The secret string should be the second argument, to avoid
906             leaking information about the length of the string.
907              
908             =head2 sha1_bytes
909              
910             my $checksum = sha1_bytes $bytes;
911              
912             Generate binary SHA1 checksum for bytes with L.
913              
914             =head2 sha1_sum
915              
916             my $checksum = sha1_sum $bytes;
917              
918             Generate SHA1 checksum for bytes with L.
919              
920             # "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
921             sha1_sum 'foo';
922              
923             =head2 slugify
924              
925             my $slug = slugify $string;
926             my $slug = slugify $string, $bool;
927              
928             Returns a URL slug generated from the input string. Non-word characters are removed, the string is trimmed and
929             lowercased, and whitespace characters are replaced by a dash. By default, non-ASCII characters are normalized to ASCII
930             word characters or removed, but if a true value is passed as the second parameter, all word characters will be allowed
931             in the result according to unicode semantics.
932              
933             # "joel-is-a-slug"
934             slugify 'Joel is a slug';
935              
936             # "this-is-my-resume"
937             slugify 'This is: my - résumé! ☃ ';
938              
939             # "this-is-my-résumé"
940             slugify 'This is: my - résumé! ☃ ', 1;
941              
942             =head2 split_cookie_header
943              
944             my $tree = split_cookie_header 'a=b; expires=Thu, 07 Aug 2008 07:07:59 GMT';
945              
946             Same as L, but handles C values from L.
947              
948             =head2 split_header
949              
950             my $tree = split_header 'foo="bar baz"; test=123, yada';
951              
952             Split HTTP header value into key/value pairs, each comma separated part gets its own array reference, and keys without
953             a value get C assigned.
954              
955             # "one"
956             split_header('one; two="three four", five=six')->[0][0];
957              
958             # "two"
959             split_header('one; two="three four", five=six')->[0][2];
960              
961             # "three four"
962             split_header('one; two="three four", five=six')->[0][3];
963              
964             # "five"
965             split_header('one; two="three four", five=six')->[1][0];
966              
967             # "six"
968             split_header('one; two="three four", five=six')->[1][1];
969              
970             =head2 steady_time
971              
972             my $time = steady_time;
973              
974             High resolution time elapsed from an arbitrary fixed point in the past, resilient to time jumps if a monotonic clock is
975             available through L.
976              
977             =head2 tablify
978              
979             my $table = tablify [['foo', 'bar'], ['baz', 'yada']];
980              
981             Row-oriented generator for text tables.
982              
983             # "foo bar\nyada yada\nbaz yada\n"
984             tablify [['foo', 'bar'], ['yada', 'yada'], ['baz', 'yada']];
985              
986             =head2 term_escape
987              
988             my $escaped = term_escape $str;
989              
990             Escape all POSIX control characters except for C<\n>.
991              
992             # "foo\\x09bar\\x0d\n"
993             term_escape "foo\tbar\r\n";
994              
995             =head2 trim
996              
997             my $trimmed = trim $str;
998              
999             Trim whitespace characters from both ends of string.
1000              
1001             # "foo bar"
1002             trim ' foo bar ';
1003              
1004             =head2 unindent
1005              
1006             my $unindented = unindent $str;
1007              
1008             Unindent multi-line string.
1009              
1010             # "foo\nbar\nbaz\n"
1011             unindent " foo\n bar\n baz\n";
1012              
1013             =head2 unquote
1014              
1015             my $str = unquote $quoted;
1016              
1017             Unquote string.
1018              
1019             =head2 url_escape
1020              
1021             my $escaped = url_escape $str;
1022             my $escaped = url_escape $str, '^A-Za-z0-9\-._~';
1023              
1024             Percent encode unsafe characters in string as described in L, the pattern
1025             used defaults to C<^A-Za-z0-9\-._~>.
1026              
1027             # "foo%3Bbar"
1028             url_escape 'foo;bar';
1029              
1030             =head2 url_unescape
1031              
1032             my $str = url_unescape $escaped;
1033              
1034             Decode percent encoded characters in string as described in L.
1035              
1036             # "foo;bar"
1037             url_unescape 'foo%3Bbar';
1038              
1039             =head2 xml_escape
1040              
1041             my $escaped = xml_escape $str;
1042              
1043             Escape unsafe characters C<&>, C>, C>, C<"> and C<'> in string, but do not escape L
1044             objects.
1045              
1046             # "<div>"
1047             xml_escape '
';
1048              
1049             # "
"
1050             use Mojo::ByteStream qw(b);
1051             xml_escape b('
');
1052              
1053             =head2 xor_encode
1054              
1055             my $encoded = xor_encode $str, $key;
1056              
1057             XOR encode string with variable length key.
1058              
1059             =head1 SEE ALSO
1060              
1061             L, L, L.
1062              
1063             =cut