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 52     52   19536 use strict;
  52         79  
  52         1424  
4 52     52   171 use warnings;
  52         62  
  52         1984  
5              
6 52     52   195 use parent qw(URI URI::_query);
  52         64  
  52         287  
7              
8 52     52   2611 use URI::Escape qw(uri_unescape);
  52         67  
  52         2115  
9 52     52   308 use Carp ();
  52         133  
  52         101035  
10              
11             our $VERSION = '5.35';
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   914 sub _no_scheme_ok { 1 }
17              
18             our $IPv6_re;
19              
20             sub _looks_like_raw_ip6_address {
21 353     353   417 my $addr = shift;
22              
23 353 100       594 if ( !$IPv6_re ) { #-- lazy / runs once / use Regexp::IPv6 if installed
24             eval {
25 26         2838 require Regexp::IPv6;
26 0         0 Regexp::IPv6->import( qw($IPv6_re) );
27 0         0 1;
28 26 50       70 } || do { $IPv6_re = qr/[:0-9a-f]{3,}/; }; #-- fallback: unambitious guess
  26         178  
29             }
30              
31 353 50       565 return 0 unless $addr;
32 353 100       1171 return 0 if $addr =~ tr/:/:/ < 2; #-- fallback must not create false positive for IPv4:Port = 0:0
33 28 100       278 return 1 if $addr =~ /^$IPv6_re$/i;
34 26         61 return 0;
35             }
36              
37              
38             sub authority
39             {
40 2538     2538 1 2812 my $self = shift;
41 2538 50       10766 $$self =~ m,^((?:$URI::scheme_re:)?)(?://([^/?\#]*))?(.*)$,os or die;
42              
43 2538 100       3991 if (@_) {
44 452         565 my $auth = shift;
45 452         723 $$self = $1;
46 452         743 my $rest = $3;
47 452 100       706 if (defined $auth) {
48 377         1394 $auth =~ s/([^$ACHAR])/ URI::Escape::escape_char($1)/ego;
  7         11  
49 377 100       1658 if ( my ($user, $host) = $auth =~ /^(.*@)?([^@]+)$/ ) { #-- special escape userinfo part
50 353   100     1091 $user ||= '';
51 353         1072 $user =~ s/([^$URI::uric4user])/ URI::Escape::escape_char($1)/ego;
  68         171  
52 353         498 $user =~ s/%40$/\@/; # recover final '@'
53 353 100       546 $host = "[$host]" if _looks_like_raw_ip6_address( $host );
54 353         636 $auth = $user . $host;
55             }
56 377         838 utf8::downgrade($auth);
57 377         625 $$self .= "//$auth";
58             }
59 452         916 _check_path($rest, $$self);
60 452         836 $$self .= $rest;
61             }
62 2538         5398 $2;
63             }
64              
65             sub path
66             {
67 1361     1361 1 1519 my $self = shift;
68 1361 50       4940 $$self =~ m,^((?:[^:/?\#]+:)?(?://[^/?\#]*)?)([^?\#]*)(.*)$,s or die;
69              
70 1361 100       1987 if (@_) {
71 317         534 $$self = $1;
72 317         436 my $rest = $3;
73 317         435 my $new_path = shift;
74 317 100       464 $new_path = "" unless defined $new_path;
75 317         951 $new_path =~ s/([^$PCHAR])/ URI::Escape::escape_char($1)/ego;
  191         213  
76 317         618 utf8::downgrade($new_path);
77 317         541 _check_path($new_path, $$self);
78 317         547 $$self .= $new_path . $rest;
79             }
80 1361         2891 $2;
81             }
82              
83             sub path_query
84             {
85 58     58 1 88 my $self = shift;
86 58 50       354 $$self =~ m,^((?:[^:/?\#]+:)?(?://[^/?\#]*)?)([^\#]*)(.*)$,s or die;
87              
88 58 100       122 if (@_) {
89 3         17 $$self = $1;
90 3         4 my $rest = $3;
91 3         4 my $new_path = shift;
92 3 50       7 $new_path = "" unless defined $new_path;
93 3         45 $new_path =~ s/([^$URI::uric])/ URI::Escape::escape_char($1)/ego;
  1         2  
94 3         5 utf8::downgrade($new_path);
95 3         7 _check_path($new_path, $$self);
96 3         7 $$self .= $new_path . $rest;
97             }
98 58         177 $2;
99             }
100              
101             sub _check_path
102             {
103 772     772   1254 my($path, $pre) = @_;
104 772         809 my $prefix;
105 772 100       1486 if ($pre =~ m,/,) { # authority present
106 620 100 100     2054 $prefix = "/" if length($path) && $path !~ m,^[/?\#],;
107             }
108             else {
109 152 50 66     580 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 772 100       1782 substr($_[0], 0, 0) = $prefix if defined $prefix;
120             }
121              
122             sub path_segments
123             {
124 91     91 1 511 my $self = shift;
125 91         182 my $path = $self->path;
126 91 100       172 if (@_) {
127 6         24 my @arg = @_; # make a copy
128 6         12 for (@arg) {
129 21 100       27 if (ref($_)) {
130 1         31 my @seg = @$_;
131 1         4 $seg[0] =~ s/%/%25/g;
132 1         3 for (@seg) { s/;/%3B/g; }
  3         6  
133 1         6 $_ = join(";", @seg);
134             }
135             else {
136 20         28 s/%/%25/g; s/;/%3B/g;
  20         40  
137             }
138 21         35 s,/,%2F,g;
139             }
140 6         33 $self->path(join("/", @arg));
141             }
142 91 100       174 return $path unless wantarray;
143 82 100       274 map {/;/ ? $self->_split_segment($_)
  197         527  
144             : uri_unescape($_) }
145             split('/', $path, -1);
146             }
147              
148              
149             sub _split_segment
150             {
151 4     4   11 my $self = shift;
152 4         1066 require URI::_segment;
153 4         22 URI::_segment->new(@_);
154             }
155              
156              
157             sub abs
158             {
159 274     274 1 644 my $self = shift;
160 274   33     710 my $base = shift || Carp::croak("Missing base argument");
161              
162 274 100       671 if (my $scheme = $self->scheme) {
163 29 100       127 return $self unless $URI::ABS_ALLOW_RELATIVE_SCHEME;
164 10 50       49 $base = URI->new($base) unless ref $base;
165 10 100       21 return $self unless $scheme eq $base->scheme;
166             }
167              
168 252 100       616 $base = URI->new($base) unless ref $base;
169 252         558 my $abs = $self->clone;
170 252         482 $abs->scheme($base->scheme);
171 252 100       901 return $abs if $$self =~ m,^(?:$URI::scheme_re:)?//,o;
172 241         502 $abs->authority($base->authority);
173              
174 241         450 my $path = $self->path;
175 241 100       468 return $abs if $path =~ m,^/,;
176              
177 222 100       362 if (!length($path)) {
178 26         74 my $abs = $base->clone;
179 26         157 my $query = $self->query;
180 26 100       75 $abs->query($query) if defined $query;
181 26         129 my $fragment = $self->fragment;
182 26 100       70 $abs->fragment($fragment) if defined $fragment;
183 26         169 return $abs;
184             }
185              
186 196         336 my $p = $base->path;
187 196         768 $p =~ s,[^/]+$,,;
188 196         288 $p .= $path;
189 196         557 my @p = split('/', $p, -1);
190 196 100 66     650 shift(@p) if @p && !length($p[0]);
191 196         273 my $i = 1;
192 196         354 while ($i < @p) {
193             #print "$i ", join("/", @p), " ($p[$i])\n";
194 616 100 100     1404 if ($p[$i-1] eq ".") {
    100          
195 32         60 splice(@p, $i-1, 1);
196 32 50       73 $i-- if $i > 1;
197             }
198             elsif ($p[$i] eq ".." && $p[$i-1] ne "..") {
199 107         153 splice(@p, $i-1, 2);
200 107 100       229 if ($i > 1) {
201 77         76 $i--;
202 77 100       146 push(@p, "") if $i == @p;
203             }
204             }
205             else {
206 477         756 $i++;
207             }
208             }
209 196 100 100     558 $p[-1] = "" if @p && $p[-1] eq "."; # trailing "/."
210 196 100       314 if ($URI::ABS_REMOTE_LEADING_DOTS) {
211 3   66     30 shift @p while @p && $p[0] =~ /^\.\.?$/;
212             }
213 196         644 $abs->path("/" . join("/", @p));
214 196         942 $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 87 my $self = shift;
220 67   33     146 my $base = shift || Carp::croak("Missing base argument");
221 67         133 my $rel = $self->clone;
222 67 50       220 $base = URI->new($base) unless ref $base;
223              
224             #my($scheme, $auth, $path) = @{$rel}{qw(scheme authority path)};
225 67         186 my $scheme = $rel->scheme;
226 67         206 my $auth = $rel->canonical->authority;
227 67         183 my $path = $rel->path;
228              
229 67 0 33     113 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         137 my $bscheme = $base->scheme;
236 67         135 my $bauth = $base->canonical->authority;
237 67         131 my $bpath = $base->path;
238              
239 67         159 for ($bscheme, $bauth, $auth) {
240 201 100       338 $_ = '' unless defined
241             }
242              
243 67 100 100     230 unless ($scheme eq $bscheme && $auth eq $bauth) {
244             # different location, can't make it relative
245 5         32 return $rel;
246             }
247              
248 62 100       85 for ($path, $bpath) { $_ = "/$_" unless m,^/,; }
  124         279  
249              
250             # Make it relative by eliminating scheme and authority
251 62         146 $rel->scheme(undef);
252 62         110 $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         76 my $li = 1;
257 62         63 while (1) {
258 137         214 my $i = index($path, '/', $li);
259 137 100 100     519 last if $i < 0 ||
      100        
260             $i != index($bpath, '/', $li) ||
261             substr($path,$li,$i-$li) ne substr($bpath,$li,$i-$li);
262 75         96 $li=$i+1;
263             }
264             # then we nuke it from both paths
265 62         124 substr($path, 0,$li) = '';
266 62         105 substr($bpath,0,$li) = '';
267              
268 62 100 100     186 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         165 $path = ('../' x $bpath =~ tr|/|/|) . $path;
276 61 100       116 $path = "./" if $path eq "";
277 61         104 $rel->path($path);
278             }
279              
280 62         314 $rel;
281             }
282              
283             1;