From: Alan Stebbens <aks%anywhere@hub.ucsb.edu>
Subject: ParseArgs options parser w/bells & whistles
Reply-To: Alan Stebbens <aks@hub.ucsb.edu>
Organization: The Internet
Date: Tue, 26 Jan 1993 19:48:14 GMT

The code in the archive below is yet another options parser, but this
one takes care of any requirement I have ever used in any program, C,
Perl, or whatever.

I've been using it for a while, but hadn't taken the time to
write the man page.  Well, using "wrapman", it now has an embedded man
page, and a Makefile to install it in your favorite place.

Below, is the first page of the man page, so you can see if you care to
even unpack the "sh" archive which follows it.

Feel free to submit bug fixes, or suggest improvements.

Enjoy.

-- Alan Stebbens, UCSB, Santa Barbara <aks@hub.ucsb.edu>
"If all you have is a hammer, then everything looks like a nail."

============================= cut here ===================================

     PARSEARGS(3)            (January 14, 1993)           PARSEARGS(3)

     NAME
          parseargs.pl - Parse the argument vector

     SYNOPSIS
          require 'parseargs.pl'

          &ParseOpts('FLAG1 FLAG2 ... FLAGn');

          &ParseArgs('FLAG1', 'ACTION1', 'FLAG2', 'ACTION2', ...);

          &ParseArgs(<<'EOF');
             FLAG1   ACTION1
             FLAG2   ACTION2
             ...
             FLAGn   ACTIONn
          EOF

     DESCRIPTION
          These subroutines parse the current command argument vector,
          @ARGV,  for  switches  which match defined FLAG expressions,
          evaluating associated ACTION expressions.

          The argument parsing stops on the first  unhandled  non-flag
          argument.   An  error  is  reported  on  any  unhandled flag
          arguments (-flag).

          There are two subroutines: ParseArgs,  and  ParseOpts.   The
          former  is more general and allows complete control over the
          flag strings and  the  associated  actions.   The  ParseOpts
          routine has a simpler interface, with useful default actions
          for the specified flag strings.

          In both routines, each FLAG is matched against  the  current
          argument,  and,  if  successful, the corresponding ACTION is
          evaluated, followed by a next.  FLAG can be a  general  Perl
          expression,  or  it  can be one of a special syntax (defined
          below), which is  used  to  create  a  Perl  expression  for
          testing.

          Any argument not matching any of the test expressions causes
          an  error.   When  an  error occurs, if the string $Usage is
          defined, it will be printed after the error message.

          ACTION can be a general  Perl  expression,  except  that  no
          embedded newlines are permitted.  An empty string, or action
          of `#' is considered to be a null action, for  which  useful
          defaults are supplied.

          Within ACTION, the string `ARG' will be substituted with the
          appropriate  Perl expression which evaluates to the argument
          of the switch which selected the action.

     Page 1                                          (printed 1/26/93)

============================= cut here ===================================

parseargs.shar
============================= cut here ===================================
#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 01/26/1993 19:43 UTC by aks@anywhere
# Source directory /fs.real/anywhere/home/aks/src/Lang/perl/Lib/parseargs
#
# existing files will NOT be overwritten unless -c is specified
#
#                                                                          
#                                                                          
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#  19844 -r--r--r-- parseargs.pl
#    534 -r--r--r-- Makefile
#    723 -rwxr-xr-x test.pl
#    113 -rwxr-xr-x test1
#
if test -r _shar_seq_.tmp; then
	echo 'Must unpack archives in sequence!'
	echo Please unpack part `cat _shar_seq_.tmp` next
	exit 1
fi
# ============= parseargs.pl ==============
if test -f 'parseargs.pl' -a X"$1" != X"-c"; then
	echo 'x - skipping parseargs.pl (File already exists)'
	rm -f _shar_wnt_.tmp
else
| _shar_wnt_.tmp
echo 'x - extracting parseargs.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'parseargs.pl' &&
# ParseArgs.pl script for perl
'di';
'ig00';
#
# Author: Alan K. Stebbens <aks@hub.ucsb.edu>
# $Id: parseargs.pl,v 1.5 1993/01/26 19:43:43 aks Exp $
# See bottom of file for documentation.
X
$InitParseProgram = '
X Arg:while (@ARGV && ($_ = shift @ARGV)) {
X       $arg = s/=(.*)// ? $1 : undef;
X       while (/^-./) {		# parse clustered switches
X ';
X
$FinishParseProgram = '
X           last;
X       }
X       if (!index(\'-debug\',$_)) {
X	   $Debug = $arg || 1; next;
X       }
X       if (!index(\'-verbose\',$_)) {
X	   $Verbose = $arg || 1; next;
X       }
X       next if /^-$/;
X       last unless /^-/;
X       die "Bad arg: $_\n", $Usage;
X    }
X    unshift(@ARGV, $_) if length;
X ';
X
sub ParseOpts {
X    local($i);
X    local(@opts) = split(' ',shift);
X    for ($i = $#opts + 1; $i > $[; $i--) {
X        splice(@opts, $i, 0, ''); # insert null actions
X    }
X    &ParseArgs(@opts);
}
X
%ParseArgFlags =   ( '<', 'ParseArgInFile',
X		     '>', 'ParseArgOutFile',
X		     '/', 'ParseArgDirectory',
X		     '.', 'ParseArgPlainFile',
X		     '!', 'ParseArgNewFile',
X		     '@', 'ParseArgsFromFile',
X		     '#', 'ParseNumberArgs',
X		     '~', 'ParseUserLogin',
X		     '(', 'ParseGroupName',
X		     '=', '',
X		     ':', '',
X		     '+', '',
X		     '*', '',
X		   );
X
$ParseArgFlags = join('',keys(%ParseArgFlags));
X
sub ParseArgs {
X    local(@arglist) = @_;	# argument list
X    local($_);
X    local($NoArg) = "Missing argument for option: ";
X    if ($#arglist == $[) {	# a single string?
X	local(@lines) = split(/\n/,$arglist[0]);
X	@arglist = ();
X	foreach (@lines) {	# parse strings into FLAG, ACTION pairs
X	    if (/^\s*(\S+)\s+(\S.*)$/) {
X		push(@arglist,$1,$2);
X	    } elsif (/^\s*(\S+)/) {
X		push(@arglist,$1,''); # default ACTION
X	    }
X	}
X    }
X    local($parser,$x,$option,$flag,$action,$expr,$arg,$interp);
X    $parser = $InitParseProgram;
X    for ($x = 0; $x <= $#arglist; $x += 2) {
X	$option = $arglist[$x];
X	$action = $arglist[$x+1];
X	length($option) || next;	# ignore null options
X	$action =~ s/\s*\#\s*$//; # remove any comments
X	$action =~ s/\bARG\b/\$arg/; # substitute
X	# check for --word, -word, +word, or _word w or w/o =
X	if ($option =~ /^(--|-|\+|_)(\w+)(\W|)/) {
X	    die "$0: Unknown option flag: $flag\n" 
X		if !defined($ParseArgFlags{$flag = $3});
X	    $expr = "\t    if (!index('$1$2',\$_)) {\n";
X	    $interp = $ParseArgFlags{$flag};
X	    if ($flag) {
X		$arg = "\$arg = defined(\$arg) ? \$arg : shift \@ARGV";
X		$arg .= " || die \"$NoArg $2\\n\", \$Usage" if $flag eq '=';
X		$expr .= "\t\t".$arg.";\n";
X		$expr .= "\t\t\$arg = \&".$interp."(\$2,\$arg);\n" if $interp;
X		$action = "\$opt_$2 = \$arg" unless $action;
X		$expr .= "\t\t".$action.";\n";
X	    } else {		# no flag char: -xyz
X		$arg = $1 eq '--' ? 1 : $1 eq '-' ? 2 : $1 eq '+' ? 4 : 8;
X		$action = "\$opt_$2 = $arg" unless $action;
X		$expr .= "\t\t".$action.";\n";
X	    }
X	    $expr .= "\t\tnext Arg;\n";
X	} elsif ($option =~ /^(\w)(\W)?/) { # how about single character option?
X	    $interp = ($flag = $2) && $ParseArgFlags{$flag};
X	    die "$0: Unknown option flag: $flag on option $option\n"
X		if index($ParseArgFlags,$flag = $2) < $[;
X	    $expr = "\t    if (s/^-$1/-/) {\n";
X	    $interp = $ParseArgFlags{$flag};
X	    if ($flag) {	# assignment option?
X		$arg = "\$arg = ";
X		if ($flag eq '+' || $flag eq '*') { # sticky args?
X		    $expr = "\t    if (s/^-$1(.*)//) {\n";
X		    $arg .= "length(\$1) ? \$1 : ";
X		}
X		$arg .= "defined(\$arg) ? \$arg : shift \@ARGV";
X		$arg .= " || die \"$NoArg $1\\n\", \$Usage" 
X		    if $flag eq '=' || $flag eq '+';
X		$expr .= "\t\t".$arg.";\n";
X		$expr .= "\t\t\$arg = \&".$interp."(\$2,\$arg);" if $interp;
X		$action = "\$opt_$1 = \$arg" unless $action;
X		$expr .= "\t\t".$action.";\n" if $action;
X		$expr .= "\t\tundef \$arg;\n";
X	    } else {
X		$expr .= "\t\t\$opt_$1++;\n" unless $action;
X		$expr .= "\t\t".$action.";\n" if $action;
X	    }
X	    $expr .= "\t\tnext;\n";
X	} else {		# use option as expr
X	    $expr = "\t    if ($option) {\n";
X	    $expr .= "\t\t".$action.";\n" if $action;
X	    $expr .= "\t\tnext Arg;\n" unless $action !~ /\bnext\b/;
X	}
X	$parser .= $expr;
X	$parser .= "\t    }\n";
X    }
X    $parser .= $FinishParseProgram;
X    print STDERR $parser 
X	if @ARGV && $ARGV[0] eq '-DEBUG_PARSER' && shift @ARGV;
X    eval $parser;
X    die $@ if $@;
}
X    
# Reference this routine in an action if you want an option to 
# insert arguments acquired by reading a file.
X
sub ParseArgsFromFile {
X    local($file) = shift;
X    open(FEXP,$file) || die "Can't open input: $file: $!\n";
X    unshift(@ARGV, grep((chop, s/\s*\#.*$//, length > 0),<FEXP>));
X    close FEXP;
}
X
sub ParseArgInFile {
X    local($opt,$_) = @_;
X    die "$0: File $_ cannot be read!\n" unless -r;
X    open("FH_$opt","<$_") || die "$O: Cannot open $_ for input: $!\n";
X    $_;
}
X
sub ParseArgOutFile {
X    local($opt,$_) = @_;
X    -e && ( -w || die "$0: File $_ cannot be written!\n" );
X    open("FH_$opt",">$_") || die "$0: Cannot open $_ for output: $!\n";
X    $_;
}
X
sub ParseArgDirectory {
X    local($opt,$_) = @_;
X    -e || die "$0: Directory $_ does not exist!\n";
X    -d || die "$0: $_ is not a directory!\n";
X    $_;
}
X
sub ParseArgPlainFile {
X    local($opt,$_) = @_;
X    -e || die "$0: File $_ does not exist!\n";
X    -f || die "$0: File $_ is not a plain file!\n";
X    $_;
}
X
sub ParseArgNewFile {
X    local($opt,$_) = @_;
X    ! -e || die "$0: File $_ already exists!\n";
X    $_;
}
X
sub ParseNumberArgs {
X    local($opt,$_) = @_;
X    /^\d+$/ || die "$0: Option $opt $_ is not a number!\n";
X    $_;
}
X
sub ParseUserLogin {
X    local($opt,$_) = @_;
X    /^\d+$/ ? 
X	$_ == (getpwuid($_))[$[+2] || die "$0: $_ is not a valid user id!\n"
X        : $_ eq (getpwnam($_))[$[] || die "$0: $_ is not a valid login name!\n";
X    $_;
}
X
sub ParseGroupName {
X    local($opt,$_) = @_;
X    /^\d+$/ ?
X	$_ == (getgrgid($_))[$[+2] || die "$0: $_ is not a valid group id!\n"
X        : $_ eq (getgrnam($_))[$[] || die "$0: $_ is not a valid group name!\n";
X    $_;
}
X
1;
__END__
###############################################################
X    # These next few lines are legal in both Perl and nroff.
X
.00;                       # finish .ig
X 
'di           \" finish diversion--previous line must be blank
.nr nl 0-1    \" fake up transition to first page again
.nr % 0         \" start at page 1
.rm ';
.\" Bullet Item
.de Bi
.TP 1i
.B \\h'.5i'\\$1
..
.TH PARSEARGS 3 "January 14, 1993"
.AT 3
.SH NAME
parseargs.pl - Parse the argument vector
.SH SYNOPSIS
.B require 'parseargs.pl'
.sp
.B &ParseOpts('FLAG1 FLAG2 ... FLAGn');
.sp
.B &ParseArgs('FLAG1', 'ACTION1', 'FLAG2', 'ACTION2', ...);
.sp
.nf
.na
.ft B
&ParseArgs(<<'EOF');
X   FLAG1   ACTION1
X   FLAG2   ACTION2
X   ...
X   FLAGn   ACTIONn
EOF
.ft R
.ad
.fi
.SH DESCRIPTION
These subroutines parse the current command argument vector, 
.BR @ARGV ,
for switches which match defined 
.B FLAG
expressions, evaluating associated 
.B ACTION
expressions.
.sp
The argument parsing stops on the first unhandled non-flag argument.
An error is reported on any unhandled flag arguments (\c
.BI -flag ).
.sp
There are two subroutines: 
.BR ParseArgs ,
and 
.BR ParseOpts .
The former is
more general and allows complete control over the flag strings and the
associated actions.  The 
.B ParseOpts
routine has a simpler interface,
with useful default actions for the specified flag strings.
.sp
In both routines, each 
.B FLAG
is matched against the current argument,
and, if successful, the corresponding 
.B ACTION 
is evaluated, followed by
a 
.BR next .
.B FLAG
can be a general Perl expression, or it can be one of
a special syntax (defined below), which is used to create a Perl
expression for testing.
.sp
Any argument not matching any of the test expressions causes an error.
When an error occurs, if the string 
.B $Usage
is defined, it will be
printed after the error message.
.sp
.B ACTION
can be a general Perl expression, except that no embedded
newlines are permitted.  An empty string, or action of 
.B `#'
is considered to be a null action, for which useful defaults are
supplied.
.sp
Within 
.BR ACTION ,
the string 
.I `ARG'
will be substituted with the
appropriate Perl expression which evaluates to the argument of the switch
which selected the action. 
.sp
In the case of 
.BR ParseOpts ,
all actions are provided by default.
.sp
As a convenience for Perl script writers (that is, myself :^), the
following flags and actions are supplied, after all other flags have
been tested: 
.sp
.nf
.B \t-debug\t$DEBUG++
.B \t-verbose\t$VERBOSE++
.fi
.sp
Also, for the case of when arguments may be obtained by reading a file
passed as an argument itself, the user may reference the subroutine
.B &ParseArgsFromFile(\c
.IB ARG )
as an action, where 
.I ARG
is the special
string recognized and substituted with an appropriate expression.
.sp
The syntax of 
.B FLAG
is:
.TP .5i
(a) \fB(--|-|+|_)\fIWORD\fB[\fIF\fB]\fR
.sp
This syntax allows the various methods of setting switches to be
recognized, with an optional trailing flag character
.IR `F' ,
indicating how to handle the
argument, if any.
If there is no graphic character suffixed to the
.IR WORD ,
then the option accepts no arguments, and is set to 1 when supplied on
the command line.
.sp
The following graphic flag characters are recognized and provide for
interpretation of the arguments, when given.  When an explicit user
action is given in conjunction with one of these flag characters, the
interpretation action is accomplished before the user action.  When no
user action is given, the default variable,
.BI `$opt_ WORD' ,
is assigned the value of the argument.
.Bi `='
this flag accepts a required argument, either in the same word following an
equal sign (\c
.BR `=' ),
or in the next word of the argument vector.  That is, both of these
argument types are valid:
.sp
.B \t-WORD=VALUE\t
and
.B \t-WORD VALUE
.sp
An omitted argument is an error with the \fB`='\fP flag character.
.Bi `:'
this flag character causes the option to take an 
optional argument, not causing an error should it be omitted.
.Bi `<'
treat the argument as an input file,
causing an error if the file is non-existent or unreadable.  The file is
opened for reading under the file handle 
.BI FH_ WORD.
.Bi `>'
treat the argument as an output file,
causing an error if the file is not writable.  The file is opened for
writing under the file handle
.BI FH_ WORD.
.Bi `/'
treat the argument as a directory, which must exist.
.Bi "`.'" "(a period)"
treat the argument as a plain file which must exist; if the file is a
directory, or a special file, then an error occurs.  Symbolic links are
ok, if they point to a plain file.
.Bi `!'
treat the argument as a new, nonexistent file name.  An error occurs if
the file already exists, in any form.
.Bi `#'
treat the argument as an integer, causing an error for any non-digits
found.
.Bi `~'
verify the argument as a valid user login name or numerical user id.
.Bi `('
treat the argument as a valid group name, or numerical group id.
.Bi `@'
treat the argument as a readable file, read the files contents, and
split the lines into the argument vector, 
.BR @ARGV .
.IP "" .5i
The switches recognized by 
.I (a)
may all be minimally abbreviated, as
the test expression used to recognize the switch is
.B `!index(FLAG,$_)'.
Thus, for the flag 
.BR `-edit' , 
the options 
.BR `-edit' ,
.BR `-edi' ,
.BR `-ed',
and
.B `-e'
will be recognized.
.sp
When designing switch names with abbreviation, care must be taken to
specify the longer abbreviations first, so they will not be masked
by shorter ones.
.sp
The default 
.B ACTION
performed by a switch matching this style 
.BR FLAG ,
is to set a Perl variable,
.BI $opt_ WORD,
to a `1', if no value was given, or to the argument supplied.
.sp
X  The following are all examples of this syntax:
.sp
.B  --list-all -edit +help -verbose _broadcast= _netmask=
.B  -input< -output> -dir/ --user~ -file@ -count#
.sp
.TP .5i
(b) \fIC\fB[=:<>/.!#~(+*]\fR
.sp
In this syntax, 
.B C
is a single character switch, which may be followed by one of the
previously documented flag characters, or one of the following two 
additional ones:
.Bi `+'
This flag character causes the option to require an argument, which may
be given either juxtaposed with the option word itself, or on the
subsequent argument vector word.
.Bi `*'
This flag character causes the option to attempt to obtain an argument,
and failing that, cause no error.
.IP "" .5i
Single character switches may be given separately, or may be clustered into
a single switch word.  For example, the options
.B -l
and
.BR -t ,
may be given as:
.B -lt
or
.BR -tl .
.sp
The single character assignable options may require arguments, or
they may be optional.  The argument syntax may be one of:
.sp
.nf
.B \t\fR(1)\fB `-O=arg'
.B \t\fR(2)\fB `-Oarg'
.B \t\fR(3)\fB `-O arg'
.fi
.sp
where 
.B `-O'
is your favorite option.  The argument flags 
.B `=' 
and 
.B `:'
indicate required or optional arguments which can only be assigned
via styles 
.I (1) 
and 
.IR (3) .
The argument flags 
.B `+' 
and 
.B `*'
also mean required and optional, but indicate that style 
.I (2)
is permissible, which disallows option clustering.
.sp
The following table should make this a little clearer:
.sp
.ta .5i +.5i +1i +1i +1i
.nf
.ft B
X	Flag	Argument?	-O=arg	-Oarg	-O arg
.ft R
X	----	---------	------	-----	------
X	 \fB=\fP	required	  ok	  no	  ok	  
X	 \fB:\fP	optional	  ok	  no	  ok
X	 \fB+\fP	required	  ok	  ok	  ok
X	 \fB*\fP	optional	  ok	  ok	  ok
.fi
.ta 0.5i
.sp
If a switch defined with an argument is given in a cluster, its
argument may come from:
.sp 
.IP \\h'.5i'1. 1i
the immediately following characters, if the 
.I FLAG
is defined as 
.B `C+' 
or 
.BR `C*' .
This behavior is similar to how 
.I lpr
parses its printer switch: 
.BR `-Pprinter' .
In this case, the flag would be given as
.BR `P+' .
If the flag is defined as 
.BR `P=' ,
then either an equal sign must be used, or the subsequent word will be
used as the argument.
.sp
.IP \\h'.5i'2. 1i
the string followed by the equal sign (\fB`='\fP)
in the same word as the switch.  This allows single character switches
to be given similar to longer switches:
.B `-l=999'
or 
.BR `-length=999' .
.IP \\h'.5i'3. 1i
the subsequent word in the argument vector, if there are no
strings resulting from cases 
.I (1)
or 
.I (2)
above.
.sp 
The default action for this style flag is to increment a Perl
variable, 
.BI $opt_ C, 
where 
.B `C' 
is the single character switch name, or to set its value to the argument
if defined to accept arguments. 
For example, if the flags 
.B `l'
and 
.B `t'
were defined, then the switch 
.B `-lt'
would cause the variables
.B $opt_l
and
.B $opt_t
to both be incremented.
.TP .5i
(c) \fBexpr\fR
This syntax is the usual Perl expression.  It is tested
directly, and no default action is assumed; actions must be
specified by the user.
.PP
Since the order of the flags is the order in which the tests will be
performed, if the user mixes flag styles, and there are overlaps between
single character options and word options, the word options should
be specified first, otherwise the single character parsing will cause
the word argument to get broken into characters.
.PP
In order to facilitate debugging the options specifications and the 
resulting parser, if the first argument is
.BR `-DEBUG_PARSER' ,
then the parser code will be printed to 
.B STDERR
before being eval'ed.
.SH EXAMPLES
.sp
.nf
.ft B
&ParseArgs(<<'EOF')            \fR# NOTE: single quotes!\fB
X    t     $tbl++
X    l     $lst++
EOF
.ft R
.fi
.IP
This example recognizes the 
.B `-t'
and 
.B `-l'
switches, which may be
given separately or combined as 
.B `-lt' 
or 
.BR `-tl' ,
and sets the associated variables accordingly.  In this case, the
default variable assignments (\c
.B $opt_t
and 
.BR $opt_l )
are NOT done.
.PP
.nf
.ft B
&ParseArgs('t', '$tbl++', 'l', '$lst++');
.ft R
.fi
.IP
This is equivalent to the previous example.
.PP
.ft B
&ParseOpts('t l');
.ft R
.IP
This is essentially equivalent to the first two examples, except
that the variables set for 
.B `-t'
and 
.B `-l'
are 
.B $opt_t
and 
.BR $opt_l ,
respectively.
.PP
.nf
.ft B
&ParseOpts('-file= -list: -edit -optimize');
.ft R
.fi
.IP
In this example, the variables set would be: 
.BR $opt_file ,
.BR $opt_list ,
.BR $opt_edit ,
and 
.BR $opt_optimize .
The 
.B `-list'
option has an optional argument, while 
.B `-file'
must be supplied with an argument or an error will result.
.PP
.nf
.ft B
&ParseArgs(<<'EOF");
X     -file=   &ParseArgsFromFile(ARG);
X     b        $Brief++
X     v        $Verbose++
X     l*       $Len = ARG || 80
X     -edit
X     -arch=   $Arch = ARG
X     -os=     $OS = ARG
EOF
.fi
.ft R
.IP
This example illustrates the combination of the various types,
including a default action.  
.sp
The 
.B `-file'
switch, which may be abbreviated as 
.B `-f'
causes the routine,
.I &ParseArgsFromFile
to be invoked with computed argument 
.B (ARG)
of the 
.B `-file'
switch.  The arguments read from the file argument are then placed on
the argument stack as if they had been typed by the user.  These new
arguments are parsed along with any remaining, original arguments.
.sp
The 
.B $Brief
and
.B $Verbose
variables will be set if their respective, clusterable option flags are
given. 
.sp
The 
.B `-l'
option may be optionally given an argument, either as
.B `-lXYZ' or 
.BR `-l\ XYZ' ;
it is assigned to 
.BR $Len .
.sp
The variable
.B $opt_edit 
will be set (to 1).
.sp
The 
.B $Arch
variable will be set to the argument supplied to the
.B `-arch'
switch; similarly, the
.B $OS 
variable will be set by the 
.B `-os'
switch, both of which may be abbreviated.
.sp
.PP
.ft B
&ParseOpts('-in< -out> -directory/ -file. -user~ -group( -num# -new!');
.ft R
.PP
This is a rather baroque example illustrating the features of useful
default actions.  The following variables will be set as follows.
.TP 1i
.B $opt_in
is set to the name of a file which is opened for input, using the
filehandle of
.BR `FH_in' .
The variable and filehandle names are always 
.BI `$opt_ OPTION'
and
.BI `FH_ OPTION'.
If the file could not be opened for input, an error would have occurred.
.TP
.B $opt_out
is set to the name of a file opened for writing, using the filehandle of
.BR `FH_out' .
.TP
.B $opt_directory
is set to the name of an existing directory; an error will occur if the
directory doesn't exist.
.TP
.B $opt_file
is set to the name of an existing file; no other tests are made, just
existence.  If the file given as an argument doesn't exist, an error
occurs.
.TP
.B $opt_user
is set to the name of a valid user login name or numeric user id.
.TP
.B $opt_group
is set to the name of a valid group name or numeric group id.
.TP
.B $opt_num
is set to a number given as the argument; anything other than a number
causes an error.
.IP
.B $opt_new
is set to the name of a non-existent file; an error occurs if the file
already exists.
.sp
.SH ENVIRONMENT
.B
.IP @ARGV
The argument vector is implicitly parsed.
.SH FILES
None.
.SH AUTHOR
Alan Stebbens <aks@hub.ucsb.edu>
.SH "SEE ALSO"
.IR getopts.pl (3),
.IR perl (1)
.SH DIAGNOSTICS
.PP
When an error occurs, one of the following error messages is generated,
followed by the value of 
.I $Usage
if it is defined.
.sp
Unknown option flag: $flag
.sp
Can't open input: $file: $!
.sp
Unknown option flag: $flag on option $option
.sp
File $_ cannot be read!
.sp
File $_ cannot be written
.sp
Cannot open $_ for output: $!
.sp
Directory $_ does not exist!
.sp
$_ is not a directory!
.sp
File $_ does not exist!
.sp
File $_ is not a plain file!
.sp
File $_ already exists!
.sp
Option $opt: $_ is not a number!
.sp
$_ is not a valid user id!
.sp
$_ is not a valid login name!
.sp
$_ is not a valid group id!
.sp
$_ is not a valid group name!
.sp
Missing argument for option: $option
.sp
Bad option: $option
.SH BUGS
Probably.
.\" Emacs Local Variables:
.\" Emacs mode: perl
.\" Emacs End:
SHAR_EOF
chmod 0444 parseargs.pl ||
echo 'restore of parseargs.pl failed'
Wc_c="`wc -c < 'parseargs.pl'`"
test 19844 -eq "$Wc_c" ||
	echo 'parseargs.pl: original size 19844, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= Makefile ==============
if test -f 'Makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping Makefile (File already exists)'
	rm -f _shar_wnt_.tmp
else
| _shar_wnt_.tmp
echo 'x - extracting Makefile (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
# Makefile to install parseargs
X
X    ROOT = /eci/share
X PERLLIB = $(ROOT)/lib/perl
X  MANDIR = $(ROOT)/man/man3
X
X INSTALL = /usr/ucb/install
X
I	= ( set -x ; $(INSTALL) -c -m 444 $? $@ )
L	= ( set -x ; ln -s $? $@ )
R	= if [ -f $@ ]; then rm -f $@ ; fi
X
default:
X	@( set -x ; $(MAKE) -n install )
X
install: $(PERLLIB)/parseargs.pl $(MANDIR)/parseargs.3
X
$(PERLLIB)/parseargs.pl:	parseargs.pl ;			@$R ; $I
$(MANDIR)/parseargs.3:		$(PERLLIB)/parseargs.pl ;	@$R ; $L
X
shar:
X	shar -L50 -o parseargs.shar parseargs.pl Makefile test.pl test1
SHAR_EOF
chmod 0444 Makefile ||
echo 'restore of Makefile failed'
Wc_c="`wc -c < 'Makefile'`"
test 534 -eq "$Wc_c" ||
	echo 'Makefile: original size 534, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= test.pl ==============
if test -f 'test.pl' -a X"$1" != X"-c"; then
	echo 'x - skipping test.pl (File already exists)'
	rm -f _shar_wnt_.tmp
else
| _shar_wnt_.tmp
echo 'x - extracting test.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'test.pl' &&
#!/bin/perl
X
require 'parseargs.pl';
X
$Usage = <<EOF;
usage: $0 -input INPUT -output OUTPUT -new NEW -exists OLD
X	  -login LOGIN -group GROUP -number NUM
EOF
X	
&ParseArgs(<<'EOF');
X    -input<
X    -dir/
X    -output>
X    -new!
X    -exists.
X    -login~
X    -group(
X    -number#
EOF
X
format STDOUT =
X input = @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
X	 $opt_input
X   dir = @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
X	 $opt_dir
output = @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
X	 $opt_output
X   new = @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
X	 $opt_new
exists = @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
X	 $opt_exists
X login = @<<<<<<<<<<<<<<
X	 $opt_login
X group = @<<<<<<<<<<<<<<<
X	 $opt_group
number = @<<<<<<<<<<<<<<
X	 $opt_number
.
X
write;
X
exit;
SHAR_EOF
chmod 0755 test.pl ||
echo 'restore of test.pl failed'
Wc_c="`wc -c < 'test.pl'`"
test 723 -eq "$Wc_c" ||
	echo 'test.pl: original size 723, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= test1 ==============
if test -f 'test1' -a X"$1" != X"-c"; then
	echo 'x - skipping test1 (File already exists)'
	rm -f _shar_wnt_.tmp
else
| _shar_wnt_.tmp
echo 'x - extracting test1 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'test1' &&
#!/bin/sh
set -x
./test.pl -input=test.pl -dir . -output=xyz -new=qrz -exists=test1 -login bin -group bin -num 1
SHAR_EOF
chmod 0755 test1 ||
echo 'restore of test1 failed'
Wc_c="`wc -c < 'test1'`"
test 113 -eq "$Wc_c" ||
	echo 'test1: original size 113, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
exit 0