File Coverage

blib/lib/URI/_generic.pm
Criterion Covered Total %
statement 171 177 96.6
branch 89 106 83.9
condition 35 44 79.5
subroutine 15 15 100.0
pod 6 6 100.0
total 316 348 90.8


line stmt bran cond sub pod time code
1             package URI::_generic;
2              
3 37     37   15887 use strict;
  37         76  
  37         999  
4 37     37   177 use warnings;
  37         69  
  37         985  
5              
6 37     37   175 use parent qw(URI URI::_query);
  37         62  
  37         220  
7              
8 37     37   2022 use URI::Escape qw(uri_unescape);
  37         74  
  37         1534  
9 37     37   207 use Carp ();
  37         68  
  37         82322  
10              
11             our $VERSION = '5.19';
12              
13             my $ACHAR = URI::HAS_RESERVED_SQUARE_BRACKETS ? $URI::uric : $URI::uric4host; $ACHAR =~ s,\\[/?],,g;
14             my $PCHAR = $URI::uric; $PCHAR =~ s,\\[?],,g;
15              
16 353     353   1080 sub _no_scheme_ok { 1 }
17              
18             our $IPv6_re;
19              
20             sub _looks_like_raw_ip6_address {
21 315     315   476 my $addr = shift;
22              
23 315 100       673 if ( !$IPv6_re ) { #-- lazy / runs once / use Regexp::IPv6 if installed
24             eval {
25 20         2910 require Regexp::IPv6;
26 0         0 Regexp::IPv6->import( qw($IPv6_re) );
27 0         0 1;
28 20 50       48 } || do { $IPv6_re = qr/[:0-9a-f]{3,}/; }; #-- fallback: unambitious guess
  20         138  
29             }
30              
31 315 50       632 return 0 unless $addr;
32 315 100       1211 return 0 if $addr =~ tr/:/:/ < 2; #-- fallback must not create false positive for IPv4:Port = 0:0
33 26 100       320 return 1 if $addr =~ /^$IPv6_re$/i;
34 24         65 return 0;
35             }
36              
37              
38             sub authority
39             {
40 2336     2336 1 3185 my $self = shift;
41 2336 50       11149 $$self =~ m,^((?:$URI::scheme_re:)?)(?://([^/?\#]*))?(.*)$,os or die;
42              
43 2336 100       4577 if (@_) {
44 414         627 my $auth = shift;
45 414         838 $$self = $1;
46 414         728 my $rest = $3;
47 414 100       836 if (defined $auth) {
48 339         1057 $auth =~ s/([^$ACHAR])/ URI::Escape::escape_char($1)/ego;
  7         19  
49 339 100       1642 if ( my ($user, $host) = $auth =~ /^(.*@)?([^@]+)$/ ) { #-- special escape userinfo part
50 315   100     1179 $user ||= '';
51 315         810 $user =~ s/([^$URI::uric4user])/ URI::Escape::escape_char($1)/ego;
  56         159  
52 315         550 $user =~ s/%40$/\@/; # recover final '@'
53 315 100       646 $host = "[$host]" if _looks_like_raw_ip6_address( $host );
54 315         629 $auth = $user . $host;
55             }
56 339         811 utf8::downgrade($auth);
57 339         720 $$self .= "//$auth";
58             }
59 414         980 _check_path($rest, $$self);
60 414         947 $$self .= $rest;
61             }
62 2336         6757 $2;
63             }
64              
65             sub path
66             {
67 1319     1319 1 1959 my $self = shift;
68 1319 50       5926 $$self =~ m,^((?:[^:/?\#]+:)?(?://[^/?\#]*)?)([^?\#]*)(.*)$,s or die;
69              
70 1319 100       2502 if (@_) {
71 301         661 $$self = $1;
72 301         458 my $rest = $3;
73 301         426 my $new_path = shift;
74 301 100       555 $new_path = "" unless defined $new_path;
75 301         851 $new_path =~ s/([^$PCHAR])/ URI::Escape::escape_char($1)/ego;
  191         379  
76 301         704 utf8::downgrade($new_path);
77 301         618 _check_path($new_path, $$self);
78 301         705 $$self .= $new_path . $rest;
79             }
80 1319         3424 $2;
81             }
82              
83             sub path_query
84             {
85 58     58 1 88 my $self = shift;
86 58 50       396 $$self =~ m,^((?:[^:/?\#]+:)?(?://[^/?\#]*)?)([^\#]*)(.*)$,s or die;
87              
88 58 100       123 if (@_) {
89 3         7 $$self = $1;
90 3         5 my $rest = $3;
91 3         6 my $new_path = shift;
92 3 50       6 $new_path = "" unless defined $new_path;
93 3         38 $new_path =~ s/([^$URI::uric])/ URI::Escape::escape_char($1)/ego;
  1         4  
94 3         12 utf8::downgrade($new_path);
95 3         7 _check_path($new_path, $$self);
96 3         11 $$self .= $new_path . $rest;
97             }
98 58         200 $2;
99             }
100              
101             sub _check_path
102             {
103 718     718   1481 my($path, $pre) = @_;
104 718         925 my $prefix;
105 718 100       1741 if ($pre =~ m,/,) { # authority present
106 566 100 100     2353 $prefix = "/" if length($path) && $path !~ m,^[/?\#],;
107             }
108             else {
109 152 50 66     734 if ($path =~ m,^//,) {
    50          
110 0 0       0 Carp::carp("Path starting with double slash is confusing")
111             if $^W;
112             }
113             elsif (!length($pre) && $path =~ m,^[^:/?\#]+:,) {
114 0 0       0 Carp::carp("Path might look like scheme, './' prepended")
115             if $^W;
116 0         0 $prefix = "./";
117             }
118             }
119 718 100       2011 substr($_[0], 0, 0) = $prefix if defined $prefix;
120             }
121              
122             sub path_segments
123             {
124 90     90 1 702 my $self = shift;
125 90         187 my $path = $self->path;
126 90 100       205 if (@_) {
127 6         28 my @arg = @_; # make a copy
128 6         19 for (@arg) {
129 21 100       37 if (ref($_)) {
130 1         25 my @seg = @$_;
131 1         2 $seg[0] =~ s/%/%25/g;
132 1         2 for (@seg) { s/;/%3B/g; }
  3         5  
133 1         4 $_ = join(";", @seg);
134             }
135             else {
136 20         30 s/%/%25/g; s/;/%3B/g;
  20         29  
137             }
138 21         32 s,/,%2F,g;
139             }
140 6         24 $self->path(join("/", @arg));
141             }
142 90 100       203 return $path unless wantarray;
143 81 100       259 map {/;/ ? $self->_split_segment($_)
  194         594  
144             : uri_unescape($_) }
145             split('/', $path, -1);
146             }
147              
148              
149             sub _split_segment
150             {
151 4     4   12 my $self = shift;
152 4         1026 require URI::_segment;
153 4         28 URI::_segment->new(@_);
154             }
155              
156              
157             sub abs
158             {
159 274     274 1 661 my $self = shift;
160 274   33     714 my $base = shift || Carp::croak("Missing base argument");
161              
162 274 100       755 if (my $scheme = $self->scheme) {
163 29 100       139 return $self unless $URI::ABS_ALLOW_RELATIVE_SCHEME;
164 10 50       33 $base = URI->new($base) unless ref $base;
165 10 100       28 return $self unless $scheme eq $base->scheme;
166             }
167              
168 252 100       659 $base = URI->new($base) unless ref $base;
169 252         622 my $abs = $self->clone;
170 252         543 $abs->scheme($base->scheme);
171 252 100       913 return $abs if $$self =~ m,^(?:$URI::scheme_re:)?//,o;
172 241         591 $abs->authority($base->authority);
173              
174 241         539 my $path = $self->path;
175 241 100       639 return $abs if $path =~ m,^/,;
176              
177 222 100       433 if (!length($path)) {
178 26         85 my $abs = $base->clone;
179 26         163 my $query = $self->query;
180 26 100       75 $abs->query($query) if defined $query;
181 26         144 my $fragment = $self->fragment;
182 26 100       79 $abs->fragment($fragment) if defined $fragment;
183 26         180 return $abs;
184             }
185              
186 196         410 my $p = $base->path;
187 196         842 $p =~ s,[^/]+$,,;
188 196         396 $p .= $path;
189 196         719 my @p = split('/', $p, -1);
190 196 100 66     975 shift(@p) if @p && !length($p[0]);
191 196         354 my $i = 1;
192 196         385 while ($i < @p) {
193             #print "$i ", join("/", @p), " ($p[$i])\n";
194 616 100 100     1768 if ($p[$i-1] eq ".") {
    100          
195 32         61 splice(@p, $i-1, 1);
196 32 50       87 $i-- if $i > 1;
197             }
198             elsif ($p[$i] eq ".." && $p[$i-1] ne "..") {
199 107         191 splice(@p, $i-1, 2);
200 107 100       220 if ($i > 1) {
201 77         97 $i--;
202 77 100       220 push(@p, "") if $i == @p;
203             }
204             }
205             else {
206 477         872 $i++;
207             }
208             }
209 196 100 100     693 $p[-1] = "" if @p && $p[-1] eq "."; # trailing "/."
210 196 100       373 if ($URI::ABS_REMOTE_LEADING_DOTS) {
211 3   66     44 shift @p while @p && $p[0] =~ /^\.\.?$/;
212             }
213 196         769 $abs->path("/" . join("/", @p));
214 196         1169 $abs;
215             }
216              
217             # The opposite of $url->abs. Return a URI which is as relative as possible
218             sub rel {
219 67     67 1 118 my $self = shift;
220 67   33     137 my $base = shift || Carp::croak("Missing base argument");
221 67         161 my $rel = $self->clone;
222 67 50       210 $base = URI->new($base) unless ref $base;
223              
224             #my($scheme, $auth, $path) = @{$rel}{qw(scheme authority path)};
225 67         159 my $scheme = $rel->scheme;
226 67         178 my $auth = $rel->canonical->authority;
227 67         172 my $path = $rel->path;
228              
229 67 0 33     140 if (!defined($scheme) && !defined($auth)) {
230             # it is already relative
231 0         0 return $rel;
232             }
233              
234             #my($bscheme, $bauth, $bpath) = @{$base}{qw(scheme authority path)};
235 67         139 my $bscheme = $base->scheme;
236 67         156 my $bauth = $base->canonical->authority;
237 67         129 my $bpath = $base->path;
238              
239 67         137 for ($bscheme, $bauth, $auth) {
240 201 100       381 $_ = '' unless defined
241             }
242              
243 67 100 100     251 unless ($scheme eq $bscheme && $auth eq $bauth) {
244             # different location, can't make it relative
245 5         29 return $rel;
246             }
247              
248 62 100       105 for ($path, $bpath) { $_ = "/$_" unless m,^/,; }
  124         343  
249              
250             # Make it relative by eliminating scheme and authority
251 62         166 $rel->scheme(undef);
252 62         136 $rel->authority(undef);
253              
254             # This loop is based on code from Nicolai Langfeldt .
255             # First we calculate common initial path components length ($li).
256 62         86 my $li = 1;
257 62         80 while (1) {
258 137         207 my $i = index($path, '/', $li);
259 137 100 100     547 last if $i < 0 ||
      100        
260             $i != index($bpath, '/', $li) ||
261             substr($path,$li,$i-$li) ne substr($bpath,$li,$i-$li);
262 75         110 $li=$i+1;
263             }
264             # then we nuke it from both paths
265 62         125 substr($path, 0,$li) = '';
266 62         97 substr($bpath,0,$li) = '';
267              
268 62 100 100     199 if ($path eq $bpath &&
      100        
269             defined($rel->fragment) &&
270             !defined($rel->query)) {
271 1         3 $rel->path("");
272             }
273             else {
274             # Add one "../" for each path component left in the base path
275 61         184 $path = ('../' x $bpath =~ tr|/|/|) . $path;
276 61 100       153 $path = "./" if $path eq "";
277 61         123 $rel->path($path);
278             }
279              
280 62         258 $rel;
281             }
282              
283             1;