home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************************
- ** ^FILE: amiga_args.c - parse AmigaDOS argument vectors
- **
- ** ^DESCRIPTION:
- ** This file contains the routines used to parse AmigaDOS argument
- ** vectors and to print AmigaDOS usage messages.
- **
- ** ^HISTORY:
- ** 27/08/91 Earl Chew <cechew@bruce.cs.monash.edu.au>
- ** - Use ProgNameLen when accessing ProgName
- ** - Use get_argdesc() to access description
- **
- ** 01/02/91 Brad Appleton <brad@ssd.csd.harris.com>
- ** - Added structured block comments
- ** - Added optional arguments to keywords
- **
- ** --/--/-- Peter da Silva <peter@ferranti.com> Created
- ***^^**********************************************************************/
-
- #include <ctype.h>
- #include <useful.h>
- #include "strfuncs.h"
- #include "pgopen.h"
- #include "exit_codes.h"
-
- #define PARSEARGS_PRIVATE /* include private definitions */
- #include "parseargs.h"
-
- EXTERN VOID syserr ARGS((const char *, ...));
- EXTERN VOID usrerr ARGS((const char *, ...));
- EXTERN char *getenv ARGS((const char *));
- EXTERN VOID get_winsize ARGS((int, int *, int *));
-
- VERSIONID("$Header: parseargs.c,v 2.1 89/12/30 20:59:48 eric Exp $");
-
- /***************************************************************************
- ** ^GLOBAL-VARIABLE: Usage_Requested
- **
- ** ^VISIBILITY:
- ** static-global (visible to all functions in this file).
- **
- ** ^DESCRIPTION:
- ** Indicates whether a usage message was requested by the user
- ** (as opposed to triggerred by a syntax error). If the message
- ** is requested by the user then it is always printed in verbose
- ** mode and does not return an error-status-code.
- ***^^**********************************************************************/
- static BOOL Usage_Requested = FALSE;
-
-
- /***************************************************************************
- ** ^FUNCTION: amiga_parse - parse Amiga_DOS arg-vectors
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- int amiga_parse( argv, argd )
- /*
- ** ^PARAMETERS:
- */
- char *argv[];
- /* -- the vector of string arguments from the command-line
- */
- ARGDESC argd[];
- /* -- the programmer description of the command and its args
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Amiga_parse will parse the arguments in the given vector of strings,
- ** assign the corresponding values to the command-line arguments specified
- ** in argd, and check the syntax of the command-line.
- **
- ** ^REQUIREMENTS:
- ** The final element in argv must be a NULL pointer.
- **
- ** ^SIDE-EFFECTS:
- ** argd is modified according to the command-line description and parameters
- **
- ** ^RETURN-VALUE:
- ** pe_SUCCESS (0) if no errors are encountered
- ** pe_SYSTEM (-1) if a system error is encountered
- ** pe_SYNTAX if a syntax error is encountered
- **
- ** ^ALGORITHM:
- ** - for each command-line argument
- ** - attempt to match the argument as a keyword
- ** - if it is a keyword argument
- ** - record and convert its value (if any)
- ** else it is a positional parameter
- ** - record and convert its value (if any)
- ** else there are too many arguments
- ** - return pe_SYNTAX
- ** end-if
- ** end-for
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- int amiga_parse( char **argv, ARGDESC argd[] )
- #endif
- {
- register ARGDESC *cmd, *args, *ad = ARGDESCNULL;
- register char **av;
- register char *p = CHARNULL;
- argName_t keyword;
- argMask_t flags;
- int parse_error = pe_SUCCESS;
- BOOL is_match = FALSE;
-
- if ( !argd ) return parse_error;
-
- /* initialize command-structure */
- if ( !CMD_isINIT(argd) ) init_args( argd );
- cmd = argd;
- cmd_prev(cmd) = ARGDESCNULL;
-
- /* run through the argument vector */
- for ( av = argv ; *av ; av++ ) {
- char c = '\0';
-
- /* If looking for keywords, see if this is one */
- if( !BTEST(cmd_state(cmd), ps_NOFLAGS) ) {
- p = strpbrk(*av, s_ARG_SEP);
- if ( p ) {
- c = *p;
- *p++ = '\0'; /* skip past arg-separator character */
- }
-
- is_match = FALSE;
- for ( args = argd ; args && !is_match ; args = cmd_defargs(args) ) {
- for (ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad)) {
- if (arg_type(ad) == argDummy) continue;
-
- if (!ARG_isPOSONLY(ad) && match(*av, arg_sname(ad)) == 0) {
- is_match = TRUE;
- break;
- }/*if*/
- }
- }
-
- if ( !is_match ) ad = ARGDESCNULL;
- }/*if !NOFLAGS*/
-
- if (c) *(p-1) = c; /* restore the equal sign */
-
- /* If we have a keyword here */
- if( !BTEST(cmd_state(cmd), ps_NOFLAGS) && ad) {
- if ( cmd_prev(cmd) ) { /* a value may have been given but wasnt */
- if ( ARG_isVALOPTIONAL(cmd_prev(cmd)) ) {
- BSET( arg_flags(cmd_prev(cmd)), ARGGIVEN );
- }
- else { /* value was required */
- (VOID)get_kwdname( arg_sname(cmd_prev(cmd)), keyword );
- usrerr( "value required for %s keyword", keyword );
- parse_error = pe_SYNTAX;
- }
- cmd_prev(cmd) = ARGDESCNULL;
- }
-
- if ( cmd_list(cmd) ) { /* end of list */
- cmd_list(cmd) = ARGDESCNULL;
- }
-
- flags = arg_flags(ad); /* save flags */
- if ( ARG_isGIVEN(ad) ) {
- BCLEAR( arg_flags(ad), ARGVALSEP | ARGKEYWORD );
- if ( !ARG_isMULTIVAL(ad) ) BCLEAR( arg_flags(ad), ARGVALGIVEN );
- }
-
- if ( p ) { /* matched NAME=VALUE */
- if ( ARG_isMULTIVAL(ad) )
- cmd_list(cmd) = ad;
- else
- cmd_list(cmd) = ARGDESCNULL;
-
- /* try to convert the type */
- if ( !HANDLE(ad, p, cmd_flags(cmd)) ) {
- arg_flags(ad) = flags; /* restore flags */
- parse_error = pe_SYNTAX;
- }
- else
- BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
- ad = ARGDESCNULL;
- }
- else {
- if (arg_type(ad) == argUsage) {
- Usage_Requested = TRUE;
- usage(argd);
- exit(exit_USAGE);
- }
- else if (arg_type(ad) == argEnd) {
- BSET( cmd_state(cmd), ps_NOFLAGS );
- BSET( arg_flags(ad), ARGGIVEN );
- }
- else if ( ARG_isVALTAKEN(ad) ) {
- cmd_prev(cmd) = ad;
- }
- else if ( !HANDLE(ad, CHARNULL, cmd_flags(cmd)) ) {
- arg_flags(ad) = flags; /* restore flags */
- parse_error = pe_SYNTAX;
- }
- else {
- BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
- }
- ad = ARGDESCNULL;
- }/*else*/
- }
- else if (cmd_prev(cmd)) { /* expecting a vlue from previous arg */
- /* reset the argument flags - if this arg was already given, some
- ** of its flags may be set to indicate how it was given before.
- ** we need to know how it was given now (but save the old ones
- ** just in case the new one fails).
- */
- flags = arg_flags(cmd_prev(cmd)); /* save flags */
- if ( ARG_isGIVEN(cmd_prev(cmd)) ) { /* reset flags */
- BCLEAR( arg_flags(cmd_prev(cmd)), ARGVALSEP );
- if ( !ARG_isMULTIVAL(ad) ) BCLEAR( arg_flags(ad), ARGVALGIVEN );
- }
-
- /* previous value may have required a keyword */
- BSET( arg_flags(cmd_prev(cmd)), ARGVALSEP );
-
- if ( ARG_isMULTIVAL(cmd_prev(cmd)) )
- cmd_list(cmd) = cmd_prev(cmd);
- else
- cmd_list(cmd) = ARGDESCNULL;
-
- /* try to convert the type */
- if ( !HANDLE(cmd_prev(cmd), *av, cmd_flags(cmd)) ) {
- arg_flags(cmd_prev(cmd)) = flags; /* restore flags */
- parse_error = pe_SYNTAX;
- }
- else
- BSET( arg_flags(cmd_prev(cmd)), ARGGIVEN | ARGVALGIVEN );
-
- ad = ARGDESCNULL;
- cmd_prev(cmd) = ARGDESCNULL;
- continue;
- }
- else { /* it's a positional argument or a list item */
- if ( cmd_list(cmd) ) { /* its a list item */
- /* reset the argument flags - if this arg was already given, some
- ** of its flags may be set to indicate how it was given before.
- ** we need to know how it was given now (but save the old ones
- ** just in case the new one fails).
- */
- flags = arg_flags(cmd_list(cmd)); /* save flags */
- if ( ARG_isGIVEN(cmd_list(cmd)) ) { /* reset flags */
- BCLEAR( arg_flags(cmd_list(cmd)), ARGVALSEP );
- }
- BSET( arg_flags(cmd_list(cmd)), ARGVALSEP );
-
- if ( !HANDLE(cmd_list(cmd), *av, cmd_flags(cmd)) ) {
- arg_flags(cmd_list(cmd)) = flags; /* restore flags */
- parse_error = pe_SYNTAX;
- }
-
- BSET( arg_flags(cmd_list(cmd)), ARGGIVEN | ARGVALGIVEN );
- continue;
- }
- else { /* its a positional argument */
-
- /* if FLAGS1ST is set then first positional marks end-of-flags */
- if ( BTEST(cmd_flags(cmd), pa_FLAGS1ST) )
- BSET( cmd_state(cmd), ps_NOFLAGS );
-
- is_match = FALSE;
- for ( args = argd; args && !is_match; args = cmd_defargs(args) ) {
- for (ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad)) {
- if (arg_type(ad) == argDummy) continue;
-
- if ( ARG_isPOSITIONAL(ad) &&
- (!ARG_isGIVEN(ad) || ARG_isMULTIVAL(ad)) ) {
- is_match = TRUE;
- break;
- }/*if*/
- }
- }
-
- if ( !is_match ) {
- usrerr("too many arguments");
- parse_error = pe_SYNTAX;
- ad = ARGDESCNULL;
- }
- else {
- /* reset the argument flags - if this arg was already given, some
- ** of its flags may be set to indicate how it was given before.
- ** we need to know how it was given now (but save the old ones
- ** just in case the new one fails).
- */
- flags = arg_flags(ad); /* save flags */
- if ( ARG_isGIVEN(ad) ) { /* reset flags for this appearance */
- BCLEAR( arg_flags(ad), ARGVALSEP );
- if (! ARG_isMULTIVAL(ad)) BCLEAR(arg_flags(ad), ARGVALGIVEN);
- }
- BSET( arg_flags(ad), ARGVALSEP );
-
- /* try to convert */
- if ( !HANDLE(ad, *av, cmd_flags(cmd)) ) {
- arg_flags(ad) = flags; /* restore flags */
- parse_error = pe_SYNTAX;
- }
- else
- BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
- ad = ARGDESCNULL;
- }
- }/*else positional*/
- }/*else not keyword*/
- }/*while*/
-
- /* If last argument was a keyword and required an option
- ** then complain about it
- */
- if ( cmd_prev(cmd) ) { /* a value may have been given but wasnt */
- if ( ARG_isVALOPTIONAL(cmd_prev(cmd)) ) {
- BSET( arg_flags(cmd_prev(cmd)), ARGGIVEN );
- }
- else { /* value was required */
- (VOID)get_kwdname( arg_sname(cmd_prev(cmd)), keyword );
- usrerr( "value required for %s keyword", keyword );
- parse_error = pe_SYNTAX;
- }
- cmd_prev(cmd) = ARGDESCNULL;
- }
-
- return parse_error;
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: fmtarg - format command-argument syntax
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static int fmtarg(ad, buf)
- /*
- ** ^PARAMETERS:
- */
- ARGDESC *ad;
- /* -- pointer to the argument to format
- */
- char *buf;
- /* -- character buffer to hold the formatted result
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Fmtarg will determine the proper command-line syntax for the
- ** given argument and write the result to the given buffer.
- **
- ** ^REQUIREMENTS:
- ** buf must be large enough to hold the formatted result (100 characters
- ** should do the trick).
- **
- ** ^SIDE-EFFECTS:
- ** buf is overwritten.
- **
- ** ^RETURN-VALUE:
- ** The number of printable characters in the argument-syntax-string
- **
- ** ^ALGORITHM:
- ** Print argument usage based on whether or not the argument is
- ** positional, hidden, multi-valued (list or vector), etc ....
- ** Optional arguments and values are enclosed in square braces.
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- static int fmtarg( const ARGDESC *ad, char *buf )
- #endif
- {
- /* buf must already be large enough */
- char * pos;
- argName_t keyword, name;
-
- (VOID) get_argname(arg_sname(ad), name);
-
- if ( ARG_isPOSITIONAL(ad) ) {
- sprintf( buf, "<%s>", name );
- }
- else {
- (VOID) get_kwdname(arg_sname(ad), keyword);
- (VOID) strcpy( buf, keyword );
- pos = buf + strlen(buf);
-
- if ( ARG_isVALTAKEN(ad) && !ARG_isBOOLEAN(ad) && !ARG_isPSEUDOARG(ad) ) {
- if ( ARG_isVALOPTIONAL(ad) )
- sprintf( pos, " [<%s>]", name );
- else
- sprintf( pos, " <%s>", name );
- }
- }/*else*/
-
- return strlen(buf);
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: amiga_usage - print a usage message
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- VOID amiga_usage( argd, usage_flags )
- /*
- ** ^PARAMETERS:
- */
- ARGDESC *argd;
- /* -- the command-descriptor array
- */
- argMask_t usage_flags;
- /* -- flags set by $USAGECNTL
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Amiga_usage will print the AmigaDOS command-line usage of the given
- ** command on standard diagnostic output (stderr). The content of the
- ** usage message is controlled by the bitmasks in usage_flags which
- ** correspond to the settings in the user's USAGECNTL variable.
- **
- ** ^REQUIREMENTS:
- ** argd should be a non-null command-line argument-descriptor array
- **
- ** ^SIDE-EFFECTS:
- ** Prints on stderr.
- **
- ** ^RETURN-VALUE:
- ** None.
- **
- ** ^ALGORITHM:
- ** - if no usage is desired then exit
- ** - if paging is requested print to the pager instead of stderr
- ** - print the command-line syntax
- ** - if the description is requested print it
- ** - if verbose mode is requested, print the description of each argument
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- void amiga_usage( const ARGDESC *argd, argMask_t usage_flags )
- #endif
- {
- register CONST ARGDESC *ad, *args, *cmd;
- int max_cols = 80, max_lines = 24;
- int margin, ll, pl, keywords, longest, positionals;
- BOOL first = TRUE;
- FILE *fp;
-
- if ( !argd ) return;
-
- /* initialize command-structure */
- if ( !CMD_isINIT(argd) ) init_args( (ARGDESC *)argd );
- cmd = argd;
-
- /* force verbose-mode if requested */
- if ( Usage_Requested ) BSET( usage_flags, usg_VERBOSE );
-
- if ( BTEST(usage_flags, usg_NONE) ) return;
-
- fp = ( BTEST(usage_flags, usg_PAGED) )
- ? pgopen( stderr, getenv("USAGE_PAGER") )
- : stderr;
-
- /* get screen size */
- get_winsize( fileno(stderr), &max_lines, &max_cols );
- fprintf(fp, "Format: %.*s", ProgNameLen, (ProgName) ? ProgName : "");
- ll = ProgNameLen + 8;
- margin = ll + 1;
- longest = 0;
-
- for ( positionals = 0 ; positionals < 2 ; positionals++ ) {
- for ( args = argd ; args ; args = cmd_defargs(args) ) {
- for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
- argName_t buf;
-
- /* don't display hidden arguments */
- if ( ARG_isHIDDEN(ad) ) continue;
- if ( !positionals && ARG_isPOSITIONAL(ad) ) continue;
- if ( positionals && !ARG_isPOSITIONAL(ad) ) continue;
-
- /* figure out how wide this parameter is (for printing) */
- pl = fmtarg(ad, buf);
-
- if ( pl > longest) longest = pl;
-
- if ( !ARG_isREQUIRED(ad) ) {
- pl += 2; /* [] */
- }
- if ( ARG_isMULTIVAL(ad) ) {
- strcat( buf, "..." );
- pl += 3;
- }
-
- /* see if this will fit */
- if ( (ll + pl + 1) > (max_cols - first) ) {
- /* no... start a new line */
- fprintf(fp, "\n%*s", margin, "");
- ll = margin;
- }
- else {
- /* yes... just throw in a space */
- fputc(' ', fp);
- ++ll;
- }
- ll += pl;
-
- /* show the argument */
- if ( !ARG_isREQUIRED(ad) ) fputc('[', fp);
- fprintf(fp, buf);
- if ( !ARG_isREQUIRED(ad) ) fputc(']', fp);
-
- first = FALSE; /* not first line anymore */
- }/*for each ad */
- }/* for each argd */
- }/* for each parm-type */
-
- fputc('\n', fp);
-
- if ( BTEST(usage_flags, usg_DESCRIPTION) ) {
- CONST char *description = cmd_description(cmd);
-
- if ( description && *description ) {
- fprintf( fp, "Description:\n" );
- indent_para(fp, max_cols, 8, "", 0, description, 0);
- fputc( '\n', fp );
- }
- }/*if*/
-
- if ( !BTEST(usage_flags, usg_VERBOSE) ) {
- if ( pgactive(fp) ) (VOID) pgclose( fp );
- return;
- }
-
- keywords = 0;
- for ( positionals = 0 ; positionals < 2 ; positionals++ ) {
- for ( args = argd ; args ; args = cmd_defargs(args) ) {
- for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
- argName_t buf;
- char *desc;
- int desclen;
-
- /* don't display hidden arguments */
- if ( ARG_isHIDDEN(ad) ) continue;
- if ( !positionals && ARG_isPOSITIONAL(ad) ) continue;
- if ( positionals && !ARG_isPOSITIONAL(ad) ) continue;
-
- if( !(keywords++) ) fprintf(fp, "Keywords/Arguments:\n");
- (VOID) fmtarg(ad, buf);
- desc = get_argdesc(arg_description(ad), &desclen);
- indent_para(fp, max_cols, 8, buf, longest+2, desc, desclen);
- }/*for each ad */
- }/* for each argd */
- }/* for each parm-type */
-
- if ( pgactive(fp) ) (VOID) pgclose( fp );
- }
-