line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
7
|
|
|
7
|
|
269328
|
use strict; |
|
7
|
|
|
|
|
55
|
|
|
7
|
|
|
|
|
221
|
|
2
|
7
|
|
|
7
|
|
38
|
use warnings; |
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
185
|
|
3
|
7
|
|
|
7
|
|
37
|
use Carp; |
|
7
|
|
|
|
|
13
|
|
|
7
|
|
|
|
|
643
|
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package Software::LicenseUtils; |
6
|
|
|
|
|
|
|
# ABSTRACT: little useful bits of code for licensey things |
7
|
|
|
|
|
|
|
$Software::LicenseUtils::VERSION = '0.104004'; |
8
|
7
|
|
|
7
|
|
49
|
use File::Spec; |
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
186
|
|
9
|
7
|
|
|
7
|
|
3369
|
use IO::Dir; |
|
7
|
|
|
|
|
143124
|
|
|
7
|
|
|
|
|
382
|
|
10
|
7
|
|
|
7
|
|
3486
|
use Module::Load; |
|
7
|
|
|
|
|
7601
|
|
|
7
|
|
|
|
|
52
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
#pod =method guess_license_from_pod |
13
|
|
|
|
|
|
|
#pod |
14
|
|
|
|
|
|
|
#pod my @guesses = Software::LicenseUtils->guess_license_from_pod($pm_text); |
15
|
|
|
|
|
|
|
#pod |
16
|
|
|
|
|
|
|
#pod Given text containing POD, like a .pm file, this method will attempt to guess |
17
|
|
|
|
|
|
|
#pod at the license under which the code is available. This method will return |
18
|
|
|
|
|
|
|
#pod either a list of Software::License classes names (as strings) or false. |
19
|
|
|
|
|
|
|
#pod |
20
|
|
|
|
|
|
|
#pod This method looks for a POD heading like 'license', 'copyright', or 'legal'. |
21
|
|
|
|
|
|
|
#pod |
22
|
|
|
|
|
|
|
#pod Calling this method in scalar context is a fatal error. |
23
|
|
|
|
|
|
|
#pod |
24
|
|
|
|
|
|
|
#pod =cut |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
my $_v = qr/(?:v(?:er(?:sion|\.))?(?: |\.)?)/i; |
27
|
|
|
|
|
|
|
my @phrases = ( |
28
|
|
|
|
|
|
|
"under the same (?:terms|license) as perl $_v?6" => [], |
29
|
|
|
|
|
|
|
'under the same (?:terms|license) as (?:the )?perl' => 'Perl_5', |
30
|
|
|
|
|
|
|
'affero g' => 'AGPL_3', |
31
|
|
|
|
|
|
|
"GNU (?:general )?public license,? $_v?([123])" => sub { "GPL_$_[0]" }, |
32
|
|
|
|
|
|
|
'GNU (?:general )?public license' => [ map {"GPL_$_"} (1..3) ], |
33
|
|
|
|
|
|
|
"GNU (?:lesser|library) (?:general )?public license,? $_v?([23])\\D" => sub { |
34
|
|
|
|
|
|
|
$_[0] == 2 ? 'LGPL_2_1' : $_[0] == 3 ? 'LGPL_3_0' : () |
35
|
|
|
|
|
|
|
}, |
36
|
|
|
|
|
|
|
'GNU (?:lesser|library) (?:general )?public license' => [ qw(LGPL_2_1 LGPL_3_0) ], |
37
|
|
|
|
|
|
|
'(?:the )?2[-\s]clause (?:Free)?BSD' => 'FreeBSD', |
38
|
|
|
|
|
|
|
'BSD license' => 'BSD', |
39
|
|
|
|
|
|
|
'FreeBSD license' => 'FreeBSD', |
40
|
|
|
|
|
|
|
"Artistic license $_v?(\\d)" => sub { "Artistic_$_[0]_0" }, |
41
|
|
|
|
|
|
|
'Artistic license' => [ map { "Artistic_$_\_0" } (1..2) ], |
42
|
|
|
|
|
|
|
"LGPL,? $_v?(\\d)" => sub { |
43
|
|
|
|
|
|
|
$_[0] == 2 ? 'LGPL_2_1' : $_[0] == 3 ? 'LGPL_3_0' : () |
44
|
|
|
|
|
|
|
}, |
45
|
|
|
|
|
|
|
'LGPL' => [ qw(LGPL_2_1 LGPL_3_0) ], |
46
|
|
|
|
|
|
|
"GPL,? $_v?(\\d)" => sub { "GPL_$_[0]" }, |
47
|
|
|
|
|
|
|
'GPL' => [ map { "GPL_$_" } (1..3) ], |
48
|
|
|
|
|
|
|
'FreeBSD' => 'FreeBSD', |
49
|
|
|
|
|
|
|
'BSD' => 'BSD', |
50
|
|
|
|
|
|
|
'Artistic' => [ map { "Artistic_$_\_0" } (1..2) ], |
51
|
|
|
|
|
|
|
'MIT' => 'MIT', |
52
|
|
|
|
|
|
|
'has dedicated the work to the Commons' => 'CC0_1_0', |
53
|
|
|
|
|
|
|
'waiving all of his or her rights to the work worldwide under copyright law' => 'CC0_1_0', |
54
|
|
|
|
|
|
|
'has waived all copyright and related or neighboring rights to' => 'CC0_1_0', |
55
|
|
|
|
|
|
|
'apache(?: |-)1.1' => "Apache_1_1", |
56
|
|
|
|
|
|
|
"Apache Software License(\\s)+Version 1.1" => "Apache_1_1", |
57
|
|
|
|
|
|
|
'apache(?: |-)2.0' => "Apache_2_0", |
58
|
|
|
|
|
|
|
"Apache License(\\s)+Version 2.0" => "Apache_2_0", |
59
|
|
|
|
|
|
|
'No license is granted to other entities' => 'None', |
60
|
|
|
|
|
|
|
); |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
my %meta_keys = (); |
63
|
|
|
|
|
|
|
my %meta1_keys = (); |
64
|
|
|
|
|
|
|
my %meta2_keys = (); |
65
|
|
|
|
|
|
|
my %spdx_expression = (); |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# find all known Software::License::* modules and get identification data |
68
|
|
|
|
|
|
|
# |
69
|
|
|
|
|
|
|
# XXX: Grepping over @INC is dangerous, as it means that someone can change the |
70
|
|
|
|
|
|
|
# behavior of your code by installing a new library that you don't load. rjbs |
71
|
|
|
|
|
|
|
# is not a fan. On the other hand, it will solve a real problem. One better |
72
|
|
|
|
|
|
|
# solution is to check "core" licenses first, then fall back, and to skip (but |
73
|
|
|
|
|
|
|
# warn about) bogus libraries. Another is, at least when testing S-L itself, |
74
|
|
|
|
|
|
|
# to only scan lib/ blib. -- rjbs, 2013-10-20 |
75
|
|
|
|
|
|
|
for my $lib (map { "$_/Software/License" } @INC) { |
76
|
|
|
|
|
|
|
next unless -d $lib; |
77
|
|
|
|
|
|
|
for my $file (IO::Dir->new($lib)->read) { |
78
|
|
|
|
|
|
|
next unless $file =~ m{\.pm$}; |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# if it fails, ignore it |
81
|
|
|
|
|
|
|
eval { |
82
|
|
|
|
|
|
|
(my $mod = $file) =~ s{\.pm$}{}; |
83
|
|
|
|
|
|
|
my $class = "Software::License::$mod"; |
84
|
|
|
|
|
|
|
load $class; |
85
|
|
|
|
|
|
|
$meta_keys{ $class->meta_name }{$mod} = undef; |
86
|
|
|
|
|
|
|
$meta1_keys{ $class->meta_name }{$mod} = undef; |
87
|
|
|
|
|
|
|
$meta_keys{ $class->meta2_name }{$mod} = undef; |
88
|
|
|
|
|
|
|
$meta2_keys{ $class->meta2_name }{$mod} = undef; |
89
|
|
|
|
|
|
|
if (defined $class->spdx_expression) { |
90
|
|
|
|
|
|
|
$spdx_expression{ $class->spdx_expression }{$class} = undef; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
my $name = $class->name; |
93
|
|
|
|
|
|
|
unshift @phrases, qr/\Q$name\E/, [$mod]; |
94
|
|
|
|
|
|
|
if ((my $name_without_space = $name) =~ s/\s+\(.+?\)//) { |
95
|
|
|
|
|
|
|
unshift @phrases, qr/\Q$name_without_space\E/, [$mod]; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
}; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
sub guess_license_from_pod { |
102
|
32
|
|
|
32
|
1
|
16791
|
my ($class, $pm_text) = @_; |
103
|
32
|
50
|
|
|
|
102
|
die "can't call guess_license_* in scalar context" unless wantarray; |
104
|
32
|
50
|
|
|
|
264
|
return unless $pm_text =~ / |
105
|
|
|
|
|
|
|
( |
106
|
|
|
|
|
|
|
=head \d \s+ |
107
|
|
|
|
|
|
|
(?:licen[cs]e|licensing|copyright|legal)\b |
108
|
|
|
|
|
|
|
) |
109
|
|
|
|
|
|
|
/ixmsg; |
110
|
|
|
|
|
|
|
|
111
|
32
|
|
|
|
|
113
|
my $header = $1; |
112
|
|
|
|
|
|
|
|
113
|
32
|
50
|
|
|
|
623
|
if ( |
114
|
|
|
|
|
|
|
$pm_text =~ m/ |
115
|
|
|
|
|
|
|
\G |
116
|
|
|
|
|
|
|
( |
117
|
|
|
|
|
|
|
.*? |
118
|
|
|
|
|
|
|
) |
119
|
|
|
|
|
|
|
(=head\\d.*|=cut.*|) |
120
|
|
|
|
|
|
|
\z |
121
|
|
|
|
|
|
|
/ixms |
122
|
|
|
|
|
|
|
) { |
123
|
32
|
|
|
|
|
122
|
my $license_text = "$header$1"; |
124
|
|
|
|
|
|
|
|
125
|
32
|
|
|
|
|
103
|
for (my $i = 0; $i < @phrases; $i += 2) { |
126
|
3099
|
|
|
|
|
7464
|
my ($pattern, $license) = @phrases[ $i .. $i+1 ]; |
127
|
3099
|
100
|
|
|
|
7651
|
$pattern =~ s{\s+}{\\s+}g |
128
|
|
|
|
|
|
|
unless ref $pattern eq 'Regexp'; |
129
|
3099
|
100
|
|
|
|
32801
|
if ( $license_text =~ /\b$pattern\b/i ) { |
130
|
31
|
|
|
|
|
102
|
my $match = $1; |
131
|
|
|
|
|
|
|
# if ( $osi and $license_text =~ /All rights reserved/i ) { |
132
|
|
|
|
|
|
|
# warn "LEGAL WARNING: 'All rights reserved' may invalidate Open Source licenses. Consider removing it."; |
133
|
|
|
|
|
|
|
# } |
134
|
31
|
100
|
100
|
|
|
224
|
my @result = (ref $license||'') eq 'CODE' ? $license->($match) |
|
|
100
|
100
|
|
|
|
|
135
|
|
|
|
|
|
|
: (ref $license||'') eq 'ARRAY' ? @$license |
136
|
|
|
|
|
|
|
: $license; |
137
|
|
|
|
|
|
|
|
138
|
31
|
50
|
|
|
|
82
|
return unless @result; |
139
|
31
|
|
|
|
|
97
|
return map { "Software::License::$_" } sort @result; |
|
31
|
|
|
|
|
250
|
|
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
1
|
|
|
|
|
12
|
return; |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
#pod =method guess_license_from_meta |
148
|
|
|
|
|
|
|
#pod |
149
|
|
|
|
|
|
|
#pod my @guesses = Software::LicenseUtils->guess_license_from_meta($meta_str); |
150
|
|
|
|
|
|
|
#pod |
151
|
|
|
|
|
|
|
#pod Given the content of the META.(yml|json) file found in a CPAN distribution, this |
152
|
|
|
|
|
|
|
#pod method makes a guess as to which licenses may apply to the distribution. It |
153
|
|
|
|
|
|
|
#pod will return a list of zero or more Software::License instances or classes. |
154
|
|
|
|
|
|
|
#pod |
155
|
|
|
|
|
|
|
#pod =cut |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub guess_license_from_meta { |
158
|
26
|
|
|
26
|
1
|
9452
|
my ($class, $meta_text) = @_; |
159
|
26
|
50
|
|
|
|
67
|
die "can't call guess_license_* in scalar context" unless wantarray; |
160
|
|
|
|
|
|
|
|
161
|
26
|
|
|
|
|
191
|
my ($license_text) = $meta_text =~ m{\b["']?license["']?\s*:\s*["']?([a-z_0-9]+)["']?}gm; |
162
|
|
|
|
|
|
|
|
163
|
26
|
50
|
33
|
|
|
159
|
return unless $license_text and my $license = $meta_keys{ $license_text }; |
164
|
|
|
|
|
|
|
|
165
|
26
|
|
|
|
|
118
|
return map { "Software::License::$_" } sort keys %$license; |
|
28
|
|
|
|
|
119
|
|
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
{ |
169
|
7
|
|
|
7
|
|
7823
|
no warnings 'once'; |
|
7
|
|
|
|
|
20
|
|
|
7
|
|
|
|
|
4188
|
|
170
|
|
|
|
|
|
|
*guess_license_from_meta_yml = \&guess_license_from_meta; |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
#pod =method guess_license_from_meta_key |
174
|
|
|
|
|
|
|
#pod |
175
|
|
|
|
|
|
|
#pod my @guesses = Software::LicenseUtils->guess_license_from_meta_key($key, $v); |
176
|
|
|
|
|
|
|
#pod |
177
|
|
|
|
|
|
|
#pod This method returns zero or more Software::License classes known to use C<$key> |
178
|
|
|
|
|
|
|
#pod as their META key. If C<$v> is supplied, it specifies whether to treat C<$key> |
179
|
|
|
|
|
|
|
#pod as a v1 or v2 meta entry. Any value other than 1 or 2 will raise an exception. |
180
|
|
|
|
|
|
|
#pod |
181
|
|
|
|
|
|
|
#pod =cut |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub guess_license_from_meta_key { |
184
|
3
|
|
|
3
|
1
|
302
|
my ($self, $key, $v) = @_; |
185
|
|
|
|
|
|
|
|
186
|
3
|
50
|
|
|
|
19
|
my $src = (! defined $v) ? \%meta_keys |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
187
|
|
|
|
|
|
|
: $v eq '1' ? \%meta1_keys |
188
|
|
|
|
|
|
|
: $v eq '2' ? \%meta2_keys |
189
|
|
|
|
|
|
|
: Carp::croak("illegal META version: $v"); |
190
|
|
|
|
|
|
|
|
191
|
3
|
100
|
|
|
|
15
|
return unless $src->{$key}; |
192
|
2
|
|
|
|
|
6
|
return map { "Software::License::$_" } sort keys %{ $src->{$key} }; |
|
2
|
|
|
|
|
16
|
|
|
2
|
|
|
|
|
11
|
|
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
my %short_name = ( |
196
|
|
|
|
|
|
|
'GPL-1' => 'Software::License::GPL_1', |
197
|
|
|
|
|
|
|
'GPL-2' => 'Software::License::GPL_2', |
198
|
|
|
|
|
|
|
'GPL-3' => 'Software::License::GPL_3', |
199
|
|
|
|
|
|
|
'LGPL-2' => 'Software::License::LGPL_2', |
200
|
|
|
|
|
|
|
'LGPL-2.1' => 'Software::License::LGPL_2_1', |
201
|
|
|
|
|
|
|
'LGPL-3' => 'Software::License::LGPL_3_0', |
202
|
|
|
|
|
|
|
'LGPL-3.0' => 'Software::License::LGPL_3_0', |
203
|
|
|
|
|
|
|
'Artistic' => 'Software::License::Artistic_1_0', |
204
|
|
|
|
|
|
|
'Artistic-1' => 'Software::License::Artistic_1_0', |
205
|
|
|
|
|
|
|
'Artistic-2' => 'Software::License::Artistic_2_0', |
206
|
|
|
|
|
|
|
); |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
#pod =method new_from_short_name |
209
|
|
|
|
|
|
|
#pod |
210
|
|
|
|
|
|
|
#pod my $license_object = Software::LicenseUtils->new_from_short_name( { |
211
|
|
|
|
|
|
|
#pod short_name => 'GPL-1', |
212
|
|
|
|
|
|
|
#pod holder => 'X. Ample' |
213
|
|
|
|
|
|
|
#pod }) ; |
214
|
|
|
|
|
|
|
#pod |
215
|
|
|
|
|
|
|
#pod Create a new L object from the license specified |
216
|
|
|
|
|
|
|
#pod with C. Known short license names are C, C , |
217
|
|
|
|
|
|
|
#pod C and C |
218
|
|
|
|
|
|
|
#pod |
219
|
|
|
|
|
|
|
#pod =cut |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
sub new_from_short_name { |
222
|
1
|
|
|
1
|
1
|
597
|
my ( $class, $arg ) = @_; |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
Carp::croak "no license short name specified" |
225
|
1
|
50
|
|
|
|
6
|
unless defined $arg->{short_name}; |
226
|
1
|
|
|
|
|
3
|
my $short = delete $arg->{short_name}; |
227
|
|
|
|
|
|
|
Carp::croak "Unknown license with short name $short" |
228
|
1
|
50
|
|
|
|
5
|
unless $short_name{$short}; |
229
|
|
|
|
|
|
|
|
230
|
1
|
|
|
|
|
3
|
my $lic_file = my $lic_class = $short_name{$short} ; |
231
|
1
|
|
|
|
|
6
|
$lic_file =~ s!::!/!g; |
232
|
1
|
|
|
|
|
6
|
require "$lic_file.pm"; |
233
|
1
|
|
|
|
|
9
|
return $lic_class->new( $arg ); |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
#pod =method new_from_spdx_expression |
237
|
|
|
|
|
|
|
#pod |
238
|
|
|
|
|
|
|
#pod my $license_object = Software::LicenseUtils->new_from_spdx_expression( { |
239
|
|
|
|
|
|
|
#pod spdx_expression => 'MPL-2.0', |
240
|
|
|
|
|
|
|
#pod holder => 'X. Ample' |
241
|
|
|
|
|
|
|
#pod }) ; |
242
|
|
|
|
|
|
|
#pod |
243
|
|
|
|
|
|
|
#pod Create a new L object from the license specified |
244
|
|
|
|
|
|
|
#pod with C. Some licenses doesn't have an spdx |
245
|
|
|
|
|
|
|
#pod identifier (for example L), so you can pass |
246
|
|
|
|
|
|
|
#pod spdx identifier but also expressions. |
247
|
|
|
|
|
|
|
#pod Known spdx license identifiers are C, C. |
248
|
|
|
|
|
|
|
#pod |
249
|
|
|
|
|
|
|
#pod =cut |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
sub new_from_spdx_expression { |
252
|
1
|
|
|
1
|
1
|
569
|
my ( $class, $arg ) = @_; |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
Carp::croak "no license spdx name specified" |
255
|
1
|
50
|
|
|
|
5
|
unless defined $arg->{spdx_expression}; |
256
|
1
|
|
|
|
|
3
|
my $spdx = delete $arg->{spdx_expression}; |
257
|
|
|
|
|
|
|
Carp::croak "Unknown license with spdx name $spdx" |
258
|
1
|
50
|
|
|
|
5
|
unless $spdx_expression{$spdx}; |
259
|
|
|
|
|
|
|
|
260
|
1
|
|
|
|
|
3
|
my ($lic_file) = my ($lic_class) = keys %{$spdx_expression{$spdx}} ; |
|
1
|
|
|
|
|
5
|
|
261
|
1
|
|
|
|
|
6
|
$lic_file =~ s!::!/!g; |
262
|
1
|
|
|
|
|
7
|
require "$lic_file.pm"; |
263
|
1
|
|
|
|
|
10
|
return $lic_class->new( $arg ); |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
1; |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
__END__ |