home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume10
/
parseargs
/
amiga_args.c
next >
Wrap
C/C++ Source or Header
|
1990-02-16
|
8KB
|
388 lines
#include <useful.h>
#include <parseargs.h>
#include <ctype.h>
VERSIONID("$Header: parseargs.c,v 2.1 89/12/30 20:59:48 eric Exp $");
/*
** PARSEARGS -- parse an argument vector, given a description
**
** Parameters:
** argv -- the argument vector as passed to main().
** argd -- the argument descriptor array.
**
** Returns:
** Nothing
** Exits with return code 20 if error in args.
** Exits with return code 10 if system error.
**
** Side Effects:
** Converts and stores arguments into variables as
** described by argd.
**
** Globals:
** DefaultPath -- the pathname of a set of places to
** look for system files, set from the ROOTPATH
** environment variable, or a default.
** ProgName -- the name of this program, saved for error
** messages and the like.
**
** Author:
** Eric Allman
** University of California, Berkeley
*/
#define ALL_AD ad = argd; ad->ad_name != '\0'; ad++
#define ALL_DEFS ad = _DefaultArgs; ad->ad_name != '\0'; ad++
char *ProgName;
#ifdef TRACESTUFF
extern BOOL argTrace ARGS((ARGDESC *, char *, BOOL));
#endif
extern BOOL argEnd ARGS((ARGDESC *, char *, BOOL));
/* default arguments -- apply to all programs */
STATIC ARGDESC _DefaultArgs[] =
{
/* name flags type valp prompt */
#ifdef TRACESTUFF
'T', ARGOPT, argTrace, ARBNULL, "TRACE",
#endif
'-', ARGOPT, argEnd, ARBNULL, "ARGS",
ENDOFARGS
};
/* override argument descriptor, if none given by user */
STATIC ARGDESC _NullArgDesc[] =
{
ENDOFARGS
};
VOID
parseargs(argv, argd)
char **argv;
ARGDESC argd[];
{
register ARGDESC *ad, *list;
register char **av;
register char *p;
BOOL noflags;
BOOL error;
extern char *getenv ARGS((char *));
av = argv++;
/* save the name of this program (for error messages) */
ProgName = *av;
/* allow null argument descriptor */
if (argd == (ARGDESC *) NULL)
argd = _NullArgDesc;
/* clear out any cruft in the argument descriptor */
for (ALL_AD)
{
ad->ad_flags &= ~ARGGIVEN;
}
for (ALL_DEFS)
{
ad->ad_flags &= ~ARGGIVEN;
}
/* run through the argument vector */
noflags = FALSE;
error = FALSE;
ad = NULL; /* No argument requested */
list = NULL;
while (*++av != CHARNULL)
{
/* Previous keyword required a value */
if(ad)
{
/* try to convert the type */
if (!(*ad->ad_type)(ad, *av, FALSE))
error = TRUE;
else
ad->ad_flags |= ARGGIVEN;
ad = NULL;
continue;
}
/* If looking for keywords, see if this is one */
if(!noflags) {
for(ALL_AD)
if(match(*av, ad->ad_prompt) == 0)
break;
if(ad->ad_name == '\0')
for(ALL_DEFS)
if(match(*av, ad->ad_prompt) == 0)
break;
}
if(ad->ad_name == '\0')
ad = NULL;
/* If we have a keyword here */
if(!noflags && ad)
{
list = NULL;
p = strchr(*av, '=');
if(p) /* matched NAME=VALUE */
{
p++;
/* try to convert the type */
if (!(*ad->ad_type)(ad, p, FALSE))
error = TRUE;
else
ad->ad_flags |= ARGGIVEN;
ad = NULL;
}
else
{
if (ad->ad_type == argBool)
{
*(BOOL *) ad->ad_valp = TRUE;
ad->ad_flags |= ARGGIVEN;
ad = NULL;
}
else if (ad->ad_type == argEnd)
{
noflags = TRUE;
ad->ad_flags |= ARGGIVEN;
ad = NULL;
}
else if (ad->ad_type == argList)
{
list = ad;
ad = NULL;
}
}
}
else /* it's a positional argument */
{
if(list) {
if (!argList(list, *av, FALSE))
error = TRUE;
list->ad_flags |= ARGGIVEN;
continue;
}
else
{
for (ALL_AD)
{
if (ad->ad_name == ' ' &&
(ad->ad_type == argList ||
!BITSET(ARGGIVEN, ad->ad_flags))
)
break;
}
if (ad->ad_name == '\0')
{
usrerr("too any arguments");
error = TRUE;
ad = NULL;
}
else
{
/* try to convert */
if (!(*ad->ad_type)(ad, *av, FALSE))
error = TRUE;
else
ad->ad_flags |= ARGGIVEN;
ad = NULL;
}
}
}
}
/* If last argument was a keyword and required an option
* then complain about it
*/
if(ad) {
usrerr("missing argument for %s", ad->ad_prompt);
error = TRUE;
}
*argv = NULL;
/* now rescan for missing required arguments */
for (ALL_AD)
{
if (BITSET(ARGREQ, ad->ad_flags) && !BITSET(ARGGIVEN, ad->ad_flags))
{
if (!BITSET(ARGGIVEN, ad->ad_flags))
{
/* still didn't get a value... sigh */
if (ad->ad_name == ' ')
{
usrerr("%s required",
ad->ad_prompt);
}
else
{
usrerr("%s required for -%c flag",
ad->ad_prompt, ad->ad_name);
}
error = TRUE;
}
}
}
if (error)
{
usage(argd);
exit(20);
}
}
/*
** USAGE -- print a usage message
**
** Parameters:
** argd -- the description of expected arguments.
**
** Returns:
** none
**
** Side Effects:
** prints on stderr
**
** Globals:
** MaxOutputLine -- the length of the maximum output line
** allowed before wrapping. This should be fetched
** from the terminal driver on systems that support
** this sort of thing.
*/
int MaxOutputLine = 72;
VOID
usage(argd)
ARGDESC *argd;
{
register ARGDESC *ad;
int ll;
int pl;
fprintf(stderr, "Usage: %s", ProgName);
ll = strlen(ProgName) + 7;
for (ALL_AD)
{
char keyword[BUFSIZ];
char name[BUFSIZ];
int i, j;
j = 0;
for(i = 0; ad->ad_prompt[i]; i++) {
if(isupper(ad->ad_prompt[i])) {
keyword[j++] = ad->ad_prompt[i];
name[i] = tolower(ad->ad_prompt[i]);
}
else
name[i] = ad->ad_prompt[i];
}
name[i] = 0;
if(j > 0)
keyword[j] = 0;
else
strcpy(keyword, ad->ad_prompt);
/* don't display hidden arguments */
if (BITSET(ARGHIDDEN, ad->ad_flags))
continue;
/* figure out how wide this parameter is (for printing) */
if (ad->ad_name != ' ')
{
pl = strlen(keyword);
if (ad->ad_type != argBool)
pl += strlen(name) + 3;/* _< > */
}
else
{
pl = strlen(name) + 2; /* < > */
}
if (!BITSET(ARGREQ, ad->ad_flags))
pl += 2; /* [ ] */
if (ad->ad_type == argList)
pl += 3; /* ... */
pl += 1; /* leading sp */
/* see if this will fit */
if (ll + pl > MaxOutputLine)
{
/* no... start a new line */
fprintf(stderr, " +\n\t");
ll = 7;
}
else
{
/* yes... just throw in a space */
fprintf(stderr, " ");
}
ll += pl;
/* show the argument */
if (!BITSET(ARGREQ, ad->ad_flags))
fprintf(stderr, "[");
if (ad->ad_name != ' ')
{
fprintf(stderr, "%s", keyword);
if (ad->ad_type != argBool)
fprintf(stderr, " ");
}
if (ad->ad_name == ' ' || ad->ad_type != argBool)
fprintf(stderr, "<%s>", name);
if (!BITSET(ARGREQ, ad->ad_flags))
fprintf(stderr, "]");
if (ad->ad_type == argList)
fprintf(stderr, "...");
}
fprintf(stderr, "\n");
}
/* match(s1, s2)
**
** Compares two strings, returning >0, <0, or =0 if they match. First a
** check is done on letters capitalised in the second word, and if this
** fails then a complete match is done. Case is ignored in both matches.
** This lets you use case to indicate what part of a keyword is significant.
*/
int match(candidate, target)
char *target, *candidate;
{
int i, j;
char c;
i = j = 0;
while(target[i] || candidate[i]) {
while(islower(target[i])) i++;
if(!target[i]) {
if(!candidate[j]) return 0;
return dictcmp(target, candidate);
}
c = islower(candidate[j])
? toupper(candidate[j])
: candidate[j];
if(target[i] != c) return dictcmp(target, candidate);
i++;
j++;
}
return 0;
}
int dictcmp(s1, s2) /* "Dictionary" comparison of two strings */
char *s1, *s2;
{
char c1, c2;
while(*s1 || *s2) {
c1 = *s1++;
c2 = *s2++;
if(!c1 || !c2) return c1 - c2;
if(isupper(c1)) c1 = tolower(c1);
if(isupper(c2)) c2 = tolower(c2);
if(c1 != c2) return c1 - c2;
}
return 0;
}