File Coverage

blib/lib/Devel/CheckOS.pm
Criterion Covered Total %
statement 436 642 67.9
branch 28 30 93.3
condition 6 11 54.5
subroutine 214 214 100.0
pod 8 8 100.0
total 692 905 76.4


line stmt bran cond sub pod time code
1             package Devel::CheckOS;
2              
3 51     51   808805 use strict;
  31         179  
  31         920  
4 51     51   8849 use warnings;
  33         215  
  33         914  
5              
6 50     50   8218 use Exporter;
  33         75  
  33         1268  
7             # if we're loading this from Makefile.PL, FFR might not yet be installed
8 39     39   17058 eval 'use File::Find::Rule';
  28         232772  
  28         247  
9 51     51   6240 use File::Spec;
  35         132  
  35         1133  
10              
11 40     40   4546 use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS %OS_ALIASES);
  31         240  
  31         15347  
12              
13             our $VERSION = '1.95';
14              
15             @ISA = qw(Exporter);
16             @EXPORT_OK = qw(
17             os_is os_isnt die_if_os_is die_if_os_isnt die_unsupported
18             list_platforms list_family_members register_alias
19             );
20             %EXPORT_TAGS = (
21             all => \@EXPORT_OK,
22             booleans => [qw(os_is os_isnt die_unsupported)],
23             fatal => [qw(die_if_os_is die_if_os_isnt)]
24             );
25              
26             # get a list of the .pm files under a list of dirs, or the empty list
27             # in taint mode
28             sub _find_pm_files_in_dirs {
29 650     650   5228 my @files;
30 644         1307 eval { @files = File::Find::Rule->file()->name('*.pm')->in(@_) };
  644         23253  
31 649         8667277 return @files;
32             }
33              
34             if(exists($INC{'File/Find/Rule.pm'})) {
35             foreach my $alias_module (
36             _find_pm_files_in_dirs(
37             grep { -d }
38             map { File::Spec->catdir($_, qw(Devel AssertOS Alias)) }
39             @INC
40             )
41             ) {
42             my(undef, undef, $file_part) = File::Spec->splitpath($alias_module);
43             $file_part =~ s/\.pm$//;
44 38     38   13443 eval "use Devel::AssertOS::Alias::$file_part";
  31     38   98  
  31     29   613  
  38         2034  
  28         62  
  28         332  
  29         2696  
  20         42  
  20         240  
45             warn("Bad alias module 'Devel::AssertOS::Alias::$file_part' ignored\n") if($@);
46             }
47             }
48              
49             =head1 NAME
50              
51             Devel::CheckOS - check what OS we're running on
52              
53             =head1 DESCRIPTION
54              
55             A learned sage once wrote on IRC:
56              
57             $^O is stupid and ugly, it wears its pants as a hat
58              
59             Devel::CheckOS provides a more friendly interface to $^O, and also lets
60             you check for various OS "families" such as "Unix", which includes things
61             like Linux, Solaris, AIX etc.
62              
63             It spares perl the embarrassment of wearing its pants on its head by
64             covering them with a splendid Fedora.
65              
66             =head1 SYNOPSIS
67              
68             use Devel::CheckOS qw(os_is);
69             print "Hey, I know this, it's a Unix system\n" if(os_is('Unix'));
70              
71             print "You've got Linux 2.6\n" if(os_is('Linux::v2_6'));
72              
73             =head1 USING IT IN Makefile.PL or Build.PL
74              
75             If you want to use this from Makefile.PL or Build.PL, do
76             not simply copy the module into your distribution as this may cause
77             problems when PAUSE and search.cpan.org index the distro. Instead, use
78             the use-devel-assertos script.
79              
80             =head1 FUNCTIONS
81              
82             Devel::CheckOS implements the following functions, which load subsidiary
83             OS-specific modules on demand to do the real work. They can all be exported
84             by listing their names after C. You can also export
85             groups of functions thus:
86              
87             use Devel::CheckOS qw(:booleans); # export the boolean functions
88             # and 'die_unsupported'
89            
90             use Devel::CheckOS qw(:fatal); # export those that die on no match
91              
92             use Devel::CheckOS qw(:all); # export everything exportable
93              
94             =head2 Boolean functions
95              
96             =head3 os_is
97              
98             Takes a list of OS names. If the current platform matches any of them,
99             it returns true, otherwise it returns false. The names can be a mixture
100             of OSes and OS families, eg ...
101              
102             os_is(qw(Unix VMS)); # Unix is a family, VMS is an OS
103              
104             Matching is case-insensitive provided that Taint-mode is not enabled, so the
105             above could also be written:
106              
107             os_is(qw(unix vms));
108              
109             =cut
110              
111             sub os_is {
112 291     302 1 33241 my @targets = @_;
113 291         746 my $rval = 0;
114              
115 302         3087 TARGET: foreach my $target (@targets) {
116             # resolve aliases
117 603         2898 ALIAS: foreach my $alias (keys %OS_ALIASES) {
118 603 100       4910 if($target =~ /^$alias$/i) {
119 15         3273 $target = $OS_ALIASES{$alias};
120 3         7 last ALIAS;
121             }
122             }
123              
124             # resolve case-insensitive names (no-op in taint-mode as list_platforms
125             # won't work)
126 600         2221 my @available_platforms = list_platforms();
127 610         5654 CANDIDATE: foreach my $candidate (@available_platforms) {
128 21726 100       152685 if($target =~ /^\Q$candidate\E$/i) {
129 600         1606 $target = $candidate;
130 609         7560 last CANDIDATE;
131             }
132             }
133              
134 600 100       5722 die("Devel::CheckOS: $target isn't a legal OS name\n")
135             unless($target =~ /^\w+(::\w+)*$/);
136 35     35   14954 eval "use Devel::AssertOS::$target";
  13     30   1530  
  13     26   300  
  30     13   9256  
  9     12   189  
  9     11   214  
  26     11   8490  
  7     10   199  
  7     10   157  
  599     10   72893  
  8     8   1856  
  13     8   1559  
  4     9   56  
  6     10   79  
  15     11   1076  
  10     11   174  
  9     11   937  
  6     4   830  
  3     3   39  
  4     8   50  
  6     3   459  
  5     3   81  
  5     7   853  
  8     2   1091  
  3     1   9  
  6     6   75  
  6     1   26  
  6     1   88  
  6     6   467  
  3     1   1294  
  0     1   0  
  0     6   0  
  7     1   564  
  2     1   6  
  2     6   62  
  2     1   502  
  1     1   3  
  1     4   36  
  1     1   474  
  0     1   0  
  0     4   0  
  6     1   137  
  1     1   3  
  1     1   50  
  1     1   8  
  1     1   3  
  1     1   38  
  1     1   481  
  0     1   0  
  0     1   0  
  6     1   648  
  1     1   2  
  1     1   20  
  1     1   7  
  1     1   3  
  1     1   40  
  1     1   489  
  0     1   0  
  0     1   0  
  6     1   598  
  3     1   9  
  3     1   89  
  1         7  
  1         6  
  1         42  
  1         502  
  0         0  
  0         0  
  6         634  
  1         2  
  1         18  
  1         7  
  1         2  
  1         37  
  1         530  
  0         0  
  0         0  
  4         95  
  0         0  
  0         0  
  1         7  
  1         3  
  1         41  
  1         479  
  0         0  
  0         0  
  4         81  
  1         3  
  1         18  
  1         8  
  1         2  
  1         42  
  1         486  
  0         0  
  0         0  
  4         84  
  1         3  
  1         21  
  1         6  
  1         3  
  1         41  
  1         485  
  0         0  
  0         0  
  4         82  
  1         3  
  1         17  
  1         7  
  1         3  
  1         41  
  1         512  
  0         0  
  0         0  
  4         87  
  1         3  
  1         25  
  1         8  
  1         4  
  1         39  
  1         555  
  0         0  
  0         0  
  4         84  
  1         2  
  1         17  
  1         7  
  1         2  
  1         33  
  1         459  
  0         0  
  0         0  
  1         26  
  0         0  
  0         0  
  1         22  
  0         0  
  0         0  
  1         24  
  0         0  
  0         0  
  1         24  
  0         0  
  0         0  
  1         24  
  0         0  
  0         0  
  1         23  
  0         0  
  1         449  
  1         19  
  0         0  
  0         0  
  1         5  
  1         2  
  1         21  
  1         16  
  0         0  
  0         0  
  1         6  
  1         2  
  1         25  
  1         383  
  0         0  
  1         31  
  1         18  
  0         0  
  0         0  
  1         5  
  1         2  
  1         25  
  1         16  
  0         0  
  0         0  
  1         6  
  1         2  
  1         22  
  1         15  
  0         0  
137 607 100       9969 if(!$@) {
138 40     40   3101 no strict 'refs';
  33         86  
  33         23656  
139 137 100       335 $rval = 1 if(&{"Devel::AssertOS::${target}::os_is"}());
  137         1129  
140             }
141             }
142 297         63101 return $rval;
143             }
144              
145             =head3 os_isnt
146              
147             If the current platform matches (case-insensitively) any of the parameters it
148             returns false, otherwise it returns true.
149              
150             =cut
151              
152             sub os_isnt {
153 31     41 1 137 my @targets = @_;
154 31         135 my $rval = 1;
155 37         2741 foreach my $target (@targets) {
156 61 100       223 $rval = 0 if(os_is($target));
157             }
158 30         532 return $rval;
159             }
160              
161             =head2 Fatal functions
162              
163             =head3 die_if_os_isnt
164              
165             As C, except that it dies instead of returning false. The die()
166             message matches what the CPAN-testers look for to determine if a module
167             doesn't support a particular platform.
168              
169             =cut
170              
171             sub die_if_os_isnt {
172 18 100   22 1 3107 os_is(@_) ? 1 : die_unsupported();
173             }
174              
175             =head3 die_if_os_is
176              
177             As C, except that it dies instead of returning false.
178              
179             =cut
180              
181             sub die_if_os_is {
182 14 100   21 1 2943 os_isnt(@_) ? 1 : die_unsupported();
183             }
184              
185             =head2 And some utility functions ...
186              
187             =head3 die_unsupported
188              
189             This function simply dies with the message "OS unsupported", which is what
190             the CPAN testers look for to figure out whether a platform is supported or
191             not.
192              
193             =cut
194              
195 301     308 1 12511 sub die_unsupported { die("OS unsupported\n"); }
196              
197             =head3 list_platforms
198              
199             When called in list context,
200             return a list of all the platforms for which the corresponding
201             Devel::AssertOS::* module is available. This includes both OSes and OS
202             families, and both those bundled with this module and any third-party
203             add-ons you have installed.
204              
205             In scalar context, returns a hashref keyed by platform with the filename
206             of the most recent version of the supporting module that is available to you.
207             This behaviour is deprecated.
208              
209             Unfortunately, on some platforms this list may have file case
210             broken. eg, some platforms might return 'freebsd' instead of 'FreeBSD'.
211             This is because they have case-insensitive filesystems so things
212             should Just Work anyway.
213              
214             This function does not work in taint-mode.
215              
216             =cut
217              
218             my $case_flag = File::Spec->case_tolerant ? '(?i)' : '';
219             my $re_Devel = qr/$case_flag ^Devel$/x;
220             my $re_AssertOS = qr/$case_flag ^AssertOS$/x;
221             my $re_Alias = qr/$case_flag ^Alias\b/x;
222              
223             sub list_platforms {
224             # sort by mtime, so oldest last. This was necessary so that if a module
225             # appears twice in @INC we pick the newer one but that functionality is
226             # no longer needed. We do need to de-dupe the list though
227             my @modules = sort {
228 562747         9785872 (stat($a->{file}))[9] <=> (stat($b->{file}))[9]
229             } grep {
230 142575         332181 $_->{module} !~ $re_Alias
231             } map {
232 142580         946610 my (undef, $dir_part, $file_part) = File::Spec->splitpath($_);
233 142574         444077 $file_part =~ s/\.pm$//;
234 142574         530820 my (@dirs) = grep {+length} File::Spec->splitdir($dir_part);
  1473250         1989828  
235 142573         346238 foreach my $i (reverse 1..$#dirs) {
236             next unless(
237 193470 100 66     1104581 $dirs[$i] =~ $re_AssertOS &&
238             $dirs[$i - 1] =~ $re_Devel
239             );;
240 142579         299827 splice @dirs, 0, $i + 1;
241 142574         194077 last;
242             }
243             {
244 142574         692234 module => join('::', @dirs, $file_part),
245             file => File::Spec->canonpath($_)
246             }
247             } _find_pm_files_in_dirs(
248 6490         66498 grep { -d }
249 618     619 1 2126486 map { File::Spec->catdir($_, qw(Devel AssertOS)) }
  6484         34113  
250             @INC
251             );
252              
253             my %modules = map {
254 611         31143 $_->{module} => $_->{file}
255 140948         266539 } @modules;
256              
257 612 100       11825 if(wantarray()) {
258 610         64808 return sort keys %modules;
259             } else {
260 10 100       566 warn("Calling list_platforms in scalar context and getting back a reference is deprecated and will go away some time after April 2024. To disable this warning set \$Devel::CheckOS::NoDeprecationWarnings::Context to a true value.\n") unless($Devel::CheckOS::NoDeprecationWarnings::Context);
261 5         126 return \%modules;
262             }
263             }
264              
265             =head3 list_family_members
266              
267             Takes the name of an OS 'family' and returns a list of all its members.
268             In list context, you get a list, in scalar context you get an arrayref.
269              
270             If called on something that isn't a family, you get an empty list (or
271             a ref to an empty array).
272              
273             =cut
274              
275             sub list_family_members {
276 101   100 107 1 5895017 my $family = shift() ||
277             die(__PACKAGE__."::list_family_members needs a parameter\n");
278              
279             # this will die if it's the wrong OS, but the module is loaded ...
280 105     4   9766 eval qq{use Devel::AssertOS::$family};
  0     1   0  
  4     1   85  
  1     4   2  
  1     1   37  
  1     1   16  
  0     4   0  
  1     1   22  
  1     1   491  
  0     1   0  
  1     4   25  
  1     1   357  
  0     1   0  
  1     1   22  
  4     1   84  
  1     1   2  
  1     1   21  
  1     1   7  
  1     1   3  
  0     1   0  
  1     1   7  
  1     1   2  
  0     4   0  
  1     4   6  
  1     4   3  
  0     4   0  
  1     4   6  
  1     1   3  
  0     1   0  
  1     1   7  
  1     1   2  
  0     1   0  
  1     1   6  
  1     1   2  
  0     1   0  
  1     1   6  
  1     1   2  
  0     1   0  
  1     1   6  
  1     1   2  
  0     1   0  
  1     1   5  
  1     1   3  
  0     1   0  
  1     1   5  
  1     1   2  
  0     1   0  
  1     1   8  
  1     1   2  
  1     1   28  
  4     1   545  
  2     1   7  
  0     1   0  
  4     1   78  
  1     1   3  
  1     1   18  
  4     1   82  
  1     1   2  
  1     1   21  
  4     1   1059  
  1     1   3  
  1     1   22  
  4     1   1042  
  1     1   2  
  0     1   0  
  1     1   26  
  0     1   0  
  0         0  
  1         22  
  0         0  
  0         0  
  1         10  
  1         2  
  0         0  
  1         25  
  0         0  
  0         0  
  1         26  
  0         0  
  0         0  
  1         22  
  0         0  
  0         0  
  1         26  
  0         0  
  0         0  
  1         443  
  0         0  
  0         0  
  1         518  
  0         0  
  0         0  
  1         537  
  0         0  
  0         0  
  1         510  
  0         0  
  0         0  
  1         25  
  0         0  
  0         0  
  1         24  
  0         0  
  0         0  
  1         26  
  0         0  
  0         0  
  1         24  
  0         0  
  0         0  
  1         28  
  0         0  
  0         0  
  1         22  
  0         0  
  0         0  
  1         27  
  0         0  
  0         0  
  1         10  
  1         2  
  0         0  
  1         29  
  0         0  
  0         0  
  1         26  
  0         0  
  0         0  
  1         25  
  0         0  
  0         0  
  1         25  
  0         0  
  0         0  
  1         22  
  0         0  
  0         0  
  1         22  
  0         0  
  0         0  
  1         6  
  1         3  
  1         24  
  1         16  
  0         0  
  0         0  
  1         6  
  1         2  
  1         23  
  1         16  
  0         0  
  0         0  
  1         6  
  1         2  
  0         0  
  1         7  
  1         2  
  1         24  
  1         16  
  0         0  
  0         0  
  1         24  
  0         0  
  0         0  
  1         7  
  1         2  
  0         0  
  1         6  
  1         3  
  1         22  
  1         16  
  0         0  
  1         21  
  1         7  
  1         2  
  1         21  
  1         18  
  0         0  
  1         25  
  1         16  
  0         0  
  1         23  
  1         15  
  0         0  
  1         21  
  1         14  
  0         0  
  1         23  
281             # ... so we can now query it
282 98     1   6037 my @members = eval qq{
  1     4   22  
  1     1   7  
  1     1   5  
  0     4   0  
  1     1   6  
  1     1   2  
  0     1   0  
  1     1   7  
  1     4   3  
  0     4   0  
  4     1   78  
  1     1   3  
  1     1   22  
  4     1   79  
  1     1   2  
  1     1   35  
  1     1   464  
  0     1   0  
  1     1   25  
  1     1   354  
  0     1   0  
  1     4   25  
  1     4   348  
  0     4   0  
  1     4   23  
  1     1   390  
  0     1   0  
  1     1   23  
  1     1   344  
  0     1   0  
  1     1   22  
  1     1   368  
  0     1   0  
  1     1   22  
  1     1   365  
  0     1   0  
  1     1   24  
  1     1   15  
  0     1   0  
  1     1   22  
  1     1   17  
  0     1   0  
  1     1   23  
  1     1   15  
  0     1   0  
  1     1   35  
  1     1   425  
  1     1   6  
  2     1   48  
  4     1   99  
  0     1   0  
  1     1   21  
  4     1   83  
  1     1   3  
  1     1   19  
  4     1   1008  
  1     1   2  
  1     1   19  
  4     1   1223  
  1     1   3  
  1     1   21  
  1     1   24  
  0     1   0  
  0     1   0  
  1     1   23  
  0         0  
  0         0  
  1         24  
  0         0  
  1         24  
  1         26  
  0         0  
  0         0  
  1         20  
  0         0  
  0         0  
  1         22  
  0         0  
  0         0  
  1         529  
  0         0  
  0         0  
  1         513  
  0         0  
  0         0  
  1         437  
  0         0  
  0         0  
  1         489  
  0         0  
  0         0  
  1         502  
  0         0  
  0         0  
  1         469  
  0         0  
  0         0  
  1         25  
  0         0  
  0         0  
  1         26  
  0         0  
  0         0  
  1         28  
  0         0  
  0         0  
  1         24  
  0         0  
  0         0  
  1         28  
  0         0  
  0         0  
  1         22  
  0         0  
  0         0  
  1         26  
  0         0  
  1         25  
  1         25  
  0         0  
  0         0  
  1         23  
  0         0  
  0         0  
  1         24  
  0         0  
  0         0  
  1         25  
  0         0  
  0         0  
  1         23  
  0         0  
  0         0  
  1         23  
  0         0  
  0         0  
  1         510  
  0         0  
  0         0  
  1         5  
  1         2  
  1         23  
  1         16  
  0         0  
  0         0  
  1         5  
  1         2  
  1         23  
  1         371  
  0         0  
  0         0  
  1         21  
  0         0  
  0         0  
  1         6  
  1         4  
  1         22  
  1         442  
  0         0  
  0         0  
  1         25  
  0         0  
  1         40  
  1         18  
  0         0  
  0         0  
  1         5  
  1         3  
  0         0  
  1         5  
  1         3  
  1         16  
  1         6  
  1         2  
  0         0  
  1         6  
  1         2  
  0         0  
  1         5  
  1         4  
  0         0  
  1         6  
  1         2  
  0         0  
  1         6  
  1         3  
283             no strict 'refs';
284             &{"Devel::AssertOS::${family}::matches"}()
285             };
286 98 100       480 if(wantarray()) {
287 103         1074 return @members;
288             } else {
289 7 100       45 warn("Calling list_family_members in scalar context and getting back a reference is deprecated and will go away some time after April 2024. To disable this warning set \$Devel::CheckOS::NoDeprecationWarnings::Context to a true value.\n") unless($Devel::CheckOS::NoDeprecationWarnings::Context);
290 7         172 return \@members;
291             }
292             }
293              
294             =head3 register_alias
295              
296             It takes two arguments, the first being an alias name, the second being the
297             name of an OS. After the alias has been registered, any queries about the
298             alias will return the appropriate result for the named OS.
299              
300             It returns true unless you invoke it incorrectly or you attempt to change
301             an existing alias.
302              
303             Aliases don't work under taint-mode.
304              
305             See L.
306              
307             =cut
308              
309             sub register_alias {
310 36     35 1 1413 my($alias, $os) = @_;
311 29 50 33     5746 ($alias && $os) || return 0;
312 29 50 33     226 if(!exists($OS_ALIASES{$alias}) || $OS_ALIASES{$alias} eq $os) {
313 37         1154 return $OS_ALIASES{$alias} = $os;
314             } else {
315 4         13 return 0
316             }
317             }
318              
319             =head1 PLATFORMS SUPPORTED
320              
321             To see the list of platforms for which information is available, run this:
322              
323             perl -MDevel::CheckOS -e 'print join(", ", Devel::CheckOS::list_platforms())'
324              
325             These are the names of the underlying Devel::AssertOS::* modules
326             which do the actual platform detection, so they have to
327             be 'legal' filenames and module names, which unfortunately precludes
328             funny characters, so platforms like OS/2 are mis-spelt deliberately.
329             Sorry.
330              
331             Also be aware that not all of them have been properly tested. I don't
332             have access to most of them and have had to work from information
333             gleaned from L and a few other places. For a complete list of
334             OS families, see L.
335              
336             If you want to add your own OSes or families, see L
337             and please feel free to upload the results to the CPAN.
338              
339             =head1 BUGS and FEEDBACK
340              
341             I welcome feedback about my code, including constructive criticism.
342             Bug reports should be made using L.
343              
344             You will need to include in your bug report the exact value of $^O, what
345             the OS is called (eg Windows Vista 64 bit Ultimate Home Edition), and,
346             if relevant, what "OS family" it should be in and who wrote it.
347              
348             If you are feeling particularly generous you can encourage me in my
349             open source endeavours by buying me something from my wishlist:
350             L
351              
352             =head1 COMPATIBILITY
353              
354             Version 1.90 made all matches case-insensitive. This is a change in behaviour, but
355             if it breaks your code then your code was already broken, you just didn't know it.
356              
357             =head1 DEPRECATIONS
358              
359             At some point after April 2024 the C and C
360             functions will stop being sensitive to whether they are called in list context or
361             not, and will always return a list. From now until then calling them in non-list
362             context will emit a warning. You can turn that off by setting
363             C<$Devel::CheckOS::NoDeprecationWarnings::Context> to a true value.
364              
365             =head1 SEE ALSO
366              
367             $^O in L
368              
369             L
370              
371             L
372              
373             L
374              
375             L
376              
377             The use-devel-assertos script
378              
379             L
380              
381             =head1 AUTHOR
382              
383             David Cantrell EFE
384              
385             Thanks to David Golden for the name and ideas about the interface, and
386             to the cpan-testers-discuss mailing list for prompting me to write it
387             in the first place.
388              
389             Thanks to Ken Williams, from whose L I lifted some of the
390             information about what should be in the Unix family.
391              
392             Thanks to Billy Abbott for finding some bugs for me on VMS.
393              
394             Thanks to Matt Kraai for information about QNX.
395              
396             Thanks to Kenichi Ishigaki and Gabor Szabo for reporting a bug on Windows,
397             and to the former for providing a patch.
398              
399             Thanks to Paul Green for some information about VOS.
400              
401             Thanks to Yanick Champoux for a patch to let Devel::AssertOS support
402             negative assertions.
403              
404             Thanks to Brian Fraser for adding Android support.
405              
406             Thanks to Dale Evans for Debian detection, a bunch of Mac OS X specific version
407             detection modules, and perl 5.6 support.
408              
409             Thanks to Graham Knop for fixing a build bug on perl 5.8.
410              
411             =head1 SOURCE CODE REPOSITORY
412              
413             L
414              
415             =head1 COPYRIGHT and LICENCE
416              
417             Copyright 2007-2022 David Cantrell
418              
419             This software is free-as-in-speech software, and may be used, distributed, and modified under the terms of either the GNU General Public Licence version 2 or the Artistic Licence. It's up to you which one you use. The full text of the licences can be found in the files GPL2.txt and ARTISTIC.txt, respectively.
420              
421             =head1 HATS
422              
423             I recommend buying a Fedora from L.
424              
425             =head1 CONSPIRACY
426              
427             This module is also free-as-in-mason software.
428              
429             =cut
430              
431             1;