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 51     51   26654 use strict;
  51         113  
  51         1960  
4 51     51   253 use warnings;
  51         92  
  51         2997  
5              
6 51     51   276 use parent qw(URI URI::_query);
  51         88  
  51         412  
7              
8 51     51   3346 use URI::Escape qw(uri_unescape);
  51         114  
  51         2866  
9 51     51   445 use Carp ();
  51         151  
  51         140095  
10              
11             our $VERSION = '5.34';
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   1146 sub _no_scheme_ok { 1 }
17              
18             our $IPv6_re;
19              
20             sub _looks_like_raw_ip6_address {
21 337     337   630 my $addr = shift;
22              
23 337 100       801 if ( !$IPv6_re ) { #-- lazy / runs once / use Regexp::IPv6 if installed
24             eval {
25 25         3588 require Regexp::IPv6;
26 0         0 Regexp::IPv6->import( qw($IPv6_re) );
27 0         0 1;
28 25 50       62 } || do { $IPv6_re = qr/[:0-9a-f]{3,}/; }; #-- fallback: unambitious guess
  25         205  
29             }
30              
31 337 50       811 return 0 unless $addr;
32 337 100       1489 return 0 if $addr =~ tr/:/:/ < 2; #-- fallback must not create false positive for IPv4:Port = 0:0
33 26 100       348 return 1 if $addr =~ /^$IPv6_re$/i;
34 24         92 return 0;
35             }
36              
37              
38             sub authority
39             {
40 2443     2443 1 3479 my $self = shift;
41 2443 50       14273 $$self =~ m,^((?:$URI::scheme_re:)?)(?://([^/?\#]*))?(.*)$,os or die;
42              
43 2443 100       4976 if (@_) {
44 436         761 my $auth = shift;
45 436         1044 $$self = $1;
46 436         987 my $rest = $3;
47 436 100       1049 if (defined $auth) {
48 361         1862 $auth =~ s/([^$ACHAR])/ URI::Escape::escape_char($1)/ego;
  7         20  
49 361 100       2280 if ( my ($user, $host) = $auth =~ /^(.*@)?([^@]+)$/ ) { #-- special escape userinfo part
50 337   100     1513 $user ||= '';
51 337         1718 $user =~ s/([^$URI::uric4user])/ URI::Escape::escape_char($1)/ego;
  67         247  
52 337         711 $user =~ s/%40$/\@/; # recover final '@'
53 337 100       733 $host = "[$host]" if _looks_like_raw_ip6_address( $host );
54 337         822 $auth = $user . $host;
55             }
56 361         1036 utf8::downgrade($auth);
57 361         885 $$self .= "//$auth";
58             }
59 436         1255 _check_path($rest, $$self);
60 436         1146 $$self .= $rest;
61             }
62 2443         7105 $2;
63             }
64              
65             sub path
66             {
67 1330     1330 1 1885 my $self = shift;
68 1330 50       6762 $$self =~ m,^((?:[^:/?\#]+:)?(?://[^/?\#]*)?)([^?\#]*)(.*)$,s or die;
69              
70 1330 100       2629 if (@_) {
71 303         837 $$self = $1;
72 303         607 my $rest = $3;
73 303         525 my $new_path = shift;
74 303 100       622 $new_path = "" unless defined $new_path;
75 303         1187 $new_path =~ s/([^$PCHAR])/ URI::Escape::escape_char($1)/ego;
  191         271  
76 303         813 utf8::downgrade($new_path);
77 303         775 _check_path($new_path, $$self);
78 303         705 $$self .= $new_path . $rest;
79             }
80 1330         3641 $2;
81             }
82              
83             sub path_query
84             {
85 58     58 1 97 my $self = shift;
86 58 50       463 $$self =~ m,^((?:[^:/?\#]+:)?(?://[^/?\#]*)?)([^\#]*)(.*)$,s or die;
87              
88 58 100       128 if (@_) {
89 3         3 $$self = $1;
90 3         4 my $rest = $3;
91 3         4 my $new_path = shift;
92 3 50       5 $new_path = "" unless defined $new_path;
93 3         39 $new_path =~ s/([^$URI::uric])/ URI::Escape::escape_char($1)/ego;
  1         3  
94 3         6 utf8::downgrade($new_path);
95 3         5 _check_path($new_path, $$self);
96 3         7 $$self .= $new_path . $rest;
97             }
98 58         243 $2;
99             }
100              
101             sub _check_path
102             {
103 742     742   1806 my($path, $pre) = @_;
104 742         1124 my $prefix;
105 742 100       2140 if ($pre =~ m,/,) { # authority present
106 590 100 100     2900 $prefix = "/" if length($path) && $path !~ m,^[/?\#],;
107             }
108             else {
109 152 50 66     778 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 742 100       2535 substr($_[0], 0, 0) = $prefix if defined $prefix;
120             }
121              
122             sub path_segments
123             {
124 91     91 1 487 my $self = shift;
125 91         285 my $path = $self->path;
126 91 100       176 if (@_) {
127 6         29 my @arg = @_; # make a copy
128 6         10 for (@arg) {
129 21 100       28 if (ref($_)) {
130 1         19 my @seg = @$_;
131 1         1 $seg[0] =~ s/%/%25/g;
132 1         1 for (@seg) { s/;/%3B/g; }
  3         4  
133 1         3 $_ = join(";", @seg);
134             }
135             else {
136 20         27 s/%/%25/g; s/;/%3B/g;
  20         23  
137             }
138 21         32 s,/,%2F,g;
139             }
140 6         19 $self->path(join("/", @arg));
141             }
142 91 100       198 return $path unless wantarray;
143 82 100       323 map {/;/ ? $self->_split_segment($_)
  197         651  
144             : uri_unescape($_) }
145             split('/', $path, -1);
146             }
147              
148              
149             sub _split_segment
150             {
151 4     4   9 my $self = shift;
152 4         916 require URI::_segment;
153 4         20 URI::_segment->new(@_);
154             }
155              
156              
157             sub abs
158             {
159 274     274 1 773 my $self = shift;
160 274   33     864 my $base = shift || Carp::croak("Missing base argument");
161              
162 274 100       925 if (my $scheme = $self->scheme) {
163 29 100       198 return $self unless $URI::ABS_ALLOW_RELATIVE_SCHEME;
164 10 50       32 $base = URI->new($base) unless ref $base;
165 10 100       23 return $self unless $scheme eq $base->scheme;
166             }
167              
168 252 100       831 $base = URI->new($base) unless ref $base;
169 252         743 my $abs = $self->clone;
170 252         678 $abs->scheme($base->scheme);
171 252 100       1086 return $abs if $$self =~ m,^(?:$URI::scheme_re:)?//,o;
172 241         711 $abs->authority($base->authority);
173              
174 241         618 my $path = $self->path;
175 241 100       680 return $abs if $path =~ m,^/,;
176              
177 222 100       481 if (!length($path)) {
178 26         85 my $abs = $base->clone;
179 26         172 my $query = $self->query;
180 26 100       91 $abs->query($query) if defined $query;
181 26         113 my $fragment = $self->fragment;
182 26 100       80 $abs->fragment($fragment) if defined $fragment;
183 26         166 return $abs;
184             }
185              
186 196         416 my $p = $base->path;
187 196         1182 $p =~ s,[^/]+$,,;
188 196         458 $p .= $path;
189 196         826 my @p = split('/', $p, -1);
190 196 100 66     823 shift(@p) if @p && !length($p[0]);
191 196         344 my $i = 1;
192 196         511 while ($i < @p) {
193             #print "$i ", join("/", @p), " ($p[$i])\n";
194 616 100 100     2118 if ($p[$i-1] eq ".") {
    100          
195 32         81 splice(@p, $i-1, 1);
196 32 50       110 $i-- if $i > 1;
197             }
198             elsif ($p[$i] eq ".." && $p[$i-1] ne "..") {
199 107         266 splice(@p, $i-1, 2);
200 107 100       303 if ($i > 1) {
201 77         130 $i--;
202 77 100       235 push(@p, "") if $i == @p;
203             }
204             }
205             else {
206 477         1023 $i++;
207             }
208             }
209 196 100 100     737 $p[-1] = "" if @p && $p[-1] eq "."; # trailing "/."
210 196 100       460 if ($URI::ABS_REMOTE_LEADING_DOTS) {
211 3   66     53 shift @p while @p && $p[0] =~ /^\.\.?$/;
212             }
213 196         926 $abs->path("/" . join("/", @p));
214 196         1364 $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 155 my $self = shift;
220 67   33     155 my $base = shift || Carp::croak("Missing base argument");
221 67         276 my $rel = $self->clone;
222 67 50       231 $base = URI->new($base) unless ref $base;
223              
224             #my($scheme, $auth, $path) = @{$rel}{qw(scheme authority path)};
225 67         209 my $scheme = $rel->scheme;
226 67         217 my $auth = $rel->canonical->authority;
227 67         180 my $path = $rel->path;
228              
229 67 0 33     152 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         177 my $bscheme = $base->scheme;
236 67         152 my $bauth = $base->canonical->authority;
237 67         146 my $bpath = $base->path;
238              
239 67         210 for ($bscheme, $bauth, $auth) {
240 201 100       406 $_ = '' unless defined
241             }
242              
243 67 100 100     295 unless ($scheme eq $bscheme && $auth eq $bauth) {
244             # different location, can't make it relative
245 5         40 return $rel;
246             }
247              
248 62 100       125 for ($path, $bpath) { $_ = "/$_" unless m,^/,; }
  124         387  
249              
250             # Make it relative by eliminating scheme and authority
251 62         209 $rel->scheme(undef);
252 62         157 $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         95 my $li = 1;
257 62         108 while (1) {
258 137         234 my $i = index($path, '/', $li);
259 137 100 100     661 last if $i < 0 ||
      100        
260             $i != index($bpath, '/', $li) ||
261             substr($path,$li,$i-$li) ne substr($bpath,$li,$i-$li);
262 75         120 $li=$i+1;
263             }
264             # then we nuke it from both paths
265 62         153 substr($path, 0,$li) = '';
266 62         126 substr($bpath,0,$li) = '';
267              
268 62 100 100     211 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         179 $path = ('../' x $bpath =~ tr|/|/|) . $path;
276 61 100       158 $path = "./" if $path eq "";
277 61         144 $rel->path($path);
278             }
279              
280 62         327 $rel;
281             }
282              
283             1;