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 38     38   16952 use strict;
  38         107  
  38         1059  
4 38     38   184 use warnings;
  38         73  
  38         1056  
5              
6 38     38   173 use parent qw(URI URI::_query);
  38         65  
  38         240  
7              
8 38     38   2007 use URI::Escape qw(uri_unescape);
  38         77  
  38         1561  
9 38     38   214 use Carp ();
  38         76  
  38         86840  
10              
11             our $VERSION = '5.21';
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   1438 sub _no_scheme_ok { 1 }
17              
18             our $IPv6_re;
19              
20             sub _looks_like_raw_ip6_address {
21 319     319   528 my $addr = shift;
22              
23 319 100       646 if ( !$IPv6_re ) { #-- lazy / runs once / use Regexp::IPv6 if installed
24             eval {
25 21         3480 require Regexp::IPv6;
26 0         0 Regexp::IPv6->import( qw($IPv6_re) );
27 0         0 1;
28 21 50       56 } || do { $IPv6_re = qr/[:0-9a-f]{3,}/; }; #-- fallback: unambitious guess
  21         142  
29             }
30              
31 319 50       643 return 0 unless $addr;
32 319 100       1134 return 0 if $addr =~ tr/:/:/ < 2; #-- fallback must not create false positive for IPv4:Port = 0:0
33 26 100       327 return 1 if $addr =~ /^$IPv6_re$/i;
34 24         70 return 0;
35             }
36              
37              
38             sub authority
39             {
40 2351     2351 1 3108 my $self = shift;
41 2351 50       11040 $$self =~ m,^((?:$URI::scheme_re:)?)(?://([^/?\#]*))?(.*)$,os or die;
42              
43 2351 100       4520 if (@_) {
44 418         622 my $auth = shift;
45 418         799 $$self = $1;
46 418         732 my $rest = $3;
47 418 100       818 if (defined $auth) {
48 343         1047 $auth =~ s/([^$ACHAR])/ URI::Escape::escape_char($1)/ego;
  7         16  
49 343 100       1625 if ( my ($user, $host) = $auth =~ /^(.*@)?([^@]+)$/ ) { #-- special escape userinfo part
50 319   100     1192 $user ||= '';
51 319         863 $user =~ s/([^$URI::uric4user])/ URI::Escape::escape_char($1)/ego;
  56         139  
52 319         579 $user =~ s/%40$/\@/; # recover final '@'
53 319 100       612 $host = "[$host]" if _looks_like_raw_ip6_address( $host );
54 319         677 $auth = $user . $host;
55             }
56 343         854 utf8::downgrade($auth);
57 343         740 $$self .= "//$auth";
58             }
59 418         1023 _check_path($rest, $$self);
60 418         892 $$self .= $rest;
61             }
62 2351         6410 $2;
63             }
64              
65             sub path
66             {
67 1321     1321 1 1985 my $self = shift;
68 1321 50       5837 $$self =~ m,^((?:[^:/?\#]+:)?(?://[^/?\#]*)?)([^?\#]*)(.*)$,s or die;
69              
70 1321 100       2529 if (@_) {
71 301         620 $$self = $1;
72 301         519 my $rest = $3;
73 301         476 my $new_path = shift;
74 301 100       550 $new_path = "" unless defined $new_path;
75 301         907 $new_path =~ s/([^$PCHAR])/ URI::Escape::escape_char($1)/ego;
  191         345  
76 301         711 utf8::downgrade($new_path);
77 301         609 _check_path($new_path, $$self);
78 301         672 $$self .= $new_path . $rest;
79             }
80 1321         3363 $2;
81             }
82              
83             sub path_query
84             {
85 58     58 1 98 my $self = shift;
86 58 50       398 $$self =~ m,^((?:[^:/?\#]+:)?(?://[^/?\#]*)?)([^\#]*)(.*)$,s or die;
87              
88 58 100       134 if (@_) {
89 3         9 $$self = $1;
90 3         4 my $rest = $3;
91 3         6 my $new_path = shift;
92 3 50       6 $new_path = "" unless defined $new_path;
93 3         41 $new_path =~ s/([^$URI::uric])/ URI::Escape::escape_char($1)/ego;
  1         4  
94 3         11 utf8::downgrade($new_path);
95 3         8 _check_path($new_path, $$self);
96 3         12 $$self .= $new_path . $rest;
97             }
98 58         184 $2;
99             }
100              
101             sub _check_path
102             {
103 722     722   1531 my($path, $pre) = @_;
104 722         904 my $prefix;
105 722 100       1782 if ($pre =~ m,/,) { # authority present
106 570 100 100     2725 $prefix = "/" if length($path) && $path !~ m,^[/?\#],;
107             }
108             else {
109 152 50 66     695 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 722 100       2068 substr($_[0], 0, 0) = $prefix if defined $prefix;
120             }
121              
122             sub path_segments
123             {
124 90     90 1 561 my $self = shift;
125 90         180 my $path = $self->path;
126 90 100       192 if (@_) {
127 6         23 my @arg = @_; # make a copy
128 6         16 for (@arg) {
129 21 100       37 if (ref($_)) {
130 1         29 my @seg = @$_;
131 1         3 $seg[0] =~ s/%/%25/g;
132 1         1 for (@seg) { s/;/%3B/g; }
  3         5  
133 1         4 $_ = join(";", @seg);
134             }
135             else {
136 20         28 s/%/%25/g; s/;/%3B/g;
  20         24  
137             }
138 21         41 s,/,%2F,g;
139             }
140 6         30 $self->path(join("/", @arg));
141             }
142 90 100       201 return $path unless wantarray;
143 81 100       260 map {/;/ ? $self->_split_segment($_)
  194         580  
144             : uri_unescape($_) }
145             split('/', $path, -1);
146             }
147              
148              
149             sub _split_segment
150             {
151 4     4   12 my $self = shift;
152 4         912 require URI::_segment;
153 4         27 URI::_segment->new(@_);
154             }
155              
156              
157             sub abs
158             {
159 274     274 1 601 my $self = shift;
160 274   33     724 my $base = shift || Carp::croak("Missing base argument");
161              
162 274 100       684 if (my $scheme = $self->scheme) {
163 29 100       137 return $self unless $URI::ABS_ALLOW_RELATIVE_SCHEME;
164 10 50       34 $base = URI->new($base) unless ref $base;
165 10 100       26 return $self unless $scheme eq $base->scheme;
166             }
167              
168 252 100       695 $base = URI->new($base) unless ref $base;
169 252         628 my $abs = $self->clone;
170 252         537 $abs->scheme($base->scheme);
171 252 100       920 return $abs if $$self =~ m,^(?:$URI::scheme_re:)?//,o;
172 241         575 $abs->authority($base->authority);
173              
174 241         549 my $path = $self->path;
175 241 100       628 return $abs if $path =~ m,^/,;
176              
177 222 100       485 if (!length($path)) {
178 26         83 my $abs = $base->clone;
179 26         167 my $query = $self->query;
180 26 100       89 $abs->query($query) if defined $query;
181 26         149 my $fragment = $self->fragment;
182 26 100       84 $abs->fragment($fragment) if defined $fragment;
183 26         164 return $abs;
184             }
185              
186 196         401 my $p = $base->path;
187 196         815 $p =~ s,[^/]+$,,;
188 196         382 $p .= $path;
189 196         694 my @p = split('/', $p, -1);
190 196 100 66     987 shift(@p) if @p && !length($p[0]);
191 196         335 my $i = 1;
192 196         429 while ($i < @p) {
193             #print "$i ", join("/", @p), " ($p[$i])\n";
194 616 100 100     1716 if ($p[$i-1] eq ".") {
    100          
195 32         62 splice(@p, $i-1, 1);
196 32 50       86 $i-- if $i > 1;
197             }
198             elsif ($p[$i] eq ".." && $p[$i-1] ne "..") {
199 107         192 splice(@p, $i-1, 2);
200 107 100       214 if ($i > 1) {
201 77         93 $i--;
202 77 100       188 push(@p, "") if $i == @p;
203             }
204             }
205             else {
206 477         860 $i++;
207             }
208             }
209 196 100 100     646 $p[-1] = "" if @p && $p[-1] eq "."; # trailing "/."
210 196 100       383 if ($URI::ABS_REMOTE_LEADING_DOTS) {
211 3   66     37 shift @p while @p && $p[0] =~ /^\.\.?$/;
212             }
213 196         748 $abs->path("/" . join("/", @p));
214 196         1029 $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 131 my $self = shift;
220 67   33     134 my $base = shift || Carp::croak("Missing base argument");
221 67         137 my $rel = $self->clone;
222 67 50       195 $base = URI->new($base) unless ref $base;
223              
224             #my($scheme, $auth, $path) = @{$rel}{qw(scheme authority path)};
225 67         153 my $scheme = $rel->scheme;
226 67         175 my $auth = $rel->canonical->authority;
227 67         139 my $path = $rel->path;
228              
229 67 0 33     153 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         145 my $bscheme = $base->scheme;
236 67         166 my $bauth = $base->canonical->authority;
237 67         138 my $bpath = $base->path;
238              
239 67         139 for ($bscheme, $bauth, $auth) {
240 201 100       378 $_ = '' unless defined
241             }
242              
243 67 100 100     262 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       97 for ($path, $bpath) { $_ = "/$_" unless m,^/,; }
  124         354  
249              
250             # Make it relative by eliminating scheme and authority
251 62         166 $rel->scheme(undef);
252 62         142 $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         77 my $li = 1;
257 62         82 while (1) {
258 137         215 my $i = index($path, '/', $li);
259 137 100 100     534 last if $i < 0 ||
      100        
260             $i != index($bpath, '/', $li) ||
261             substr($path,$li,$i-$li) ne substr($bpath,$li,$i-$li);
262 75         137 $li=$i+1;
263             }
264             # then we nuke it from both paths
265 62         126 substr($path, 0,$li) = '';
266 62         115 substr($bpath,0,$li) = '';
267              
268 62 100 100     229 if ($path eq $bpath &&
      100        
269             defined($rel->fragment) &&
270             !defined($rel->query)) {
271 1         4 $rel->path("");
272             }
273             else {
274             # Add one "../" for each path component left in the base path
275 61         191 $path = ('../' x $bpath =~ tr|/|/|) . $path;
276 61 100       163 $path = "./" if $path eq "";
277 61         143 $rel->path($path);
278             }
279              
280 62         250 $rel;
281             }
282              
283             1;