line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
2
|
3
|
|
|
3
|
|
3261
|
use v5.26; |
|
3
|
|
|
|
|
12
|
|
3
|
3
|
|
|
3
|
|
1227
|
use utf8; |
|
3
|
|
|
|
|
32
|
|
|
3
|
|
|
|
|
16
|
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package App::url; |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = '1.007'; |
8
|
|
|
|
|
|
|
|
9
|
3
|
|
|
3
|
|
162
|
use Carp qw(carp); |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
176
|
|
10
|
3
|
|
|
3
|
|
1012
|
use Mojo::Base -strict, -signatures; |
|
3
|
|
|
|
|
392634
|
|
|
3
|
|
|
|
|
26
|
|
11
|
3
|
|
|
3
|
|
9862
|
use Mojo::URL; |
|
3
|
|
|
|
|
22731
|
|
|
3
|
|
|
|
|
25
|
|
12
|
3
|
|
|
3
|
|
1667
|
use String::Sprintf; |
|
3
|
|
|
|
|
1594
|
|
|
3
|
|
|
|
|
147
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=encoding utf8 |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 NAME |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
App::url - format a URL according to a sprintf-like template |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 SYNOPSIS |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
$ url '%h' http://www.example.com/a/b/c |
23
|
|
|
|
|
|
|
www.example.com |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
$ url '%H' http://www.example.com/a/b/c |
26
|
|
|
|
|
|
|
www |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
$ url '%P' http://www.example.com/a/b/c |
29
|
|
|
|
|
|
|
/a/b/c |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head1 DESCRIPTION |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
Decompose the URL and reformat it according to |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head2 The formats |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=over 4 |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=item * C<%a> - the path |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=item * C<%f> - the fragment |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=item * C<%h> - the hostname, with domain info |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=item * C<%H> - the hostname without domain info |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=item * C<%i> - the hostname in punycode |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=item * C<%I> - space-separated list of IP addresses for the host |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=item * C<%P> - the password of the userinfo portion |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=item * C<%p> - the port |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=item * C<%q> - the query string |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=item * C<%s> - the scheme |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=item * C<%S> - the public suffix |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=item * C<%u> - the complete URL |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=item * C<%U> - the username of the userinfo portion |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=back |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
There are also some bonus formats unrelated to the URL: |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=over 4 |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=item * C<%n> - newline |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=item * C<%t> - tab |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=item * C<%%> - literal percent |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=back |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=head2 Methods |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=over 4 |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
=item * run( TEMPLATE, ARRAY ) |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
Format each URL in ARRAY according to TEMPLATE and return an array |
86
|
|
|
|
|
|
|
reference |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
=back |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
=head1 COPYRIGHT |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
Copyright © 2020-2021, brian d foy, all rights reserved. |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
=head1 LICENSE |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
You can use this code under the terms of the Artistic License 2. |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=cut |
99
|
|
|
|
|
|
|
|
100
|
3
|
|
|
3
|
|
20
|
no warnings 'uninitialized'; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
3449
|
|
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# $w - width of field |
103
|
|
|
|
|
|
|
# $v - value that corresponds to position in template |
104
|
|
|
|
|
|
|
# $V - list of all values |
105
|
|
|
|
|
|
|
# $l - letter |
106
|
|
|
|
|
|
|
my $formatter = String::Sprintf->formatter( |
107
|
|
|
|
|
|
|
a => sub ( $w, $v, $V, $l ) { $V->[0]->path }, |
108
|
|
|
|
|
|
|
f => sub ( $w, $v, $V, $l ) { $V->[0]->fragment }, |
109
|
|
|
|
|
|
|
h => sub ( $w, $v, $V, $l ) { $V->[0]->host }, |
110
|
|
|
|
|
|
|
H => sub ( $w, $v, $V, $l ) { ( split /\./, $V->[0]->host )[0] }, |
111
|
|
|
|
|
|
|
i => sub ( $w, $v, $V, $l ) { $V->[0]->ihost }, |
112
|
|
|
|
|
|
|
I => sub ( $w, $v, $V, $l ) { |
113
|
|
|
|
|
|
|
state $rc = require Socket; |
114
|
|
|
|
|
|
|
my @addresses = gethostbyname( $V->[0]->host ); |
115
|
|
|
|
|
|
|
@addresses = map { Socket::inet_ntoa($_) } @addresses[4..$#addresses]; |
116
|
|
|
|
|
|
|
"@addresses"; |
117
|
|
|
|
|
|
|
}, |
118
|
|
|
|
|
|
|
p => sub ( $w, $v, $V, $l ) { $V->[0]->port // do { |
119
|
|
|
|
|
|
|
if( $V->[0]->protocol eq 'http' ) { 80 } |
120
|
|
|
|
|
|
|
elsif( $V->[0]->protocol eq 'https' ) { 443 } |
121
|
|
|
|
|
|
|
}; |
122
|
|
|
|
|
|
|
}, |
123
|
|
|
|
|
|
|
P => sub ( $w, $v, $V, $l ) { $V->[0]->password }, |
124
|
|
|
|
|
|
|
'q' => sub ( $w, $v, $V, $l ) { $V->[0]->query }, |
125
|
|
|
|
|
|
|
's' => sub ( $w, $v, $V, $l ) { $V->[0]->protocol }, |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
S => sub ( $w, $v, $V, $l ) { |
128
|
|
|
|
|
|
|
state $rc = eval { require Net::PublicSuffixList }; |
129
|
|
|
|
|
|
|
unless( $rc ) { |
130
|
|
|
|
|
|
|
carp "%${l} requires Net::PublicSuffixList\n"; |
131
|
|
|
|
|
|
|
return; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
state $psl = Net::PublicSuffixList->new; |
134
|
|
|
|
|
|
|
my $hash = $psl->split_host( $V->[0]->host ); |
135
|
|
|
|
|
|
|
$hash->{suffix}; |
136
|
|
|
|
|
|
|
}, |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
U => sub ( $w, $v, $V, $l ) { $V->[0]->username }, |
139
|
|
|
|
|
|
|
u => sub ( $w, $v, $V, $l ) { $V->[0]->to_string }, |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
n => sub { "\n" }, |
142
|
|
|
|
|
|
|
t => sub { "\t" }, |
143
|
|
|
|
|
|
|
'%' => sub { '%' }, |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
'*' => sub ( $w, $v, $V, $l ) { warn "Invalid specifier <$l>\n" }, |
146
|
|
|
|
|
|
|
); |
147
|
|
|
|
|
|
|
|
148
|
0
|
|
|
0
|
1
|
|
sub run ( $class, $template, @urls ) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
149
|
0
|
|
|
|
|
|
my @strings; |
150
|
|
|
|
|
|
|
|
151
|
0
|
|
|
|
|
|
foreach my $url ( @urls ) { |
152
|
0
|
|
|
|
|
|
push @strings, $formatter->sprintf( $template, Mojo::URL->new($url) ); |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
0
|
|
|
|
|
|
return \@strings; |
156
|
|
|
|
|
|
|
} |