1#---------------------------------------------------------------------
2package Getopt::Mixed;
3#
4# Copyright 1995 Christopher J. Madsen
5#
6# Author: Christopher J. Madsen <ac608@yfn.ysu.edu>
7# Created: 1 Jan 1995
8# Version: $Revision: 1.8 $ ($Date: 1996/02/09 00:05:00 $)
9#    Note that RCS revision 1.23 => $Getopt::Mixed::VERSION = "1.023"
10#
11# This program is free software; you can redistribute it and/or modify
12# it under the terms of the GNU General Public License as published by
13# the Free Software Foundation; either version 2, or (at your option)
14# any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License
22# along with Perl; see the file COPYING.  If not, write to the
23# Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24#
25# Process both single-character and extended options
26#---------------------------------------------------------------------
27
28require 5.000;
29use Carp;
30
31require Exporter;
32@ISA = qw(Exporter);
33@EXPORT = ();
34@EXPORT_OK = qw(abortMsg getOptions nextOption);
35
36#=====================================================================
37# Package Global Variables:
38
39BEGIN
40{
41    # The permissible settings for $order:
42    $REQUIRE_ORDER   = 0;
43    $PERMUTE         = 1;
44    $RETURN_IN_ORDER = 2;
45
46    # Regular expressions:
47    $intRegexp   = '^[-+]?\d+$';               # Match an integer
48    $floatRegexp = '^[-+]?(\d*\.?\d+|\d+\.)$'; # Match a real number
49    $typeChars   = 'sif';                      # Match type characters
50
51    # Convert RCS revision number (must be main branch) to d.ddd format:
52    ' $Revision: 1.8 $ ' =~ / (\d+)\.(\d{1,3}) /
53        or die "Invalid version number";
54    $VERSION = sprintf("%d.%03d",$1,$2);
55} # end BEGIN
56
57#=====================================================================
58# Subroutines:
59#---------------------------------------------------------------------
60# Initialize the option processor:
61#
62# You should set any customization variables *after* calling init.
63#
64# For a description of option declarations, see the documentation at
65# the end of this file.
66#
67# Input:
68#   List of option declarations (separated by whitespace)
69#     If the first argument is entirely non-alphanumeric characters
70#     with no whitespace, it is the characters that start options.
71
72sub init
73{
74    undef %options;
75    my($opt,$type);
76
77    $ignoreCase  = 1;           # Ignore case by default
78    $optionStart = "-";         # Dash is the default option starter
79
80    # If the first argument is entirely non-alphanumeric characters
81    # with no whitespace, it is the desired value for $optionStart:
82    $optionStart = shift @_ if $_[0] =~ /^[^a-z0-9\s]+$/i;
83
84    foreach $group (@_) {
85        # Ignore case unless there are upper-case options:
86        $ignoreCase = 0 if $group =~ /[A-Z]/;
87        foreach $option (split(/\s+/,$group)) {
88            croak "Invalid option declaration `$option'"
89                unless $option =~ /^([^=:>]+)([=:][$typeChars]|>[^=:>]+)?$/o;
90            $opt  = $1;
91            $type = $2 || "";
92            if ($type =~ /^>(.*)$/) {
93                $type = $1;
94                croak "Invalid synonym `$option'"
95                    if (not defined $options{$type}
96                        or $options{$type} =~ /^[^:=]/);
97            } # end if synonym
98            $options{$opt} = $type;
99        } # end foreach option
100    } # end foreach group
101
102    # Handle POSIX compliancy:
103    if (defined $ENV{"POSIXLY_CORRECT"}) {
104        $order = $REQUIRE_ORDER;
105    } else {
106        $order = $PERMUTE;
107    }
108
109    $optionEnd = 0;
110    $badOption = \&badOption;
111    $checkArg  = \&checkArg;
112} # end init
113
114#---------------------------------------------------------------------
115# Clean up when we're done:
116#
117# This just releases the memory used by the %options hash.
118#
119# If 'help' was defined as an option, a new hash with just 'help' is
120# created, in case the program calls abortMsg.
121
122sub cleanup
123{
124    my $help = defined($options{'help'});
125    undef %options;
126    $options{'help'} = "" if $help;
127} # end cleanup
128
129#---------------------------------------------------------------------
130# Abort program with message:
131#
132# Prints program name and arguments to STDERR
133# If --help is an option, prints message saying 'Try --help'
134# Exits with code 1
135
136sub abortMsg
137{
138    my $name = $0;
139    $name =~ s|^.+[\\/]||;      # Remove any directories from name
140    print STDERR $name,": ",@_,"\n";
141    print STDERR "Try `$name --help' for more information.\n"
142        if defined $options{"help"};
143    exit 1;
144} # end abortMsg
145
146#---------------------------------------------------------------------
147# Standard function for handling bad options:
148#
149# Prints an error message and exits.
150#
151# You can override this by setting $Getopt::Mixed::badOption to a
152# function reference.
153#
154# Input:
155#   Index into @ARGV
156#   The option that caused the error
157#   An optional string describing the problem
158#     Currently, this can be
159#       undef        The option was not recognized
160#       'ambiguous'  The option could match several long options
161#
162# Note:
163#   The option has already been removed from @ARGV.  To put it back,
164#   you can say:
165#     splice(@ARGV,$_[0],0,$_[1]);
166#
167#   If your function returns, it should return whatever you want
168#   nextOption to return.
169
170sub badOption
171{
172    my ($index, $option, $problem) = @_;
173
174    $problem = 'unrecognized' unless $problem;
175
176    abortMsg("$problem option `$option'");
177} # end badOption
178
179#---------------------------------------------------------------------
180# Make sure we have the proper argument for this option:
181#
182# You can override this by setting $Getopt::Mixed::checkArg to a
183# function reference.
184#
185# Input:
186#   $i:       Position of argument in @ARGV
187#   $value:   The text appended to the option (undef if no text)
188#   $option:  The pretty name of the option (as the user typed it)
189#   $type:    The type of the option
190#
191# Returns:
192#   The value of the option's argument
193
194sub checkArg
195{
196    my ($i,$value,$option,$type) = @_;
197
198    abortMsg("option `$option' does not take an argument")
199        if (not $type and defined $value);
200
201    if ($type =~ /^=/) {
202        # An argument is required for this option:
203        $value = splice(@ARGV,$i,1) unless defined $value;
204        abortMsg("option `$option' requires an argument")
205            unless defined $value;
206    }
207
208    if ($type =~ /i$/) {
209        abortMsg("option `$option' requires integer argument")
210            if (defined $value and $value !~ /$intRegexp/o);
211    }
212    elsif ($type =~ /f$/) {
213        abortMsg("option `$option' requires numeric argument")
214            if (defined $value and $value !~ /$floatRegexp/o);
215    }
216    elsif ($type =~ /^[=:]/ and ref($checkType)) {
217        $value = &$checkType($i,$value,$option,$type);
218    }
219
220    $value = "" if not defined $value and $type =~ /^:/;
221
222    $value;
223} # end checkArg
224
225#---------------------------------------------------------------------
226# Find a match for an incomplete long option:
227#
228# Input:
229#   The option text to match
230#
231# Returns:
232#   The option that matched, or
233#   undef, if no option matched, or
234#   (undef, 'ambiguous'), if multiple options matched
235
236sub findMatch
237{
238    my $opt = shift;
239
240    $opt =~ s/-/[^-]*-/g;
241    $opt .= ".*";
242
243    my @matches = grep(/^$opt$/, keys %options);
244
245    return undef       if $#matches <  0;
246    return $matches[0] if $#matches == 0;
247
248    $opt = $matches[0];
249    $opt = $options{$opt} if $options{$opt} =~ /^[^=:]/;
250
251    foreach (@matches) {
252        return (undef, 'ambiguous')
253            unless $_ eq $opt or $options{$_} eq $opt;
254    }
255
256    $opt;
257} # end findMatch
258
259#---------------------------------------------------------------------
260# Return the next option:
261#
262# Returns a list of 3 elements:  (OPTION, VALUE, PRETTYNAME), where
263#   OPTION is the name of the option,
264#   VALUE is its argument, and
265#   PRETTYNAME is the option as the user entered it.
266# Returns the null list if there are no more options to process
267#
268# If $order is $RETURN_IN_ORDER, and this is a normal argument (not an
269# option), OPTION will be the null string, VALUE will be the argument,
270# and PRETTYNAME will be undefined.
271
272sub nextOption
273{
274    return () if $#ARGV < 0;    # No more arguments
275
276    if ($optionEnd) {
277        # We aren't processing any more options:
278        return ("", shift @ARGV) if $order == $RETURN_IN_ORDER;
279        return ();
280    }
281
282    # Find the next option:
283    my $i = 0;
284    while (length($ARGV[$i]) < 2 or
285           index($optionStart,substr($ARGV[$i],0,1)) < 0) {
286        return ()                if $order == $REQUIRE_ORDER;
287        return ("", shift @ARGV) if $order == $RETURN_IN_ORDER;
288        ++$i;
289        return () if $i > $#ARGV;
290    } # end while
291
292    # Process the option:
293    my($option,$opt,$value,$optType,$prettyOpt);
294    $option = $ARGV[$i];
295    if (substr($option,0,1) eq substr($option,1,1)) {
296        # If the option start character is repeated, it's a long option:
297        splice @ARGV,$i,1;
298        if (length($option) == 2) {
299            # A double dash by itself marks the end of the options:
300            $optionEnd = 1;     # Don't process any more options
301            return nextOption();
302        } # end if bare double dash
303        $opt = substr($option,2);
304        if ($opt =~ /^([^=]+)=(.*)$/) {
305            $opt = $1;
306            $value = $2;
307        } # end if option is followed by value
308        $opt =~ tr/A-Z/a-z/ if $ignoreCase;
309        $prettyOpt = substr($option,0,2) . $opt;
310        my $problem;
311        ($opt, $problem) = findMatch($opt)
312            unless defined $options{$opt} and length($opt) > 1;
313        return &$badOption($i,$option,$problem) unless $opt;
314        $optType = $options{$opt};
315        if ($optType =~ /^[^:=]/) {
316            $opt = $optType;
317            $optType = $options{$opt};
318        }
319        $value = &$checkArg($i,$value,$prettyOpt,$optType);
320    } # end if long option
321    else {
322        # It's a short option:
323        $opt = substr($option,1,1);
324        $opt =~ tr/A-Z/a-z/ if $ignoreCase;
325        return &$badOption($i,$option) unless defined $options{$opt};
326        $optType = $options{$opt};
327        if ($optType =~ /^[^:=]/) {
328            $opt = $optType;
329            $optType = $options{$opt};
330        }
331        if (length($option) == 2 or $optType) {
332            # This is the last option in the group, so remove the group:
333            splice(@ARGV,$i,1);
334        } else {
335            # Just remove this option from the group:
336            substr($ARGV[$i],1,1) = "";
337        }
338        if ($optType) {
339            $value = (length($option) > 2) ? substr($option,2) : undef;
340            $value =~ s/^=// if $value; # Allow either -d3 or -d=3
341        } # end if option takes an argument
342        $prettyOpt = substr($option,0,2);
343        $value = &$checkArg($i,$value,$prettyOpt,$optType);
344    } # end else short option
345    ($opt,$value,$prettyOpt);
346} # end nextOption
347
348#---------------------------------------------------------------------
349# Get options:
350#
351# Input:
352#   The same as for init()
353#   If no parameters are supplied, init() is NOT called.  This allows
354#   you to call init() yourself and then change the configuration
355#   variables.
356#
357# Output Variables:
358#   Sets $opt_X for each `-X' option encountered.
359#
360#   Note that if --apple is a synonym for -a, then --apple will cause
361#   $opt_a to be set, not $opt_apple.
362
363sub getOptions
364{
365    &init if $#_ >= 0;        # Pass arguments (if any) on to init
366
367    # If you want to use $RETURN_IN_ORDER, you have to call
368    # nextOption yourself; getOptions doesn't support it:
369    $order = $PERMUTE if $order == $RETURN_IN_ORDER;
370
371    my ($option,$value,$package);
372
373    $package = (caller)[0];
374
375    while (($option, $value) = nextOption()) {
376        $option =~ s/\W/_/g;    # Make a legal Perl identifier
377        $value = 1 unless defined $value;
378        eval("\$" . $package . '::opt_' . $option . ' = $value;');
379    } # end while
380
381    cleanup();
382} # end getOptions
383
384#=====================================================================
385# Package return value:
386
387$VERSION;
388
389__END__
390
391=head1 NAME
392
393Getopt::Mixed - getopt processing with both long and short options
394
395=head1 SYNOPSIS
396
397    use Getopt::Mixed;
398    Getopt::Mixed::getOptions(...option-descriptions...);
399    ...examine $opt_* variables...
400
401or
402
403    use Getopt::Mixed "nextOption";
404    Getopt::Mixed::init(...option-descriptions...);
405    while (($option, $value) = nextOption()) {
406        ...process option...
407    }
408    Getopt::Mixed::cleanup();
409
410=head1 DESCRIPTION
411
412This package is my response to the standard modules Getopt::Std and
413Getopt::Long.  C<Std> doesn't support long options, and C<Long>
414doesn't support short options.  I wanted both, since long options are
415easier to remember and short options are faster to type.
416
417This package is intended to be the "Getopt-to-end-all-Getop's".  It
418combines (I hope) flexibility and simplicity.  It supports both short
419options (introduced by C<->) and long options (introduced by C<-->).
420Short options which do not take an argument can be grouped together.
421Short options which do take an argument must be the last option in
422their group, because everything following the option will be
423considered to be its argument.
424
425There are two methods for using Getopt::Mixed:  the simple method and
426the flexible method.  Both methods use the same format for option
427descriptions.
428
429=head2 Option Descriptions
430
431The option-description arguments required by C<init> and C<getOptions>
432are strings composed of individual option descriptions.  Several
433option descriptions can appear in the same string if they are
434separated by whitespace.
435
436Each description consists of the option name and an optional trailing
437argument specifier.  Option names may consist of any characters but
438whitespace, C<=>, C<:>, and C<E<gt>>.
439
440Values for argument specifiers are:
441
442  <none>   option does not take an argument
443  =s :s    option takes a mandatory (=) or optional (:) string argument
444  =i :i    option takes a mandatory (=) or optional (:) integer argument
445  =f :f    option takes a mandatory (=) or optional (:) real number argument
446  >new     option is a synonym for option `new'
447
448The C<E<gt>> specifier is not really an argument specifier.  It
449defines an option as being a synonym for another option.  For example,
450"a=i apples>a" would define B<-a> as an option that requires an
451integer argument and B<--apples> as a synonym for B<-a>.  Only one
452level of synonyms is supported, and the root option must be listed
453first.  For example, "apples>a a=i" and "a=i apples>a oranges>apples"
454are illegal; use "a=i apples>a oranges>a" if that's what you want.
455
456For example, in the option description:
457     "a b=i c:s apple baker>b charlie:s"
458         -a and --apple do not take arguments
459         -b takes a mandatory integer argument
460         --baker is a synonym for -b
461         -c and --charlie take an optional string argument
462
463If the first argument to C<init> or C<getOptions> is entirely
464non-alphanumeric characters with no whitespace, it represents the
465characters which can begin options.
466
467=head2 User Interface
468
469From the user's perspective, short options are introduced by a dash
470(C<->) and long options are introduced by a double dash (C<-->).
471Short options may be combined ("-a -b" can be written "-ab"), but an
472option that takes an argument must be the last one in its group,
473because anything following it is considered part of the argument.  A
474double dash by itself marks the end of the options; all arguments
475following it are treated as normal arguments, not options.  A single
476dash by itself is treated as a normal argument, I<not> an option.
477
478Long options may be abbreviated.  An option B<--all-the-time> could be
479abbreviated B<--all>, B<--a--tim>, or even B<--a>.  Note that B<--time>
480would not work; the abbreviation must start at the beginning of the
481option name.  If an abbreviation is ambiguous, an error message will
482be printed.
483
484In the following examples, B<-i> and B<--int> take integer arguments,
485B<-f> and B<--float> take floating point arguments, and B<-s> and
486B<--string> take string arguments.  All other options do not take an
487argument.
488
489  -i24            -f24.5               -sHello
490  -i=24 --int=-27 -f=24.5 --float=0.27 -s=Hello --string=Hello
491
492If the argument is required, it can also be separated by whitespace:
493
494  -i 24 --int -27 -f 24.5 --float 0.27 -s Hello --string Hello
495
496Note that if the option is followed by C<=>, whatever follows the C<=>
497I<is> the argument, even if it's the null string.  In the example
498
499  -i= 24 -f= 24.5 -s= Hello
500
501B<-i> and B<-f> will cause an error, because the null string is not a
502number, but B<-s> is perfectly legal; its argument is the null string,
503not "Hello".
504
505Remember that optional arguments I<cannot> be separated from the
506option by whitespace.
507
508=head2 The Simple Method
509
510The simple method is
511
512    use Getopt::Mixed;
513    Getopt::Mixed::getOptions(...option-descriptions...);
514
515You then examine the C<$opt_*> variables to find out what options were
516specified and the C<@ARGV> array to see what arguments are left.
517
518If B<-a> is an option that doesn't take an argument, then C<$opt_a>
519will be set to 1 if the option is present, or left undefined if the
520option is not present.
521
522If B<-b> is an option that takes an argument, then C<$opt_b> will be
523set to the value of the argument if the option is present, or left
524undefined if the option is not present.  If the argument is optional
525but not supplied, C<$opt_b> will be set to the null string.
526
527Note that even if you specify that an option I<requires> a string
528argument, you can still get the null string (if the user specifically
529enters it).  If the option requires a numeric argument, you will never
530get the null string (because it isn't a number).
531
532When converting the option name to a Perl identifier, any non-word
533characters in the name will be converted to underscores (C<_>).
534
535If the same option occurs more than once, only the last occurrence
536will be recorded.  If that's not acceptable, you'll have to use the
537flexible method instead.
538
539=head2 The Flexible Method
540
541The flexible method is
542
543    use Getopt::Mixed "nextOption";
544    Getopt::Mixed::init(...option-descriptions...);
545    while (($option, $value, $pretty) = nextOption()) {
546        ...process option...
547    }
548    Getopt::Mixed::cleanup();
549
550This lets you process arguments one at a time.  You can then handle
551repeated options any way you want to.  It also lets you see option
552names with non-alphanumeric characters without any translation.  This
553is also the only method that lets you find out what order the options
554and other arguments were in.
555
556First, you call Getopt::Mixed::init with the option descriptions.
557Then, you keep calling nextOption until it returns an empty list.
558Finally, you call Getopt::Mixed::cleanup when you're done.  The
559remaining (non-option) arguments will be found in @ARGV.
560
561Each call to nextOption returns a list of the next option, its value,
562and the option as the user typed it.  The value will be undefined if
563the option does not take an argument.  The option is stripped of its
564starter (e.g., you get "a" and "foo", not "-a" or "--foo").  If you
565want to print an error message, use the third element, which does
566include the option starter.
567
568=head1 OTHER FUNCTIONS
569
570Getopt::Mixed provides one other function you can use.  C<abortMsg>
571prints its arguments on STDERR, plus your program's name and a
572newline.  It then exits with status 1.  For example, if F<foo.pl>
573calls C<abortMsg> like this:
574
575  Getopt::Mixed::abortMsg("Error");
576
577The output will be:
578
579  foo.pl: Error
580
581=head1 CUSTOMIZATION
582
583There are several customization variables you can set.  All of these
584variables should be set I<after> calling Getopt::Mixed::init and
585I<before> calling nextOption.
586
587If you set any of these variables, you I<must> check the version
588number first.  The easiest way to do this is like this:
589
590    use Getopt::Mixed 1.006;
591
592If you are using the simple method, and you want to set these
593variables, you'll need to call init before calling getOptions, like
594this:
595
596    use Getopt::Mixed 1.006;
597    Getopt::Mixed::init(...option-descriptions...);
598    ...set configuration variables...
599    Getopt::Mixed::getOptions();      # IMPORTANT: no parameters
600
601=over 4
602
603=item $order
604
605$order can be set to $REQUIRE_ORDER, $PERMUTE, or $RETURN_IN_ORDER.
606The default is $REQUIRE_ORDER if the environment variable
607POSIXLY_CORRECT has been set, $PERMUTE otherwise.
608
609$REQUIRE_ORDER means that no options can follow the first argument
610which isn't an option.
611
612$PERMUTE means that all options are treated as if they preceded all
613other arguments.
614
615$RETURN_IN_ORDER means that all arguments maintain their ordering.
616When nextOption is called, and the next argument is not an option, it
617returns the null string as the option and the argument as the value.
618nextOption never returns the null list until all the arguments have
619been processed.
620
621=item $ignoreCase
622
623Ignore case when matching options.  Default is 1 unless the option
624descriptions contain an upper-case letter.
625
626=item $optionStart
627
628A string of characters that can start options.  Default is "-".
629
630=item $badOption
631
632A reference to a function that is called when an unrecognized option
633is encountered.  The function receives three arguments.  $_[0] is the
634position in @ARGV where the option came from.  $_[1] is the option as
635the user typed it (including the option start character).  $_[2] is
636either undef or a string describing the reason the option was not
637recognized (Currently, the only possible value is 'ambiguous', for a
638long option with several possible matches).  The option has already
639been removed from @ARGV.  To put it back, you can say:
640
641    splice(@ARGV,$_[0],0,$_[1]);
642
643The function can do anything you want to @ARGV.  It should return
644whatever you want nextOption to return.
645
646The default is a function that prints an error message and exits the
647program.
648
649=item $checkArg
650
651A reference to a function that is called to make sure the argument
652type is correct.  The function receives four arguments.  $_[0] is the
653position in @ARGV where the option came from.  $_[1] is the text
654following the option, or undefined if there was no text following the
655option.  $_[2] is the name of the option as the user typed it
656(including the option start character), suitable for error messages.
657$_[3] is the argument type specifier.
658
659The function can do anything you want to @ARGV.  It should return
660the value for this option.
661
662The default is a function that prints an error message and exits the
663program if the argument is not the right type for the option.  You can
664also adjust the behavior of the default function by changing
665$intRegexp or $floatRegexp.
666
667=item $intRegexp
668
669A regular expression that matches an integer.  Default is
670'^[-+]?\d+$', which matches a string of digits preceded by an
671optional sign.  Unlike the other configuration variables, this cannot
672be changed after nextOption is called, because the pattern is compiled
673only once.
674
675=item $floatRegexp
676
677A regular expression that matches a floating point number.  Default is
678'^[-+]?(\d*\.?\d+|\d+\.)$', which matches the following formats:
679"123", "123.", "123.45", and ".123" (plus an optional sign).  It does
680not match exponential notation.  Unlike the other configuration
681variables, this cannot be changed after nextOption is called, because
682the pattern is compiled only once.
683
684=item $typeChars
685
686A string of the characters which are legal argument types.  The
687default is 'sif', for String, Integer, and Floating point arguments.
688The string should consist only of letters.  Upper case letters are
689discouraged, since this will hamper the case-folding of options.  If
690you change this, you should set $checkType to a function that will
691check arguments of your new type.  Unlike the other configuration
692variables, this must be set I<before> calling init(), and cannot be
693changed afterwards.
694
695=item $checkType
696
697If you add new types to $typeChars, you should set this to a function
698which will check arguments of the new types.
699
700=back
701
702=head1 BUGS
703
704=over 4
705
706=item *
707
708This document should be expanded.
709
710=item *
711
712A long option must be at least two characters long.  Sorry.
713
714=item *
715
716The C<!> argument specifier of Getopt::Long is not supported, but you
717could have options B<--foo> and B<--nofoo> and then do something like:
718
719    $opt_foo = 0 if $opt_nofoo;
720
721=item *
722
723The C<@> argument specifier of Getopt::Long is not supported.  If you
724want your values pushed into an array, you'll have to use nextOption
725and do it yourself.
726
727=back
728
729=head1 LICENSE
730
731Getopt::Mixed is distributed under the terms of the GNU General Public
732License as published by the Free Software Foundation; either version
7332, or (at your option) any later version.
734
735This means it is distributed in the hope that it will be useful, but
736I<without any warranty>; without even the implied warranty of
737I<merchantability> or I<fitness for a particular purpose>.  See the
738GNU General Public License for more details.
739
740Since Perl scripts are only compiled at runtime, and simply calling
741Getopt::Mixed does I<not> bring your program under the GPL, the only
742real restriction is that you can't use Getopt::Mixed in an
743binary-only distribution produced with C<dump> (unless you also
744provide source code).
745
746=head1 AUTHOR
747
748Christopher J. Madsen E<lt>F<ac608@yfn.ysu.edu>E<gt>
749
750Thanks are also due to Andreas Koenig for helping Getopt::Mixed
751conform to the standards for Perl modules and for answering a bunch of
752questions.  Any remaining deficiencies are my fault.
753
754=cut
755