File Coverage

blib/lib/Applify.pm
Criterion Covered Total %
statement 283 291 97.2
branch 134 154 87.0
condition 26 36 72.2
subroutine 47 47 100.0
pod 10 10 100.0
total 500 538 92.9


line stmt bran cond sub pod time code
1             package Applify;
2 18     18   54178 use strict;
  18         57  
  18         482  
3 17     17   109 use warnings;
  17         37  
  17         911  
4 16     16   96 use Carp ();
  16         39  
  16         614  
5 14     14   78 use File::Basename ();
  14         29  
  14         599  
6              
7 14 50   14   912 use constant SUB_NAME_IS_AVAILABLE => $INC{'App/FatPacker/Trace.pm'}
  14 50       10262  
  0         0  
  0         0  
8             ? 0 # this will be true when running under "fatpack"
9 14     14   93 : eval 'use Sub::Name; 1' ? 1 : 0;
  14         37  
10              
11             our $VERSION = '0.14';
12             our $PERLDOC = 'perldoc';
13             our $SUBCMD_PREFIX = "command";
14             my $ANON = 1;
15              
16             sub app {
17 43     43 1 6520 my $self = shift;
18 43   66     227 my $code = $self->{app} ||= shift;
19 43         138 my $parser = $self->_option_parser;
20 43         2840 my (%options, @options_spec, $application_class, $app);
21              
22             # has to be run before calculating option spec.
23             # cannot do ->can() as application_class isn't created yet.
24 43 100       185 if ($self->_subcommand_activate($ARGV[0])) { shift @ARGV; }
  3         11  
25 42         108 for my $option (@{$self->{options}}) {
  42         159  
26 115         614 my $switch = $self->_attr_to_option($option->{name});
27 115         305 push @options_spec, $self->_calculate_option_spec($option);
28 115 50       462 $options{$switch} = $option->{default} if exists $option->{default};
29 115 100       373 $options{$switch} = [@{$options{$switch}}] if ref($options{$switch}) eq 'ARRAY';
  24         72  
30             }
31              
32 42 50       190 unless ($parser->getoptions(\%options, @options_spec, $self->_default_options)) {
33 0         0 $self->_exit(1);
34             }
35              
36 42 100       26680 if ($options{help}) {
    100          
    100          
37 1         9 $self->print_help;
38 1         6 $self->_exit('help');
39             }
40             elsif ($options{man}) {
41 1         5 system $PERLDOC => $self->documentation;
42 1         66 $self->_exit($? >> 8);
43             }
44             elsif ($options{version}) {
45 1         10 $self->print_version;
46 1         10 $self->_exit('version');
47             }
48              
49 42   66     278 $application_class = $self->{application_class} ||= $self->_generate_application_class($code);
50             $app = $application_class->new(
51 42         204 {map { my $k = $self->_option_to_attr($_); $k => $self->_upgrade($k, $options{$_}) } keys %options});
  118         388  
  118         587  
52              
53 42 50       607 return $app if defined wantarray; # $app = do $script_file;
54 0         0 $self->_exit($app->run(@ARGV));
55             }
56              
57             sub documentation {
58 67 100   67 1 71097 return $_[0]->{documentation} if @_ == 1;
59 9 100       56 $_[0]->{documentation} = $_[1] or die 'Usage: documentation $file|$module_name;';
60 8         86 return $_[0];
61             }
62              
63             sub extends {
64 4     4 1 11 my $self = shift;
65 4         31 $self->{extends} = [@_];
66 4         102 return $self;
67             }
68              
69             sub import {
70 21     21   14150 my ($class, %args) = @_;
71 21         281 my @caller = caller;
72 21         141 my $self = $class->new({caller => \@caller});
73 21         80 my $ns = $caller[0] . '::';
74 21         44 my %export;
75              
76 21         163 strict->import;
77 21         318 warnings->import;
78              
79 21         138 $self->{skip_subs} = {app => 1, option => 1, version => 1, documentation => 1, extends => 1, subcommand => 1};
80              
81 14     14   105 no strict 'refs';
  14         29  
  14         1817  
82 21         1781 for my $name (keys %$ns) {
83 5118         9777 $self->{'skip_subs'}{$name} = 1;
84             }
85              
86 21         301 for my $k (qw(app extends option version documentation subcommand)) {
87 126   66     447 my $name = $args{$k} // $k;
88 126 50       262 next unless $name;
89 126 50       528 $export{$k} = $name =~ /::/ ? $name : "$caller[0]\::$name";
90             }
91              
92 14     14   98 no warnings 'redefine'; # need to allow redefine when loading a new app
  14         26  
  14         24412  
93 21     22   125 *{$export{app}} = sub (&) { $self->app(@_) };
  21         169  
  22         299  
94 21     30   86 *{$export{option}} = sub { $self->option(@_) };
  21         105  
  30         185  
95 21     4   342 *{$export{version}} = sub { $self->version(@_) };
  21         126  
  4         24  
96 21     6   84 *{$export{documentation}} = sub { $self->documentation(@_) };
  21         119  
  6         45  
97 21     4   77 *{$export{extends}} = sub { $self->extends(@_) };
  21         87  
  4         31  
98 21     8   77 *{$export{subcommand}} = sub { $self->subcommand(@_) };
  21         2607  
  8         66  
99             }
100              
101             sub new {
102 21     21 1 72 my ($class, $args) = @_;
103 21         66 my $self = bless $args, $class;
104              
105 21   50     273 $self->{options} ||= [];
106 21 50       86 $self->{caller} or die 'Usage: $self->new({ caller => [...], ... })';
107              
108 21         63 return $self;
109             }
110              
111             sub option {
112 39     39 1 4898 my $self = shift;
113 39 100       129 my $type = shift or die 'Usage: option $type => ...';
114 38 100       113 my $name = shift or die 'Usage: option $type => $name => ...';
115 37 100       117 my $documentation = shift or die 'Usage: option $type => $name => $documentation, ...';
116 36         69 my ($default, %args);
117              
118 36 100       122 if (@_ % 2) {
119 6         14 $default = shift;
120 6         19 %args = @_;
121             }
122             else {
123 30         77 %args = @_;
124             }
125              
126 36 100 66     120 if ($args{alias} and !ref $args{alias}) {
127 1         4 $args{alias} = [$args{alias}];
128             }
129              
130 36         62 push @{$self->{options}}, {default => $default, %args, type => $type, name => $name, documentation => $documentation};
  36         212  
131              
132 36         397 return $self;
133             }
134              
135 4     4 1 29 sub options { $_[0]->{options} }
136              
137             sub print_help {
138 6     6 1 5635 my $self = shift;
139 6         16 my @options = @{$self->{options}};
  6         26  
140 6         16 my $width = 0;
141              
142 6         28 push @options, {name => ''};
143 6         30 push @options, {name => 'help', documentation => 'Print this help text'};
144 6 100       28 push @options, {name => 'man', documentation => 'Display manual for this application'} if $self->documentation;
145 6 100       26 push @options, {name => 'version', documentation => 'Print application name and version'} if $self->version;
146 6         26 push @options, {name => ''};
147              
148 6         33 $self->_print_synopsis;
149              
150             OPTION:
151 6         22 for my $option (@options) {
152 40         83 my $length = length $option->{name};
153 40 100       120 $width = $length if $width < $length;
154             }
155              
156 6         66 print "Usage:\n";
157              
158 6 100       20 if (%{$self->{subcommands} || {}}) {
  6 100       49  
159 2         4 my $subcmds = [sort { $a->{name} cmp $b->{name} } values %{$self->{subcommands}}];
  2         16  
  2         18  
160 2         9 my ($width) = sort { $b <=> $a } map { length($_->{name}) } @$subcmds;
  2         7  
  4         16  
161 2         137 print "\n ", File::Basename::basename($0), " [command] [options]\n";
162 2         9 print "\ncommands:\n";
163 2         13 printf(" %-${width}s %s\n", @{$_}{'name', 'desc'}) for @$subcmds;
  4         31  
164 2         9 print "\noptions:\n";
165             }
166              
167             OPTION:
168 6         24 for my $option (@options) {
169 40 100       115 my $name = $self->_attr_to_option($option->{name}) or do { print "\n"; next OPTION };
  12         36  
  12         34  
170              
171             printf(
172             " %s %2s%-${width}s %s\n",
173             $option->{required} ? '*' : ' ',
174             length($name) > 1 ? '--' : '-',
175             $name, $option->{documentation},
176 28 100       242 );
    100          
177             }
178              
179 6         58 return $self;
180             }
181              
182             sub print_version {
183 4     4 1 877 my $self = shift;
184 4 100       13 my $version = $self->version or die 'Cannot print version without version()';
185              
186 3 100       27 unless ($version =~ m!^\d!) {
187 1 50       69 eval "require $version; 1" or die "Could not load $version: $@";
188 1         20 $version = $version->VERSION;
189             }
190              
191 3         231 printf "%s version %s\n", File::Basename::basename($0), $version;
192             }
193              
194             sub subcommand {
195 23     23 1 74 my ($self, $name) = (shift, shift);
196 23 100       148 return $self->{subcommand} unless @_;
197 8         40 $self->{subcommands}{$name} = {name => $name, desc => $_[0], adaptation => $_[1]};
198 8         100 return $self;
199             }
200              
201             sub version {
202 63 100   63 1 2349 return $_[0]->{version} if @_ == 1;
203 8 100       39 $_[0]->{version} = $_[1] or die 'Usage: version $module_name|$num;';
204 7         59 return $_[0];
205             }
206              
207             sub _attr_to_option {
208 282 100   282   823 local $_ = $_[1] or return;
209 270         728 s!_!-!g;
210 270         705 $_;
211             }
212              
213             sub _calculate_option_spec {
214 126     126   1027 my ($self, $option) = @_;
215 126         294 my $spec = $self->_attr_to_option($option->{name});
216              
217 126 100       744 if (ref $option->{alias} eq 'ARRAY') {
218 2         6 $spec .= join '|', '', @{$option->{alias}};
  2         9  
219             }
220              
221 126 100       1044 if ($option->{type} =~ /^(?:bool|flag)/i) { $spec .= '!' }
  7 100       21  
    100          
    100          
    100          
    100          
    100          
222 1         4 elsif ($option->{type} =~ /^inc/) { $spec .= '+' }
223 55         145 elsif ($option->{type} =~ /^str/) { $spec .= '=s' }
224 1         3 elsif ($option->{type} =~ /^int/i) { $spec .= '=i' }
225 3         8 elsif ($option->{type} =~ /^num/i) { $spec .= '=f' }
226 48         593 elsif ($option->{type} =~ /^file/) { $spec .= '=s' } # TODO
227 10         30 elsif ($option->{type} =~ /^dir/) { $spec .= '=s' } # TODO
228 1         12 else { die 'Usage: option {bool|flag|inc|str|int|num|file|dir} ...' }
229              
230 125 100       344 if (my $n_of = $option->{n_of}) {
231 26 100       68 $spec .= $n_of eq '@' ? $n_of : "{$n_of}";
232             $option->{default}
233 26 50 66     118 and ref $option->{default} ne 'ARRAY'
234             and die 'Usage option ... default => [Need to be an array ref]';
235 26   100     84 $option->{default} ||= [];
236             }
237              
238 125         395 return $spec;
239             }
240              
241             sub _default_options {
242 44     44   2348 my $self = shift;
243 44         84 my @default;
244              
245 44         109 push @default, 'help';
246 44 100       138 push @default, 'man' if $self->documentation;
247 44 100       166 push @default, 'version' if $self->version;
248              
249 44         235 return @default;
250             }
251              
252             sub _exit {
253 4     4   21 my ($self, $reason) = @_;
254 4 100       47 exit 0 unless ($reason =~ /^\d+$/); # may change without warning...
255 4         31 exit $reason;
256             }
257              
258             sub _generate_application_class {
259 22     22   85 my ($self, $code) = @_;
260 22         84 my $application_class = $self->{caller}[1];
261 22   100     142 my $extends = $self->{extends} || [];
262 22         62 my ($meta, @required);
263              
264 22         235 $application_class =~ s!\W!_!g;
265 22         156 $application_class = join '::', ref($self), "__ANON__${ANON}__", $application_class;
266 22         64 $ANON++;
267              
268 13 50   13   124 eval qq[
  13         31  
  13         60843  
  22         2067  
269             package $application_class;
270             use base qw(@$extends);
271             1;
272             ] or die "Failed to generate application class: $@";
273              
274             {
275 14     14   127 no strict 'refs';
  14         37  
  14         14099  
  22         70  
276 42     42   145 _sub("$application_class\::new" => sub { my $class = shift; bless shift, $class })
  42         124  
277 22 50       256 unless grep { $_->can('new') } @$extends;
  3         76  
278 22     27   183 _sub("$application_class\::_script" => sub {$self});
  27         3842  
279             _sub(
280             "$application_class\::run" => sub {
281 7     7   2682 my ($app, @extra) = @_;
282              
283 7 100       36 if (@required = grep { not defined $app->{$_} } @required) {
  2         21  
284 1         5 my $required = join ', ', map { '--' . $self->_attr_to_option($_) } @required;
  1         6  
285 1         5 $app->_script->print_help;
286 1         8 die "Required attribute missing: $required\n";
287             }
288              
289             # get subcommand code - which should have a registered subroutine
290             # or fallback to app {} block.
291 6   66     25 $code = $app->_script->_subcommand_code($app) || $code;
292 6         172 return $app->$code(@extra);
293             }
294 22         211 );
295              
296 22         92 for ('app', $self->{caller}[0]) {
297 44         87 my $ns = \%{"$_\::"};
  44         172  
298              
299 44         1545 for my $name (keys %$ns) {
300 5580 100       12289 $self->{skip_subs}{$name} and next;
301 129 100       239 my $code = eval { ref $ns->{$name} eq 'CODE' ? $ns->{$name} : *{$ns->{$name}}{CODE} } or next;
  129 100       401  
  119         703  
302 14         68 my $fqn = join '::', $application_class, $name;
303 14         69 _sub($fqn => $code);
304 14         62 delete $ns->{$name}; # may be a bit too destructive?
305             }
306             }
307              
308 22 50 33     356 $meta = $application_class->meta if $application_class->isa('Moose::Object') and $application_class->can('meta');
309              
310 22         67 for my $option (@{$self->{options}}) {
  22         89  
311 32         77 my $name = $option->{name};
312 32         97 my $fqn = join '::', $application_class, $name;
313 32 50       85 if ($meta) {
314 0         0 $meta->add_attribute($name => {is => 'rw', default => $option->{default}});
315             }
316             else {
317 32 100   63   166 _sub($fqn => sub { @_ == 2 and $_[0]->{$name} = $_[1]; $_[0]->{$name} });
  63         4352  
  63         391  
318             }
319 32 100       158 push @required, $name if $option->{required};
320             }
321             }
322              
323 22         129 return $application_class;
324             }
325              
326             sub _load_class {
327 62 100   62   378 my $class = shift or return undef;
328 26 100       745 return $class if $class->can('new');
329 1 50       134 return eval "require $class; 1" ? $class : "";
330             }
331              
332             sub _option_parser {
333 45   66 45   178 $_[0]->{_option_parser} ||= do {
334 21         7606 require Getopt::Long;
335 21         161809 Getopt::Long::Parser->new(config => [qw(no_auto_help no_auto_version pass_through)]);
336             };
337             }
338              
339             sub _option_to_attr {
340 118 50   118   324 local $_ = $_[1] or return;
341 118         367 s!-!_!g;
342 118         456 $_;
343             }
344              
345             sub _print_synopsis {
346 6     6   27 my $self = shift;
347 6 100       20 my $documentation = $self->documentation or return;
348 3         8 my $print;
349              
350 3 50       264 unless (-e $documentation) {
351 0 0       0 eval "use $documentation; 1" or die "Could not load $documentation: $@";
352 0         0 $documentation =~ s!::!/!g;
353 0         0 $documentation = $INC{"$documentation.pm"};
354             }
355              
356 3 50       113 open my $FH, '<', $documentation or die "Failed to read synopsis from $documentation: $@";
357              
358 3         74 while (<$FH>) {
359 175 100 100     442 last if $print and /^=(?:cut|head1)/;
360 173 100       339 print if $print;
361 173 100       572 $print = 1 if /^=head1 SYNOPSIS/;
362             }
363             }
364              
365             sub _sub {
366 112     112   279 my ($fqn, $code) = @_;
367 14     14   123 no strict 'refs';
  14         39  
  14         2376  
368 112 100       674 return if *$fqn{CODE};
369 111         422 *$fqn = SUB_NAME_IS_AVAILABLE ? Sub::Name::subname($fqn, $code) : $code;
370             }
371              
372             sub _subcommand_activate {
373 43     43   148 my ($self, $name) = @_;
374 43 100 100     391 return undef unless $name and $name =~ /^\w+/;
375 5 100       28 return undef unless $self->{subcommands}{$name};
376 4         15 $self->{subcommand} = $name;
377             {
378 14     14   107 no warnings 'redefine';
  14         32  
  14         7029  
  4         9  
379             local *Applify::app = sub {
380 1     1   241 Carp::confess(
381             "Looks like you have a typo in your script! Cannot have app{} inside a subcommand options block.");
382 4         27 };
383 4         107 $self->{subcommands}{$name}{adaptation}->($self);
384             }
385 3         16 return 1;
386             }
387              
388             sub _subcommand_code {
389 10     10   819 my ($self, $app, $name) = (shift, shift);
390 10 100       41 return undef unless $name = $self->subcommand;
391 4         51 return $app->can("${SUBCMD_PREFIX}_${name}");
392             }
393              
394             sub _upgrade {
395 118     118   297 my ($self, $name, $input) = @_;
396 118 100       436 return $input unless defined $input;
397              
398 62         132 my ($option) = grep { $_->{name} eq $name } @{$self->{options}};
  305         858  
  62         147  
399 62 100       219 return $input unless my $class = _load_class($option->{isa});
400 25 100       146 return ref $input eq 'ARRAY' ? [map { $class->new($_) } @$input] : $class->new($input);
  5         26  
401             }
402              
403             1;
404              
405             =encoding utf8
406              
407             =head1 NAME
408              
409             Applify - Write object oriented scripts with ease
410              
411             =head1 VERSION
412              
413             0.14
414              
415             =head1 DESCRIPTION
416              
417             This module should keep all the noise away and let you write scripts
418             very easily. These scripts can even be unittested even though they
419             are define directly in the script file and not in a module.
420              
421             =head1 SYNOPSIS
422              
423             #!/usr/bin/perl
424             use Applify;
425              
426             option file => input_file => 'File to read from';
427             option dir => output_dir => 'Directory to write files to';
428             option flag => dry_run => 'Use --no-dry-run to actually do something', 1;
429              
430             documentation __FILE__;
431             version 1.23;
432              
433             sub generate_exit_value {
434             return int rand 100;
435             }
436              
437             app {
438             my($self, @extra) = @_;
439             my $exit_value = 0;
440              
441             print "Extra arguments: @extra\n" if(@extra);
442             print "Will read from: ", $self->input_file, "\n";
443             print "Will write files to: ", $self->output_dir, "\n";
444              
445             if($self->dry_run) {
446             die 'Will not run script';
447             }
448              
449             return $self->generate_exit_value;
450             };
451              
452             =head1 APPLICATION CLASS
453              
454             This module will generate an application class, which C<$self> inside the
455             L block refere to. This class will have:
456              
457             =over 4
458              
459             =item * new()
460              
461             An object constructor. This method will not be auto generated if any of
462             the classes given to L has the method C.
463              
464             =item * run()
465              
466             This method is basically the code block given to L.
467              
468             =item * Other methods
469              
470             Other methods defined in the script file will be accesible from C<$self>
471             inside C.
472              
473             =item * _script()
474              
475             This is an accessor which return the L object which
476             is refered to as C<$self> in this documentation.
477              
478             NOTE: This accessor starts with an underscore to prevent conflicts
479             with L.
480              
481             =item * Other accessors
482              
483             Any L (application switch) will be available as an accessor on the
484             application object.
485              
486             =back
487              
488             =head1 EXPORTED FUNCTIONS
489              
490             =head2 option
491              
492             option $type => $name => $documentation;
493             option $type => $name => $documentation, $default;
494             option $type => $name => $documentation, $default, @args;
495             option $type => $name => $documentation, @args;
496              
497             This function is used to define options which can be given to this
498             application. See L for example code. This function can also be
499             called as a method on C<$self>.
500              
501             =over 4
502              
503             =item * $type
504              
505             Used to define value types for this input.
506              
507             =over 4
508              
509             =item bool, flag
510              
511             =item inc
512              
513             =item str
514              
515             =item int
516              
517             =item num
518              
519             =item file (TODO)
520              
521             =item dir (TODO)
522              
523             =back
524              
525             =item * $name
526              
527             The name of an application switch. This name will also be used as
528             accessor name inside the application. Example:
529              
530             # define an application switch:
531             option file => some_file => '...';
532              
533             # call the application from command line:
534             > myapp.pl --some-file /foo/bar
535              
536             # run the application code:
537             app {
538             my $self = shift;
539             print $self->some_file # prints "/foo/bar"
540             return 0;
541             };
542              
543             =item * C<$documentation>
544              
545             Used as description text when printing the usage text.
546              
547             =item * C<@args>
548              
549             =over 4
550              
551             =item * C
552              
553             The script will not start if a required field is omitted.
554              
555             =item * C
556              
557             Allow the option to hold a list of values. Examples: "@", "4", "1,3".
558             See L for details.
559              
560             =item * C
561              
562             Specify the class an option should be instantiated as. Example:
563              
564             option file => output => "output file", isa => "Mojo::File";
565              
566             The C attribute will then later return an object of L,
567             instead of just a plain string.
568              
569             =item * Other
570              
571             Any other L attribute argument may/will be supported in
572             future release.
573              
574             =back
575              
576             =back
577              
578             =head2 documentation
579              
580             documentation __FILE__; # current file
581             documentation '/path/to/file';
582             documentation 'Some::Module';
583              
584             Specifies where to retrieve documentaion from when giving the C<--man>
585             switch to your script.
586              
587             =head2 version
588              
589             version 'Some::Module';
590             version $num;
591              
592             Specifies where to retrieve the version number from when giving the
593             C<--version> switch to your script.
594              
595             =head2 extends
596              
597             extends @classes;
598              
599             Specify which classes this application should inherit from. These
600             classes can be L based.
601              
602             =head2 subcommand
603              
604             subcommand list => 'provide a listing objects' => sub {
605             option flag => long => 'long listing';
606             option flag => recursive => 'recursively list objects';
607             };
608              
609             subcommand create => 'create a new object' => sub {
610             option str => name => 'name of new object', required => 1;
611             option str => description => 'description for the object', required => 1;
612             };
613              
614             sub command_create {
615             my ($self, @extra) = @_;
616             ## do creating
617             return 0;
618             }
619              
620             sub command_list {
621             my ($self, @extra) = @_;
622             ## do listing
623             return 0;
624             }
625              
626             app {
627             my ($self, @extra) = @_;
628             ## fallback when no command given.
629             $self->_script->print_help;
630             return 0;
631             };
632              
633             This function allows for creating multiple related sub commands within the same
634             script in a similar fashion to C. The L, L and
635             L exported functions may sensibly be called within the
636             subroutine. Calling the function with no arguments will return the running
637             subcommand, i.e. a valid C<$ARGV[0]>. Non valid values for the subcommand given
638             on the command line will result in the help being displayed.
639              
640             =head2 app
641              
642             app CODE;
643              
644             This function will define the code block which is called when the application
645             is started. See L for example code. This function can also be
646             called as a method on C<$self>.
647              
648             IMPORTANT: This function must be the last function called in the script file
649             for unittests to work. Reason for this is that this function runs the
650             application in void context (started from command line), but returns the
651             application object in list/scalar context (from L).
652              
653             =head1 ATTRIBUTES
654              
655             =head2 options
656              
657             $array_ref = $self->options;
658              
659             Holds the application options given to L.
660              
661             =head1 METHODS
662              
663             =head2 new
664              
665             $self = $class->new({ options => $array_ref, ... });
666              
667             Object constructor. Creates a new object representing the script meta
668             information.
669              
670             =head2 print_help
671              
672             Will print L to selected filehandle (STDOUT by default) in
673             a normalized matter. Example:
674              
675             Usage:
676             --foo Foo does this and that
677             * --bar Bar does something else
678              
679             --help Print this help text
680             --man Display manual for this application
681             --version Print application name and version
682              
683             =head2 print_version
684              
685             Will print L to selected filehandle (STDOUT by default) in
686             a normalized matter. Example:
687              
688             some-script.pl version 1.23
689              
690             =head2 import
691              
692             Will export the functions listed under L. The functions
693             will act on a L object created by this method.
694              
695             =head1 COPYRIGHT & LICENSE
696              
697             This library is free software. You can redistribute it and/or modify
698             it under the same terms as Perl itself.
699              
700             =head1 AUTHORS
701              
702             Jan Henning Thorsen - C
703              
704             Roy Storey - C
705              
706             =cut