home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume31
/
cmdline
/
part05
< prev
next >
Wrap
Text File
|
1992-07-27
|
63KB
|
2,073 lines
Newsgroups: comp.sources.misc
From: brad@hcx1.ssd.csd.harris.com (Brad Appleton)
Subject: v31i052: cmdline - C++ Library for parsing command-line arguments, Part05/07
Message-ID: <1992Jul27.020821.29747@sparky.imd.sterling.com>
X-Md4-Signature: 1a604c4b595c1ea79014837919db452c
Date: Mon, 27 Jul 1992 02:08:21 GMT
Approved: kent@sparky.imd.sterling.com
Submitted-by: brad@hcx1.ssd.csd.harris.com (Brad Appleton)
Posting-number: Volume 31, Issue 52
Archive-name: cmdline/part05
Environment: C++
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 5 (of 7)."
# Contents: doc/cmdparse.man1 src/lib/cmdargs.c src/lib/private.c
# Wrapped by brad@hcx1 on Mon Jul 20 10:41:31 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'doc/cmdparse.man1' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'doc/cmdparse.man1'\"
else
echo shar: Extracting \"'doc/cmdparse.man1'\" \(21692 characters\)
sed "s/^X//" >'doc/cmdparse.man1' <<'END_OF_FILE'
X.\"========== TO PRINT, USE: {n,t}roff -man file ==========
X.if n .po 1
X.if n .ll 78
X.nh
X.ds NM \f4cmdparse\fP
X.ds | \f4|\fP
X.so macros.man
X.\"===================================
X.TH cmdparse 1
X.\"===================================
X.SH NAME
Xcmdparse \- parse command-line arguments for shell-scripts
X.\"===================================
X.SH SYNOPSIS
X.na
X.TP 12
X\fBcmdparse\fP
X[\fB\-anywhere\fP]
X[\fB\-ignore-case\fP]
X[\fB\-noabort\fP]
X[\fB\-noguessing\fP]
X[\fB\-prompt\fP]
X.if t .br
X[\fB\-options-only\fP]
X[\fB\-keywords-only\fP]
X[\fB\-quiet\fP]
X[\fB\-arrays\fP]
X[\fB\-usage\fP]
X[\fB\-version\fP]
X.if t .br
X[\fB\-true\fP\ \fIstring\fP]
X[\fB\-false\fP\ \fIstring\fP]
X[\fB\-suffix\fP\ \fIstring\fP]
X.if n .br
X[\fB\-shell\fP\ \fIshellname\fP]
X.if t .br
X[\fB\-file\fP\ \fIfilename\fP]
X[\fB\-env\fP\ \fIvarname\fP]
X[\fB\-decls\fP\ \fIstring\fP]
X.if t .br
X\*(--\ \
X\fIprogram-name\fP\ \
X\fIarguments\fP\ .\^.\^.
X.ad
X.\"===================================
X.SH DESCRIPTION
X\*(NM will parse the user's command-line arguments using the
X\f4CmdLine\fP(3\*(C+) library (taking into account any user-specified
Xpreferences) and will print on standard output, a host of variable
Xsettings using the syntax of the specified shell. The user must then
X"evaluate" the output of \*(NM in order to set the corresponding
Xvariables for his (or her) shell-script.
X
XIf none of \fB\-file\fP, \fB\-env\fP, or \fB\-decls\fP is given then
Xthe argument declarations will be read from standard input (unless
Xstandard input is associated with a terminal, in which case an error
Xwill result).
X
XIf more than one of \fB\-file\fP, \fB\-env\fP, or \fB\-decls\fP is given
Xthen argument declarations are read from all the places specified but
Xin the following order:
X
X.RS
XFirst, argument declarations are read from the string supplied with the
X\fB\-decls\fP option.
X
XSecond, any argument declarations contained in the environment variable
Xspecified by the \fB\-env\fP option are appended to the current set of
Xargument declarations.
X
XLastly, any argument declarations contained in the file specified
Xby the \fB\-file\fP option are appended to the current set of
Xargument declarations.
X.RE
X
XThe order in which the set of argument declarations are processed is important
Xbecause any positional parameters that were specified are expected to occur
Xin the same order as the order in which the corresponding argument declarations
Xwere processed.
X
XDepending upon which shell you are using, you may want to evaluate the output
Xof \*(NM directly (as in "\f4eval $cmdparse_output\fP") or you may wish to
Xfirst redirect output to a file and then evaluate it (as in "\f4.\ cmdparse_output\fP"). Some shells may not preserve all the special
Xcharacters (such as a newline) correctly when the former approach is used.
XOther shells may not permit you to change the value of "local" variables
Xin "sourced" files when the latter approach is used. You will have to
Xdecide which approach to use based upon your needs, and the shell you are
Xusing.
X
X.\"===================================
X.SH EXAMPLE
X.nf
X.ft 4
X#!/bin/sh
X#
X# Here is a Bourne Shell script named "cmdname".
X#
X# The short-option syntax is:
X# cmdname [\-c number] [\-x] [\-s char]
X# input-file [output-file \*(..]
X#
X# The long-option syntax is:
X# cmdname [\*(--count number] [\*(--xmode] [\*(--separator char]
X# input-file [output-file \*(..]
X#
XNAME="`basename $0`"
X
X## Declare the arguments.
XARGS='
X ArgInt count "[c|count number]" "number of copies to print."
X ArgBool xflag "[x|xmode]" "turn on x-mode."
X ArgChar fdsep "[s|separator char]" "field-separator to use."
X ArgStr input "input-file" "input file to read."
X ArgStr output "[output-file \*(..]" "where to print output."
X\&'
X
X## Parse the arguments
Xif cmdparse \-shell=sh \-decls="$ARGS" \*(-- $NAME "$@" > tmp$$
Xthen
X ## Success \- evaluate the result.
X \&. tmp$$
X rm \-f tmp$$
Xelse
X ## Either usage was printed or we found a syntax error.
X EXITVAL=$?
X rm \-f tmp$$
X exit $EXITVAL
Xfi
X
X## Print the arguments
Xecho "xflag=" $xflag
Xecho "count=" $count
Xecho "fdsep=" $fdsep
Xecho "input=" $input
Xif [ "$output" ] ; then
X echo "output=" $output
Xfi
X.ft R
X.fi
X.\"===================================
X.SH OPTIONS
XOnly a unique prefix of each option-name needs to be given (and the
Xoptions are matched case-insensitive).
XThe possible options are as follows:
X
X.TP
X\fB\-anywhere\fP
XAllow options (and keywords) to follow positional parameters.
XUnless this option is specified, anything that follows a positional
Xparameter that resembles an option (begins with a `\-') will be
Xtreated as yet another positional parameter.
X.TP
X\fB\-ignore-case\fP
XIgnore character case on single-character options.
X.TP
X\fB\-noabort\fP
XDon't exit if improper command-line syntax was used. Just ignore the
Xerrors and continue parsing.
X.TP
X\fB\-noguessing\fP
XBy default, if an unknown single-character option appears on the command-line,
X\*(NM will "guess" by seeing if the option corresponds to a keyword.
XSimilarly, if an unknown keyword (long-option) is encountered, \*(NM
Xwill see if it matches a single-character option. Specifying this option
Xdisables this behavior.
X.TP
X\fB\-prompt\fP
XPrompt the user interactively for any missing required arguments.
X.TP
X\fB\-options-only\fP
XDon't match keywords (long-options). Look only for single-character options.
X.TP
X\fB\-keywords-only\fP
XDon't match options. Look only for keywords (long-options). Using this
Xoption also allows the single-character option prefix (`\-') to be used
Xfor long-options.
X.TP
X\fB\-quiet\fP
XDon't print command-line syntax error messages.
X.TP
X\fB\-arrays\fP
XUse alternative syntax for arrays. See the appropriate subsection of
Xthe section \s-1\fBSHELLS\fP\s+1 to see how (and if) this option will affect
Xthe output of \*(NM.
X.TP
X\fB\-usage\fP
XPrint command-line usage and exit. Don't parse anything!
X.TP
X\fB\-version\fP
XPrint version information and exit. Don't parse anything!
X.TP
X\fB\-true\fP\ \fIstring\fP
XThe string to use for boolean arguments that are turned \fIon\fP.
XThe default string is \f4"TRUE"\fP (unless the \fIperl\fP or \fItcl\fP shells
Xare used, in which case the default is \f4"1"\fP).
X.TP
X\fB\-false\fP\ \fIstring\fP
XThe string to use for boolean arguments that are turned \fIoff\fP.
XThe default string is \f4""\fP (unless the \fIperl\fP or \fItcl\fP shells
Xare used, in which case the default is \f4"0"\fP).
X.TP
X\fB\-suffix\fP\ \fIstring\fP
XWhen no value is supplied for an option that takes an optional value,
Xthe variable \fInamesuffix\fP, is set to \s-1TRUE\s+1 (where \fIname\fP
Xis the name of the corresponding variable and \fIsuffix\fP is the string
Xargument given to this option). If this option is not specified then
Xthe suffix "\f4_FLAG\fP" will be used.
X.TP
X\fB\-shell\fP\ \fIshellname\fP
XSet program arguments using the syntax of the given shell
X(default=\fIsh\fP).
X.TP
X\fB\-file\fP\ \fIfilename\fP
XThe file from which program argument declarations are read.
XIf \fIfilename\fP is ``\-'' then standard input is read.
X.TP
X\fB\-env\fP\ \fIvarname\fP
XThe name of the environment variable containing the program argument
Xdeclarations.
X.TP
X\fB\-decls\fP\ \fIstring\fP
XThe string that contains the program argument declarations.
X.TP
X\*(--
XIndicates the end of options/keywords.
X.TP
X\fIprogram-name\fP
XThe name of the program whose arguments are to be parsed.
XIf desired, the \fIprogram-name\fP may be specified as a keyword
X(instead of positionally) using the syntax \fB\-name\fP=\fIprogram-name\fR.
X.TP
X\fIarguments\fP\ .\^.\^.
XThe program-arguments to be parsed
X
X.\"===================================
X.SH EXIT STATUS
X\*(NM will exit with one of the following status codes:
X
X.IP 0
XArguments were successfully parsed. No syntax errors were found and the
Xshell-script variable settings have been printed on standard output.
X
X.IP 1
XEither usage or version information was explicitly requested. The desired
Xinformation was printed on standard diagnostic output. No arguments were
Xparsed.
X
X.IP 2
XSome type of command-line syntax error occurred. Any syntax error messages
Xhave been printed on standard diagnostic output.
X
X.IP 3
XAn invalid or unknown shell (command-interpreter) was specified on the
Xcommand-line to \*(NM. See the section entitled \s-1\fBSHELLS\fP\s+1
Xfor a list of the known shells.
X
X.IP 4
XA syntax error of some type occurred in one or more command-line argument
Xdeclarations. Any syntax error messages
Xhave been printed on standard diagnostic output.
X.\"===================================
X.so parsing.man
X.\"===================================
X.SH ARGUMENT DECLARATIONS
XThe syntax for a single argument for \*(NM looks like the following:
X
X.RS
X<\fIarg-type\fP> <\fIarg-name\fP> <\fIsyntax\fP> <\fIdescription\fP>
X.RE
X
XWhere <\fIarg-type\fP> is one of the following (case-insensitive):
X
X.RS
X.IP \f4ArgInt\fP 15
XAn integer value (or list of values).
X.IP \f4ArgFloat\fP 15
XA floating-point value (or list of values).
X.IP \f4ArgChar\fP 15
XA character value (or list of values).
X.IP \f4ArgStr\fP 15
XA string value (or list of values).
X.IP \f4ArgBool\fP 15
XA boolean flag that is initially \s-1FALSE\s+1 and is turned \fIon\fP
Xwhenever it is matched.
X.IP \f4ArgClear\fP 15
XA boolean flag that is initially \s-1TRUE\s+1 and is turned \fIoff\fP
Xwhenever it is matched.
X.IP \f4ArgToggle\fP 15
XA boolean flag that is initially \s-1FALSE\s+1 and is \fItoggled\fP
Xwhenever it is matched.
X.IP \f4ArgUsage\fP 15
XPrint usage and exit.
X.IP \f4ArgDummy\fP 15
XA dummy argument.
X.RE
X
XIf desired, the leading "\f4Arg\fP" portion of the type-name may be omitted.
X
XThe field <\fIarg-name\fP> is simply the name of the variable in your script
Xthat you wish to contain the resultant value from the command-line.
XAny default value must be assigned to the variable \fIbefore\fP invoking
X\*(NM.
X
XThe fields <\fIsyntax\fP> and <\fIdescription\fP> \s-1MUST\s+1 be enclosed
Xin either single or double quotes! If you want the character you are using
Xto quote the field to also appear within the field, then precede the quote
Xcharacter (inside the quotes) with a backslash (`\\').
X
XThe <\fIdescription\fP> is simply a textual description of the argument.
X
XThe <\fIsyntax\fP> is a little trickier, there are three basic forms of syntax:
X
X.RS
X.TP
X\f4"c|keyword"\fP
XAn option that may be matched by \fB\-c\fP or by \fB\*(--keyword\fP
Xand takes no value.
X.TP
X\f4"c|keyword\ \ value"\fP
XAn option that may be matched by \fB\-c\fP or by \fB\*(--keyword\fP
Xand requires a value.
X.TP
X\f4"value"\fP
XA positional parameter.
X.RE
X
XNote that the option-character \s-1MUST\s+1 precede the keyword-name and that
Xthere must be \s-1NO\s+1 spaces surrounding the `\*|' in
X``\f4c|keyword\fP'' (unless either the option-character or the keyword-name
Xis intended to be empty). If you wish a keyword to have no corresponding
Xshort-option (or vice versa) than put a blank in the option-character
X(or keyword) portion of the syntax declaration.
X
XAny optional parts of the argument should appear inside square-brackets
X(`[' and `]') and a list of values is denoted by an ellipsis (`` .\^.\^.'').
XMost options will be inside of square brackets to reflect the fact that
Xthey are "optional".
X
XSome example <\fIsyntax\fP> strings follow:
X
X.RS
X.TP
X\f4"c|keyword"\fP
XA required option.
X.TP
X\f4"[c|keyword]"\fP
XAn option with no value.
X.TP
X\f4"[c|keyword\ \ value]"\fP
XAn option that takes a value.
X.TP
X\f4"[c|keyword\ \ [value]]"\fP
XAn option that takes an optional value.
X.TP
X\f4"[c|keyword\ \ value\ .\^.\^.]"\fP
XAn option that takes \fIone or more\fP values.
X.TP
X\f4"[c|keyword\ \ [value \*(..]]"\fP
XAn option that takes \fIzero or more\fP values.
X.TP
X\f4"value"\fP
XA required positional parameter.
X.TP
X\f4"[value]"\fP
XAn optional positional-parameter.
X.TP
X\f4"[\ \ |keyword]"\fP
XAn option that may be matched by keyword but has no corresponding
Xsingle character option.
X.TP
X\f4"[c|\ \ value]"\fP
XAn option that takes a value but has no corresponding keyword name.
X.TP
X\f4"[c|keyword]\ \ value"\fP
XA required argument that may be matched either positionally or by keyword.
X.RE
X
X.SS SYNTAX FLAGS
X.RS
XNormally, the value to an option may be supplied either in the same
Xcommand-line token (as in "\fB\-c\fIvalue\fR"), or in a separate token
X(as in "\fB\-c\ \ \fIvalue\fR").
XIf desired, the <\fIsyntax\fP> field may optionally be followed by a colon
X(`:') and one of "\s-1\f4SEPARATE\fP\s+1" or "\s-1\f4STICKY\fP\s+1".
XThe former specifies that the argument value may only occur in a separate
Xcommand-line token, the latter specifies that the argument value may only
Xoccur in the same command-line token.
X.RE
X.\"===================================
X.SH SHELLS
XAt present, \*(NM knows about the following shells:
X.RS
X.IP \fIsh\fP 6
XThe Bourne Shell. This shell is the standard unix shell
X(designed and written by Stephen R. Bourne).
X.IP \fIcsh\fP 6
XThe C Shell. Bill Joy's answer to \fIsh\fP using C-like syntax.
X.IP \fIksh\fP 6
XThe Korn shell. David G. Korn's shell combining all the "best" features
Xof \fIsh\fP and \fIcsh\fP in a "clean" fashion.
X.IP \fIbash\fP 6
XThe Bourne Again Shell. The Free Software Foundation's answer to \fIksh\fP.
X.IP \fIzsh\fP 6
XThe Z Shell. Paul Falstad's creation combining all the "best" features
Xof \fIksh\fP and \fIcsh\fP plus some stuff of his own.
X.IP \fIrc\fP 6
XThe Plan 9 Unix shell designed by Tom Duff. A public domain implementation
X(with some enhancements) has been released by Byron Rakitzis.
X.IP \fIperl\fP 6
XLarry Wall's practical extraction and report-generation language. \fIPerl\fP is
Xnot a "shell" in the same sense as the others but it is a (powerful) language
Xin which Unix scripts may be written.
X.IP \fItcl\fP 6
XJohn K. Ousterhout's Tool Command Language. Karl Lehenbauer and friends have
Xdeveloped a \fItcl\fP shell based on Ousterhout's command language.
X.RE
X
XIn addition, \fIash\fP is considered by \*(NM to be equivalent to
X\fIsh\fP; and \fItcsh\fP and \fIitcsh\fP are considered to be equivalent to
X\fIcsh\fP.
X
XFor each supported shell, \*(NM will output a combination of
Xshell-variable and/or shell-array settings that correspond to the
Xarguments that were supplied on the command-line. In addition, if
Xan argument that takes an optional value was given on the command-line
Xbut \s-1NO\s+1 value was supplied, then the shell-variable named
X\fIname\f4_FLAG\fR is assigned the value \s-1TRUE\s+1 (where \fIname\fP
Xwas the name specified in the <\fIarg-name\fP> field of the corresponding
Xargument declaration). If desired, a suffix other than \f4_FLAG\fP may be
Xused by specifying the \fB\-suffix\fP option.
X
XAny desired initial values for variables from the argument declaration
Xstring should be assigned \s-1BEFORE\s+1 invoking \*(NM.
X\*(NM will \s-1NOT\s+1 output variable settings for any arguments
Xthat were \s-1NOT\s+1 supplied on the command-line. The only exception to
Xthis is when a positional argument that corresponds to the positional
Xparameters of the shell-script is \s-1NOT\s+1 supplied on the command-line;
XIn this particular case, the positional parameters of the shell-script are
Xunset (set to an empty list).
X
XThe exact syntax used to set variables and arrays for the corresponding
Xshells is the subject of the next several subsections.
X
X.\"-----------------------------------
X.SS BOURNE SHELL
X.RS
XFor the Bourne shell, shell variables are assigned using the following syntax:
X
X.RS
X.ft 4
Xname='value';
X.ft R
X.RE
X
XShell arrays are assigned using the following syntax:
X
X.XS
Xname='value1 value2 \*(..';
X.XE
X
XIf the \fB\-arrays\fP option was specified then the following syntax is
Xused to set arrays:
X
X.XS
Xname_count=3;
Xname1='value1';
Xname2='value2';
Xname3='value3';
X.XE
X
XIf the <\fIarg-name\fP> field of an argument is one of "\-", "\*(--",
X"*", or "@" then the argument corresponds to the positional parameters
Xof the shell-script and the following syntax is used to set its value(s):
X
X.XS
Xset \*(-- 'value1' 'value2' \*(.. ;
X.XE
X
X.RE
X.\"-----------------------------------
X.SS KORN SHELL
X.RS
XFor the Korn shell, shell variables are assigned using the following syntax:
X
X.RS
X.ft 4
Xname='value';
X.ft R
X.RE
X
XShell arrays are assigned using the following syntax:
X
X.XS
Xset \-A name 'value1' 'value2' \*(.. ;
X.XE
X
XIf the \fB\-arrays\fP option was specified then the following syntax is
Xused to set arrays:
X
X.XS
Xset +A name 'value1' 'value2' \*(.. ;
X.XE
X
XIf the <\fIarg-name\fP> field of an argument is one of "\-", "\*(--",
X"*", or "@" then the argument corresponds to the positional parameters
Xof the shell-script and the following syntax is used to set its value(s):
X
X.XS
Xset \*(-- 'value1' 'value2' \*(.. ;
X.XE
X
X.RE
X.\"-----------------------------------
X.SS C SHELL
X.RS
XFor the C shell, shell variables are assigned using the following syntax:
X
X.RS
X.ft 4
Xset name='value';
X.ft R
X.RE
X
XShell arrays are assigned using the following syntax:
X
X.XS
Xset name=('value1' 'value2' \*(..) ;
X.XE
X
XIf the <\fIarg-name\fP> field of an argument is "\f4argv\fP"
Xthen the argument corresponds to the positional parameters
Xof the script and may be unset.
X
X.RE
X.\"-----------------------------------
X.SS BOURNE AGAIN SHELL
X.RS
XAt present, the Bourne Again shell is treated exactly the same as the
XBourne Shell.
X.RE
X.\"-----------------------------------
X.SS Z SHELL
X.RS
XFor the Z shell, shell variables are assigned using the following syntax:
X
X.RS
X.ft 4
Xname='value';
X.ft R
X.RE
X
XShell arrays are assigned using the following syntax:
X
X.XS
Xname=('value1' 'value2' \*(..) ;
X.XE
X
XIf the <\fIarg-name\fP> field of an argument is one of "\-", "\*(--",
X"*", "@", or "\f4argv\fP" then the argument corresponds to the positional
Xparameters of the shell-script and the following syntax is used to set its
Xvalue(s):
X
X.XS
Xargv=('value1' 'value2' \*(..) ;
X.XE
X.RE
X.\"-----------------------------------
X.SS PLAN 9 SHELL
X.RS
XFor \fIrc\fP (the Plan 9 Shell), shell variables are assigned using the
Xfollowing syntax:
X
X.RS
X.ft 4
Xname='value';
X.ft R
X.RE
X
XShell arrays are assigned using the following syntax:
X
X.XS
Xname=('value1' 'value2' \*(..) ;
X.XE
X
XIf the <\fIarg-name\fP> field of an argument is "\f4*\fP"
Xthen the argument corresponds to the positional parameters
Xof the script and may be unset.
X
X.RE
X.\"-----------------------------------
X.SS PERL
X.RS
XFor Perl, variables are assigned using the following syntax:
X
X.RS
X.ft 4
X$name = 'value';
X.ft R
X.RE
X
Xarrays are assigned using the following syntax:
X
X.XS
X@name = ('value1', 'value2', \*(..) ;
X.XE
X
XIf the <\fIarg-name\fP> field of an argument is "\f4ARGV\fP"
Xthen the argument corresponds to the positional parameters
Xof the script and may be unset.
X
XA \fIperl\fP interface to \*(NM should have been installed in your
Xstandard perl library when \*(NM was installed. It may be used
Xby saying:
X
X.XS
Xrequire "cmdparse.pl" ;
X.XE
X
Xsomewhere in your perl-script. This will give you access to a perl function
Xnamed "\*(NM" which may be used as follows:
X
X.XS
Xeval &cmdparse("\-decls=$ARGDECLS", $0, @ARGV);
X.XE
X
XWhere \f4$ARGDECLS\fP is a variable containing a string of command-line
Xargument declarations for \*(NM(1). The arguments to the perl function
Xshould be a vector of arguments to pass to \*(NM(1) on the command-line.
XThe file \f4cmdparse.pl\fP in your \fIperl\fP library directory contains
Xthe implementation and documentation for the \fIperl\fP interface to \*(NM.
X
XIf a syntax error occurred on the command-line and \fB\-noabort\fP was
Xnot specified then the \*(NM function will terminate the execution of the
X\fIperl\fP script and will \s-1NOT\s+1 return to the caller.
X
X.RE
X.\"-----------------------------------
X.SS TCL
X.RS
XFor Tcl, variables are assigned using the following syntax:
X
X.RS
X.ft 4
Xset name "value";
X.ft R
X.RE
X
Xarrays are assigned using the following syntax:
X
X.XS
Xset name [ list "value1" "value2" \*(.. ];
X.XE
X
XIf the <\fIarg-name\fP> field of an argument is "\f4argv\fP" or
X"\f4args\fP" then the argument corresponds to the positional
Xparameters of the script and may be unset.
X
XA \fItcl\fP interface to \*(NM should have been installed in your
Xstandard \fItcl\fP library when \*(NM was installed. It may be used
Xby saying:
X
X.XS
Xload "cmdparse.tcl" ;
X.XE
X
Xsomewhere in your \fItcl\fP script. This will give you access to a \fItcl\fP
Xprocedure named "\*(NM" which may be used as follows:
X
X.XS
Xeval [ cmdparse \-decls=$argDecls $scriptName $argv ];
X.XE
X
XWhere \f4$argDecls\fP is a variable containing a string of command-line
Xargument declarations for \*(NM(1).
X
XThe arguments to the \fItcl\fP \*(NM procedure are exactly the same as for
X\*(NM(1).
XThe file \f4cmdparse.tcl\fP in your \fItcl\fP library directory contains
Xthe implementation and documentation for the \fItcl\fP interface to \*(NM.
X
XIf a syntax error occurred on the command-line and \fB\-noabort\fP
Xwas not specified then the \*(NM procedure will terminate the execution of the
X\fItcl\fP script and will \s-1NOT\s+1 return to the caller.
X
X
X.IP "\fBNote:\fP" 3
XThe \fItcl\fP \*(NM procedure will only work with \fItcl\fP scripts that use
Xa version of the \fItcl\fP shell that contains the \f4execl\fP command!
X
X.RE
X.\"===================================
X.so environ.man
X.\"===================================
X.SH FILES
X.IP \f4\*b/cmdparse\fP
XThe executable file for \*(NM(1).
X.IP \f4\*p/cmdparse.pl\fP
XThe \fIperl\fP interface (including documentation) to \*(NM(1).
X.IP \f4\*t/cmdparse.tcl\fP
XThe \fItcl\fP interface (including documentation) to \*(NM(1).
X.\"===================================
X.SH SEE ALSO
X\f4CmdLine\fP(3\*(C+), \f4cmdargs\fP(3\*(C+)
X.\"===================================
X.so caveats.man
X.\"===================================
X.so bugs.man
X.\"===================================
X.SH AUTHOR
XBrad Appleton, Harris Computer Systems, <\f4brad@ssd.csd.harris.com\fP>.
END_OF_FILE
if test 21692 -ne `wc -c <'doc/cmdparse.man1'`; then
echo shar: \"'doc/cmdparse.man1'\" unpacked with wrong size!
fi
# end of 'doc/cmdparse.man1'
fi
if test -f 'src/lib/cmdargs.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/lib/cmdargs.c'\"
else
echo shar: Extracting \"'src/lib/cmdargs.c'\" \(16718 characters\)
sed "s/^X//" >'src/lib/cmdargs.c' <<'END_OF_FILE'
X//------------------------------------------------------------------------
X// ^FILE: cmdargs.c - implement the various predefined CmdArg subclasses
X//
X// ^DESCRIPTION:
X// This file implements the CmdArg derived classes that are declared
X// in <cmdargs.h>
X//
X// ^HISTORY:
X// 03/25/92 Brad Appleton <brad@ssd.csd.harris.com> Created
X//-^^---------------------------------------------------------------------
X
X#include <stdlib.h>
X#include <iostream.h>
X#include <string.h>
X#include <ctype.h>
X
X#include "cmdargs.h"
X#include "exits.h"
X#include "fifolist.h"
X
X // return values for operator()
Xenum { SUCCESS = 0, FAILURE = -1 } ;
X
X
X//-----------------------------------------------------------------------------
X// ^FUNCTION: compile, operator() - handle an argument from the command-line
X//
X// ^SYNOPSIS:
X// int operator()(arg, cmd);
X// int compile(arg, cmd, value);
X// int compile(arg, cmd, value, default_value);
X//
X// ^PARAMETERS:
X// const char * & arg;
X// -- the prospective value for this command argument.
X// upon returning this value should be updated to point to the first
X// character of "arg" that was NOT used as part of the value for this
X// argument (set "arg" to NULL if all of it was used).
X//
X// CmdLine & cmd;
X// -- the command that matched this argument on its command-line
X//
X// <Type> & value;
X// -- The internal value (of some appropriate type) that is "managed"
X// by this command argument.
X//
X// unsigned default_value;
X// -- What to assign to "value" if "arg" is NOT a value for this command
X// argument.
X//
X// ^DESCRIPTION:
X// These member functions are responsible for taking whatever action
X// is appropriate when its corresponding command argument is matched
X// on the command-line. For argument-types that simply "compile"
X// their argument into some kind of internal value, "compile()" does
X// all the work and operator() merely calls compile() with the proper
X// value as a reference parameter.
X//
X// ^REQUIREMENTS:
X// The "arg_flags" data member of this command-argument must have been
X// set appropriately (by "cmd") to indicate to us exactly how "arg" was
X// specified on the command-line for this (and only this) occurrence of
X// "arg".
X//
X// ^SIDE-EFFECTS:
X// - If (cmd.flags() & QUIET) is NOT TRUE and FAILURE is to be returned,
X// then error messages should be printed using cmd.error().
X//
X// - arg is modified to be NULL of to point to the unused portion of itself.
X//
X// - If (cmd.flags() & TEMP) is TRUE and we need the value of "arg"
X// to stick around, then storage is allocated in order to make
X// a copy of "arg" (and the command-argument is responsible for
X// de-allocating this storage).
X//
X// ^RETURN-VALUE:
X// FAILURE (non-zero) If something went wrong when performing the
X// desired actions for this command-argument.
X// A common problem would be that "arg" is
X// syntactically incorrect.
X//
X// SUCCESS (zero) If "arg" is NULL and/or we were able to succesfully
X// perform all desired actions for this command argument.
X//-^^--------------------------------------------------------------------------
X
X
X//-------------------------------------------------------------- Dummy Argument
X
XCmdArgDummy::~CmdArgDummy(void) {}
X
Xint
XCmdArgDummy::is_dummy(void) { return 1; }
X
X // For a CmdArgDummy - operator() is a No-OP and should NEVER
X // be called.
X //
Xint
XCmdArgDummy::operator()(const char * & , CmdLine & )
X{
X return SUCCESS;
X}
X
X//-------------------------------------------------------------- Usage Argument
X
XCmdArgUsage::~CmdArgUsage(void) {}
X
X // Just need to call cmd.usage and exit.
X //
Xint
XCmdArgUsage::operator()(const char * & , CmdLine & cmd)
X{
X cmd.usage(cmd.error(CmdLine::NOPRINT), CmdLine::VERBOSE_USAGE);
X ::exit(e_USAGE);
X return SUCCESS; // get the compiler to shut up about NO return value!
X}
X
X//----------------------------------------------------------- Integer Arguments
X
XCmdArgIntCompiler::~CmdArgIntCompiler(void) {}
X
X // Compile a string into an integer value.
Xint
XCmdArgIntCompiler::compile(const char * & arg, CmdLine & cmd, int & value)
X{
X const char * ptr = NULL ;
X long result = 0 ;
X
X if (arg == NULL) {
X return SUCCESS ; // no value given - nothing to do
X } else if (! *arg) {
X if (! (cmd.flags() & CmdLine::QUIET)) {
X cmd.error() << "empty integer value specified." << endl ;
X }
X return FAILURE ;
X }
X
X // compile the string into an integer
X result = ::strtol(arg, (char **) &ptr, 0); // watch out for -c0xa vs -axc0!
X if (ptr == arg) {
X // do we have a valid integer?
X if (! (cmd.flags() & CmdLine::QUIET)) {
X cmd.error() << "invalid integer value \"" << arg << "\"." << endl ;
X }
X return FAILURE ;
X }
X value = (int) result;
X arg = ptr;
X
X return SUCCESS ;
X}
X
X
XCmdArgInt::~CmdArgInt(void) {}
X
Xint
XCmdArgInt::operator()(const char * & arg, CmdLine & cmd)
X{
X return compile(arg, cmd, val);
X}
X
X
Xostream &
Xoperator<<(ostream & os, const CmdArgInt & int_arg)
X{
X return (os << (int) int_arg) ;
X}
X
X//---------------------------------------------------- Floating-point Arguments
X
XCmdArgFloatCompiler::~CmdArgFloatCompiler(void) {}
X
X // Compile a string into a floating-point value.
Xint
XCmdArgFloatCompiler::compile(const char * & arg, CmdLine & cmd, float & value)
X{
X const char * ptr = NULL ;
X double result = 0 ;
X
X if (arg == NULL) {
X return SUCCESS ; // no value given -- nothing to do
X } else if (! *arg) {
X if (! (cmd.flags() & CmdLine::QUIET)) {
X cmd.error() << "empty floating-point value specified." << endl ;
X }
X return FAILURE ;
X }
X
X result = ::strtod(arg, (char **) &ptr); // compile the string into a float
X if (ptr == arg) {
X // do we have a valid float?
X if (! (cmd.flags() & CmdLine::QUIET)) {
X cmd.error() << "invalid floating-point value \"" << arg << "\"."
X << endl ;
X }
X return FAILURE ;
X }
X value = (float) result;
X arg = ptr;
X
X return SUCCESS ;
X}
X
X
XCmdArgFloat::~CmdArgFloat(void) {}
X
Xint
XCmdArgFloat::operator()(const char * & arg, CmdLine & cmd)
X{
X return compile(arg, cmd, val);
X}
X
X
Xostream &
Xoperator<<(ostream & os, const CmdArgFloat & float_arg)
X{
X return (os << (float) float_arg) ;
X}
X
X//--------------------------------------------------------- Character Argumrnts
X
XCmdArgCharCompiler::~CmdArgCharCompiler(void) {}
X
Xint
XCmdArgCharCompiler::compile(const char * & arg, CmdLine & cmd, char & value)
X{
X if (arg == NULL) {
X return SUCCESS ; // no value given - nothing to do
X }
X
X // If "arg" contains more than 1 character, then the other characters
X // are either extraneous, or they are options (bundled together).
X //
X if (*arg && *(arg+1) &&
X ((! (flags() & CmdArg::OPTION)) || (flags() & CmdArg::VALSEP)))
X {
X if (! (cmd.flags() & CmdLine::QUIET)) {
X cmd.error() << "invalid character value \"" << arg << "\"." << endl ;
X }
X return FAILURE ;
X }
X
X value = *arg;
X if (*arg) {
X ++arg;
X } else {
X arg = NULL;
X }
X
X return SUCCESS ;
X}
X
X
XCmdArgChar::~CmdArgChar(void) {}
X
Xint
XCmdArgChar::operator()(const char * & arg, CmdLine & cmd)
X{
X return compile(arg, cmd, val);
X}
X
Xostream &
Xoperator<<(ostream & os, const CmdArgChar & char_arg)
X{
X return (os << (char) char_arg) ;
X}
X
X//------------------------------------------------------------ String Arguments
X
Xtypedef CmdArgStrCompiler::string CmdArgString ;
X
XCmdArgString::~string(void)
X{
X if (is_alloc) delete [] (char *)str;
X}
X
X // Copy a string (allocating storage if necessary)
Xvoid
XCmdArgString::copy(unsigned is_temporary, const char * s)
X{
X if (is_alloc) delete (char *)str;
X is_alloc = (is_temporary) ? 1 : 0;
X str = s;
X if (is_alloc && s) {
X char * new_s = new char[::strlen(s) + 1] ;
X (void) ::strcpy(new_s, s);
X str = new_s;
X }
X}
X
X
XCmdArgStrCompiler::~CmdArgStrCompiler(void) {}
X
Xint
XCmdArgStrCompiler::compile(const char * & arg,
X CmdLine & cmd,
X CmdArgString & value)
X{
X if (arg == NULL) {
X return SUCCESS; // no value given -- nothing to do
X }
X
X value.copy((cmd.flags() & CmdLine::TEMP), arg);
X arg = NULL;
X
X return SUCCESS;
X}
X
X
XCmdArgStr::~CmdArgStr(void) {}
X
Xint
XCmdArgStr::operator()(const char * & arg, CmdLine & cmd)
X{
X return compile(arg, cmd, val);
X}
X
Xostream &
Xoperator<<(ostream & os, const CmdArgStrCompiler::string & str)
X{
X return (os << str.str) ;
X}
X
Xostream &
Xoperator<<(ostream & os, const CmdArgStr & str_arg)
X{
X return (os << (const char *) str_arg) ;
X}
X
X//-------------------------------------------------------------- List Arguments
X
X //------------------- Integer List -------------------
X
XDECLARE_FIFO_LIST(IntList, int);
X
Xstruct CmdArgIntListPrivate {
X IntList list;
X IntListArray array;
X
X CmdArgIntListPrivate(void);
X} ;
X
X
XCmdArgIntListPrivate::CmdArgIntListPrivate(void)
X : array(list)
X{
X list.self_cleaning(1);
X}
X
X // Compile the argument into an integer and append it to the list
Xint
XCmdArgIntList::operator()(const char * & arg, CmdLine & cmd)
X{
X int value;
X const char * save_arg = arg;
X int rc = compile(arg, cmd, value);
X if (save_arg && (rc == SUCCESS)) {
X if (val == NULL) val = new CmdArgIntListPrivate;
X int * new_value = new int;
X *new_value = value;
X val->list.add(new_value);
X }
X return rc;
X}
X
Xunsigned
XCmdArgIntList::count(void) const
X{
X return (val) ? val->list.count() : 0 ;
X}
X
Xint &
XCmdArgIntList::operator[](unsigned index)
X{
X return val->array[index];
X}
X
XCmdArgIntList::~CmdArgIntList(void) {}
X
X
X //------------------- Float List -------------------
X
X
XDECLARE_FIFO_LIST(FloatList, float);
X
Xstruct CmdArgFloatListPrivate {
X FloatList list;
X FloatListArray array;
X
X CmdArgFloatListPrivate(void);
X} ;
X
XCmdArgFloatListPrivate::CmdArgFloatListPrivate(void)
X : array(list)
X{
X list.self_cleaning(1);
X}
X
X
X // Compile the argument into a float and append it to the list
Xint
XCmdArgFloatList::operator()(const char * & arg, CmdLine & cmd)
X{
X float value;
X const char * save_arg = arg;
X int rc = compile(arg, cmd, value);
X if (save_arg && (rc == SUCCESS)) {
X if (val == NULL) val = new CmdArgFloatListPrivate;
X float * new_value = new float;
X *new_value = value;
X val->list.add(new_value);
X }
X return rc;
X}
X
Xunsigned
XCmdArgFloatList::count(void) const
X{
X return (val) ? val->list.count() : 0 ;
X}
X
Xfloat &
XCmdArgFloatList::operator[](unsigned index)
X{
X return val->array[index];
X}
X
XCmdArgFloatList::~CmdArgFloatList(void) {}
X
X //------------------- String List -------------------
X
XDECLARE_FIFO_LIST(StringList, CmdArgString);
X
Xstruct CmdArgStrListPrivate {
X StringList list;
X StringListArray array;
X
X CmdArgStrListPrivate(void);
X} ;
X
XCmdArgStrListPrivate::CmdArgStrListPrivate(void)
X : array(list)
X{
X list.self_cleaning(1);
X}
X
Xint
XCmdArgStrList::operator()(const char * & arg, CmdLine & cmd)
X{
X CmdArgString * value = new CmdArgString ;
X const char * save_arg = arg;
X int rc = compile(arg, cmd, *value);
X if (save_arg && (rc == SUCCESS)) {
X if (val == NULL) val = new CmdArgStrListPrivate;
X val->list.add(value);
X } else {
X delete value;
X }
X return rc;
X}
X
Xunsigned
XCmdArgStrList::count(void) const
X{
X return (val) ? val->list.count() : 0 ;
X}
X
XCmdArgString &
XCmdArgStrList::operator[](unsigned index)
X{
X return val->array[index];
X}
X
XCmdArgStrList::~CmdArgStrList(void) {}
X
X//----------------------------------------------------------- Boolean Arguments
X
XCmdArgBoolCompiler::~CmdArgBoolCompiler(void) {}
X
Xint
XCmdArgBoolCompiler::compile(const char * & arg,
X CmdLine & cmd,
X unsigned & value,
X unsigned default_value)
X{
X if (arg == NULL) {
X // if no argument was given use the default
X value = default_value ;
X } else {
X char ch = *arg;
X const char * kwd = arg++;
X
X // Map the argument to the corresponding value. We will accept
X // the following (case insensitive):
X //
X // "+", "1", "ON", or "YES" means set the value
X // "-", "0", "OFF", or "NO" means clear the value
X // "~", "^", or "!" means toggle the value
X //
X // Anything else is considered to be an argument that is NOT
X // meant for us but for some other argument so we just use the
X // default value that was supplied and return SUCCESS.
X //
X if (isupper(ch)) ch = tolower(ch);
X switch(ch) {
X case '1' :
X case '+' : value = 1 ; break;
X
X case '0' :
X case '-' : value = 0 ; break;
X
X case '~' :
X case '^' :
X case '!' : value = (! value) ; break;
X
X default:
X if (flags() & CmdArg::KEYWORD) {
X char ch2 = *arg;
X arg = NULL;
X if (cmd.strmatch(kwd, "yes") != CmdLine::str_NONE) {
X value = 1 ;
X return SUCCESS ;
X } else if (cmd.strmatch(kwd, "no") != CmdLine::str_NONE) {
X value = 0 ;
X return SUCCESS ;
X } else if (cmd.strmatch(kwd, "true") != CmdLine::str_NONE) {
X value = 1 ;
X return SUCCESS ;
X } else if (cmd.strmatch(kwd, "false") != CmdLine::str_NONE) {
X value = 0 ;
X return SUCCESS ;
X } else if ((ch == 'o') && (! ch2)) {
X // ambiguous - could be "ON" or "OFF"
X if (! (cmd.flags() & CmdLine::QUIET)) {
X cmd.error() << "ambiguous boolean value \"" << kwd
X << "\"." << endl ;
X }
X return FAILURE ;
X } else if (cmd.strmatch(kwd, "on") != CmdLine::str_NONE) {
X value = 1 ;
X return SUCCESS ;
X } else if (cmd.strmatch(kwd, "off") != CmdLine::str_NONE) {
X value = 0 ;
X return SUCCESS ;
X } else {
X // unknown
X if (! (cmd.flags() & CmdLine::QUIET)) {
X cmd.error() << "unknown boolean value \"" << kwd
X << "\"." << endl ;
X }
X return FAILURE ;
X }
X } //if keyword
X arg = kwd; // no characters used!
X value = default_value ;
X break;
X } //switch
X } //else
X
X return SUCCESS ;
X}
X
Xostream &
Xoperator<<(ostream & os, const CmdArgBool & bool_arg)
X{
X return (os << ((int)bool_arg)) ;
X}
X
X//------------------------------------------------------------------ CmdArgBool
X
XCmdArgBool::~CmdArgBool(void) {}
X
Xint
XCmdArgBool::operator()(const char * & arg, CmdLine & cmd)
X{
X unsigned value = val;
X int rc = compile(arg, cmd, value, 1);
X val = value;
X return rc;
X}
X
X//----------------------------------------------------------------- CmdArgClear
X
XCmdArgClear::~CmdArgClear(void) {}
X
Xint
XCmdArgClear::operator()(const char * & arg, CmdLine & cmd)
X{
X unsigned value = val;
X int rc = compile(arg, cmd, value, 0);
X val = value;
X return rc;
X}
X
X//---------------------------------------------------------------- CmdArgToggle
X
XCmdArgToggle::~CmdArgToggle(void) {}
X
Xint
XCmdArgToggle::operator()(const char * & arg, CmdLine & cmd)
X{
X unsigned value = val;
X int rc = compile(arg, cmd, value, (! value));
X val = value;
X return rc;
X}
X
X//--------------------------------------------------------------- CmdArgBoolRef
X
XCmdArgBoolRef::~CmdArgBoolRef(void) {}
X
Xint
XCmdArgBoolRef::operator()(const char * & arg, CmdLine & cmd)
X{
X unsigned val = ref;
X int rc = ref.compile(arg, cmd, val, 1);
X ref = val;
X return rc;
X}
X
X//-------------------------------------------------------------- CmdArgClearRef
X
XCmdArgClearRef::~CmdArgClearRef(void) {}
X
Xint
XCmdArgClearRef::operator()(const char * & arg, CmdLine & cmd)
X{
X unsigned val = ref;
X int rc = ref.compile(arg, cmd, val, 0);
X ref = val;
X return rc;
X}
X
X//------------------------------------------------------------- CmdArgToggleRef
X
XCmdArgToggleRef::~CmdArgToggleRef(void) {}
X
Xint
XCmdArgToggleRef::operator()(const char * & arg, CmdLine & cmd)
X{
X unsigned val = ref;
X int rc = ref.compile(arg, cmd, val, (! val));
X ref = val;
X return rc;
X}
X
END_OF_FILE
if test 16718 -ne `wc -c <'src/lib/cmdargs.c'`; then
echo shar: \"'src/lib/cmdargs.c'\" unpacked with wrong size!
fi
# end of 'src/lib/cmdargs.c'
fi
if test -f 'src/lib/private.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/lib/private.c'\"
else
echo shar: Extracting \"'src/lib/private.c'\" \(19806 characters\)
sed "s/^X//" >'src/lib/private.c' <<'END_OF_FILE'
X//------------------------------------------------------------------------
X// ^FILE: private.c - private/protected functions used by the CmdLine library
X//
X// ^DESCRIPTION:
X// This file implements functions that are for the exclusive use of
X// the CmdLine library. The following functions are implemented:
X//
X// ck_need_val() -- see if we left an argument without a value
X// handle_arg() -- compile the string value of an argument
X// syntax() -- find out the desired syntax for usage messages
X// missing_args() -- check for missing required arguments
X// opt_match() -- match an option
X// kwd_match() -- match a keyword
X// pos_match() -- match a positional parameter
X//
X// ^HISTORY:
X// 01/09/92 Brad Appleton <brad@ssd.csd.harris.com> Created
X//-^^---------------------------------------------------------------------
X
X#include <iostream.h>
X#include <strstream.h>
X#include <fstream.h>
X#include <stdlib.h>
X#include <string.h>
X#include <ctype.h>
X
Xextern "C" {
X int isatty(int fd);
X
X#ifdef GNU_READLINE
X# include <readline.h>
X#endif
X
X}
X
X#ifndef GNU_READLINE
X# ifdef unix
X# include <malloc.h>
X# else
X extern "C" void * malloc(size_t);
X extern "C" void free(void *);
X# endif
X#endif
X
X
X#include "cmdline.h"
X#include "states.h"
X#include "arglist.h"
X
X
X// Need a portable version of tolower
X//
X// NOTE:: I would make this inline except that cfront refuses
X// to inline it because it is used twice in expressions
X//
X#define TO_LOWER(c) ((isupper(c)) ? tolower(c) : c)
X
X#ifdef vms
X# define getenv getsym
X extern const char * getsym(const char *);
X#endif
X
X//-------
X// ^FUNCTION: CmdLine::handle_arg - compile the string value of an argument
X//
X// ^SYNOPSIS:
X// extern int CmdLine::handle_arg(cmdarg, arg);
X//
X// ^PARAMETERS:
X// CmdArg * cmdarg;
X// -- the matched argument whose value is to be "handled"
X//
X// const char * & arg;
X// -- the string value for the argument (from the command-line).
X// upon exit, this will be NULL (if all of "arg" was used) or will
X// point to the first character or "arg" that was not used by the
X// argument's "compile" function.
X//
X// ^DESCRIPTION:
X// After we have matched an argument on the command-line to an argument
X// in the "cmd" object, we need to "handle" the value supplied for that
X// argument. This entails updating the state of the argument and calling
X// its "compile" function as well as updating the state of the command.
X//
X// ^REQUIREMENTS:
X// None that weren't covered in the PARAMETERS section.
X//
X// ^SIDE-EFFECTS:
X// - modifies the value pointed to by "arg"
X// - prints a message on stderr if "arg" is invalid and QUIET is NOT set.
X// - modifies the state of "cmd".
X// - modifies the "value" and "flags" of "cmdarg".
X//
X// ^RETURN-VALUE:
X// The value returned by calling the "compile" function associated
X// with the argument "cmdarg".
X//
X// ^ALGORITHM:
X// - if this is a cmdargUsage argument then print usage and call exit(3C)
X// - call the operator() of "cmdarg" and save the result.
X// - if the above call returned SUCCESS then set the GIVEN and VALGIVEN
X// flags of the argument.
X// - update the parse_state of "cmd" if we were waiting for this value.
X// - if "cmdarg" corresponds to a LIST then set things up so that succeeding
X// arguments will be values for this "cmdarg"'s list.
X//-^^----
Xint
XCmdLine::handle_arg(CmdArg * cmdarg, const char * & arg)
X{
X int bad_val ;
X
X // call the argument compiler
X const char * save_arg = arg ; // just in case someone forgets to set it
X bad_val = (*cmdarg)(arg, *this);
X if (! bad_val) {
X cmdarg->set(CmdArg::GIVEN) ;
X if (arg != save_arg) {
X cmdarg->set(CmdArg::VALGIVEN) ;
X }
X }
X
X // if we were waiting for a value - we just got it
X if (arg != save_arg) {
X if (cmdarg == cmd_matched_arg) cmd_parse_state = cmd_START_STATE ;
X }
X
X // if this is a list - optional values may follow the one given
X if ((cmdarg->syntax() & CmdArg::isLIST) && (arg != save_arg)) {
X cmd_matched_arg = cmdarg ;
X cmd_parse_state = cmd_WANT_VAL ;
X }
X
X return bad_val ;
X}
X
X
X//-------
X// ^FUNCTION: CmdLine::ck_need_val - See if an argument needs a value
X//
X// ^SYNOPSIS:
X// extern void CmdLine::ck_needval(void)
X//
X// ^PARAMETERS:
X// NONE.
X//
X// ^DESCRIPTION:
X// We parse command-lines using something akin to a deterministic
X// finite state machine. Each argv[] element on the command-line is
X// considered a single input to the machine and we keep track of an
X// associated machine-state that tells us what to do next for a given
X// input.
X//
X// In this function, we are merely trying to query the "state" of the
X// machine by asking it if it is expecting to see a value for an
X// argument that was matched in a previous argv[] element.
X//
X// It is assumed that this function is called only after it has already
X// been determined that the current argv[] element is NOT an argument
X// value.
X//
X// ^REQUIREMENTS:
X//
X// ^SIDE-EFFECTS:
X// - updates the "state" of the command.
X// - updates the "status" of the command.
X// - modifies the last matched argument if it takes an optional value.
X// - prints a message on stderr if cmd_QUIET is NOT set and we were
X// expecting a required value.
X//
X// ^RETURN-VALUE:
X// None.
X//
X// ^ALGORITHM:
X// If we were expecting an optional value then
X// - set the GIVEN flag of the last arg we matched (DO NOT set VALGIVEN).
X// - call the compiler-function of the last-matched arg using NULL
X// as the argument (unless the arg is a LIST and VALGIVEN is set).
X// - reset the command-state
X// Else if we were expecting a required value then
X// - print a an error message if cmd_QUIET is not set
X// - set the command-status to VAL_MISSING
X// - reset the command-state
X// Endif
X//-^^----
Xvoid
XCmdLine::ck_need_val(void)
X{
X const char * null_str = NULL;
X if (cmd_parse_state == cmd_WANT_VAL) {
X // argument was given but optional value was not
X cmd_matched_arg->set(CmdArg::GIVEN) ;
X if ((! (cmd_matched_arg->syntax() & CmdArg::isLIST)) ||
X (! (cmd_matched_arg->flags() & CmdArg::VALGIVEN))) {
X (void) handle_arg(cmd_matched_arg, null_str) ;
X }
X cmd_parse_state = cmd_START_STATE ;
X } else if (cmd_parse_state == cmd_NEED_VAL) {
X // argument was given but required value was not
X if (! (cmd_flags & QUIET)) {
X arg_error("value required for", cmd_matched_arg) << "." << endl ;
X }
X cmd_status |= VAL_MISSING ;
X cmd_parse_state = cmd_START_STATE ;
X }
X}
X
X
X#ifndef GNU_READLINE
X//
X// readline() -- indigent person's version of the GNU readline() function
X//
X
X#define PROMPT_BUFSIZE 256
X
Xstatic char *
Xreadline(const char * prompt)
X{
X char * buf = (char *) ::malloc(PROMPT_BUFSIZE);
X if (buf == NULL) return NULL ;
X *buf = '\0';
X
X // prompt the user and collect input
X cerr << prompt << flush ;
X cin.getline(buf, PROMPT_BUFSIZE);
X
X return buf ;
X}
X#endif // ! GNU_READLINE
X
X
X//-------
X// ^FUNCTION: CmdLine::prompt_user - prompt the user for a missing argument
X//
X// ^SYNOPSIS:
X// unsigned CmdLine::prompt_user(cmdarg);
X//
X// ^PARAMETERS:
X// CmdArg * cmdarg;
X// -- the argument that we need to prompt for
X//
X// ^DESCRIPTION:
X// If cin is connected to a terminal, then we will prompt the user
X// for an argument corresponding to "cmdarg" and attempt to "compile" it
X// into the desired internal format. The user only has one chance
X// to get the "argument" right; we do not continue prompting if the
X// value that was entered is invalid.
X//
X// ^REQUIREMENTS:
X// "cmdarg" should be a REQUIRED argument that has already been determined
X// to be missing from the command-line.
X//
X// ^SIDE-EFFECTS:
X// - modifies the status of the command.
X// - modifies "cmdarg".
X// - prints a prompt on cerr and reads from cin
X//
X// ^RETURN-VALUE:
X// 0 if the argument was succesfully entered by the user,
X// ARG_MISSING otherwise.
X//
X// ^ALGORITHM:
X// - if cin is not a terminal return ARG_MISSING.
X// - if "cmdarg" is a LIST, make sure we prompt the use once for each
X// possible value in the list.
X// - prompt the user for an argument and read the result.
X// - if the user just typed <RETURN> return ARG_MISSING.
X// - "handle" the value that was entered.
X// - continue prompting if we are a LIST and a valid, non-empty,
X// value was given
X// - if an invalid value was given return ARG_MISSING
X// - else return 0
X//-^^----
Xunsigned
XCmdLine::prompt_user(CmdArg * cmdarg)
X{
X // dont prompt if cin or cerr is not interactive
X int fd = ((filebuf *)(cin.rdbuf()))->fd();
X if (! ::isatty(fd)) return ARG_MISSING ;
X
X fd = ((filebuf *)(cerr.rdbuf()))->fd();
X if (! ::isatty(fd)) return ARG_MISSING ;
X
X // if we have a list, need to prompt repeatedly
X if (cmdarg->syntax() & CmdArg::isLIST) {
X cerr << "Enter one " << cmdarg->value_name() << " per line "
X << "(enter a blank-line to stop)." << endl ;
X }
X char prompt[256], * buf = NULL;
X ostrstream oss(prompt, sizeof(prompt));
X oss << "\rEnter " << cmdarg->value_name() << ": " << ends ;
X int errs = 0, first = 1;
X do { // need repeated prompting for a LIST
X if (buf) ::free(buf);
X buf = ::readline(prompt) ;
X if (buf == NULL) return ARG_MISSING ;
X
X // make sure we read something!
X if (! *buf) {
X if (first) {
X error() << "error - no " << cmdarg->value_name()
X << " given!" << endl ;
X ++errs;
X }
X continue;
X }
X
X#ifdef GNU_READLINE
X // add this line to the history list
X ::add_history(buf);
X#endif
X
X // try to handle the value we read (remember - buf is temporary)
X if (! errs) {
X const char * arg = buf;
X unsigned save_cmd_flags = cmd_flags;
X cmd_flags |= TEMP;
X errs = handle_arg(cmdarg, arg);
X if (errs) {
X arg_error("bad value for", cmdarg) << "." << endl ;
X }
X cmd_flags = save_cmd_flags;
X }
X
X first = 0;
X } while (!errs && (cmdarg->syntax() & CmdArg::isLIST) && *buf);
X
X if (! errs) cmdarg->set(CmdArg::VALSEP);
X
X if (buf) ::free(buf);
X return (errs) ? ARG_MISSING : NO_ERROR ;
X}
X
X//-------
X// ^FUNCTION: CmdLine::syntax - determine usage message syntax
X//
X// ^SYNOPSIS:
X// CmdLine::CmdLineSyntax CmdLine::syntax(void);
X//
X// ^PARAMETERS:
X//
X// ^DESCRIPTION:
X// One of the things we keep track of in the CmdLine object is whether
X// options and/or keywords (long-options) were used on the command-line.
X// If a command-line syntax error occurs and only options (keywords)
X// were used then the usage message will only contain option (keyword)
X// syntax. If BOTH were used or if usage was specifically requested via
X// a cmdargUsage option (which we also keep track of) then we want the
X// the usage message to contain the syntac for both options and keywords.
X//
X// If neither options nor keywords were given (meaning only positional
X// parameters were used) then we only use option-syntax (for brevity).
X//
X// ^REQUIREMENTS:
X//
X// ^SIDE-EFFECTS:
X// None.
X//
X// ^RETURN-VALUE:
X// The desired usage message syntax to use.
X//
X// ^ALGORITHM:
X// Trivial.
X//-^^----
XCmdLine::CmdLineSyntax
XCmdLine::syntax(void) const
X{
X if (cmd_flags & KWDS_ONLY) {
X return cmd_KWDS_ONLY;
X } else if (cmd_flags & OPTS_ONLY) {
X return cmd_OPTS_ONLY;
X } else if ((cmd_state & cmd_OPTIONS_USED) &&
X (cmd_state & cmd_KEYWORDS_USED)) {
X return cmd_BOTH ;
X } else if (cmd_state & cmd_KEYWORDS_USED) {
X return cmd_KWDS_ONLY ;
X } else {
X return cmd_OPTS_ONLY ;
X }
X}
X
X
X//-------
X// ^FUNCTION: CmdLine::missing_args - check for missing required arguments
X//
X// ^SYNOPSIS:
X// unsigned CmdLine::missing_args(void);
X//
X// ^PARAMETERS:
X//
X// ^DESCRIPTION:
X// This function checks to see if there is a required argument in the
X// CmdLine object that was NOT specified on the command. If this is
X// the case and PROMPT_USER is set (or $PROMPT_USER exists and is
X// non-empty) then we attempt to prompt the user for the missing argument.
X//
X// ^REQUIREMENTS:
X//
X// ^SIDE-EFFECTS:
X// - modifies the status of "cmd".
X// - terminates execution by calling exit(3C) if cmd_NOABORT is NOT
X// set and a required argument (that was not properly supplied by
X// the user) is not given.
X// - prints on stderr if an argument is missing and cmd_QUIET is NOT set.
X// - also has the side-effects of prompt_user() if we need to prompt
X// the user for input.
X//
X// ^RETURN-VALUE:
X// The current value of the (possibly modified) command status. This is a
X// combination of bitmasks of type cmdline_flags_t defined in <cmdline.h>
X//
X// ^ALGORITHM:
X// Foreach argument in cmd
X// if argument is required and was not given
X// if required, prompt for the missing argument
X// if prompting was unsuccesful add ARG_MISSING to cmd-status
X// endif
X// else add ARG_MISSING to cmd-status
X// endif
X// endif
X// endfor
X// return the current cmd-status
X//-^^----
Xunsigned
XCmdLine::missing_args(void)
X{
X char buf[256];
X
X CmdArgListListIter list_iter(cmd_args);
X for (CmdArgList * alist = list_iter() ; alist ; alist = list_iter()) {
X CmdArgListIter iter(alist);
X for (CmdArg * cmdarg = iter() ; cmdarg ; cmdarg = iter()) {
X if (cmdarg->is_dummy()) continue;
X if ((cmdarg->syntax() & CmdArg::isREQ) &&
X (! (cmdarg->flags() & CmdArg::GIVEN)))
X {
X if (! (cmd_flags & QUIET)) {
X fmt_arg(cmdarg, buf, sizeof(buf), syntax(), TERSE_USAGE);
X error() << buf << " required." << endl ;
X }
X if (cmd_status & ARG_MISSING) {
X // user didnt supply the missing argument
X return cmd_status ;
X } else if ((! (cmd_flags & NO_ABORT)) && cmd_status) {
X // other problems
X return cmd_status ;
X } else if (cmd_flags & PROMPT_USER) {
X cmd_status |= prompt_user(cmdarg);
X } else {
X char * env = ::getenv("PROMPT_USER");
X if (env && *env) {
X cmd_status |= prompt_user(cmdarg);
X } else {
X cmd_status |= ARG_MISSING ;
X }
X }
X } //if
X } //for iter
X } //for list_iter
X
X return cmd_status ;
X}
X
X
X//-------
X// ^FUNCTION: CmdLine::opt_match - attempt to match on option
X//
X// ^SYNOPSIS:
X// CmdArg * CmdLine::opt_match(optchar);
X//
X// ^PARAMETERS:
X// char optchar;
X// -- a possible option for "cmd"
X//
X// ^DESCRIPTION:
X// If "cmd" has an argument that has "optchar" as a single-character
X// option then this function will find and return that argument.
X//
X// ^REQUIREMENTS:
X//
X// ^SIDE-EFFECTS:
X// None.
X//
X// ^RETURN-VALUE:
X// If we find a match, then we return a pointer to its argdesc,
X// otherwise we return NULL.
X//
X// ^ALGORITHM:
X// Trivial.
X//-^^----
XCmdArg *
XCmdLine::opt_match(char optchar) const
X{
X CmdArgListListIter list_iter(cmd_args);
X for (CmdArgList * alist = list_iter() ; alist ; alist = list_iter()) {
X CmdArgListIter iter(alist);
X for (CmdArg * cmdarg = iter() ; cmdarg ; cmdarg = iter()) {
X if (cmdarg->is_dummy()) continue;
X if (optchar == cmdarg->char_name()) {
X // exact match
X return cmdarg ;
X } else if (cmd_flags & ANY_CASE_OPTS) {
X // case-insensitive match
X if (TO_LOWER(optchar) == TO_LOWER(cmdarg->char_name())) {
X return cmdarg ;
X }
X }
X } //for iter
X } //for list_iter
X return NULL ;
X}
X
X//-------
X// ^FUNCTION: CmdLine::kwd_match - purpose
X//
X// ^SYNOPSIS:
X// extern CmdArg * CmdLine::kwd_match(kwd, len, is_ambiguous, match_value);
X//
X// ^PARAMETERS:
X// const char * kwd;
X// -- a possible kewyord of "cmd"
X//
X// int len;
X// -- the number of character of "kwd" to consider (< 0 if all characters
X// of "kwd" should be used).
X//
X// int & is_ambiguous;
X// -- upon return, the value pointed to is set to 1 if the keyword
X// matches more than 1 keyword in "cmd"; Otherwise it is set to 0.
X//
X// int match_value;
X// -- if this is non-zero, then if a keyword_name is NULL use the
X// value_name instead.
X//
X// ^DESCRIPTION:
X// If "cmd" has an argument that matches "kwd" as a kewyord
X// then this function will find and return that argument.
X//
X// ^REQUIREMENTS:
X//
X// ^SIDE-EFFECTS:
X// None.
X//
X// ^RETURN-VALUE:
X// If we find a match, then we return a pointer to its argdesc,
X// otherwise we return NULL.
X//
X// ^ALGORITHM:
X// Set is_ambigous to 0.
X// For each argument in cmd
X// if argument's keyword-name matches kwd then
X// if this was an exact match then return this argument
X// else if we had a previous partial match of this argument then
X// if argument is a default argument return the previous match
X// else set is_ambiguous to 1 and return NULL
X// else remember we had a partial match here and keep trying
X// endif
X// endif
X// end for
X// if we has a partial match and we get to here then it is NOT ambiguous do
X// go ahead and return the argument we matched.
X//-^^----
XCmdArg *
XCmdLine::kwd_match(const char * kwd,
X int len,
X int & is_ambiguous,
X int match_value) const
X{
X CmdArg * matched = NULL;
X
X is_ambiguous = 0 ;
X
X CmdArgListListIter list_iter(cmd_args);
X for (CmdArgList * alist = list_iter() ; alist ; alist = list_iter()) {
X CmdArgListIter iter(alist);
X for (CmdArg * cmdarg = iter() ; cmdarg ; cmdarg = iter()) {
X if (cmdarg->is_dummy()) continue;
X
X // attempt to match this keyword
X strmatch_t result ;
X const char * source = cmdarg->keyword_name();
X if (source && *source) {
X result = strmatch(source, kwd, len) ;
X } else if (match_value) {
X result = strmatch(cmdarg->value_name(), kwd, len) ;
X }
X
X if (result == str_EXACT) {
X return cmdarg ;
X } else if (result == str_PARTIAL) {
X if (matched) {
X is_ambiguous = 1 ;
X return NULL ; // ambiguous keyword
X }
X matched = cmdarg ; // we matched this one
X }
X } //for iter
X if (matched) break;
X } //for list_iter
X return matched ;
X}
X
X//-------
X// ^FUNCTION: CmdLine::pos_match - match a positional argument
X//
X// ^SYNOPSIS:
X// CmdArg * CmdLine::pos_match(void)
X//
X// ^PARAMETERS:
X//
X// ^DESCRIPTION:
X// If "cmd" has an positional argument that has not yet been given
X// then this function will find and return the first such argument.
X//
X// ^REQUIREMENTS:
X//
X// ^SIDE-EFFECTS:
X// None.
X//
X// ^RETURN-VALUE:
X// If we find a match, then we return a pointer to its argument,
X// otherwise we return NULL.
X//
X// ^ALGORITHM:
X// Trivial.
X//-^^----
XCmdArg *
XCmdLine::pos_match(void) const
X{
X CmdArgListListIter list_iter(cmd_args);
X for (CmdArgList * alist = list_iter() ; alist ; alist = list_iter()) {
X CmdArgListIter iter(alist);
X for (CmdArg * cmdarg = iter() ; cmdarg ; cmdarg = iter()) {
X if (cmdarg->is_dummy()) continue;
X if ((cmdarg->syntax() & CmdArg::isPOS) &&
X (! (cmdarg->flags() & CmdArg::GIVEN)))
X {
X return cmdarg ;
X }
X } //for iter
X } //for list_iter
X return NULL ;
X}
END_OF_FILE
if test 19806 -ne `wc -c <'src/lib/private.c'`; then
echo shar: \"'src/lib/private.c'\" unpacked with wrong size!
fi
# end of 'src/lib/private.c'
fi
echo shar: End of archive 5 \(of 7\).
cp /dev/null ark5isdone
MISSING=""
for I in 1 2 3 4 5 6 7 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 7 archives.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
exit 0 # Just in case...