1package FFI::Platypus::Bundle;
2
3use strict;
4use warnings;
5use 5.008004;
6use Carp ();
7
8# ABSTRACT: Bundle foreign code with your Perl module
9our $VERSION = '1.56'; # VERSION
10
11
12package FFI::Platypus;
13
14sub _bundle
15{
16  my @arg_ptrs;
17
18  if(defined $_[-1] && ref($_[-1]) eq 'ARRAY')
19  {
20    @arg_ptrs = @{ pop @_ };
21  }
22
23  push @arg_ptrs, undef;
24
25  my($self, $package) = @_;
26  $package = caller unless defined $package;
27
28  require List::Util;
29
30  my($pm) = do {
31    my $pm = "$package.pm";
32    $pm =~ s{::}{/}g;
33    # if the module is already loaded, we can use %INC
34    # otherwise we can go through @INC and find the first .pm
35    # this doesn't handle all edge cases, but probably enough
36    List::Util::first(sub { (defined $_) && (-f $_) }, ($INC{$pm}, map { "$_/$pm" } @INC));
37  };
38
39  Carp::croak "unable to find module $package" unless $pm;
40
41  my @parts = split /::/, $package;
42  my $incroot = $pm;
43  {
44    my $c = @parts;
45    $incroot =~ s![\\/][^\\/]+$!! while $c--;
46  }
47
48  my $txtfn = List::Util::first(sub { -f $_ }, do {
49    my $dir  = join '/', @parts;
50    my $file = $parts[-1] . ".txt";
51    (
52      "$incroot/auto/$dir/$file",
53      "$incroot/../arch/auto/$dir/$file",
54    );
55  });
56
57  my $lib;
58
59  if($txtfn)
60  {
61    $lib = do {
62      my $fh;
63      open($fh, '<', $txtfn) or die "unable to read $txtfn $!";
64      my $line = <$fh>;
65      close $fh;
66      $line =~ /^FFI::Build\@(.*)$/
67        ? "$incroot/$1"
68        : Carp::croak "bad format $txtfn";
69    };
70    Carp::croak "bundle code is missing: $lib" unless -f $lib;
71  }
72  elsif(-d "$incroot/../ffi")
73  {
74    require FFI::Build::MM;
75    require Capture::Tiny;
76    require Cwd;
77    require File::Spec;
78    my $save = Cwd::getcwd();
79    chdir "$incroot/..";
80    my($output, $error) = Capture::Tiny::capture_merged(sub {
81      $lib = eval {
82        my $dist_name = $package;
83        $dist_name =~ s/::/-/g;
84        my $fbmm = FFI::Build::MM->new( save => 0 );
85        $fbmm->mm_args( DISTNAME => $dist_name );
86        my $build = $fbmm->load_build('ffi', undef, 'ffi/_build');
87        $build->build;
88      };
89      $@;
90    });
91    if($error)
92    {
93      chdir $save;
94      print STDERR $output;
95      die $error;
96    }
97    else
98    {
99      $lib = File::Spec->rel2abs($lib);
100      chdir $save;
101    }
102  }
103  else
104  {
105    Carp::croak "unable to find bundle code for $package";
106  }
107
108  my $handle = FFI::Platypus::DL::dlopen($lib, FFI::Platypus::DL::RTLD_PLATYPUS_DEFAULT())
109    or Carp::croak "error loading bundle code: $lib @{[ FFI::Platypus::DL::dlerror() ]}";
110
111  $self->{handles}->{$lib} =  $handle;
112
113  $self->lib($lib);
114
115  if(my $init = eval { $self->function( 'ffi_pl_bundle_init' => [ 'string', 'sint32', 'opaque[]' ] => 'void' ) })
116  {
117     $init->call($package, scalar(@arg_ptrs)-1, \@arg_ptrs);
118  }
119
120  if(my $init = eval { $self->function( 'ffi_pl_bundle_constant' => [ 'string', 'opaque' ] => 'void' ) })
121  {
122    require FFI::Platypus::Constant;
123    my $api = FFI::Platypus::Constant->new($package);
124    $init->call($package, $api->ptr);
125  }
126
127  if(my $address = $self->find_symbol( 'ffi_pl_bundle_fini' ))
128  {
129    push @{ $self->{fini} }, sub {
130      my $self = shift;
131      $self->function( $address => [ 'string' ] => 'void' )
132           ->call( $package );
133    };
134  }
135
136  $self;
137}
138
1391;
140
141__END__
142
143=pod
144
145=encoding UTF-8
146
147=head1 NAME
148
149FFI::Platypus::Bundle - Bundle foreign code with your Perl module
150
151=head1 VERSION
152
153version 1.56
154
155=head1 SYNOPSIS
156
157C<ffi/foo.c>:
158
159 #include <ffi_platypus_bundle.h>
160 #include <string.h>
161
162 typedef struct {
163   char *name;
164   int value;
165 } foo_t;
166
167 foo_t*
168 foo__new(const char *class_name, const char *name, int value)
169 {
170   (void)class_name;
171   foo_t *self = malloc( sizeof( foo_t ) );
172   self->name = strdup(name);
173   self->value = value;
174   return self;
175 }
176
177 const char *
178 foo__name(foo_t *self)
179 {
180   return self->name;
181 }
182
183 int
184 foo__value(foo_t *self)
185 {
186   return self->value;
187 }
188
189 void
190 foo__DESTROY(foo_t *self)
191 {
192   free(self->name);
193   free(self);
194 }
195
196C<lib/Foo.pm>:
197
198 package Foo;
199
200 use strict;
201 use warnings;
202 use FFI::Platypus 1.00;
203
204 {
205   my $ffi = FFI::Platypus->new( api => 1 );
206
207   $ffi->type('object(Foo)' => 'foo_t');
208   $ffi->mangler(sub {
209     my $name = shift;
210     $name =~ s/^/foo__/;
211     $name;
212   });
213
214   $ffi->bundle;
215
216   $ffi->attach( new =>     [ 'string', 'string', 'int' ] => 'foo_t'  );
217   $ffi->attach( name =>    [ 'foo_t' ]                   => 'string' );
218   $ffi->attach( value =>   [ 'foo_t' ]                   => 'int'    );
219   $ffi->attach( DESTROY => [ 'foo_t' ]                   => 'void'   );
220 }
221
222 1;
223
224C<t/foo.t>
225
226 use Test2::V0;
227 use Foo;
228
229 my $foo = Foo->new("platypus", 10);
230 isa_ok $foo, 'Foo';
231 is $foo->name, "platypus";
232 is $foo->value, 10;
233
234 done_testing;
235
236C<Makefile.PL>:
237
238 use ExtUtils::MakeMaker;
239 use FFI::Build::MM;
240 my $fbmm = FFI::Build::MM->new;
241 WriteMakefile(
242   $fbmm->mm_args(
243     NAME     => 'Foo',
244     DISTNAME => 'Foo',
245     VERSION  => '1.00',
246     # ...
247   )
248 );
249
250 sub MY::postamble
251 {
252   $fbmm->mm_postamble;
253 }
254
255or C<dist.ini>:
256
257 name    = Foo
258 version = 0.01
259 ...
260
261 [FFI::Build]
262 version = 1.04
263
264=head1 DESCRIPTION
265
266This document serves as a tutorial for using the new bundling interface provided
267by L<FFI::Platypus> as of api version 1.  It requires L<FFI::Platypus> of at least
2681.00.
269
270Sometimes when writing FFI bindings you need to include a little C code (or your
271favorite compiled language) to finish things off.  Alternatively, you might just
272want to write some C code (or your favorite compiled language) to include with your
273Perl module to make a tight loop faster.  The bundling interface has you covered.
274
275=head2 Basic example
276
277To illustrate we will go through the files in the synopsis and explain
278how and why they work.  To start with we have some C code which emulates object
279oriented code using C<foo__> as a prefix.  We use a C struct that we call
280C<foo_t> to store our object data.  On the C level the struct acts as a class,
281when combined with its functions that act as methods.  The constructor just
282allocates the memory it needs for the C<foo_t> instance, fills in the
283appropriate fields and returns the pointer:
284
285 foo_t*
286 foo__new(const char *class_name, const char *name, int value)
287 {
288   (void) class_name;
289   foo_t *self = malloc( sizeof( foo_t ) );
290   self->name = strdup(name);
291   self->value = value;
292   return self;
293 }
294
295We include a class name as the first argument, because Perl will include that
296when calling the constructor, but we do not use it here.  An exercise for the
297reader would be to add hierarchical inheritance.
298
299There are also some methods which return member values.  This class has only
300read only members, but you could have read/write or other methods depending
301on your needs.
302
303 const char *
304 foo__name(foo_t *self)
305 {
306   return self->name;
307 }
308
309We also include a destructor so that the memory owned by the object can be
310freed when it is no longer needed.
311
312 void
313 foo__DESTROY(foo_t *self)
314 {
315   free(self->name);
316   free(self);
317 }
318
319This might start to look a little like a Perl module, and when we look at the Perl
320code that binds to this code, you will see why.  First lets prepare the
321L<FFI::Platypus> instance and specify the correct api version:
322
323 my $ffi = FFI::Platypus->new( api => 1 );
324
325The bundle interface is only supported with api version 1, so if you try to use
326version 0 it will not work.  Next we define an object type for C<foo_t> which will
327associate it with the Perl class C<Foo>.
328
329 $ffi->type('object(Foo)' => 'foo_t');
330
331As object type is a blessed reference to an opaque (default) or integer type which
332can be used as a Perl object.  Platypus does the translating of Perl object to and
333from the foo_t pointers that the C code understands.  For more details on Platypus
334types see L<FFI::Platypus::Type>.
335
336Next we set the mangler on the Platypus instance so that we can refer to function
337names without the C<foo__> prefix.  You could just not use the prefix in your C
338code and skip this step, or you could refer to the function names in their full
339in your Perl code, however, this saves extra typing and allows you to bundle more
340than one class with your Perl code without having to worry about name conflicts.
341
342 $ffi->mangler(sub {
343   my $name = shift;
344   $name =~ s/^/foo__/;
345   $name;
346 });
347
348Finally we let Platypus know that we will be bundling code.
349
350 $ffi->bundle;
351
352By default, this searches for the appropriate place for your dynamic libraries using
353the current package.  In some cases you may need to override this, for example if your
354dist is named C<Foo-Bar> but your specific class is named C<Foo::Bar::Baz>, you'd
355want something like this:
356
357 package Foo::Bar::Baz;
358 use FFI::Platypus 1.00;
359 my $ffi = FFI::Platypus->new( api => 1 );
360 $ffi->bundle('Foo::Bar');
361 ...
362
363Now, finally we can attach the methods for our class:
364
365 $ffi->attach( new =>     [ 'string', 'int' ] => 'foo_t'  );
366 $ffi->attach( name =>    [ 'foo_t' ]         => 'string' );
367 $ffi->attach( value =>   [ 'foo_t' ]         => 'int'    );
368 $ffi->attach( DESTROY => [ 'foo_t' ]         => 'void'   );
369
370Note that we do not have to include the C<foo__> prefix because of the way we set up
371the mangler.  If we hadn't done that then we could instead attach with the full names:
372
373 $ffi->attach( [ 'foo__new'  => 'new' ]  => [ 'string', 'int' ] => 'foo_t'  );
374 $ffi->attach( [ 'foo__name' => 'name' ] => [ 'foo_t' ]         => 'string' );
375 ...
376
377You're done!  You can now use this class.  Lets write a test to make sure it works,
378
379 use Test2::V0;
380 use Foo;
381
382 my $foo = Foo->new("platypus", 10);
383 isa_ok $foo, 'Foo';
384 is $foo->name, "platypus";
385 is $foo->value, 10;
386
387 done_testing;
388
389and use C<prove> to check that it works:
390
391 % prove -lvm
392 t/foo.t ..
393 ok 1 - An object of class 'Foo' isa 'Foo'
394 ok 2
395 ok 3
396 1..3
397 ok
398 All tests successful.
399 Files=1, Tests=3,  0 wallclock secs ( 0.02 usr  0.00 sys +  0.14 cusr  0.03 csys =  0.19 CPU)
400 Result: PASS
401
402Platypus automatically compiles and links the dynamic library for you:
403
404 % ls ffi/_build
405 foo.c.o  libFoo.so
406
407The C code will be rebuilt next time if the source code is newer than the object or dynamic libraries
408files.  If the source files are not changed, then it won't be rebuilt to save time.  If you are using
409the code without MakeMaker, or another build system you are responsible for cleaning up these files.
410This is intended as a convenience to allow you to test your code without having to invoke MakeMaker,
411or C<dzil> or whatever build system you are using.
412
413When you distribute your module though, you will want the dynamic library built just once
414at build-time and installed correctly so that it can be found at run-time.  You don't need
415to make any changes to your C or Perl code, but you do need to tell MakeMaker to build and
416install the appropriate files using L<FFI::Build::MM>:
417
418 use ExtUtils::MakeMaker;
419 use FFI::Build::MM;
420 my $fbmm = FFI::Build::MM->new;
421 WriteMakefile(
422   $fbmm->mm_args(
423     NAME     => 'Foo',
424     DISTNAME => 'Foo',
425     VERSION  => '1.00',
426     # ...
427   )
428 );
429
430 sub MY::postamble
431 {
432   $fbmm->mm_postamble;
433 }
434
435And we can invoke all the normal MakeMaker style stuff and our C code will be compiled, linked
436and installed at the appropriate steps.
437
438 % perl Makefile.PL
439 Generating a Unix-style Makefile
440 Writing Makefile for Foo
441 Writing MYMETA.yml and MYMETA.json
442 % make
443 cp lib/Foo.pm blib/lib/Foo.pm
444 "/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" -MFFI::Build::MM=cmd -e fbx_build
445 CC ffi/foo.c
446 LD blib/lib/auto/share/dist/Foo/lib/libFoo.dylib
447 % make test
448 "/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" -MFFI::Build::MM=cmd -e fbx_build
449 "/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" -MFFI::Build::MM=cmd -e fbx_test
450 PERL_DL_NONLAZY=1 "/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" "-MExtUtils::Command::MM" "-MTest::Harness" "-e" "undef *Test::Harness::Switches; test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
451 t/foo.t .. ok
452 All tests successful.
453 Files=1, Tests=3,  0 wallclock secs ( 0.01 usr  0.00 sys +  0.06 cusr  0.01 csys =  0.08 CPU)
454 Result: PASS
455
456If the C<Makefile.PL> file above looks overly complicated, you can use the
457L<Dist::Zilla::Plugin::FFI::Build> plugin to simplify your life if you are
458using L<Dist::Zilla>:
459
460 [FFI::Build]
461 version = 1.04
462
463Specifying version 1.04 will ensure that any C<.o> or C<.so> files are pruned
464from your build tree and not distributed by mistake.
465
466=head2 Initialization example
467
468The bundle interface also gives you entry points which will be called automatically
469when your code is loaded and unloaded if they are found.
470
471=over 4
472
473=item C<ffi_pl_bundle_init>
474
475 void ffi_pl_bundle_init(const char *package, int argc, void *argv[]);
476
477Called when the dynamic library is loaded.  C<package> is the Perl package
478that called C<bundle> from Perl space.  C<argc> and C<argv> represents an
479array of opaque pointers that can be passed as an array to bundle as the
480last argument.  (the count C<argc> is a little redundant because C<argv>
481is also NULL terminated).
482
483=item C<ffi_pl_bundle_constant>
484
485 void ffi_pl_bundle_constant(const char *package, ffi_platypus_constant_t *c);
486
487Called immediately after C<ffi_pl_bundle_init>, and is intended to allow
488you to set Perl constants from C space.  For details on how this works
489and what methods you can call on the C<ffi_platypus_constant_t> instance,
490see L<FFI::Platypus::Constant>.
491
492=item C<ffi_pl_bundle_fini>
493
494 void ffi_pl_bundle_fini(const char *package);
495
496Called when the dynamic library is unloaded.  C<package> is the Perl
497package that called C<bundle> from Perl space when the library was
498loaded.  B<CAVEAT>: if you attach any functions then this will
499never be called, because attaching functions locks the Platypus
500instance into memory along with the libraries which it is using.
501
502=back
503
504Here is an example that passes the version and a callback back into Perl
505space that emulates the Perl 5.10 C<say> feature.
506
507C<ffi/init.c>:
508
509 #include <ffi_platypus_bundle.h>
510
511 char buffer[512];
512 const char *version;
513 void (*say)(const char *);
514
515 void
516 ffi_pl_bundle_init(const char *package, int argc, void *argv[])
517 {
518   version = argv[0];
519   say     = argv[1];
520
521   say("in init!");
522
523   snprintf(buffer, 512, "package = %s, version = %s", package, version);
524   say(buffer);
525
526   snprintf(buffer, 512, "args = %d", argc);
527   say(buffer);
528 }
529
530 void
531 ffi_pl_bundle_fini(const char *package)
532 {
533   say("in fini!");
534 }
535
536C<lib/Init.pm>:
537
538 package Init;
539
540 use strict;
541 use warnings;
542 use FFI::Platypus 1.00;
543
544 our $VERSION = '1.00';
545
546 {
547   my $ffi = FFI::Platypus->new( api => 1 );
548
549   my $say = $ffi->closure(sub {
550     my $string = shift;
551     print "$string\n";
552   });
553
554   $ffi->bundle([
555     $ffi->cast( 'string' => 'opaque', $VERSION ),
556     $ffi->cast( '(string)->void' => 'opaque', $say ),
557   ]);
558
559   undef $ffi;
560   undef $say;
561 }
562
563 1;
564
565The deinitialization order for the C<$say> callback and the C<$ffi>
566instance is essential here, so we do it manually with C<undef>:
567
568 undef $ffi;
569 undef $say;
570
571First we deallocate C<$ffi> which calls C<ffi_pl_bundle_fini>,
572which calls C<$say>, so we want to make sure the latter is still
573allocated.  Once C<ffi_pl_bundle_fini> is done, we can safely
574deallocate C<$say>.
575
576If C<ffi_pl_bundle_fini> didn't call back into Perl space like
577this then we don't have to be as careful about deallocating
578things in Perl space.
579
580=head2 Compiler or linker flags example
581
582There are times when you will want to specify your own compiler and
583linker flags for the C code that you are bundling.  The C<TL;DR> is that
584you can put a C<.fbx> file in your C<ffi> directory.  This is a Perl
585script that returns a hash reference that is passed into the
586L<FFI::Build> constructor.  This allows you to set a number of options,
587including compiler and linker flags.  A more detailed example follows:
588
589You may want or need to set compiler and linker flags for your bundled
590C code.  For example, say we have a header file, but instead of
591putting it in the C<ffi> directory we want to put it in a separate
592directory called C<include>.
593
594C<include/answer.h>:
595
596 #ifndef ANSWER_H
597 #define ANSWER_H
598
599 int answer(void);
600
601 #endif
602
603C<ffi/answer.c>:
604
605 #include <answer.h>
606
607 int
608 answer(void)
609 {
610   /* the answer to life the universe and everything */
611   return 42;
612 }
613
614C<lib/Answer.pm>:
615
616 package Answer;
617
618 use strict;
619 use warnings;
620 use FFI::Platypus 1.00;
621 use Exporter qw( import );
622
623 our @EXPORT = qw( answer );
624
625 my $ffi = FFI::Platypus->new( api => 1 );
626 $ffi->bundle;
627 $ffi->attach( answer => [] => 'int' );
628
629 1;
630
631If you try to use this module just as-is you will get an error, about
632not being able to find the header file.  Probably something like this:
633
634 ffi/answer.c:1:10: fatal error: 'answer.h' file not found
635
636So we put a C<answer.fbx> file in the C<ffi> directory.  (In case you
637are wondering FBX stands for "Ffi Build and file eXtensions should
638whenever possible be three characters long").  The name of the file
639can be anything so long as it ends in C<.fbx>, we just choose C<answer>
640here because that is the name of the project.
641
642C<ffi/answer.fbx>:
643
644 our $DIR;
645
646 return {
647   cflags => "-I/include",
648   source => "$DIR/*.c",
649 }
650
651The C<$DIR> variable is provided by the builder code.  It is the root
652of the distribution, and is helpful if you need a fully qualified path.
653In this case you could have also used C<ffi/*.c>.
654
655The script returns a hash reference which is passed into the L<FFI::Build>
656constructor, so you can use any of the options supported by that
657class.  Now we should be able to use our bundled module:
658
659 % perl -Ilib -MAnswer=answer -E 'say answer'
660 42
661
662=head2 Using bundled code with Alien.
663
664A useful technique is to use Platypus with L<Alien> technology.  The
665L<Alien> namespace is reserved for providing external non-Perl dependencies
666for CPAN modules.  The nominal L<Alien> module when installed looks
667for the library locally, and if it can't be found it fetches it from
668the internet, builds it, and installs it in a private directory so that
669it can be used by other CPAN modules.  For L<Aliens> that provide
670shared libraries, and that have simple interfaces that do not require
671additional C code you can easily just pass the shared libraries
672to Platypus directly.  For modules that require some bundled C code
673and an L<Alien> you have to link the L<Alien> library with your bundled
674code.  If the L<Alien> uses the L<Alien::Base> interface then all you have
675to do is give the name of the L<Alien> to L<FFI::Build>.
676
677For example, the C<bzip2> library provides an interface that requires
678the caller to allocate a C C<struct> and then pass it to its various
679functions.  The C<struct> is actually pretty simple and you could use
680L<FFI::C> or L<FFI::Platypus::Record>, but here is an example of how you
681would connect bundled C code with an L<Alien>.
682
683C<ffi/compress.c>:
684
685 #include <bzlib.h>
686 #include <stdlib.h>
687
688 int
689 bzip2__new(bz_stream **stream, int blockSize100k, int verbosity, int workFactor )
690 {
691   *stream = malloc(sizeof(bz_stream));
692   (*stream)->bzalloc = NULL;
693   (*stream)->bzfree  = NULL;
694   (*stream)->opaque  = NULL;
695
696   return BZ2_bzCompressInit(*stream, blockSize100k, verbosity, workFactor );
697 }
698
699C<lib/Bzip2.pm>:
700
701 package Bzip2;
702
703 use strict;
704 use warnings;
705 use FFI::Platypus 1.00;
706 use FFI::Platypus::Memory qw( free );
707
708 my $ffi = FFI::Platypus->new( api => 1 );
709 $ffi->bundle;
710
711 $ffi->mangler(sub {
712   my $name = shift;
713   $name =~ s/^/bzip2__/ unless $name =~ /^BZ2_/;
714   $name;
715 });
716
717 =head2 new
718
719  my $bzip2 = Bzip2->new($block_size_100k, $verbosity, $work_flow);
720
721 =cut
722
723 $ffi->attach( new => ['opaque*', 'int', 'int', 'int'] => 'int' => sub {
724   my $xsub = shift;
725   my $class = shift;
726   my $ptr;
727   my $ret = $xsub->(\$ptr, @_);
728   return bless \$ptr, $class;
729 });
730
731 $ffi->attach( [ BZ2_bzCompressEnd => 'DESTROY' ] => ['opaque'] => 'int' => sub {
732   my $xsub = shift;
733   my $self = shift;
734   my $ret = $xsub->($$self);
735   free $$self;
736 });
737
738 1;
739
740The C<.fbx> file that goes with this to make it work with L<Alien::Libbz2>
741is now pretty trivial:
742
743C<ffi/bz2.fbx>:
744
745 {
746   alien => ['Alien::Libbz2'],
747   source => ['ffi/*.c'],
748 };
749
750=head1 AUTHOR
751
752Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
753
754Contributors:
755
756Bakkiaraj Murugesan (bakkiaraj)
757
758Dylan Cali (calid)
759
760pipcet
761
762Zaki Mughal (zmughal)
763
764Fitz Elliott (felliott)
765
766Vickenty Fesunov (vyf)
767
768Gregor Herrmann (gregoa)
769
770Shlomi Fish (shlomif)
771
772Damyan Ivanov
773
774Ilya Pavlov (Ilya33)
775
776Petr Písař (ppisar)
777
778Mohammad S Anwar (MANWAR)
779
780Håkon Hægland (hakonhagland, HAKONH)
781
782Meredith (merrilymeredith, MHOWARD)
783
784Diab Jerius (DJERIUS)
785
786Eric Brine (IKEGAMI)
787
788szTheory
789
790José Joaquín Atria (JJATRIA)
791
792Pete Houston (openstrike, HOUSTON)
793
794=head1 COPYRIGHT AND LICENSE
795
796This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
797
798This is free software; you can redistribute it and/or modify it under
799the same terms as the Perl 5 programming language system itself.
800
801=cut
802