antsusage.pl
author Andreas Thurnherr <ant@ldeo.columbia.edu>
Tue, 05 Mar 2019 10:03:40 -0500
changeset 38 15c603bc4f70
parent 35 d3f6ca34c4ea
child 47 dde46143288c
permissions -rw-r--r--
after UK cruise

#/usr/bin/perl
#======================================================================
#                    A N T S U S A G E . P L 
#                    doc: Fri Jun 19 13:43:05 1998
#                    dlm: Wed Dec 13 09:09:58 2017
#                    (c) 1998 A.M. Thurnherr
#                    uE-Info: 167 49 NIL 0 0 70 2 2 4 NIL ofnI
#======================================================================

# HISTORY:
#	Dec 30, 1998: - removed directory from $0
#				  - added global -P option (pass comments)
#	Jan 02, 1999: - changed -P to -T and added -P)refix for [./fnr]
# 	Feb 08,	1999: - added &antsUsageError()
#	Feb 17, 1999: - added -N
#	Feb 28, 1999: - forced string interpretation for -O, -R, -I
#	Mar 08, 1999: - added -L, $antsLibs
#	Mar 20, 1999: - added exit(1) for unknown options
#				  - added optional argument to &antsUsageError()
#	May 28, 1999: - library loading generated headers even on -Q
#	Jun 31, 1999: - added &antsDescription()
#	Jul 31, 1999: - added parameter typechecking (field, float, card, int, file)
#	Aug 02, 1999: - changed &antsDescription() to be option-dependent
#	Sep 18, 1999: - added option delimiter --
#				  - treat cardinals & integers differently
#				  - added option typechecking funs, e.g. &antsIntOpt()
#				  - auto set -Q if stdout is tty
#	Sep 19, 1999: - changed &getopts() from Perl4 to Perl5
#	Sep 21, 1999: - load local libraries first
#	Mar 06, 2000: - added for-M)at
#	Mar 07, 2000: - worked on -M
#	Aug 24, 2000: - removed setting -Q on tty (bad for [yoyo] and [Split])
#	Aug 28, 2000: - added -Z
#				  - changed -P to -A
#				  - added new -P
#	Sep 19, 2000: - set opt_M (dodgily) if not specifically set (only affects
#					[count] so far)
#	Sep 20, 2000: - added []-syntax to -P
#				  - added workaround for -M %0xd bug
#	Sep 25, 2000: - changed order of -P and -L processing
#	Sep 26, 2000: - cosmetics
#	Nov 13, 2000: - BUG: -- had left a single empty argument
#	Nov 15, 2000: - added &antsParamParam()
#				  - added &antsFileOpt()
#	Nov 17, 2000: - made -P override any header PARAMs
#	Jan  4, 2001: - moved -L processing before -P
#	Feb  8, 2001: - added -G)range option
#   Mar 17, 2001: - param->arg
#				  - added @file:field argument syntax
#				  - added #num-num[:step] syntax
#	Mar 23, 2001: - added prefix{#-#}suff syntax
#	Mar 31, 2001: - changed -G)range to #[..#]{,#[..#}
#				  - added -F)ields f{,f}
#	Apr  3, 2001: - added +f syntax to -F)ields
#	Apr  5, 2001: - added f+ syntax to -F)ields
#	Apr 24, 2001: - removed err msg in case of -G f:* (select numbers)
#	Apr 30, 2001: - shortened date, added pwd
#	Jun 23, 2001: - removed default setting of $opt_M
#	Jul  6, 2001: - added degree notation to &antsFloatArg() via str2num()
#	Jul 10, 2001: - added select field names (for V.3)
#	Jul 13, 2001: - store ONLY field names (replaced on 1st use)
#				  - added quotes to usage history
#	Jul 15, 2001: - added &antsNewFieldOpt()
#	Jul 16, 2001: - made it work with Description again
#	Jul 24, 2001: - removed fnr lookup on -G
#	Jul 30, 2001: - BUG: made parseHeader conditional on $antsFixedFormat
#	Aug  9, 2001: - chgd pref{#-#}suff syntax to expand only to exist files
#	Oct 28, 2001: - BUG: added -K handling before parseHeader
#	Nov 22, 2001: - moved logic into &antsParseHeader()
#	Nov 28, 2001: - cosmetics
#	Jan 18, 2002: - old -N => -X; new -N
#	Mar 24, 2002: - BUG: &antsFieldArg('file') did not handle %PARAMs correctly
#	Jul 26, 2002: - removed common usage from antsUsageError unless -U is set
#	Jan  6, 2003: - added regexp option to -G
#	Feb  9, 2003: - BUG: {103-103}.ens hung
#	Jun 26, 2004: - made sure that near-zero \#args are rounded to zero
#	Jun 27, 2004: - BUG: \#22-14 did not work correctly any more
#	Jul 12, 2004: - removed &antsDescription()
#	May  5, 2005: - added &antsNewField()
#	May 17, 2005: - allowed &antsFieldArg() to check [Layout]
#	Nov  1, 2005: - disallowed numeric options by adding -- if first argument
#				    begins with -[0-9]
#	Nov  8, 2005: - removed -P, -T => -P, -Z => -T, added -Z
#	Nov 17, 2005: - removed $antsLibs
#				  - removed remainder of -D
#				  - added $antsARGV0 for [yoyo]
#				  - added !<arg> quoting (for filenames)
#	Nov 18, 2005: - finally allowed %PARAMs in -G
#	Nov 21, 2005: - BUG: had not been allowed in -G fieldname
#   Dec  7, 2005: - antsFName -> antsLayout (not tested)
#	Dec  9, 2005: - Version 3.2 (see [HISTORY])
#	Dec 11, 2005: - error on 0 args & tty stdin
#	Dec 20, 2005: - created &antsFieldInFileArg() & added flag to &antsFieldArg
#				  - simplified opt_M, because it now works w/o $#
#	Dec 22, 2005: - added $antsInteractive for [abc]
#   Dec 23, 2005: - replaced defined(@array) (c.f. perlfunc(1))
#	Dec 31, 2005: - BUG: @-notation was broken (used antique [fields]!!!)
#	Jan  3, 2006: - added support for -S)elect
#	Jan  9, 2006: - removed old line-masking code
#	Jan 12, 2006: - removed -A support
#				  - removed support for $ENV{ANTS}
#				  - changed from old -H)eader <skip> to -H)ead <n lines>
#	Jan 13, 2006: - moved -G handling to -S
#				  - BUG: -G regexpr did not allow :
#				  - renamed -T)rim to -C)anonical
#				  - removed warnings on -M/-C
#				  - removed weird -Z)ap
#	Jan 14, 2006: - removed -G (now handled by -S)
#				  - changed semantics of pref{#-#}suff special arg to
#					expand non-existing file names
#	Jul 28, 2006: - made special arg #-#:# numerically more robust
#	Aug 18, 2006: - improved special arg to pref{#,#-#,...} and allow / instead of ,
#	Dec 14, 2006: - exported handling of -X to [antsio.pl]
#				  - disallow -P & -Q
#	May 31, 2007: - added -G
#	Nov 28, 2007: - replaced / by + to separate ranges in {} arguments
#	Mar  4, 2008: - disallow partial fname matches in antsNewField*()
#	Mar 24, 2008: - new usage formatting (glorious!)
#	Mar 25, 2008: - added $antsSummary
#   Mar 26, 2008: - extended -F syntax
#	Mar 27, 2008: - modified &antsUsage() to allow disabling common options
#	Apr 24, 2008: - added &antsFieldListOpt()
#	May  7, 2008: - disabled -N/-S for utilities without header parsing
#	May 13, 2008: - moved -U to standard usage message
#	Aug  5, 2008: - suppress empty usage lines
#   Nov 12, 2008: - added opt_T
#	Aug 24, 2009: - added V4 dependency on @file:field special args, file args & opts
#	Oct  3, 2009: - BUG: sometime recently I had changed the pref{}suff semantics to be
#				    much more permissive; this led to problems as args like {print $0}
#					were erroneously expanded; changed => pref{}suff is only expanded
#					if first expanded element is existing file
#				  - special args expanding to zilch are not expanded any more
#	Aug 16, 2010: - added -A)ctivate output (F/S Poseidon, P403, Lucky Strike)
#	Aug 28, 2010: - added suppress -D)ependency check option
#				  - improve common-options usage help
#	Oct 15, 2010: - removed diagnostic output about loading libs
#	Oct 29, 2010: - replaced list by Cat in expansion of @-special args
#	Dec 21, 2010: - made $@ at end of -F list optional (i.e. -F can end with ,)
#	Jul 21, 2011: - modified -D usage info
#	Sep 19, 2011: - SEMANTICS: pref{#-#}suff does now produce warning on missing files
#	Oct  3, 2011: - BUG: pref{}suff special args were (again) too permissive and matched
#						 output formats; this time, I solved problem by making regexp
#						 more restrictive; if this does not work, I can go back to
#					     earlier solution (see BUG Oct 3 2009)
#	Oct 16, 2011: - added support for \, escape in -F to protect commas used, e.g. in
#					function calls, from splitting the opt_F argument string
#	Nov 11, 2011: - BUG: antsNewField did not work for external layouts
#	Dec 29, 2011: - BUG: antsNewField did not work Cat -f c=1,1,10 without input
#				  - BUG: antsNewField did still not work for external layouts (the bug
#						 resulted in always extending the layout, even when the field already
#						 existed)
#	Feb 13, 2012: - antsNewFieldOpt simplified by using 2nd arg to fnrNoErr
#	Oct 29, 2012: - diabled "no file" messages on special args
#	Mar 29, 2013: - added support for $ANTSLIBS
#	Apr  2, 2013: - BUG: pref{}suff special args did sometimes produce unexpanded as well
#						 as expanded output (unexpanded should be produced only if the
#						 expansion is empty)
#	Jul 30, 2014: - added special args to -U)sage output
#	Jan 30, 2015: - added &antsFunOpt()
#	Jan 31, 2015: - made it work
#	May 15, 2015: - changed (()) semantics to expand only to existing files
#	Aug 24, 2016: - removed -ve number heuristics (unshifting a -- under certain conditions),
#				  	which I believe is ancient
#	Aug 30, 2017: - BUG: new (()) semantics did not work for single-value ranges
#	Dec  9, 2017: - added -E, $antsSuppressCommonOptions
#				  - common options cosmetics
#	Dec 13, 2017: - BUG: common options cosmetics

# NOTES:
#	- ksh expands {}-arguments with commas in them!!! Use + instead

use Getopt::Std;

sub antsUsageError() {									# die with Usage error
	if (defined($antsSummary)) {
		print(STDERR "\n$0 -- $antsSummary\n\n")
	} else {
		print(STDERR "\n$0\n\n")
	}
	if ($opt_U) {
		print(STDERR "Options & Arguments: $antsCurUsage$_[0]\n\n" .
			"Common Input Options:\n" .
	            "\t[-I)nput <field-separator>]\n" .
				"\t[-S)elect <addr-expr>] [-N)ums f[,...]] [-H)ead <n lines>]\n" .
				"\t[-L)oad <lib,...>]\n" .
				"\t[allows -E)mbedded layout changes]\n\n" .
			"Common Output Options:\n" .
				"\t[-F)ields {%P|f|[\$@]|[f]=expr}[,...]]\n" .
				"\t[num for-M)at] [-C)anonical numbers] [-G)eographic lat/lon]\n" .
				"\t[-A)ctivate output] [LaTeX -T)able output]\n" .
				"\t[-P)ass comments] [-Q)uiet (no headers)] [-X (no new header)]\n" .
				"\t[suppress -D)ependency checks & addition of new dependencies]\n" .
	            "\t[-O)utput <field-separator>] [output -R)ecord <separator>]\n\n" .
			"Special Argument Expansion:\n" .
				"\t@<file>:<field>\t\t\t<field> values in <file>\n" .
				"\t#<from>-<to>[:<step>]\t\tenumerated values\n" .
				"\t[prefix]{<ranges>}[suffix]\texisting files (ranges: <from>[-<to>][+...])\n" .
				"\t[prefix]((<file>))[suffix]\tfiles, using <file> with ranges\n");
	} else {
		print(STDERR "Options & Arguments: $antsCurUsage$_[0]\n");
	}
	croak("\n");
}

# NB: "-" as first char in opts string disables common-option processing

sub antsUsage($$@) {									# handle options
	my($opts,$min,@usage) = @_;
	my($cOpts) = 'ADEM:QN:XCGPH:UI:O:R:L:F:S:T';
	my($d,$p);

	if ($antsSuppressCommonOptions) {					# non-core utilites (e.g. LADCP_w) do not use common options
		$cOpts = ''
	} else {
		$antsCurUsage .= "\n\t[print full -U)sage]"
	}
	
	foreach my $uln (@usage) {
		$antsCurUsage .= "\n\t$uln"						# suppress emtpy, e.g.
			unless ($uln eq '');						# for interp. model usage
	}

	&antsUsageError()									# no args && tty stdin
		if (!$antsInteractive && $min == 0 && @ARGV == 0 && -t 0);
	
#		The following line of code, disabled Aug 24, 2016, prevents
#		numerical options from working, e.g. in LADCP_w.
#
#	unshift(@ARGV,'--') if ($ARGV[0] =~ /^-\d/);		# -ve number heuristics

	chomp($0 = `basename $0`);							# set scriptname
	chop($d = `date +%D`);								# build header line
	chop($p = `pwd`);
	$p = "..." . substr($p,-17) if (length($p) > 20);
	$antsCurHeader = "#ANTS# [$d $p] $0";
	my($i,$eoo);
	for ($i=0; $i<=$#ARGV; $i++) {
		$antsCurHeader .= " '$ARGV[$i]'";
		$eoo = 1 if ($ARGV[$i] eq '--');				# -- handling
		$ARGV[$i] = "!$ARGV[$i+1]" if ($eoo); 			# make -ve non-options
	}
	$antsCurHeader .= "\n";
	pop(@ARGV) if ($eoo);								# remove last ARG

	if ($opts =~ m{^-}) {								# no common options processing
		$opts = $';
		undef($cOpts);
	}
	&antsUsageError(), exit(1)							# parse options
		unless (&getopts($cOpts . $opts));

	unless ($antsParseHeader) {
		croak("$0: -S not supported (implementation restriction)\n")
			if defined($opt_S);
		croak("$0: -N not supported (implementation restriction)\n")
			if defined($opt_N);
	}

	if ($eoo) {											# reset args
		for ($i=0; $i<=$#ARGV; $i++) {
			$ARGV[$i] = substr($ARGV[$i],1);
		}
	}

	if (defined($cOpts)) {								# process common options

		croak("$0: illegal option combination (-P & -Q)\n")
			if ($opt_P && $opt_Q);

		&antsActivateOut() if ($opt_A);					# activate output

		$antsAllowEmbeddedLayoutChange = $opt_E;		# allow embedded layout changes

		if ($opt_T) {									# LaTeX table output
			croak("$0: illegal option combination (-T & -G)\n")
				if ($opt_G);
			croak("$0: illegal option combination (-T & -O)\n")
				if defined($opt_O);
			$opt_O = ' & ';
			croak("$0: illegal option combination (-T & -R)\n")
				if defined($opt_R);
			$opt_R = ' \\\\\\\\\n';
        }
        
		if (defined($opt_I)) {							# defaults
			eval('$opt_I = "' . $opt_I .'";');			# interpret strings 
		} else {										# ... as perl strings
			$opt_I = '\s+';
		}
		if (defined($opt_O)) {
			eval('$opt_O = "' . $opt_O .'";');
		} else {
			$opt_O = "\t";
		}
		if (defined($opt_R)) {
			eval('$opt_R = "' . $opt_R .'";');
		} else {
			$opt_R = "\n";
		}
	
		if (defined($opt_L)) {							# load libraries
			foreach $lib (split(',',$opt_L)) {
				if (-r "lib$lib.pl") {
					require "lib$lib.pl";
				} elsif (-r "$ANTS/lib$lib.pl") {
					require "$ANTS/lib$lib.pl";
				} elsif (-r "$ANTSLIBS/lib$lib.pl") {
					require "$ANTSLIBS/lib$lib.pl";
				} else {
					croak("$0: cannot load {.,$ANTS,$ANTSLIBS}/lib$lib.pl\n");
				}
			}
		}
	
		if (defined($opt_N)) {							# parse -N)ums
			@antsNFNames = split(',',$opt_N);
	    }

		if (defined($opt_F)) {							# parse -F)ields
			$opt_F =~ s/\\,/aNtScOmMa/g;
			@antsOutExprs = split(',',$opt_F);	
			push(@antsOutExprs,'$@') if ($opt_F =~ /,$/);
			foreach my $e (@antsOutExprs) {
				$e =~ s/aNtScOmMa/,/;
			}
		}
	}

	my($ai);
	for ($ai=0; $ai<=$#ARGV; $ai++) {					# parse special args
		my(@exp);
		if ($ARGV[$ai] =~ /^@([^:]+):(.+)/) {			# @file:field
			&antsAddDeps($1);
			@exp = `Cat -QF$2 $1`;
			croak("(...while expanding $ARGV[$ai])\n") if ($?);
		} elsif ($ARGV[$ai] =~ /^#(-?[\d\.]+)-(-?[\d\.]+):?(-?[\d\.]+)?/) {
			my($step) = 1;								# #num-num:step
			if (defined($3)) {
				$step = $3;
			} elsif ($2 < $1) {
				$step = -1;
			}
			if ($step > 0) {
				for (my($c)=0,my($i)=$1; $i<=$2+$step/1e6; $c++,$i=$1+$c*$step) {
					$i = 0 if (abs($i) < abs($step) / 1e6);
					push(@exp,$i);
				}
			} else {
				for (my($c)=0,my($i)=$1; $i>=$2+$step/1e6; $c++,$i=$1+$c*$step) {
					$i = 0 if (abs($i) < abs($step) / 1e6);
					push(@exp,$i);
				}
			}
		} elsif ($ARGV[$ai] =~ m{\{([-\+,\d]+)\}}) {	# pref{list of ranges}suff
			my($pref) = $`; my($suff) = $';
			foreach my $range (split('[,\+]',$1)) {
				if ($range =~ /^(\d+)-(\d+)$/) {
					my($fmt) = length($1)==length($2) ?
							   sprintf("$pref%%0%dd$suff",length($1)) : "$pref%d$suff";
					if ($2 > $1) {
						for (my($i)=$1; $i<=$2; $i++) {
							my($f) = sprintf($fmt,$i);
							if (-f $f) { push(@exp,$f); }
#							else { &antsInfo("$ARGV[$ai]: no file <$f>"); }
						}
					} else {
						for (my($i)=$1; $i>=$2; $i--) {
							my($f) = sprintf($fmt,$i);
							if (-f $f) { push(@exp,$f); }
#							else { &antsInfo("$ARGV[$ai]: no file <$f>"); }
	                    }
	                }
				} else {
					my($f) = "$pref$range$suff";
#					print(STDERR "f = $pref . $range . $suff\n");
					if (-f $f) { push(@exp,$f); }
#					else { &antsInfo("$ARGV[$ai]: no file <$f>"); }
	            }
	        }
			@exp = ($ARGV[$ai])							# make sure it *was* special arg
				unless (@exp);
		} elsif ($ARGV[$ai] =~ m{\(\(([^\)]+)\)\)}) {		# pref((file))suff
			my($pref) = $`; my($suff) = $';
			&antsAddDeps($1);
			open(F,$1) || croak("$1: $!\n");
			while (<F>) {
				s/#.*//g; next if /^\s+$/;					# handle comments and empty lines
				s/\s*//g;
				chomp($_);
				if ($_ =~ /^(\d+)-(\d+)$/) {
					my($fmt) = length($1)==length($2) ?
							   sprintf("$pref%%0%dd$suff",length($1)) : "$pref%d$suff";
					if ($2 > $1) {
						for (my($i)=$1; $i<=$2; $i++) {
							my($f) = sprintf($fmt,$i);
							push(@exp,$f) if (-f $f);
						}
					} else {
						for (my($i)=$1; $i>=$2; $i--) {
							my($f) = sprintf($fmt,$i);
							push(@exp,$f) if (-f $f);
	                    }
	                }
				} else {
					push(@exp,"$pref$_$suff") if (-f "$pref$_$suff");
	            }
	        }
			close(F);
		} else {										# regular argument
			next;
		}
		&antsInfo("WARNING: special arg $ARGV[$ai] expands to nothing"),
		push(@exp,$ARGV[$ai])
			unless ($#exp >= 0);
		splice(@ARGV,$ai,1,@exp);
	}

	my($nargs) = $#ARGV + 1;							# check arg count
	&antsUsageError() if ($opt_U || ($min > 0) && ($nargs < $min));

	$antsARGV0 = $ARGV[0];								# save 1st filename
	&antsParseHeader();									# get fields & params

	for (my($i)=0; $i<=$#ARGV; $i++) {					# remove leading ! from args
		$ARGV[$i] =~ s/^!//;
	}
	
	return $nargs;
}

#======================================================================
# argument typechecking
#======================================================================

sub antsFieldInFileArg($)
{
	my($fn) = @_;
	my($fnr);
	
	&antsUsageError() unless defined($ARGV[0]);
	open(F,$fn) || croak("$fn: $!\n");
	if ($ARGV[0] =~ /^%/) {
		croak("$0: no PARAM $ARGV[0] in $fn\n")
			unless (defined(&antsFileScanParam(F,$')));
		$fnr = $ARGV[0];
	} else {
		$fnr = &antsFileScanFnr(F,$ARGV[0]);
		unless (defined($fnr)) {
			print(STDERR "$0: WARNING: no field $ARGV[0] in $fn\n");
			$fnr = &fnr($ARGV[0]);
		}	    
	}
    close(F);

	shift(@ARGV);
	return $fnr;
}

sub antsFieldArg($)
{
	&antsUsageError() unless defined($ARGV[0]);
	my($paramsAllowed) = @_;
	my($fnr) = &fnr($ARGV[0]);
	croak("$0: $ARGV[0] is not a field\n")
		unless (numberp($fnr) || $paramsAllowed);
	shift(@ARGV);
	return $fnr;
}

sub antsFieldOpt(@)
{
	my($opt,$default) = @_;
	if (ref($opt)) {									# reference => set
		if (defined(${$opt})) {							# defined => check,set
			${$opt} = &fnr(${$opt});
		} elsif (defined($default)) {					# not defined => default
			${$opt} = &fnr($default);
		}
		return ${$opt};
	} else {											# not ref => do not set
		return defined($opt) ? &fnr($opt) :
					defined($default) ? &fnr($default) : $opt;
	}
}

sub antsFieldListOpt($)
{
	my($opt) = @_;
	my(@fn) = split(',',$opt);
	my(@fi);

	for (my($i)=0; $i<@fn; $i++) {
		$fi[$i] = &fnr($fn[$i]);
	}
	return @fi;
}

sub antsNewField($)										# allocate if needed
{
	my($fname) = @_;
	my($fnr);
	
	$fnr = &fnrNoErr($fname,1);							# exact match
	unless (defined($fnr)) {
		return $antsBufNFields++						# external layout
			unless ($antsBufNFields==0 || @antsLayout);
		@antsNewLayout = @antsLayout
			unless (@antsNewLayout);
		push(@antsNewLayout,$fname);
		$fnr = $#antsNewLayout;
	}
	$antsBufNFields = $fnr+1 if ($fnr >= $antsBufNFields);
	return $fnr;
}

sub antsNewFieldOpt(@)									# allocate if does not exist
{
	my($opt,$default) = @_;
	my($fname,$fnr);

	if (ref($opt)) {									# reference => set
		if (defined(${$opt})) {							# defined => check,set
			$fname = ${$opt};
		} elsif (defined($default)) {					# not defined => default
			$fname = $default;
		}
		if (defined($fname)) {
			$fnr = &antsNewField($fname);
			${$opt} = $fnr;
			return $fnr;
		} else { return undef; }
	} else {											# not ref => do not set
		if (defined($opt)) {
			$fname = $opt;
		} elsif (defined($default)) {
			$fname = $default;
		}
		return defined($fname) ? &antsNewField($fname) : undef;
	}
}

sub antsNoFileErr($$)
{
	croak("$0: $_[0] $_[1] is not a valid file\n")
		unless (-r $_[1]);
	&antsAddDeps($_[1]);
}

sub antsFileArg()	# arg 1 => do not shift
{
	&antsUsageError() unless defined($ARGV[0]);
	&antsNoFileErr("Argument",$ARGV[0]);
	my($res) = $ARGV[0];
	shift(@ARGV) unless ($_[0]);
	return $res;
}

sub antsFileOpt($)
{
	my($opt) = @_;
	&antsNoFileErr("Option Argument",$opt)
		if (defined($opt));
}

sub antsParamArg()
{
	&antsUsageError() unless defined($ARGV[0]);
	croak("$0: Argument $ARGV[0] is not a valid PARAM\n")
		unless ($ARGV[0] =~ /^%/);
	shift(@ARGV);
	return $';
}

sub antsNoCardErr($$)
{
	croak("$0: $_[0] $_[1] is not a cardinal number\n")
		unless (cardinalp($_[1]));
}

sub antsCardArg()
{
	&antsUsageError() unless defined($ARGV[0]);
	$ARGV[0] = &{&antsCompileConstExpr($')}
		if ($ARGV[0] =~ m{^=});
	&antsNoCardErr("Argument",$ARGV[0]);
	my($res) = 1.0*$ARGV[0];
	shift(@ARGV);
	return $res;
}

sub antsCardOpt(@)
{
	my($opt,$default) = @_;
	if (ref($opt)) {									# reference => set
		if (defined(${$opt})) {							# defined => check
			$$opt = &{&antsCompileConstExpr($')} if ($$opt =~ m{^=});
			&antsNoCardErr("Option Argument",${$opt});
		} else {										# not defined => default
			${$opt} = $default;
		}
		return ${$opt};
	} else {											# not ref => do not set
		if (defined($opt)) {
			$opt = &{&antsCompileConstExpr($')} if ($opt =~ m{^=});
			&antsNoCardErr("Option Argument",$opt);
	        return $opt;
	    } else {
	    	return $default;
	    }
	}
}

sub antsNoIntErr($$)
{
	croak("$0: $_[0] $_[1] is not an integer\n")
		unless (integerp($_[1]));
}

sub antsIntArg()
{
	&antsUsageError() unless defined($ARGV[0]);
	$ARGV[0] = &{&antsCompileConstExpr($')}
		if ($ARGV[0] =~ m{^=});
	&antsNoIntErr("Argument",$ARGV[0]);
	my($res) = 1.0*$ARGV[0];
	shift(@ARGV);
	return $res;
}

sub antsIntOpt(@)
{
	my($opt,$default) = @_;
	if (ref($opt)) {									# reference => set
		if (defined(${$opt})) {							# defined => check
			$$opt = &{&antsCompileConstExpr($')} if ($$opt =~ m{^=});
			&antsNoIntErr("Option Argument",${$opt});
		} else {										# not defined => default
			${$opt} = $default;
		}
		return ${$opt};
	} else {											# not ref => do not set
		if (defined($opt)) {
			$opt = &{&antsCompileConstExpr($')} if ($opt =~ m{^=});
			&antsNoIntErr("Option Argument",$opt);
	        return $opt;
	    } else {
	    	return $default;
	    }
	}
}

sub antsNoFloatErr($$)
{
	croak("$0: $_[0] $_[1] is not a number\n")
		unless (numberp($_[1]));
}

sub antsFloatArg()
{
	&antsUsageError() unless defined($ARGV[0]);
	$ARGV[0] = &{&antsCompileConstExpr($')}
		if ($ARGV[0] =~ m{^=});
	my($res) = str2num($ARGV[0]);
	&antsNoFloatErr("Argument",$res);
	shift(@ARGV);
	return $res;
}

sub antsFloatOpt(@)
{
	my($opt,$default) = @_;
	if (ref($opt)) {									# reference => set
		if (defined(${$opt})) {							# defined => check
			$$opt = &{&antsCompileConstExpr($')} if ($$opt =~ m{^=});
			&antsNoFloatErr("Option Argument",${$opt});
		} else {										# not defined => default
			${$opt} = $default;
		}
		return ${$opt};
	} else {											# not ref => do not set
		if (defined($opt)) {
			$opt = &{&antsCompileConstExpr($')} if ($opt =~ m{^=});
			&antsNoFloatErr("Option Argument",$opt);
	        return $opt;
	    } else {
	    	return $default;
	    }
	}
}

#----------------------------------------------------------------------
# antsFunOpt(\$opt_x) parses the contents of $opt_x as follows:
#
#	name(value)	=> $opt_x = 'name'; $name = "value";
#	name		=> no change
#----------------------------------------------------------------------

sub antsFunOpt(@)
{
	my($opt) = @_;
	return unless defined($opt);
	croak("antsusage.pl: antsFunOpt(@_): argument is not a ref\n")
		unless ref($opt);
	my($name,$param) = (${$opt} =~ m{^([^\)]+)\(([^\)]+)\)$});
	return unless defined($param);
	eval(sprintf('$%s = "%s";',$name,$param));
	${$opt} = $name;
}

1;														# return true