home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 January
/
usenetsourcesnewsgroupsinfomagicjanuary1994.iso
/
sources
/
misc
/
volume29
/
parseargs
/
part06
/
vms_args.c
< prev
Wrap
C/C++ Source or Header
|
1992-05-19
|
32KB
|
1,078 lines
/*************************************************************************
** ^FILE: vms_args.c - parse VMS/DCL argument vectors
**
** ^DESCRIPTION:
** This file contains the routines used to parse VMS/DCL argument
** vectors and to print VMS/DCL usage messages.
**
** ^HISTORY:
** 11/21/91 Brad Appleton <brad@ssd.csd.harris.com>
** - added Mike Levins fix to is_cmdline() to check for 0 length
** returned by lib$get_foreign.
** - added check of ps_NOTCMDLINE state-flag before calling is_cmdline()
** - fixed problem in vms_parse() where ARGVALGIVEN was getting set in
** place of ARGGIVEN.
**
** 08/27/91 Earl Chew <cechew@bruce.cs.monash.edu.au>
** - Use ProgNameLen when accessing ProgName
** - Use get_argdesc() to access description
**
** 12/03/90 Brad Appleton <brad@ssd.csd.harris.com> Created
***^^**********************************************************************/
#include <stdio.h>
#include <ctype.h>
#include <useful.h>
#ifdef vms
# include <descrip.h>
#endif
#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 VOID get_winsize ARGS((int, int *, int *));
EXTERN BOOL argInput ARGS((ARGDESC *, char *, BOOL));
EXTERN BOOL argOutput ARGS((ARGDESC *, char *, BOOL));
VERSIONID("$Header: vms_args.c,v 1.1 90/08/23 18:00:00 brad 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;
#define MAXCMDLINE 255
#define VNULL (VOID *) 0
#define TOGGLE(flag) flag = (flag) ? FALSE : TRUE
/* define mappings */
#define c_SPACE '\001' /* whitespace */
#define c_QUAL '\002' /* qualifier-delimiter */
#define c_EQUAL '\003' /* qualifier-argument separator */
#define c_COLON '\004' /* qualifier-argument separator */
#define c_PLUS '\005' /* list-item separator character */
#define c_COMMA '\006' /* list-item separator character */
typedef enum {
Parameter, /* token is a parameter */
Qualifier, /* token is a qualifier */
EndOfLine /* NUL-token (signifies end of tokens) */
} dcl_arg_t;
typedef struct {
dcl_arg_t type; /* token type */
char *token; /* token value */
} dcl_token_t;
/***************************************************************************
** ^FUNCTION: is_cmdline - retrieve the original command-line
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
static BOOL is_cmdline( argv, result )
/*
** ^PARAMETERS:
*/
char *argv[];
/* -- array of strings
*/
char **result;
/* -- pointer to resultant command-line
*/
#endif /* !__ANSI_C__ */
/* ^DESCRIPTION:
** Is_cmdline will compare the given vector of strings to the actual
** string given on the command-line. If the two are approximately
** equivalent (modulo quotes and case) then the original command-line
** is copied to result, otherwise all the elements of argv are concat-
** enated together (in order and separated by whitespace) and assigned
** to *result.
**
** ^REQUIREMENTS:
** argv must be non-null
**
** ^SIDE-EFFECTS:
** *result is assigned to either the concatenated argv string or the
** original command-line. The result should be freed using free().
**
** ^RETURN-VALUE:
** FALSE if the argv given is different from the command-line;
** TRUE otherwise.
**
** ^CAVEATS:
** The comparison is case blind and double quotes are ignored in the
** command-line. This is because lib$get_foreign returns double quotes
** intact, while VAX-C strips them off.
**
** ^ACKNOWLEDGEMENTS:
** Thanx to Jim Barbour for writing most of this code. --BDA
**
** ^ALGORITHM:
** - Make a single string out of argv
** - compare the "big" string to the command-line
** - IF they are "equivalent" assign command-line to result & return TRUE.
** ELSE assign the "big" string to result and return FALSE.
***^^**********************************************************************/
#ifdef __ANSI_C__
static BOOL is_cmdline( const char *argv[], char **result )
#endif
{
register CONST char *avstr;
#ifdef vms
unsigned long int stat;
unsigned short int len;
register CONST char *aptr, *sptr;
static char str[ MAXCMDLINE ];
static BOOL got_cmd_line = FALSE;
$DESCRIPTOR(str_d, str);
#endif
/* make a single string out of argv */
avstr = strjoin( argv, " " );
#ifndef vms
*result = (char *)avstr;
return FALSE;
#else
/* get the original command-line */
if ( ! got_cmd_line ) {
stat = lib$get_foreign( &str_d, VNULL, &len, VNULL );
str[len] = '\0';
got_cmd_line = TRUE;
if (! (stat & 1)) exit( stat );
}
/* if we didnt have a command-line, dont bother comparing */
if ( !*str ) {
*result = (char *)avstr;
return FALSE;
}
/* compare the two */
for ( aptr = avstr, sptr = str ; *aptr && *sptr ; sptr++ ) {
if ( toupper(*sptr) == toupper(*aptr) ) {
++aptr;
}
else if ( *sptr != '"' ) {
*result = (char *)avstr;
return FALSE;
}
}
*result = strdup( str );
free( avstr );
return TRUE;
#endif
}
/***************************************************************************
** ^FUNCTION: dcl_strxlat - translate a string according to DCL syntax
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
static char *dcl_strxlat( str )
/*
** ^PARAMETERS:
*/
char *str;
/* -- the string to translate.
*/
#endif /* !__ANSI_C__ */
/* ^DESCRIPTION:
** Dcl_strxlat will attempt to convert the given string to canonical
** form by escaping any unquoted special characters, and removing any
** unquoted whitespace around special characters (such as '=' and '/').
** Since the special characters are replaced with special codes, quotes
** are also removed.
**
** ^REQUIREMENTS:
** <str> should be non-null and non-empty
**
** ^SIDE-EFFECTS:
** <str> is "trimmed" to canonical form and special characters are mapped
** to a unique code.
**
** ^RETURN-VALUE:
** The address of the translated string.
**
** ^ALGORITHM:
** - remove all unquoted whitespace following any unquoted "/:=+("
** - remove all unquoted whitespace preceding any unquoted "/:=+)"
** - compress all unquoted whitespace,
** - remove all unquoted parentheses,
** - re-map all other unquoted special characters and remove quotes.
** use the following mapping:
** whitespace ==> '\001'
** '/' ==> '\002'
** ':' & '=' ==> '\003'
** ',' & '+' ==> '\004'
***^^**********************************************************************/
#ifdef __ANSI_C__
static char *dcl_strxlat( char *str )
#endif
{
register char c, *pread = str, *pwrite = str;
BOOL quoted = FALSE;
/*
** pass1 - scan forward, removing all whitespace after unquoted "/:=+,("
*/
while ( c = *pwrite++ = *pread++ ) {
if ( c == '"' ) TOGGLE(quoted);
if ( !quoted && strchr("/:=+,(", c) )
while( isspace(*pread) ) ++pread;
}
*--pwrite = '\0'; /* NUL terminate */
/*
** pass2 - scan backward, removing all whitespace before unquoted "/:=+,)"
*/
pread = --pwrite; /* set to last NON-NUL char */
quoted = FALSE;
while ( pread >= str ) {
c = *pwrite-- = *pread--;
if ( c == '"' ) TOGGLE(quoted);
if ( !quoted && strchr("/:=+,)", c) )
while( isspace(*pread) ) --pread;
}
strcpy(str, ++pwrite); /* reset BOS */
/*
** pass3 - compress all unquoted whitespace,
** remove all unquoted parentheses,
** re-map all other unquoted special characters and remove quotes.
** use the following mapping:
** whitespace -> '\001'
** '/' -> '\002'
** ':' & '=' -> '\003'
** ',' & '+' -> '\004'
*/
pread = pwrite = str;
quoted = FALSE;
while ( c = *pread++ ) {
if ( c == '"' )
TOGGLE(quoted);
else if ( !quoted && isspace(c) ) {
*pwrite++ = c_SPACE;
while( isspace(*pread) ) ++pread;
}
else if ( !quoted && (c == '(' || c == ')') )
continue;
else if ( !quoted && c == '/' )
*pwrite++ = c_QUAL;
else if ( !quoted && c == ':' )
*pwrite++ = c_COLON;
else if ( !quoted && c == '=' )
*pwrite++ = c_EQUAL;
else if ( !quoted && c == '+' )
*pwrite++ = c_PLUS;
else if ( !quoted && c == ',' )
*pwrite++ = c_COMMA;
else
*pwrite++ = c;
}/*while*/
*pwrite = '\0'; /* NUL-terminate */
return str;
}
/***************************************************************************
** ^FUNCTION: dcl_split - split a string up into a vector of DCL tokens
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
static dcl_token_t *dcl_split( str )
/*
** ^PARAMETERS:
*/
char *str;
/* -- the string to split up into tokens
*/
#endif /* !__ANSI_C__ */
/* ^DESCRIPTION:
** Dcl_split will split a string up into tokens (according to DCL grammar
** rules) and will additionally associate each token with a type (namely:
** a qualifier, a positional paramater, or the End-of-Tokens symbol).
**
** ^REQUIREMENTS:
** Assume dcl_strxlat(str) has already been performed.
**
** ^SIDE-EFFECTS:
** <str> is modified in much the same manner as it would have
** been modified if it were passed as the vector_string to strsplit().
**
** ^RETURN-VALUE:
** A vector of dcl_tokens.
**
** ^ALGORITHM:
** - first count the number of tokens and also try to interpret stuff
** like "parm1.1/qual1,parm1.2" by replacing the comma with a space.
** - allocate space for the vector of DCL tokens.
** - assign the approriate value and type for each token.
**
** ^CAVEATS:
** Does not treate "/qual=(val1,val2/str,..)" as illegal
** ( parses it as if it were "/qual=(val1,val2)/str" )
**
** Replaces "parm1.1/qual,parm1.2" with "parm1.1/qual parm1.2"
** which works only because parseargs requires a VMS
** positional list to be comma OR whitespace separated
** (not just comma separated).
***^^**********************************************************************/
#ifdef __ANSI_C__
static dcl_token_t *dcl_split( char *str )
#endif
{
int tokc = 1; /* number of tokens */
dcl_token_t *tokv = (dcl_token_t *)NULL; /* vector of tokens */
register char *pread, c;
register int i;
if ( !str || !(*str) ) return (dcl_token_t *)NULL;
/* 1st pass (left-to-right) : count tokens */
pread = ( *str == c_QUAL ) ? (str + 1) : str;
while ( c = *pread++ ) {
if ( c == c_QUAL || c == c_SPACE ) ++tokc;
if ( c == c_QUAL ) {
/* replace "p1.1/qual,p1.2" with "p1.1/qual p1.2" */
char *p, delims[5];
sprintf( delims, "%c%c%c%c", c_EQUAL, c_PLUS, c_QUAL, c_SPACE );
if ( (p = strpbrk((str + 1), delims)) &&
((*p == c_PLUS) || (*p == c_COMMA)) )
*p == c_SPACE;
}
}
/* allocate vector */
tokv = (dcl_token_t *)malloc( (tokc + 1) * sizeof(dcl_token_t) );
if ( tokv == (dcl_token_t *)NULL ) {
syserr( "malloc() failed in dcl_split()" );
}
tokv[ tokc ].type = EndOfLine;
tokv[ tokc ].token = CHARNULL;
/* 2nd pass (right-to-left) : assign tokens to strings */
for ( i = 1, --pread ; pread >= str ; pread-- ) {
if ( *pread == c_SPACE || *pread == c_QUAL ) {
tokv[ tokc - i ].token = pread + 1;
tokv[ tokc - i ].type = ( *pread == c_QUAL ) ? Qualifier : Parameter;
*pread = '\0';
++i;
}
}
if ( *str ) { /* then 1st char could NOT have been '/' */
tokv -> token = str;
tokv -> type = Parameter;
}
return tokv;
}
/***************************************************************************
** ^FUNCTION: dcl_restore - restore the `escaped' characters in a token
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
static char *dcl_restore( tokstr )
/*
** ^PARAMETERS:
*/
char *tokstr;
/* -- the token string to restore
*/
#endif /* !__ANSI_C__ */
/* ^DESCRIPTION:
** Dcl_restore will attempt to restore any DCL special characters (such as
** '/' and '=') that may have been escaped by dcl_strxlat().
**
** ^REQUIREMENTS:
** tokstr should be non-null and non-empty
**
** ^SIDE-EFFECTS:
** Any escape characters (such as c_QUAL) are restored to their ascii
** representation.
**
** ^RETURN-VALUE:
** The address of the restored string
**
** ^ALGORITHM:
** - for each character in tokstr
** - if it is special then replace it with its ascii code
** end-if
** end-for
**
** ^CAVEATS:
** The string is not restored to way it was before it was processed by
** dcl_strxlat(). Any characters that were removed are still missing.
***^^**********************************************************************/
#ifdef __ANSI_C__
static char *dcl_restore( char *tokstr )
#endif
{
register char *str = tokstr;
if ( !str || !*str ) return str;
for ( ; *str ; str++ ) {
switch( *str ) {
case c_SPACE : *str = ' '; break;
case c_QUAL : *str = '/'; break;
case c_EQUAL : *str = '='; break;
case c_COLON : *str = ':'; break;
case c_PLUS : *str = '+'; break;
case c_COMMA : *str = ','; break;
default : break;
}
}
return tokstr;
}
/***************************************************************************
** ^FUNCTION: split_list - function to handle ARGLISTs and ARGVECs
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
static BOOL split_list( ad, vp, cmd )
/*
** ^PARAMETERS:
*/
ARGDESC *ad;
/* -- the argument which takes multiple values
*/
char *vp;
/* -- the string of values for the argument
*/
ARGDESC *cmd;
/* -- the command to which the argument belongs
*/
#endif /* !__ANSI_C__ */
/* ^DESCRIPTION:
** Split_list will split the string containing the set of values into
** a set of tokens and will then attempt to convert each token (in the
** order given) using the ad_type function of the argument structure.
**
** ^REQUIREMENTS:
** <vp> must already be preprocessed by dcl_strxlat to escape any quoted
** characters and to map special characters to their corresponding values.
**
** ^SIDE-EFFECTS:
** Ad has some of its flags modified as well as any modifications that
** are made by the ad_type function.
**
** <vp> is modified by strsplit().
**
** ^RETURN-VALUE:
** TRUE if all is hunky-dory; FALSE otherwise
**
** ^ALGORITHM:
** - Split vp into a vector of tokens
** - foreach token
** - call ad_type(ad, token, copyf)
** end-if
** - set the ARGGIVEN and ARGVALGIVEN flags accordingly
***^^**********************************************************************/
#ifdef __ANSI_C__
static BOOL split_list( ARGDESC *ad, char *vp, ARGDESC *cmd )
#endif
{
char **arg_vec = (char **)NULL;
int i, arg_num = 0;
BOOL err = FALSE;
char delims[3];
/* set-up delimiter string */
*delims = c_PLUS;
*(delims + 1) = c_COMMA;
*(delims + 2) = '\0';
/* break string up into to tokens and handle each one */
arg_num = strsplit( &arg_vec, vp, delims );
for ( i = 0 ; i < arg_num ; i++ ) {
vp = arg_vec[i];
/* try to convert the type */
if ( !HANDLE(ad, dcl_restore(vp), cmd_flags(cmd)) ) err = TRUE;
}
if ( !err ) BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
if ( ARG_isPOSITIONAL(ad) ) {
cmd_list(cmd) = ad;
}
else {
cmd_list(cmd) = ARGDESCNULL;
}
free( arg_vec );
return !err;
}
/***************************************************************************
** ^FUNCTION: vms_parse - parse VMS/DCL arg-vectors
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
int vms_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:
** Vms_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:
** - compare argv to the command-line (use the command-line if equal)
** - put argv back into a single string and translate it using dcl_strxlat
** - reparse the string into DCL tokens using dcl_strsplit
** - for each DCL token
** - attempt to match the token as a qualifier
** - if it is a qualifier
** - 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 vms_parse( char *argv[], ARGDESC argd[] )
#endif
{
register ARGDESC *ad, *args, *cmd;
register char *p;
char *avstr;
BOOL is_match = FALSE;
int parse_error = pe_SUCCESS;
dcl_token_t *tok, *tokvec;
argName_t keyword;
argMask_t saveflags, flags;
if ( !argd ) return parse_error;
/* initialize command-structure */
if ( !CMD_isINIT(argd) ) init_args( argd );
cmd = argd;
saveflags = cmd_flags(cmd);
if ( !argv || !*argv ) return parse_error;
if ( !BTEST(cmd_state(cmd), ps_NOTCMDLINE) ) {
(VOID) is_cmdline( (CONST char **)argv, &avstr );
}
else {
avstr = strjoin( argv, " " );
}
BSET( cmd_flags(cmd), pa_COPYF );
(VOID) dcl_strxlat( avstr );
if ( !avstr || !*avstr ) return parse_error;
tokvec = dcl_split( avstr );
/* run through the token vector */
for ( tok = tokvec ; (p = tok -> token) ; tok++ ) {
if ( tok -> type == Qualifier && !BTEST(cmd_state(cmd), ps_NOFLAGS) ) {
char c = '\0', *s, delims[3];
/* set-up delimiter string */
*delims = c_EQUAL;
*(delims + 1) = c_COLON;
*(delims + 2) = '\0';
/* skip past qualifier prefix and look for possible argument */
s = strpbrk(p, delims);
if (s) {
c = *s;
*s++ = '\0';
}
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(p, arg_sname(ad)) == 0) {
is_match = TRUE;
break;
}/*if*/
}
}
if (c) *(s-1) = c; /* restore the equal sign */
if ( !is_match ) {
if (s) *(s-1) = '\0';
usrerr( "undefined qualifier %s", s_KWD_PFX, p );
if (s) *(s-1) = c;
parse_error = pe_SYNTAX;
continue;
}
/* end-qualifiers */
if ( arg_type(ad) == argEnd ) {
BSET( cmd_state(cmd), ps_NOFLAGS );
continue;
}
/* if usage - just print usage and exit */
if ( arg_type(ad) == argUsage ) {
Usage_Requested = TRUE;
usage( argd );
free( avstr );
if ( tokvec ) free( tokvec );
cmd_flags(cmd) = saveflags;
exit(exit_USAGE);
}
/* 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);
if ( ARG_isGIVEN(ad) ) {
BCLEAR( arg_flags(ad), ARGVALSEP );
if ( !ARG_isMULTIVAL(ad) ) BCLEAR( arg_flags(ad), ARGVALGIVEN );
}
/* ARGNOVALs are special, having no value */
if ( ! ARG_isVALTAKEN(ad) ) {
if ( !HANDLE(ad, dcl_restore(s), cmd_flags(cmd)) ) {
arg_flags(ad) = flags;
parse_error = pe_SYNTAX;
}
else {
BSET( arg_flags(ad), ARGGIVEN );
ad = ARGDESCNULL;
}
continue;
}/*if ARGNOVAL*/
/* now get the real value */
if ( !s || !(*s) ) {
if ( ARG_isVALOPTIONAL(ad) ) {
BSET( arg_flags(ad), ARGGIVEN );
}
else {
(VOID) get_kwdname( arg_sname(ad), keyword );
usrerr("qualifier %s requires an argument", keyword);
arg_flags(ad) = flags;
parse_error = pe_SYNTAX;
}
continue;
}/*if*/
if( ARG_isMULTIVAL(ad) ) {
if( !split_list(ad, s, cmd) ) {
arg_flags(ad) = flags;
parse_error = pe_SYNTAX;
}
else {
BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
}
continue;
}/*if list*/
/* try to convert the type */
if ( !HANDLE(ad, dcl_restore(s), cmd_flags(cmd)) ) {
arg_flags(ad) = flags;
parse_error = pe_SYNTAX;
}
else {
BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
}
continue;
}/*if qual*/
else {
/* parsing a vector of arguments */
if ( cmd_list(cmd) ) {
ad = cmd_list(cmd);
flags = arg_flags(ad);
if ( ARG_isGIVEN(ad) ) {
BCLEAR( arg_flags(ad), ARGVALSEP );
}
BSET( arg_flags(ad), ARGVALSEP );
if( ARG_isMULTIVAL(ad) ) {
if( !split_list(ad, p, cmd) ) parse_error = pe_SYNTAX;
}
else if ( !HANDLE(ad, dcl_restore(p), cmd_flags(cmd)) ) {
arg_flags(ad) = flags;
parse_error = pe_SYNTAX;
}
if ( !parse_error ) BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
continue;
}
/* positional argument */
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;
continue;
}
/* if FLAGS1ST is set then first positional marks end-of-flags */
if ( BTEST(cmd_flags(cmd), pa_FLAGS1ST) ) {
BSET( cmd_state(cmd), ps_NOFLAGS );
}
/* 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);
if ( ARG_isGIVEN(ad) ) {
BCLEAR( arg_flags(ad), ARGVALSEP );
if ( !ARG_isMULTIVAL(ad) ) BCLEAR( arg_flags(ad), ARGVALGIVEN );
}
BSET( arg_flags(ad), ARGVALSEP );
if( ARG_isMULTIVAL(ad) ) {
if( !split_list(ad, p, cmd) ) {
arg_flags(ad) = flags;
parse_error = pe_SYNTAX;
}
else {
BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
}
continue;
}/*if list*/
/* try to convert */
if ( !HANDLE(ad, dcl_restore(p), cmd_flags(cmd)) ) {
arg_flags(ad) = flags;
parse_error = TRUE;
}
else {
BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
}
}/*if parameter*/
}/*while*/
free( avstr );
if ( tokvec ) free( tokvec );
cmd_flags(cmd) = saveflags;
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 );
sprintf( buf, "%c%s", *s_KWD_PFX, keyword );
pos = buf + strlen(buf);
if ( ARG_isVALTAKEN(ad) && !ARG_isBOOLEAN(ad) && !ARG_isPSEUDOARG(ad) ) {
if ( ARG_isVALOPTIONAL(ad)) {
sprintf( pos, "[%c<%s>]", *s_ARG_SEP, name );
}
else {
sprintf( pos, "%c<%s>", *s_ARG_SEP, name );
}
}/*if*/
}/*else*/
return (int) strlen(buf);
}
/***************************************************************************
** ^FUNCTION: vms_usage - print a usage message
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
VOID vms_usage( argd, usage_flags )
/*
** ^PARAMETERS:
*/
ARGDESC *argd;
/* -- the command-descriptor array
*/
argMask_t usage_flags;
/* -- flags set by $USAGECNTL
*/
#endif /* !__ANSI_C__ */
/* ^DESCRIPTION:
** Vms_usage will print the VMS/DCL 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 symbol.
**
** ^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 vms_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, qualifiers, longest, positionals;
BOOL first = TRUE;
FILE *fp;
if ( !argd ) return;
/* initialize command-structure */
if ( !CMD_isINIT(argd) ) init_args( (ARGDESC *)argd );
cmd = argd;
/* get screen size */
get_winsize( fileno(stderr), &max_lines, &max_cols );
/* 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;
/* allow null argument descriptor */
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, name;
/* 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_isMULTIVAL(ad) ) {
(VOID) get_argname( arg_sname(ad), name );
strcat(buf, "[,<");
strcat(buf, name);
strcat(buf, ">...]");
pl += 8 + strlen(name);
}
if ( !ARG_isREQUIRED(ad) ) {
pl += 2; /* [] */
}
/* 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;
}
qualifiers = 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 ( !qualifiers++ ) fprintf(fp, "Qualifiers/Parameters:\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 );
}