line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package PGXN::Site::Locale; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
68424
|
use 5.10.0; |
|
1
|
|
|
|
|
12
|
|
4
|
1
|
|
|
1
|
|
7
|
use utf8; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
5
|
1
|
|
|
1
|
|
569
|
use parent 'Locale::Maketext'; |
|
1
|
|
|
|
|
311
|
|
|
1
|
|
|
|
|
4
|
|
6
|
1
|
|
|
1
|
|
12420
|
use I18N::LangTags::Detect; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
24
|
|
7
|
1
|
|
|
1
|
|
4
|
use File::Spec; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
27
|
|
8
|
1
|
|
|
1
|
|
5
|
use Carp; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
951
|
|
9
|
|
|
|
|
|
|
our $VERSION = v0.22.2; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# Allow unknown phrases to just pass-through. |
12
|
|
|
|
|
|
|
our %Lexicon = ( |
13
|
|
|
|
|
|
|
# _AUTO => 1, |
14
|
|
|
|
|
|
|
listcomma => ',', |
15
|
|
|
|
|
|
|
listand => 'and', |
16
|
|
|
|
|
|
|
openquote => '“', |
17
|
|
|
|
|
|
|
shutquote => '”', |
18
|
|
|
|
|
|
|
in => 'in', |
19
|
|
|
|
|
|
|
hometitle => 'PGXN: PostgreSQL Extension Network', |
20
|
|
|
|
|
|
|
'PostgreSQL Extension Network' => 'PostgreSQL Extension Network', |
21
|
|
|
|
|
|
|
'PGXN Gear' => 'PGXN Gear', |
22
|
|
|
|
|
|
|
'Recent' => 'Recent', |
23
|
|
|
|
|
|
|
'Recent Releases' => 'Recent Releases', |
24
|
|
|
|
|
|
|
'About' => 'About', |
25
|
|
|
|
|
|
|
'About PGXN' => 'About PGXN', |
26
|
|
|
|
|
|
|
'PGXN Users' => 'PGXN Users', |
27
|
|
|
|
|
|
|
'Recent' => 'Recent', |
28
|
|
|
|
|
|
|
'User' => 'User', |
29
|
|
|
|
|
|
|
'Users' => 'Users', |
30
|
|
|
|
|
|
|
'Recent' => 'Recent', |
31
|
|
|
|
|
|
|
'Recent Releases' => 'Recent Releases', |
32
|
|
|
|
|
|
|
'Blog' => 'Blog', |
33
|
|
|
|
|
|
|
'PGXN Blog' => 'PGXN Blog', |
34
|
|
|
|
|
|
|
'FAQ' => 'FAQ', |
35
|
|
|
|
|
|
|
'Frequently Asked Questions' => 'Frequently Asked Questions', |
36
|
|
|
|
|
|
|
'Release It' => 'Release It', |
37
|
|
|
|
|
|
|
'Release it on PGXN' => 'Release it on PGXN', |
38
|
|
|
|
|
|
|
code => 'code', |
39
|
|
|
|
|
|
|
design => 'design', |
40
|
|
|
|
|
|
|
logo => 'logo', |
41
|
|
|
|
|
|
|
'Go to [_1]' => 'Go to [_1]', |
42
|
|
|
|
|
|
|
Mirroring => 'Mirroring', |
43
|
|
|
|
|
|
|
'Mirroring PGXN' => 'Mirroring PGXN', |
44
|
|
|
|
|
|
|
Feedback => 'Feedback', |
45
|
|
|
|
|
|
|
Identity => 'Identity', |
46
|
|
|
|
|
|
|
Extensions => 'Extensions', |
47
|
|
|
|
|
|
|
Tags => 'Tags', |
48
|
|
|
|
|
|
|
'Release Tags' => 'Release Tags', |
49
|
|
|
|
|
|
|
Distributions => 'Distributions', |
50
|
|
|
|
|
|
|
'PGXN Search' => 'PGXN Search', |
51
|
|
|
|
|
|
|
pgxn_summary_paragraph => 'PGXN, the PostgreSQL Extension network, is a central distribution system for open-source PostgreSQL extension libraries.', |
52
|
|
|
|
|
|
|
Founders => 'Founders', |
53
|
|
|
|
|
|
|
Patrons => 'Patrons', |
54
|
|
|
|
|
|
|
Benefactors => 'Benefactors', |
55
|
|
|
|
|
|
|
Sponsors => 'Sponsors', |
56
|
|
|
|
|
|
|
Advocates => 'Advocates', |
57
|
|
|
|
|
|
|
Supporters => 'Supporters', |
58
|
|
|
|
|
|
|
Boosters => 'Boosters', |
59
|
|
|
|
|
|
|
'Donors' => 'Donors', |
60
|
|
|
|
|
|
|
'See a longer list of recent releases.' => 'See a longer list of recent releases.', |
61
|
|
|
|
|
|
|
'More Releases' => 'More Releases →', |
62
|
|
|
|
|
|
|
'Not Found' => 'Not Found', |
63
|
|
|
|
|
|
|
'Resource not found.' => 'Resource not found.', |
64
|
|
|
|
|
|
|
'Resource Not Found' => 'Resource Not Found', |
65
|
|
|
|
|
|
|
'Internal Server Error' => 'Internal Server Error', |
66
|
|
|
|
|
|
|
'Internal server error.' => 'Internal server error.', |
67
|
|
|
|
|
|
|
'Download' => 'Download', |
68
|
|
|
|
|
|
|
'Download [_1] [_2]' => 'Download [_1] [_2]', |
69
|
|
|
|
|
|
|
'Browse [_1] [_2]' => 'Browse [_1] [_2]', |
70
|
|
|
|
|
|
|
'Alas, [_1] has yet to release a distribution.' => 'Alas, [_1] has yet to release a distribution.', |
71
|
|
|
|
|
|
|
'This Release' => 'This Release', |
72
|
|
|
|
|
|
|
'Date' => 'Date', |
73
|
|
|
|
|
|
|
'Latest Stable' => 'Latest Stable', |
74
|
|
|
|
|
|
|
'Latest Testing' => 'Latest Testing', |
75
|
|
|
|
|
|
|
'Latest Unstable' => 'Latest Unstable', |
76
|
|
|
|
|
|
|
'Other Releases' => 'Other Releases', |
77
|
|
|
|
|
|
|
'Status' => 'Status', |
78
|
|
|
|
|
|
|
'stable' => 'Stable', |
79
|
|
|
|
|
|
|
'testing' => 'Testing', |
80
|
|
|
|
|
|
|
'unstable' => 'Unstable', |
81
|
|
|
|
|
|
|
'Abstract' => 'Abstract', |
82
|
|
|
|
|
|
|
'Description' => 'Description', |
83
|
|
|
|
|
|
|
'Maintainer' => 'Maintainer', |
84
|
|
|
|
|
|
|
'Maintainers' => 'Maintainers', |
85
|
|
|
|
|
|
|
'License' => 'License', |
86
|
|
|
|
|
|
|
'Resources' => 'Resources', |
87
|
|
|
|
|
|
|
'www' => 'www', |
88
|
|
|
|
|
|
|
'bugs' => 'bugs', |
89
|
|
|
|
|
|
|
'repo' => 'repo', |
90
|
|
|
|
|
|
|
'Special Files' => 'Special Files', |
91
|
|
|
|
|
|
|
'Tags' => 'Tags', |
92
|
|
|
|
|
|
|
'Other Documentation' => 'Other Documentation', |
93
|
|
|
|
|
|
|
'Released By' => 'Released By', |
94
|
|
|
|
|
|
|
'README' => 'README', |
95
|
|
|
|
|
|
|
'Documentation' => 'Documentation', |
96
|
|
|
|
|
|
|
'Nickname' => 'Nickname', |
97
|
|
|
|
|
|
|
'URL' => 'URL', |
98
|
|
|
|
|
|
|
'Email' => 'Email', |
99
|
|
|
|
|
|
|
'Mastodon' => 'Mastodon', |
100
|
|
|
|
|
|
|
'Follow PGXN on Mastodon' => 'Follow PGXN on Mastodon', |
101
|
|
|
|
|
|
|
'Twitter' => 'Twitter', |
102
|
|
|
|
|
|
|
'Follow PGXN on Twitter' => 'Follow PGXN on Twitter', |
103
|
|
|
|
|
|
|
'Browse' => 'Browse', |
104
|
|
|
|
|
|
|
'Tag: [_1]' => 'Tag: “[_1]”', |
105
|
|
|
|
|
|
|
'PGXN Search' => 'PGXN Search', |
106
|
|
|
|
|
|
|
'In the [_1] distribution' => 'In the [_1] distribution', |
107
|
|
|
|
|
|
|
'Released by [_1]' => 'Released by [_1]', |
108
|
|
|
|
|
|
|
'Search matched no documents.' => 'Search matched no documents.', |
109
|
|
|
|
|
|
|
'Previous results' => 'Previous results', |
110
|
|
|
|
|
|
|
'Next results' => 'Next results', |
111
|
|
|
|
|
|
|
'← Prev' => '← Prev', |
112
|
|
|
|
|
|
|
'Next →' => 'Next →', |
113
|
|
|
|
|
|
|
'[_1]-[_2] of [_3] found' => '[_1]-[_2] of [_3] found', |
114
|
|
|
|
|
|
|
'No Releases Yet' => 'No Releases Yet', |
115
|
|
|
|
|
|
|
'PGXN Meta Spec' => 'PGXN Meta Spec', |
116
|
|
|
|
|
|
|
'Bad Request' => 'Bad Request', |
117
|
|
|
|
|
|
|
'Bad request: Missing or invalid "[_1]" query parameter.' => 'Bad request: Missing or invalid “[_1]” query parameter.', |
118
|
|
|
|
|
|
|
'Search Users' => 'Search Users', |
119
|
|
|
|
|
|
|
'Or select a letter' => 'Or select a letter', |
120
|
|
|
|
|
|
|
'Nicknames starting with "[_1]"' => 'User nicknames starting with “[_1]”', |
121
|
|
|
|
|
|
|
'None found' => 'None found', |
122
|
|
|
|
|
|
|
'Search all indexed extensions, distributions, users, and tags on the PostgreSQL Extension Network.' => 'Search all indexed extensions, distributions, users, and tags on the PostgreSQL Extension Network.', |
123
|
|
|
|
|
|
|
'No user nicknames found starting with "[_1]"' => 'No user nicknames found starting with “[_1]”', |
124
|
|
|
|
|
|
|
'Contact and extension release information for PGXN user "[_1]"' => 'Contact and extension release information for PGXN user “[_1]”', |
125
|
|
|
|
|
|
|
'Search for tags on PostgreSQL extension releases on PGXN' => 'Search for tags on PostgreSQL extension releases on PGXN', |
126
|
|
|
|
|
|
|
'Search for PostgreSQL Extension Network users' => 'Search for PostgreSQL Extension Network users', |
127
|
|
|
|
|
|
|
'A list of PGXN extensions tagged "[_1]"' => 'A list of PGXN extensions tagged “[_1]”', |
128
|
|
|
|
|
|
|
'PGXN [_1] search results for "[_2]"' => 'PGXN [_1] search results for “[_2]”', |
129
|
|
|
|
|
|
|
'Submit feedback to PGXN or join the mail list' => 'Submit feedback to PGXN or join the mail list', |
130
|
|
|
|
|
|
|
'Background on PGXN' => 'All about PGXN, what it’s for, what it contains, who made it, and why', |
131
|
|
|
|
|
|
|
'donor description' => 'Many thanks to these fine organizations and people who contributed support to make the develpoment of PGXN possible', |
132
|
|
|
|
|
|
|
'identity description' => 'All about the PGXN identity: who created it, its license, and downloadable assets', |
133
|
|
|
|
|
|
|
'faq description' => 'Frequently asked questions about the PostgreSQL Extension Network', |
134
|
|
|
|
|
|
|
'mirroring description' => 'Step-by-step instructions for mirroring PGXN on your own server', |
135
|
|
|
|
|
|
|
'Recent PostgreSQL extension releases on PGXN' => 'Recent PostgreSQL extension releases on PGXN', |
136
|
|
|
|
|
|
|
donors_intro => 'All the great folks who funded the inital development of PGXN will be listed in perpetuity here on the “Donors” page of PGXN.org. All donors are invited to the PGXN Launch Party at PGCon in May, 2011.', |
137
|
|
|
|
|
|
|
); |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
sub accept { |
140
|
2
|
|
|
2
|
1
|
1364
|
shift->get_handle( I18N::LangTags::Detect->http_accept_langs(shift) ); |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub list { |
144
|
6
|
|
|
6
|
1
|
3358
|
my ($lh, $items) = @_; |
145
|
6
|
50
|
|
|
|
12
|
return unless @{ $items }; |
|
6
|
|
|
|
|
15
|
|
146
|
6
|
100
|
|
|
|
10
|
return $items->[0] if @{ $items } == 1; |
|
6
|
|
|
|
|
21
|
|
147
|
4
|
|
|
|
|
5
|
my $last = pop @{ $items }; |
|
4
|
|
|
|
|
9
|
|
148
|
4
|
|
|
|
|
13
|
my $comma = $lh->maketext('listcomma'); |
149
|
4
|
|
|
|
|
137
|
my $ret = join "$comma ", @$items; |
150
|
4
|
100
|
|
|
|
17
|
$ret .= $comma if @{ $items } > 1; |
|
4
|
|
|
|
|
13
|
|
151
|
4
|
|
|
|
|
13
|
my $and = $lh->maketext('listand'); |
152
|
4
|
|
|
|
|
129
|
return "$ret $and $last"; |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
sub qlist { |
156
|
6
|
|
|
6
|
1
|
3732
|
my ($lh, $items) = @_; |
157
|
6
|
50
|
|
|
|
17
|
return unless @{ $items }; |
|
6
|
|
|
|
|
18
|
|
158
|
6
|
|
|
|
|
16
|
my $open = $lh->maketext('openquote'); |
159
|
6
|
|
|
|
|
185
|
my $shut = $lh->maketext('shutquote'); |
160
|
6
|
100
|
|
|
|
160
|
return $open . $items->[0] . $shut if @{ $items } == 1; |
|
6
|
|
|
|
|
25
|
|
161
|
4
|
|
|
|
|
5
|
my $last = pop @{ $items }; |
|
4
|
|
|
|
|
9
|
|
162
|
4
|
|
|
|
|
8
|
my $comma = $lh->maketext('listcomma'); |
163
|
4
|
|
|
|
|
111
|
my $ret = $open . join("$shut$comma $open", @$items) . $shut; |
164
|
4
|
100
|
|
|
|
7
|
$ret .= $comma if @{ $items } > 1; |
|
4
|
|
|
|
|
13
|
|
165
|
4
|
|
|
|
|
10
|
my $and = $lh->maketext('listand'); |
166
|
4
|
|
|
|
|
118
|
return "$ret $and $open$last$shut"; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
my %PATHS_FOR; |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
sub DESTROY { |
172
|
9
|
|
|
9
|
|
6157
|
delete $PATHS_FOR{ ref shift }; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub from_file { |
176
|
3
|
|
|
3
|
1
|
12
|
my ($self, $path) = (shift, shift); |
177
|
3
|
|
|
|
|
7
|
my $class = ref $self; |
178
|
3
|
|
33
|
|
|
31
|
my $file = $PATHS_FOR{$class}{$path} ||= _find_file($class, $path); |
179
|
3
|
50
|
|
|
|
126
|
open my $fh, '<:utf8', $file or die "Cannot open $file: $!\n"; |
180
|
3
|
|
|
|
|
9
|
my $value = do { local $/; $self->_compile(<$fh>); }; |
|
3
|
|
|
|
|
13
|
|
|
3
|
|
|
|
|
164
|
|
181
|
3
|
100
|
|
|
|
1401
|
return ref $value eq 'CODE' ? $value->($self, @_) : ${ $value }; |
|
2
|
|
|
|
|
47
|
|
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
sub _find_file { |
185
|
3
|
|
|
3
|
|
6
|
my $class = shift; |
186
|
3
|
|
|
|
|
11
|
my @path = split m{/}, shift; |
187
|
3
|
|
|
|
|
19
|
(my $dir = __FILE__) =~ s{[.]pm$}{}; |
188
|
1
|
|
|
1
|
|
8
|
no strict 'refs'; |
|
1
|
|
|
|
|
23
|
|
|
1
|
|
|
|
|
160
|
|
189
|
3
|
|
|
|
|
6
|
foreach my $super ($class, @{$class . '::ISA'}, __PACKAGE__ . '::en') { |
|
3
|
|
|
|
|
16
|
|
190
|
7
|
|
|
|
|
41
|
my $file = File::Spec->catfile($dir, $super->language_tag, @path); |
191
|
7
|
100
|
|
|
|
309
|
return $file if -e $file; |
192
|
|
|
|
|
|
|
} |
193
|
0
|
|
|
|
|
|
croak "No file found for path " . join('/', @path); |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
1; |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=encoding utf8 |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=head1 Name |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
PGXN::Site::Locale - Localization for PGXN::Site |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=head1 Synopsis |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
use PGXN::Site::Locale; |
207
|
|
|
|
|
|
|
my $mt = PGXN::Site::Locale->accept($env->{HTTP_ACCEPT_LANGUAGE}); |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=head1 Description |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
This class provides localization support for PGXN::Site. Each locale must |
212
|
|
|
|
|
|
|
create a subclass named for the locale and put its translations in the |
213
|
|
|
|
|
|
|
C<%Lexicon> hash. It is further designed to support easy creation of |
214
|
|
|
|
|
|
|
a handle from an HTTP_ACCEPT_LANGUAGE header. |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=head1 Interface |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
The interface inherits from L and adds the following |
219
|
|
|
|
|
|
|
method. |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=head2 Constructor Methods |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=head3 C |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
my $mt = PGXN::Site::Locale->accept($env->{HTTP_ACCEPT_LANGUAGE}); |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
Returns a PGXN::Site::Locale handle appropriate for the specified |
228
|
|
|
|
|
|
|
argument, which must take the form of the HTTP_ACCEPT_LANGUAGE string |
229
|
|
|
|
|
|
|
typically created in web server environments and specified in L
|
230
|
|
|
|
|
|
|
3282|https://tools.ietf.org/html/rfc3282>. The parsing of this header is |
231
|
|
|
|
|
|
|
handled by L. |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=head2 Instance Methods |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
=head3 C |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
# "Missing these keys: foo, bar, and baz" |
238
|
|
|
|
|
|
|
say $mt->maketext( |
239
|
|
|
|
|
|
|
'Missing these keys: [list,_1])' |
240
|
|
|
|
|
|
|
[qw(foo bar baz)], |
241
|
|
|
|
|
|
|
); |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
Formats a list of items. The list of items to be formatted should be passed as |
244
|
|
|
|
|
|
|
an array reference. If there is only one item, it will be returned. If there |
245
|
|
|
|
|
|
|
are two, they will be joined with " and ". If there are more, there will be a |
246
|
|
|
|
|
|
|
comma-separated list with the final item joined on ", and ". |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
Note that locales can control the localization of the comma and "and" via the |
249
|
|
|
|
|
|
|
C and C entries in their C<%Lexicon>s. |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
=head3 C |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
# "Missing these keys: “foo”, “bar”, and “baz” |
254
|
|
|
|
|
|
|
say $mt->maketext( |
255
|
|
|
|
|
|
|
'Missing these keys: [qlist,_1]' |
256
|
|
|
|
|
|
|
[qw(foo bar baz)], |
257
|
|
|
|
|
|
|
); |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
Like C but quotes each item in the list. Locales can specify the |
260
|
|
|
|
|
|
|
quotation characters to be used via the C and C entries |
261
|
|
|
|
|
|
|
in their C<%Lexicon>s. |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=head3 C |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
my $text = $mt->from_file('foo/bar.html'); |
266
|
|
|
|
|
|
|
my $msg = $mt->from_file('feedback.html', 'pgxn@example.com'); |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
Returns the contents of a localized file. The file argument should be |
269
|
|
|
|
|
|
|
specified with Unix semantics, regardless of operating system. Whereas |
270
|
|
|
|
|
|
|
subclasses contain short strings that need translating, the files can contain |
271
|
|
|
|
|
|
|
complete documents. As with C, the support the full range variable |
272
|
|
|
|
|
|
|
substitution, such as C<[_1]> and friends. |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
If a file doesn't exist for the current language, C will fall |
275
|
|
|
|
|
|
|
back on the same file path for any of its parent classes. If none has the |
276
|
|
|
|
|
|
|
file, it will fall back on the English file. |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
Localized files are maintained in L format by translators |
279
|
|
|
|
|
|
|
and converted to HTML at build time. The live in a subdirectory named for the |
280
|
|
|
|
|
|
|
last part of a subclass's package name. For example, the |
281
|
|
|
|
|
|
|
L class lives in F. Localized |
282
|
|
|
|
|
|
|
files will live in F. So for the argument |
283
|
|
|
|
|
|
|
C, the localized file will be |
284
|
|
|
|
|
|
|
F, and the HTML file (created at build time) |
285
|
|
|
|
|
|
|
will be F. |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=head1 Author |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
David E. Wheeler |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
=head1 Copyright and License |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
Copyright (c) 2010-2021 David E. Wheeler. |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
This module is free software; you can redistribute it and/or modify it under |
296
|
|
|
|
|
|
|
the L. |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
Permission to use, copy, modify, and distribute this software and its |
299
|
|
|
|
|
|
|
documentation for any purpose, without fee, and without a written agreement is |
300
|
|
|
|
|
|
|
hereby granted, provided that the above copyright notice and this paragraph |
301
|
|
|
|
|
|
|
and the following two paragraphs appear in all copies. |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
In no event shall David E. Wheeler be liable to any party for direct, |
304
|
|
|
|
|
|
|
indirect, special, incidental, or consequential damages, including lost |
305
|
|
|
|
|
|
|
profits, arising out of the use of this software and its documentation, even |
306
|
|
|
|
|
|
|
if David E. Wheeler has been advised of the possibility of such damage. |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
David E. Wheeler specifically disclaims any warranties, including, but not |
309
|
|
|
|
|
|
|
limited to, the implied warranties of merchantability and fitness for a |
310
|
|
|
|
|
|
|
particular purpose. The software provided hereunder is on an "as is" basis, |
311
|
|
|
|
|
|
|
and David E. Wheeler has no obligations to provide maintenance, support, |
312
|
|
|
|
|
|
|
updates, enhancements, or modifications. |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=cut |