/*************************************************************************
** ^FILE: parseargs.h -- definitions for argument parsing library
**
** ^DESCRIPTION:
**    This file contains all the necessary macros, type, and function
**    declarations necessary to use the parseargs library. For most purposes,
**    no special symbols need be #defined before including this file; however,
**    for implementors (and more sophisticated uses), the following symbols
**    may be #defined before including this file in order to include/exclude
**    various portions of the file:
**
**    PARSEARGS_PRIVATE
**       Include private definitions that are needed to implement/enhance
**       various functions in the parseargs library.
**
**    PARSEARGS_NEXTERNS
**       Do NOT include the external function declarations for the members
**       of the parseargs library.
**
**    PARSEARGS_NARGTYPES
**       Do NOT include the external function declarations any of the
**       pre-defined argument-type (argXxxx) functions.
**
** ^HISTORY:
**    12/03/90	Brad Appleton	<brad@ssd.csd.harris.com>
**    - Added all the #ifdef stuff
**    - Added public and private macros for getting and setting
**      the attributes of an argdesc-array and an argdesc
**
**    --/--/--	Peter da Silva	<peter@ferranti.com>
**
**    --/--/--	Eric P. Allman	<eric@Berkeley.EDU> 	Created
***^^**********************************************************************/

/* $Header: parseargs.h,v 2.0 89/12/24 00:56:29 eric Exp $ */

#ifndef PARSEARGS_H
#define  PARSEARGS_H

#include <useful.h>

   /* type definition for argument prompt strings */
#define  MAX_ARGNAME  80
typedef char argName_t[ MAX_ARGNAME ];

   /* type definition for bitmasks */
typedef unsigned short  argMask_t;

/**********************************************************************
** ^STRUCT: ARGDESC - argument-descriptor
**
** ^DESCRIPTION:
**    The basic type used by the parseargs library is the argument descriptor
**    (or "argdesc" for short). An ARGDESC structure is used to describe a
**    command-line argument. Each command line argument contains various
**    fields which need to be set and/or queried by the programmer. Each
**    field is described in further detail below:
*/ 
typedef struct _argdesc {
   char ad_name;
/*    -- This is a single character which corresponds to the option-letter
**       (case-sensitive) from the command-line that matches the argument
**       described by this structure. Positional-arguments are denoted by
**       putting a a space character in this field.
*/ 
   argMask_t  ad_flags;
/*    -- This field contains the various bitflags that describe the semantics
**       of this argument. See the ARGFLAGS section for more information on
**       the possible combinations of bitmasks for this field.
*/ 
   BOOL (*ad_type) ARGS((struct _argdesc *, char *, BOOL));
/*    -- This field is a pointer to a type conversion function (such as the
**       ones provided in argtype(3). The type conversion function is respon-
**       sible for verifying the validity of the argument, allocating any
**       necessary storage for its internal representation, and converting
**       the command-line argument into its required internal form. The type
**       conversion function used may be one of the pre-defined argtype(3)
**       functions. The function is given three parameters: The first is
**       a pointer to the ARGDESC struct in question, the second is the
**       string-value (if any) supplied on the command-line, and the third
**       is a boolean value that is TRUE only if the second parameter points
**       to temporary storage (and hence may need to be copied).
**       In the case of parseargs(1) this field must correspond to the name
**       of one of the argument type functions described in argtype(3).
*/ 
   ARBPTR ad_valp;
/*    -- This field is a generic pointer to the storage used to represent
**       the internal value of the command-line argument. It may be a
**       pointer to a number, a boolean value, a string, a list, or anything
**       else for which there exists a corresponding arg-type function to
**       use in the ad_type field. In the case of of parseargs(1) this field
**       must be the name of the corresponding shell variable which eventually
**       hold the value of the argument given on the command-line.
*/ 
   CONST char *ad_prompt;
/*    -- This field contains the long-name of the argument and an optional
**       description (the description must be separated from the long-name by
**       at least one whitespace characters and may optionally be enclosed in
**       a set of balanced delimiters (such as parentheses, curly-braces,
**       square-brackets, or angle-brackets. If the long-name contains any
**       uppercase characters, then the substring of long-name consisting of
**       all uppercase characters is used as the argument name and the entire
**       long-name is used as the name of the argument-value (if a value my be
**       supplied). The long-name may be matched by supplying a unique prefix
**       of either the argument name or the argument-value name.
*/ 
} ARGDESC;
/**^^**********************************************************************/


   /* define a NULL pointer to an arg-descriptor */
#define ARGDESCNULL   (ARGDESC *)NULL

   /* define a pointer to an argtype function */
typedef BOOL (*argTypePtr_t) ARGS((ARGDESC *, char *, BOOL));


/**********************************************************************
** ^SECTION: RETURN-CODES
**    The XparseXXXX functions in the parseargs library may return any of
**    the following return codes (which are #define'd below):
*/
#define  pe_SYSTEM   -1
/*    -- a system error occurred. The global variable errno may indicate
**       the problem (then again, it may not).
*/
#define  pe_SUCCESS   0
/*    -- success, no errors encountered.
*/
#define  pe_SYNTAX    1
/*    -- a command-line syntax error was encountered
*/
#define  pe_DEFARGS   2
/*    -- an attempt (using parsecntl()) was made to change the
**       default arg-search list of a command to point to an argdesc-array
**       which already has the given command on its default arg-search list
**       (which would cause an infinite loop when attempting to match an
**       unknown command-line argument).
*/
#define  pe_NOMATCH   3
/*    -- unable to match the named argument. This occurs
**       when the argument keyword name passed to parsecntl() (using the 
**       pc_ARGFLAGS functions code) was found in the given argdesc-array
**       or in its default-list.
*/
#define  pe_BADMODE   4
/*    -- bad mode for given command in parsecntl(). This occurs when 
**       pc_WRITE or pc_RDWR mode is passed to parsecntl() in conjunction
**       with the pc_ARGFLAGS functions code. Parsecntl will not modify
**       existing arguments.
*/
#define  pe_BADCNTL   5
/*    -- bad command for parsecntl. This occurs if an unknown function-code
**       was passed to parsecntl().
*/
#define  pe_BADOPEN   6
/*    -- error opening file
*/
#define  pe_BADREAD   7
/*    -- error reading file
*/
/**^^**********************************************************************/


/**********************************************************************
** ^SECTION: ARGUMENT-FLAGS
**    These are the possible bitmasks that may be turned ON or OFF in
**    the ad_flags field of an ARGDESC structure.
*/
#define ARGOPT       0x0000
/*    -- This flag is actually a dummy flag (i.e. it is the default). This flag
**       specifies that the command-line argument is optional (need not appear
**       on the command-line). It is only needed if no other flags are used to
**       define the given argument.  If other flags are given and ARGREQ is NOT
**       one of them, then ARGOPT is always assumed.
*/
#define ARGREQ       0x0001
/*    -- The associated command argument is required on the command-line.
*/
#define ARGPOS       0x0002
/*    -- The associated command argument is positonal. The difference between
**       using this flag to indicate a positional argument and between using
**       a blank in the ad_name field to indicate a positional arguments is
**       the following: If this flag is set but the ad_name of the argument
**       is non-blank, then this argument may be matched either positionally
**       or by keyword. If the ad_name field is blank, then this argument may
**       only be matched positionally.
*/
#define ARGNOVAL     0x0004
/*    -- The associated command argument takes no value (as in "-x value");
**       Its mere presence (or lack thereof) on the command-line is sufficient
**       to determine the necessary action(s) to take (as in "-x").
**
**       NOTE: all argBool, and arg[STU]Bool arguments are always forced to be
**       ARGNOVAL (as are argUsage arguments).
*/
#define ARGVALOPT    0x0008
/*    -- This flag is used to indicate that the command-line argument takes a
**       value (as in "-s string" or "/str=string") but that the value to this
**       command-line argument is NOT required (hence simply "-s" or "/str" is
**       also permissable).
*/
#define ARGVALREQ    0x0000
/*    -- Another "dummy" flag. Unless ARGNOVAL or ARGVALOPT is specified,
**       ARGVALREQ is always assumed. This flag indicates that the value to a
**       command-line argument is required (hence "-s string" is okay but just
**       "-s" is not).
*/
#define ARGHIDDEN    0x0010
/*    -- Don't display this argument in usage messages but still attempt to
**       match it against strings given on the command-line.
*/
#define ARGLIST      0x0020
/*    -- A variable number of values are used for this argument (and hence may
**       use more than one or two argv elements from the command-line as in
**       "-l val1 val2 ..."). The list of values must be stored in an arglist
**       structure (NOT a vector structure), an the corresponding argument-type
**       function should be one of the listXxxx functions.
*/
#define ARGVEC       0x0040
/*    -- A variable number of values are used for this argument (and hence may
**       use more than one or two argv elements from the command-line as in
**       in "-v val1 val2 ..."). The list of values must be stored in a vector
**       structure (NOT an arglist structure).
*/

/*   The following bitmasks may also be present, but, unlike the above masks
**   which must be specified by the programmer at initialization time, the
**   following masks must only be read (never set) by the programmer:
*/
#define ARGGIVEN     0x0080
/*    -- The argument WAS given on the command-line.
*/
#define ARGVALGIVEN  0x0100
/*    -- The value for this argument was given on the command-line.
*/
#define ARGVALSEP    0x0200
/*    -- The value to this argument was supplied in a separate argv element
**       from the argument itself (as in "-x value" as opposed to "-xvalue").
*/
#define ARGKEYWORD   0x0400
/*    -- This argument was matched as a keyword (long-form) on the command-line
**       and not as a single character.
*/
#define ARGDESCRIBED 0x0800
/*    -- This argument was given a description by the programmer at
**       initialization.
*/
#define ARGCOPYF     0x1000
/*    -- This flag is only used for lists and vectors (multivalued arguments)
**       and is used on a per-item basis. If it is set, it means that the
**       corresponding value in the vector/list required space to be allocated
**       (such as the duplication of a temporary string).
*/
/**^^**********************************************************************/


/**********************************************************************
** ^SECTION: ARGDESC-MACROS
**    The following macros are used to extract and query the attributes
**    of a pointer to a preprocessed arg-descriptor:
*/
#define  arg_cname(ad)        ((ad) -> ad_name)
/*    -- return the single-character name of an argument.
*/
#define  arg_flags(ad)        ((ad) -> ad_flags)
/*    -- return the argument flags of an argument.  The flags themselves
**       may be manipulated using the BTEST, BSET, and BCLEAR macros
**       #defined in <useful.h>.
*/
#define  arg_type(ad)         ((ad) -> ad_type)
/*    -- return the pointer to the value-translation-routine of an argument.
*/
#define  arg_valp(ad)         ((ad) -> ad_valp)
/*    -- return the pointer to the value of this argument.
*/
#define  arg_sname(ad)        ((ad) -> ad_prompt)
/*    -- return the string name of an argument.
*/
#define  arg_sdesc(ad)        ( arg_sname(ad) + strlen(arg_sname(ad)) + 1 )
/*    -- return the description of an argument. If a description was supplied,
**       the ARGDESCRIBED flag will be set and the description will immediately
**       follow the NUL byte of the string name.
*/
#define  ARG_isDESCRIBED(ad)  BTEST( arg_flags(ad), ARGDESCRIBED )
/*    -- Evaluates to TRUE only if an argument description was provided.
*/
#define  arg_description(ad)  ( ARG_isDESCRIBED(ad) ? arg_sdesc(ad) : "" )
/*    -- Return the description string (or an empty string if no description
**       was given) for this argument.
*/
#define  ARG_isPOSITIONAL(ad) BTEST( arg_flags(ad), ARGPOS )
/*    -- Evaluates to TRUE if this argument may be positionally matched.
*/
#define  ARG_isPOSONLY(ad) ( arg_cname(ad) == ' ' )
/*    -- Evaluates to TRUE if this argument may only be positionally matched.
*/
#define  ARG_isLIST(ad)       ( BTEST(arg_flags(ad), ARGLIST) )
/*    -- Evaluates to TRUE if this argument is an arglist.
*/
#define  ARG_isVEC(ad)        ( BTEST(arg_flags(ad), ARGVEC) )
/*    -- Evaluates to TRUE if this argument is a vector.
*/
#define  ARG_isMULTIVAL(ad)   ( BTEST(arg_flags(ad), ARGVEC | ARGLIST) )
/*    -- Evaluates to TRUE if this argument is an arglist or a vector.
*/
#define  ARG_isVALTAKEN(ad)      ( ! BTEST(arg_flags(ad), ARGNOVAL) )
/*    -- Evaluates to TRUE if this argument does NOT take a value.
*/
#define  ARG_isGIVEN(ad)      ( BTEST(arg_flags(ad), ARGGIVEN) )
/*    -- Evaluates to TRUE if this argument was given on the command-line.
*/
#define  ARG_isVALGIVEN(ad)      ( BTEST(arg_flags(ad), ARGVALGIVEN) )
/*    -- Evaluates to TRUE if the argument value was given on the command-line.
*/
#define  ARG_isREQUIRED(ad)   ( BTEST(arg_flags(ad), ARGREQ) )
/*    -- Evaluates to TRUE if this argument is required.
*/
#define  ARG_isVALOPTIONAL(ad)   ( BTEST(arg_flags(ad), ARGVALOPT) )
/*    -- Evaluates to TRUE if the argument value is optional.
*/
#define  ARG_isVALSEPARATE(ad)   ( BTEST(arg_flags(ad), ARGVALSEP) )
/*    -- Evaluates to TRUE if the argument value is optional.
*/
#define  ARG_isHIDDEN(ad)     ( BTEST(arg_flags(ad), ARGHIDDEN) )
/*    -- Evaluates to TRUE if this argument is omitted from usage messages.
*/
/**^^**********************************************************************/


   /* macro to define a NULL argument-type function */
#define argNULL    (argTypePtr_t)NULL

   /* macro for an empty argument descriptor */
#define ARG_EMPTY   { '\0', 0x0000, argNULL, ARBNULL, CHARNULL }

   /*
   ** macro to denote start & end of an ARGDESC array declared without
   ** the CMD_XXXXXXX macros which follow.
   */
#define STARTOFARGS  ARG_EMPTY
#define ENDOFARGS    ARG_EMPTY, ARG_EMPTY

/***************************************************************************
** ^SECTION: CMD-MACROS
**    Parseargs.h defines a set of macros to allow a more "self documenting"
**    approach to declaring argument-descriptor arrays. The "old-style" is
**    still accepted (but if used it is recommended that the STARTOFARGS
**    macro is used in conjunction with ENDOFARGS).  An example use of these
**    macros (which, with one exception, all begin with "CMD") follows:
**
**         #include <parseargs.h>
**    
**         static BOOL bflag = FALSE;
**         static char *arg1 = CHARNULL;
**         static char *arg2 = CHARNULL;
**    
**         static
**         CMD_OBJECT
**            MyCmd
**
**         CMD_NAME
**            "mycmd -- one line statement of purpose"
**    
**         CMD_DESCRIPTION
**            "Mycmd will try really really hard to run without errors \
**         and do whatever the heck it is supposed to do. If (God forbid) \
**         something should actually go wrong it will say so."
**    
**         CMD_ARGUMENTS
**            'b', ARGOPT, argSBool, __ &bflag,
**            "bflag -- turn on `b'-mode (whatever that is)",
**    
**            ' ', ARGREQ, argStr, __ &arg1,
**            "arg1 -- first argument to this spiffy program",
**    
**            ' ', ARGOPT, argStr, __ &arg2,
**            "arg2 -- optional second argument to this spiffy program",
**    
**            END_ARGUMENTS
**         CMD_END
**    
**         main( argc, argv )
**            int argc;
**            char *argv[];
**         {
**            (void) parseargs( argv, MyCmd );
**            (void) dostuff();
**            exit( 0 );
**         }
***^^**********************************************************************/
#define CMD_OBJECT       ARGDESC
#define CMD_NAME         [] = { { '\0', (argMask_t)0x0000, (argTypePtr_t)
#define CMD_DESCRIPTION  , ARBNULL,
#define CMD_ARGUMENTS    },
#define START_ARGUMENTS  ARG_EMPTY
#define END_ARGUMENTS    ARG_EMPTY
#define CMD_END          };

   /*
   ** shorthand for declaring main program
   */
#ifdef __ANSI_C__
# define MAIN(argc,argv)  int main( int argc, char *argv[] )
#else
# define MAIN(argc,argv)  int main( argc, argv ) int argc; char *argv[];
#endif

/***************************************************************************
** ^SECTION: MULTI-VALUED_ARGUMENTS
**    Parseargs supports two different types of multi-valued arguments:
**    linked-lists and vectors. The linked-lists are called argument lists
**    (or arg-lists) and are specified by supplying the ARGLIST flag along
**    with an associated listXxxx argument-translation routine. The value
**    associated with an arg-list should be a list structure of type ArgList.
**    The include file <parseargs.h> defines four macros for manipulating
**    ArgList structures:  ARGLISTNULL, L_NEXT, L_STRING, and L_FLAGS.
**
**    ARGLISTNULL is simply the NULL argument-list pointer.  L_NEXT and
**    L_STRING each take a pointer to a non-NULL ArgList structure. L_NEXT
**    returns the address of the next item in the list and L_STRING returns
**    the string-value of the current list-item.  L_FLAGS return the arg-
**    flags for a given item in the list. With non-multivalued, only the
**    flags in the argument descriptor are needed; lists and vectors however
**    need a set of flags for each item they contain. Once an arg-list has
**    been created, it may be deallocated using the function listFree. List-
**    Free takes two parameters, the first of which is the address of the
**    first item in the arg-list, and the second of which is a boolean value
**    that is TRUE only if each value pointed to by each item should also be
**    deallocated.
**
**    An alternative to argument-lists is argument vectors (or arg-vectors).
**    Arg-vectors use the ARGVEC flag instead of the ARGLIST flag and do not
**    require a special listXxxx function for each vector-type.  Each of the
**    argXxxx functions is responsible for handling vectors of its type
**    (although some argXxx functions such as the boolean types do not sup-
**    port vectors). An arg-vector is a structure which contains a count, an
**    array of elements (i.e. an argc/argv pair), and an array of flags, one
**    for each element of argv. There are two macros in defined in
**    <parseargs.h> which are used for arg-vectors. ARGVEC_T may be used to
**    declare a vector structure or a vector type; ARGVEC_EMPTY may be used
**    to initialize the structure.  It is strongly recommended that ARGVEC_T
**    be used to declare vector types in a typedef statement (particularly
**    if one is using function prototypes) but for those who insist, it may
**    be used to directly declare a  structure.  String-vectors will always
**    have an extra NULL-pointer at the end such that:
**
**         ( StrVec.array[ StrVec.count ] == (char *)NULL )
**
**    is always true, and character-vectors will always have an extra NUL-
**    character at the end such that:
**
**         ( CharVec.array[ CharVec.count ] == '\0' )
**
**    is always true. Integer and floating point vectors contain no extra
**    "null" elements.
**
**    Once created, arg-vectors may be deallocated by calling the macro vec-
**    Free or the macro vecDeepFree and passing it the arg-vector structure.
**    The differemce between these two macros is that the latter will also
**    free each item in the vector that required space to be allocated (at
**    the expense of traversing the vector).  At this writing, the only
**    predefined argument-type(s) that would benefit from vecDeepFree is
**    argStr vectors.
**
**    An example use of arg-lists, and of arg-vectors follows:
**
**         #include <parseargs.h>
**
**         typedef ARGVEC_T(char *) strvec_t;
**
**         static ArgList  *StringList = ARGLISTNULL;
**         static strvec_t  StringVec = ARGVEC_EMPTY(char *);
**         static ARGVEC_T(int)  NumberVec = ARGVEC_EMPTY(int);
**
**         static
**          CMD_OBJECT  Args
**          CMD_NAME    "foo -- do whatever foo does"
**          CMD_DESCRIPTION  "put a brief paragraph here"
**          CMD_ARGUMENTS
**            'l', ARGLIST, listStr, __ &StrList, "LiSt {list of strings}",
**            's', ARGVEC,  argStr,  __ &StrVec,  "STRing {vector of strings}",
**            'i', ARGVEC,  argInt,  __ &NumVec,  "NUMber {vector of numbers}",
**            END_ARGUMENTS
**          CMD_END
**
**         main( int argc, char *argv[] )
**         {
**            int i, *ls;
**
**            if ( parseargs(argv, Args) )  syserr( "parseargs failed" );
**
**            for ( ls = StrList, i=1 ; ls ; ls = L_NEXT(ls), i++ )
**               printf( "List item %d=%s, flags=%x0\n",
**                       i, L_STRING(ls), L_FLAGS(ls) );
**
**            for ( i = 0 ; i < StrVec.count ; i++ )
**               printf( "String[%d]=%s, flags=%x0\n",
**                       i, StrVec.array[i], StrVec.flags[i] );
**
**            for ( i = 0 ; i < NumVec.count ; i++ )
**               printf( "Number[%d]=%s, flags=%x0\n",
**                       i, NumVec.array[i], NumVec.flags[i] );
**
**            listFree( StrList );
**            StrList = ARGLISTNULL;
**
**            vecDeepFree( StrVec, char * );
**            vecFree( NumVec, int );
**
**            exit( 0 );
**         }
**
**^^***********************************************************************/

   /* definition of an arg-list */
typedef struct arglist {
   struct arglist  *nl_next;  /* pointer to next item */
   ARBPTR           nl_val;   /* value of current item */
   argMask_t        nl_flags; /* flags for current item */
} ArgList;
#define ARGLISTNULL  (ArgList *)NULL

   /* definition of an arg-list-head (the first two fields MUST exactly
   ** overlay with their corresponding elements in an ArgList struct)
   */
typedef struct arglisthead {
   ArgList  *nl_next;  /* pointer to next item */
   ARBPTR    nl_val;   /* value of current item */
   argMask_t nl_flags; /* flags for current item */
   ArgList  *nl_tail;  /* pointer to last item */
} ArgListHead;
#define ARGLISTHEADNULL  (ArgListHead *)NULL

   /*
   ** macros to manipulate arg-lists
   */
#define L_STRING(ls)   ((char *)((ls)->nl_val))  /* Item as a string */
#define L_NEXT(ls)     ((ls)->nl_next)           /* Next item of list */
#define L_ADVANCE(ls)  (ls) = (ArgList *)L_NEXT(ls) /* Advance list ptr */
#define L_FLAGS(ls)    ((ls)->nl_flags)          /* flags of current item */

   /*
   ** macros to declare and initialize arg-vectors
   **   (NOTE: this wont work for vectors of function pointers)
   */
#define ARGVEC_T(type) \
	struct { type  *array; unsigned short  count; argMask_t *flags; }
#define ARGVEC_EMPTY(type) \
	{ (type *) NULL, (unsigned short) 0, (argMask_t *) NULL }


/**********************************************************************
** ^SECTION: PARSE-FLAGS
**    The following bitmasks may be combined in order to modify the
**    behavior of the parseargs library. The parse flags for a given
**    may be set through the use of the parsecntl() function.
*/
#define pa_PROMPT    0x0001
/*    -- Prompt the user for any missing arguments that are required on the
**       command-line. No special escaping or quoting is performed on the
**       user input. Required arguments that expect a list of values will
**       be repeatedly prompted for (one item per line) until a blank line
**       (followed by a carriage return) is entered.
*/
#define pa_IGNORE    0x0002
/*    -- Ignore any unrecognized or improperly specified command-line arguments
**       and continue execution of the program. Normally, if an argument is
**       unmatched (or is improperly specified), a usage message is printed
**       program execution is terminated.
*/
#define pa_OPTSONLY  0x0004
/*    -- Under UNIX, setting this flag will disable the parsing of long-option
**       syntax. This will cause all arguments starting with '+' to always be
**       treated as a positional parameter (instead of a long-option).
*/
#define pa_KWDSONLY  0x0008
/*    -- Under UNIX, setting this flag disables the parsing of single-character
**       options.  This will cause all arguments starting with '-' to always
**       be treated as a positional parameter (instead of an option).
*/
#define pa_FLAGS1ST  0x0010
/*    -- Setting this flag causes the parseargs library to force any and all
**       non-positional arguments to be specified before any positional ones.
**       As an example, under UNIX, if this flag is SET then parseargs will
**       consider the command line "cmd -x arg" to consist of one option and
**       one positional argument; however the command line "cmd arg -x" would
**       be considered to consist of two positional arguments (the -x option
**       will be unmatched).
**
**       If this flag is UNSET, then both of the previous examples are
**       considered to consist of one option and one positional argument.
*/
#define pa_ANYCASE   0x0020
/*    -- Setting this flag cause character-case to be ignored when attempting
**       to match single-character argument names (i.e. causes "-i" and "-I"
**       will be considered equivalent).
*/
#define pa_ARGV0     0x0040
/*    -- Normally, the parseargs library will assume that the first argument
**       on the command-line is the name of the command. Setting this flag
**       tells parseargs that this is NOT the case and that the very first
**       argument on the command-line is a bona-fide argument to the command.
*/
#define pa_NOCHECK   0x0080
/*    -- Setting this flag will prevent parseargs from checking for any 
**       required arguments that were not given on the command-line. This
**       is useful when more than one call to the parseargs library is needed
**       to parse all the command-line arguments (which could occur if the
**       command-line argument came from a file or from two argv-vectors).
**
**       Keeping this flag on until the final set of arguments is parsed will
**       cause parseargs to not check for missing arguments until the last set
**       of arguments has been parsed (by the final call to *parseargs).
*/
#define pa_CONTINUE  0x0100
/*    -- Setting this flag will cause subsequent calls to the parseargs library
**       to NOT reset the current command-state. Hence, all arguments will not
**       be initially set to "NOT GIVEN" and other (normal) initializations are
**       not be performed.  This is useful in conjunction with the pa_NOCHECK
**       when more than one call to parseargs is required to parse all the
**       command arguments. In this scenario, pa_CONTINUE should be unset (the
**       default setting) for the very first call to parseargs, but should then
**       be set before any subsequent calls to parseargs are made.
*/
#define pa_NOCMDENV  0x0200
/*    -- Setting this flag prevents parseargs from checking the <CMD-NAME>_ARGS
**       environment variable for any user-defined default command arguments.
*/
#define pa_COPYF     0x0400
/*    -- When this flag is OFF (the default), a value of FALSE is provided as
**       the <copyf> argument to all the arg-type (argXxxxx) functions when an
**       argument is matched. Setting this flag will cause a value of TRUE to
**       be provided as the <copyf> argument to all the arg-type (argXxxxx)
**       functions when an argument is matched.
*/
/**^^**********************************************************************/


/**********************************************************************
** ^SECTION: PARSE-CNTLS - specify which attributes to get/set
**    Each of the following function codes specifies an attribute that
**    is to be manipulated by parsecntl(3).  The function code is the
**    second parameter to parsecntl(3). With the exception of pc_ARGFLAGS,
**    each of the function codes corresponds to a call to parsecntl(3) 
**    using four parameters (pc_ARGFLAGS uses 5 parameters). In each case,
**    the last parameter is either the address of a buffer to write the
**    attribute to, or the actual buffer to read the attribute from 
**    (depending upon the mode -- the third parameter to parsecntl).
*/
typedef enum  {
   pc_PARSEFLAGS,
/*    -- get/set parse flags
**
**    This function code is used to read and/or modify the existing parsing 
**    parsing behavior. The fourth parameter to parsecntl should be a 
**    combination of pc_XXXX bitmasks if the parse-flags are only being
**    written, otherwise it should be a pointer to an argMask_t variable.
*/
   pc_ARGFLAGS,
/*    -- get/set argument flags
**
**    This function code may only be used to read the argument-flags of
**    a named argument. It is an error to specify a mode that attempts
**    to write the argument-flags with this function code. The fourth
**    parameter to parsecntl should be the keyword name of the argument
**    whose flags are to be read. The fifth (and final) argument should
**    be a pointer to the argMask_t variable which will receive the resulting
**    argument-flags.
*/
   pc_DEFARGS,
/*    -- get/set the default arguments
**
**    This function code is used to query or modify the current default
**    argument-descriptor list for the given command. The fourth parameter
**    to parsecntl should be the argument-descriptor array to assign as the
**    new default-list (or the address of an argdesc-array if the default
**    list is being retrieved).
**
**    If a given option/qualifier does not appear to match any items in the
**    argdesc-array, a  default argdesc-array is then searched to match the
**    option. If it is STILL unmatched then it is flagged as such. The
**    default-argdesc array is automatically used by all programmer-defined
**    argdesc-array but may be unset or reset using the pc_DEFARGS function
**    of parsecntl(3). In such a  manner, a  programmer could specify a dif-
**    ferent set of default-arguments to search for. Furthermore, default
**    argdesc-arrays may also be assigned default argdesc-arrays, thus
**    allowing the programmer to define a whole search-list of default
**    argdesc-arrays for a given command.
**
**    This could prove useful in a situation where a set of commands have a
**    few common-options and differ in their remaining ones. If the same
**    main() were used for each command, then main could define one common
**    argdesc-array and then a set of argdesc-arrays for each command. Main
**    could then figure out which argdesc-array to used based on the name in
**    argv[0], and set its default argdesc-array to be the common argdesc-
**    array, as in the following:
**
**         #include <parseargs.h>
**             .
**             . variable declarations
**             .
**
**         static ARGDESC common_args[] = {
**            STARTOFARGS,
**            'L', ARGOPT, argBool, __ &lflag, "list (list available items)"
**            'I', ARGOPT, argStr, __ &item, "item (specify item to use)",
**            ENDOFARGS
**         };
**
**         static ARGDESC cmd1_args[] = {
**            STARTOFARGS,
**            's', ARGOPT, argBool, __ &sflag, "S (set S)",
**            't', ARGOPT, argBool, __ &tflag, "T (set T)",
**            ENDOFARGS
**         };
**
**         static ARGDESC cmd2_args[] = {
**            STARTOFARGS,
**            'x', ARGOPT, argBool, __ &xflag, "X (set X)",
**            'y', ARGOPT, argBool, __ &yflag, "Y (set Y)",
**            ENDOFARGS
**         };
**
**         main( argc, argv ) int argc; char *argv[];
**         {
**            ARGDESC *cmd = cmd1_args;
**            int status;
**
**            if ( strcmp(*argv, "cmd2") == 0 ) cmd = cmd2_args;
**
**            if ( parsecntl( cmd, pc_DEFARGS, pc_WRITE, common_args ) != 0 )
**               syserr( "unable to set default args" );
**
**            status = parseargs( argv, cmd );
**                   .
**                   .
**                   .
**         }
**
**    Note that in the above call to parsecntl(3), that zero will be
**    returned upon success and non-zero upon failure. If pe_DEFARGS is
**    returned, then cmd is already on common_args's list of defaults (and
**    would result in an infinite loop while parsing).
*/
   pc_NAME,
/*    -- get/set the command-name
*/
   pc_PURPOSE,
/*    -- get/set the command-purpose
*/
   pc_DESCRIPTION
/*    -- get/set the command-description
*/
/*    Each of these last three function codes are used to modify or query the
**    name, purpose, or description associated with a command. The fourth
**    parameter to parsecntl should be the new string to use (or the address
**    of the string, a char** variable, to recieve the current value).
*/
} parsecntl_t;

/**^^**********************************************************************/


/**********************************************************************
** ^SECTION: PARSE-MODES - modes to get/set command attributes.
**    Parsecntl may be used to read current command attributes, write/assign
**    new command attributes, or both. The mode argument to parsecntl
**    determines the which of these three alternatives are desired. If the
**    programmer merely wishes to assign new attributes, then invoking
**    parsecntl in pc_WRITE mode and passing the new attributes will do the
**    job. If the programmer wishes simply to query attributes, then
**    invoking parsecntl in pc_READ mode and passing a pointer to the
**    desired object in which to write the attribute settings will suffice.
**
**    If the programmer wishes to assign new attributes and at the same time
**    find out what the attributes were before making the assignment, then
**    programmer must invoke parsecntl for pc_RDWR mode and pass a pointer
**    to the object containing the new attribute settings; When parsecntl
**    returns, then (assuming it returns 0) the desired attributes will have
**    been assigned and the object that contained the new attribute settings
**    will now contain the attribute settings that were in effect before
**    parsecntl was invoked.
*/
typedef enum  {
   pc_READ,
/*    -- read-mode: attributes are retrieved
*/
   pc_WRITE,
/*    -- write-mode: attributes are assigned new values
*/
   pc_RDWR
/*    -- read/write-mode: attributes are retrieved and then assigned
*/

} parsemode_t;
/**^^**********************************************************************/


   /*
   ** private (implementation specific) definitions
   */
#ifdef PARSEARGS_PRIVATE

   /* macros to define command-line style specific character sequences */
# ifdef amiga_style
#   define  s_ARG_SEP  "=:"  /* AmigaDOS argument separator characters */
# endif
# ifdef ibm_style
#   define  s_ARG_SEP  "="  /* MS-DOS and OS/2 argument separator characters */
# endif
# ifdef unix_style
#   define  c_OPT_PFX  '-'   /* Unix option prefix character */
#   define  c_KWD_PFX  '+'   /* Unix keyword prefix character */
#   define  s_ARG_SEP  "=:"  /* Unix keyword-value separator characters */
# endif
# ifdef vms_style
#   define  s_KWD_PFX  "/"   /* VMS qualifier prefix character */
#   define  s_LSI_SEP  ",+"  /* VMS LiSt Item separator characters */
#   define  s_ARG_SEP  "=:"  /* VMS qualifier-value separator characters */
# endif


   /* call the function to parse the given argument-value string */
# define HANDLE(ad,vp,pflags) ((*arg_type(ad))(ad, vp, BTEST(pflags, pa_COPYF)))


   /* parse-state flags */
# define  ps_OLDSTYLE     0x01  /* force backward compatibility? */
# define  ps_NOFLAGS      0x02  /* opt/kwd parsing in effect? */
# define  ps_NOCMDENV     0x04  /* <CMD>_ARGS environment-variable parsed? */
# define  ps_NOPARSECNTL  0x08  /* PARSECNTL environment-variable parsed? */

typedef unsigned char   ps_flags_t;


   /*
   ** structure to hold arg-desc pointers maintained in the command-context
   */
typedef struct {
   ARGDESC  *default_argd;   /* pointer to default args */
   ARGDESC  *current_list;   /* pointer to ad with arglist (or argvector)
                             ** that is currently being appended.
                             */
# ifdef amiga_style
     ARGDESC  *previous_ad;  /* pointer to previously matched ad */
# endif
} ARGDPTRS;

   /*
   ** structures to replace the first and last argument descriptor
   ** in a command (each field must exactly overlay its counterpart
   ** in an ARGDESC struct).
   */
typedef struct  {
   char        id;      /* id is ALWAYS '\0' for first and last ad */
   ps_flags_t  state_flags;  /* current parse-state */
   CONST char *argv0;   /* argv[0] from the command-line */
   ARGDPTRS   *argdp;   /* structure with ARGDESC pointers */
   CONST char *purpose; /* one-line purpose provided with CMD_NAME */
} CTXDESC;  /* the command-context */

typedef struct  {
   char        id;      /* id is ALWAYS '\0' for first and last ad */
   argMask_t   parse_flags;  /* current parse-flag bitmasks */
   CONST char *name;    /* command-name provided with CMD_NAME */
   CTXDESC    *context; /* pointer to command-context */
   CONST char *description;  /* description provided with CMD_DESCRIPTION */
} CMDDESC; /* the command-descriptor */

   /*
   ** macros to extract command-line attributes in the command-object
   */
# define  cmd_desc(cmd)         (CMDDESC *)cmd
# define  cmd_id(cmd)           (cmd_desc(cmd)) -> id
# define  cmd_flags(cmd)        (cmd_desc(cmd)) -> parse_flags
# define  cmd_name(cmd)         (cmd_desc(cmd)) -> name
# define  cmd_description(cmd)  (cmd_desc(cmd)) -> description
# define  cmd_context(cmd)      (cmd_desc(cmd)) -> context
# define  cmd_ctxid(cmd)        (cmd_context(cmd)) -> id
# define  cmd_state(cmd)        (cmd_context(cmd)) -> state_flags
# define  cmd_argv0(cmd)        (cmd_context(cmd)) -> argv0
# define  cmd_purpose(cmd)      (cmd_context(cmd)) -> purpose
# define  cmd_ptrs(cmd)         (cmd_context(cmd)) -> argdp
# define  cmd_defargs(cmd)      (cmd_ptrs(cmd)) -> default_argd
# define  cmd_list(cmd)         (cmd_ptrs(cmd)) -> current_list
# ifdef amiga_style
#   define  cmd_prev(cmd)  (cmd_ptrs(cmd)) -> previous_ad
# endif

   /* macro to determine if a command-object has been initialized */
# define  CMD_isINIT(cmd)  \
   ( !cmd_id(cmd)  &&  cmd_context(cmd) )

   /*
   ** macros to help ascertain argument type
   */
# define ARG_isBOOLEAN(ad)   \
   ( arg_type(ad) == argBool  || arg_type(ad) == argSBool || \
     arg_type(ad) == argUBool || arg_type(ad) == argTBool    \
   )
# define ARG_isPSEUDOARG(ad) \
   ( arg_type(ad) == argEnd   || \
     arg_type(ad) == argUsage || \
     arg_type(ad) == argDummy    \
   )

   /*
   ** macros to assist in traversing a command-object
   */
# define  ARG_FIRST(cmd)   ((cmd) + 1)
# define  ARG_LAST(cmd)    ( ((ARGDESC *)cmd_context(cmd)) - 1 )
# define  ARG_isEND(ad)    ( arg_cname(ad)  ==  '\0' )
# define  ARG_ADVANCE(ad)  (ad)++
# define  ARG_RETREAT(ad)  (ad)--


/**********************************************************************
** ^SECTION: USAGECNTL
**    Each of the different values in $USAGECNTL corresponds to a
**    bitmask as follows:
*/
# define  usg_NONE         0x0001
/*    -- "Quiet", "Silent", and "None" : dont print usage
*/
# define  usg_VERBOSE      0x0002
/*    -- "Verbose", "!Terse" : print argument descriptions
*/
# define  usg_OPTS         0x0004
/*    -- "Options" -- print option syntax
*/
# define usg_LONGOPTS     0x0008
/*    -- "LongOpts", "KeyWords" : print long-option/keyword syntax
*/
# define  usg_DESCRIPTION  0x0010
/*    -- "Description" : print the command description
*/
# define  usg_PAGED        0x0020
/*    -- "Paged" : pipe the usage message through a paging program
*/
/**^^**********************************************************************/

#endif /* PARSEARGS_PRIVATE */

   /*
   ** pre-defined types available for ad_type
   */
#ifndef PARSEARGS_NARGTYPES
# define  ARGTYPE(name)  EXTERN  BOOL  name ARGS(( ARGDESC *, char *, BOOL ))
   ARGTYPE( argUsage );
   ARGTYPE( argEnd );
   ARGTYPE( argDummy );
   ARGTYPE( argBool );
   ARGTYPE( argSBool );
   ARGTYPE( argUBool );
   ARGTYPE( argTBool );
   ARGTYPE( argChar );
   ARGTYPE( argStr );
   ARGTYPE( argInt );
   ARGTYPE( argShort );
   ARGTYPE( argLong );
   ARGTYPE( argFloat );
   ARGTYPE( argDouble );
   ARGTYPE( listStr );
   EXTERN  VOID  listFree ARGS((ArgList *argls));
# define vecFree(vec,type)  \
	do { \
		if ( vec.count > 0 ) { \
			if ( vec.array )  free( vec.array ); \
			if ( vec.flags )  free( vec.flags ); \
		} \
        vec.array = (type *)NULL; \
        vec.flags = (argMask_t *)NULL; \
		vec.count = 0; \
	} while ( 0 )
# define vecDeepFree(vec,type)  \
	do { \
		register int i; \
 \
		for ( i = 0 ; i < vec.count ; i++ ) \
			if ( BTEST(vec.flags[i], ARGCOPYF) ) \
				free( (ARBPTR) vec.array[i] ); \
 \
		if ( vec.count > 0 ) { \
			if ( vec.array )  free( vec.array ); \
			if ( vec.flags )  free( vec.flags ); \
		} \
        vec.array = (type *)NULL; \
        vec.flags = (argMask_t *)NULL; \
		vec.count = 0; \
	} while ( 0 )

# undef ARGTYPE
#endif  /* PARSEARGS_NARGTYPES */

   /*
   ** parseargs library function-prototypes
   */
#ifndef  PARSEARGS_NEXTERNS
   EXTERN int   fparseargs  ARGS(( FILE *, ARGDESC * ));
   EXTERN int   lparseargs  ARGS(( ArgList *, ARGDESC * ));
   EXTERN int   sparseargs  ARGS(( char *, ARGDESC * ));
   EXTERN int   vparseargs  ARGS(( ARGDESC *, int, ...));
   EXTERN int   parseargs   ARGS(( char **, ARGDESC * ));
   EXTERN int   parsecntl   ARGS(( ARGDESC *, parsecntl_t, parsemode_t, ...));
   EXTERN VOID  usage       ARGS(( const ARGDESC * ));
   EXTERN VOID  init_args   ARGS(( ARGDESC * ));
   extern CONST char *ProgName;
#endif  /* PARSEARGS_NEXTERNS */

#endif  /* PARSEARGS_H */
