| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package URL::Encode::PP; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 2 |  |  | 2 |  | 82389 | use strict; | 
|  | 2 |  |  |  |  | 6 |  | 
|  | 2 |  |  |  |  | 80 |  | 
| 4 | 2 |  |  | 2 |  | 12 | use warnings; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 62 |  | 
| 5 |  |  |  |  |  |  |  | 
| 6 | 2 |  |  | 2 |  | 12 | use Carp qw[]; | 
|  | 2 |  |  |  |  | 15 |  | 
|  | 2 |  |  |  |  | 159 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | BEGIN { | 
| 9 | 2 |  |  | 2 |  | 4 | our $VERSION   = '0.03'; | 
| 10 | 2 |  |  |  |  | 7 | our @EXPORT_OK = qw[ url_encode | 
| 11 |  |  |  |  |  |  | url_encode_utf8 | 
| 12 |  |  |  |  |  |  | url_decode | 
| 13 |  |  |  |  |  |  | url_decode_utf8 | 
| 14 |  |  |  |  |  |  | url_params_each | 
| 15 |  |  |  |  |  |  | url_params_flat | 
| 16 |  |  |  |  |  |  | url_params_mixed | 
| 17 |  |  |  |  |  |  | url_params_multi ]; | 
| 18 | 2 |  |  |  |  | 10 | require Exporter; | 
| 19 | 2 |  |  |  |  | 293 | *import = \&Exporter::import; | 
| 20 |  |  |  |  |  |  | } | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | my (%DecodeMap, %EncodeMap); | 
| 23 |  |  |  |  |  |  | BEGIN { | 
| 24 | 2 |  |  | 2 |  | 7 | for my $ord (0..255) { | 
| 25 | 512 |  |  |  |  | 838 | my $chr = pack 'C', $ord; | 
| 26 | 512 |  |  |  |  | 825 | my $hex = sprintf '%.2X', $ord; | 
| 27 | 512 |  |  |  |  | 3173 | $DecodeMap{lc $hex} = $chr; | 
| 28 | 512 |  |  |  |  | 948 | $DecodeMap{uc $hex} = $chr; | 
| 29 | 512 |  |  |  |  | 11320 | $DecodeMap{sprintf '%X%x', $ord >> 4, $ord & 15} = $chr; | 
| 30 | 512 |  |  |  |  | 1028 | $DecodeMap{sprintf '%x%X', $ord >> 4, $ord & 15} = $chr; | 
| 31 | 512 |  |  |  |  | 1532 | $EncodeMap{$chr} = '%' . $hex; | 
| 32 |  |  |  |  |  |  | } | 
| 33 | 2 |  |  |  |  | 2085 | $EncodeMap{"\x20"} = '+'; | 
| 34 |  |  |  |  |  |  | } | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | sub url_decode { | 
| 37 | 196 | 50 |  | 196 | 0 | 81542 | @_ == 1 || Carp::croak(q/Usage: url_decode(octets)/); | 
| 38 | 196 |  |  |  |  | 269 | my ($s) = @_; | 
| 39 | 196 | 50 |  |  |  | 533 | utf8::downgrade($s, 1) | 
| 40 |  |  |  |  |  |  | or Carp::croak(q/Wide character in octet string/); | 
| 41 | 196 |  |  |  |  | 341 | $s =~ y/+/\x20/; | 
| 42 | 196 |  |  |  |  | 1364 | $s =~ s/%([0-9A-Za-z]{2})/$DecodeMap{$1}/gs; | 
| 43 | 196 |  |  |  |  | 921 | return $s; | 
| 44 |  |  |  |  |  |  | } | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | sub url_decode_utf8 { | 
| 47 | 1 | 50 |  | 1 | 0 | 6 | @_ == 1 || Carp::croak(q/Usage: url_decode_utf8(octets)/); | 
| 48 | 1 |  |  |  |  | 4 | my $s = &url_decode; | 
| 49 | 1 | 50 |  |  |  | 6 | utf8::decode($s) | 
| 50 |  |  |  |  |  |  | or Carp::croak(q/Malformed UTF-8 in URL-decoded octets/); | 
| 51 | 1 |  |  |  |  | 5 | return $s; | 
| 52 |  |  |  |  |  |  | } | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | sub url_encode { | 
| 55 | 195 | 50 |  | 195 | 0 | 85821 | @_ == 1 || Carp::croak(q/Usage: url_encode(octets)/); | 
| 56 | 195 |  |  |  |  | 262 | my ($s) = @_; | 
| 57 | 195 | 50 |  |  |  | 545 | utf8::downgrade($s, 1) | 
| 58 |  |  |  |  |  |  | or Carp::croak(q/Wide character in octet string/); | 
| 59 | 195 |  |  |  |  | 1367 | $s =~ s/([^0-9A-Za-z_.~-])/$EncodeMap{$1}/gs; | 
| 60 | 195 |  |  |  |  | 971 | return $s; | 
| 61 |  |  |  |  |  |  | } | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | sub url_encode_utf8 { | 
| 64 | 1 | 50 |  | 1 | 0 | 715 | @_ == 1 || Carp::croak(q/Usage: url_encode_utf8(string)/); | 
| 65 | 1 |  |  |  |  | 3 | my ($s) = @_; | 
| 66 | 1 |  |  |  |  | 4 | utf8::encode($s); | 
| 67 | 1 |  |  |  |  | 4 | return url_encode($s); | 
| 68 |  |  |  |  |  |  | } | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | sub url_params_each { | 
| 71 | 69 | 50 | 66 | 69 | 0 | 1181 | @_ == 2 || @_ == 3 || Carp::croak(q/Usage: url_params_each(octets, callback [, utf8])/); | 
| 72 | 69 |  |  |  |  | 154 | my ($s, $callback, $utf8) = @_; | 
| 73 |  |  |  |  |  |  |  | 
| 74 | 69 | 50 |  |  |  | 197 | utf8::downgrade($s, 1) | 
| 75 |  |  |  |  |  |  | or Carp::croak(q/Wide character in octet string/); | 
| 76 |  |  |  |  |  |  |  | 
| 77 | 69 |  |  |  |  | 299 | foreach my $pair (split /[&;]/, $s, -1) { | 
| 78 | 130 |  |  |  |  | 2661 | my ($k, $v) = split '=', $pair, 2; | 
| 79 | 130 | 100 |  |  |  | 270 | $k = '' unless defined $k; | 
| 80 | 130 | 100 |  |  |  | 281 | for ($k, defined $v ? $v : ()) { | 
| 81 | 184 |  |  |  |  | 210 | y/+/\x20/; | 
| 82 | 184 |  |  |  |  | 263 | s/%([0-9a-fA-F]{2})/$DecodeMap{$1}/gs; | 
| 83 | 184 | 50 |  |  |  | 454 | if ($utf8) { | 
| 84 | 0 | 0 |  |  |  | 0 | utf8::decode($_) | 
| 85 |  |  |  |  |  |  | or Carp::croak("Malformed UTF-8 in URL-decoded octets"); | 
| 86 |  |  |  |  |  |  | } | 
| 87 |  |  |  |  |  |  | } | 
| 88 | 130 |  |  |  |  | 285 | $callback->($k, $v); | 
| 89 |  |  |  |  |  |  | } | 
| 90 |  |  |  |  |  |  | } | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | sub url_params_flat { | 
| 93 | 20 | 50 | 33 | 20 | 0 | 13833 | @_ == 1 || @_ == 2 || Carp::croak(q/Usage: url_params_flat(octets [, utf8])/); | 
| 94 | 20 |  |  |  |  | 30 | my @p; | 
| 95 |  |  |  |  |  |  | my $callback = sub { | 
| 96 | 33 |  |  | 33 |  | 50 | my ($k, $v) = @_; | 
| 97 | 33 |  |  |  |  | 102 | push @p, $k, $v; | 
| 98 | 20 |  |  |  |  | 104 | }; | 
| 99 | 20 |  |  |  |  | 65 | url_params_each($_[0], $callback, $_[1]); | 
| 100 | 20 |  |  |  |  | 155 | return \@p; | 
| 101 |  |  |  |  |  |  | } | 
| 102 |  |  |  |  |  |  |  | 
| 103 |  |  |  |  |  |  | sub url_params_mixed { | 
| 104 | 24 | 50 | 33 | 24 | 0 | 19193 | @_ == 1 || @_ == 2 || Carp::croak(q/Usage: url_params_mixed(octets [, utf8])/); | 
| 105 | 24 |  |  |  |  | 114 | my %p; | 
| 106 |  |  |  |  |  |  | my $callback = sub { | 
| 107 | 47 |  |  | 47 |  | 71 | my ($k, $v) = @_; | 
| 108 | 47 | 100 |  |  |  | 96 | if (exists $p{$k}) { | 
| 109 | 14 |  |  |  |  | 26 | for ($p{$k}) { | 
| 110 | 14 | 50 |  |  |  | 64 | $_ = [$_] unless ref $_ eq 'ARRAY'; | 
| 111 | 14 |  |  |  |  | 58 | push @$_, $v; | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  | } | 
| 114 |  |  |  |  |  |  | else { | 
| 115 | 33 |  |  |  |  | 104 | $p{$k} = $v; | 
| 116 |  |  |  |  |  |  | } | 
| 117 | 24 |  |  |  |  | 116 | }; | 
| 118 | 24 |  |  |  |  | 78 | url_params_each($_[0], $callback, $_[1]); | 
| 119 | 24 |  |  |  |  | 212 | return \%p; | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | sub url_params_multi { | 
| 123 | 24 | 50 | 33 | 24 | 0 | 22462 | @_ == 1 || @_ == 2 || Carp::croak(q/Usage: url_params_multi(octets [, utf8])/); | 
| 124 | 24 |  |  |  |  | 40 | my %p; | 
| 125 |  |  |  |  |  |  | my $callback = sub { | 
| 126 | 47 |  |  | 47 |  | 81 | my ($k, $v) = @_; | 
| 127 | 47 |  | 100 |  |  | 51 | push @{ $p{$k} ||= [] }, $v; | 
|  | 47 |  |  |  |  | 289 |  | 
| 128 | 24 |  |  |  |  | 117 | }; | 
| 129 | 24 |  |  |  |  | 79 | url_params_each($_[0], $callback, $_[1]); | 
| 130 | 24 |  |  |  |  | 188 | return \%p; | 
| 131 |  |  |  |  |  |  | } | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | 1; | 
| 134 |  |  |  |  |  |  |  |