home *** CD-ROM | disk | FTP | other *** search
- #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 -- pointer to the argument vector as passed to main().
- ** argd -- the argument descriptor array.
- **
- ** Returns:
- ** Nothing.
- ** Exits with return code 2 if error in args.
- ** Exits with return code 1 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++
-
- #define HANDLE(a,n,f) ((*(a)->ad_type)(a,n,f))
-
- char *ProgName;
-
- extern BOOL argEnd ARGS((ARGDESC *, char *, BOOL));
-
- /* default arguments -- apply to all programs */
- STATIC ARGDESC _DefaultArgs[] =
- {
- /* name flags type valp prompt */
- '-', 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;
- int argc;
- BOOL noflags;
- BOOL error;
- extern char *getenv ARGS((char *));
-
- av = argv++;
- argc = 1;
-
- /* 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;
- list = NULL;
- while ((p = *++av) != CHARNULL)
- {
- if(*p == '+' && !noflags)
- {
- char *s;
-
- s = strchr(p, '=');
- if(s) {
- *s++ = 0;
- }
-
- if(*++p == '+' && p[1] == '\0')
- {
- /* ++ indicates end of flags */
- noflags = TRUE;
- list = NULL;
- continue;
- }
- for(ALL_AD)
- {
- if(match(p, ad->ad_prompt) == 0)
- break;
- }
- if (ad->ad_name == '\0')
- {
- for (ALL_DEFS)
- {
- if(match(p, ad->ad_prompt) == 0)
- break;
- }
- if (ad->ad_name == '\0')
- {
- usrerr("option %s unknown", p);
- error = TRUE;
- continue;
- }
- }
-
- /* booleans are special, having no value */
- if (ad->ad_type == argBool)
- {
- if(s) /* Error... no arg to bool */
- {
- usrerr("option %s takes no arguments", p);
- error = TRUE;
- continue;
- }
- *(BOOL *) ad->ad_valp = TRUE;
- ad->ad_flags |= ARGGIVEN;
- continue;
- }
-
- /* now get the real value */
- if (!s)
- {
- s = *++av;
- if (s == CHARNULL || *s == '-' || *s == '+')
- {
- av--;
- usrerr("option %s requires an argument",
- ad->ad_prompt);
- error = TRUE;
- continue;
- }
- }
-
- /* try to convert the type */
- if (!HANDLE(ad, s, FALSE))
- error = TRUE;
- else
- ad->ad_flags |= ARGGIVEN;
- if(ad->ad_flags & ARGLIST)
- list = ad;
- else
- list = NULL;
- continue;
- }
- else if (*p == '-' && !noflags)
- {
- /* flag argument */
- if (*++p == '-' && p[1] == '\0')
- {
- /* -- indicates end of flags */
- noflags = TRUE;
- list = NULL;
- continue;
- }
- while (*p != '\0')
- {
- /* find the flag in the list */
- for (ALL_AD)
- {
- if (ad->ad_name == *p)
- break;
- }
- if (ad->ad_name == '\0')
- {
- for (ALL_DEFS)
- {
- if (ad->ad_name == *p)
- break;
- }
- if (ad->ad_name == '\0')
- {
- usrerr("flag -%c unknown", *p++);
- error = TRUE;
- continue;
- }
- }
-
- /* move p up to point to the (possible) value */
- p++;
-
- /* booleans are special, having no value */
- if (ad->ad_type == argBool)
- {
- *(BOOL *) ad->ad_valp = TRUE;
- ad->ad_flags |= ARGGIVEN;
- continue;
- }
-
- /* now get the real value */
- if (*p == '\0')
- {
- p = *++av;
- if (p == CHARNULL || *p == '-' || *p == '+')
- {
- av--;
- usrerr("%s required for -%c flag",
- ad->ad_prompt, ad->ad_name);
- error = TRUE;
- break;
- }
- }
-
- /* try to convert the type */
- if (!HANDLE(ad, p, FALSE))
- error = TRUE;
- else
- ad->ad_flags |= ARGGIVEN;
- if(ad->ad_flags & ARGLIST)
- list = ad;
- else
- list = NULL;
- break;
- }
- }
- else
- {
- /* parsing a list of arguments */
- if(list) {
- if (!HANDLE(list, p, FALSE))
- error = TRUE;
- continue;
- }
- /* positional argument */
- for (ALL_AD)
- {
- if (ad->ad_name == ' ' &&
- ( (ad->ad_flags & ARGLIST) ||
- !BITSET(ARGGIVEN, ad->ad_flags))
- )
- break;
- }
- if (ad->ad_name == '\0')
- {
- usrerr("too any arguments");
- error = 1;
- continue;
- }
-
- /* try to convert */
- if (!HANDLE(ad, p, FALSE))
- error = TRUE;
- else
- ad->ad_flags |= ARGGIVEN;
- }
- }
-
- /* 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(2);
- }
-
- cleanup_lists(argd);
- }
- /*
- ** 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)
- {
- /* 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 = 2; /* -x */
- pl += strlen(ad->ad_prompt) + 3;/* _< > or _( ) */
- }
- else
- {
- pl = strlen(ad->ad_prompt) + 2; /* < > */
- }
- if (!BITSET(ARGREQ, ad->ad_flags))
- pl += 2; /* [ ] */
- if (ad->ad_flags & 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, "-%c ", ad->ad_name);
- }
- if (ad->ad_name == ' ' || ad->ad_type != argBool)
- fprintf(stderr, "<%s>", ad->ad_prompt);
- else
- fprintf(stderr, "(%s)", ad->ad_prompt);
- if (!BITSET(ARGREQ, ad->ad_flags))
- fprintf(stderr, "]");
- if (ad->ad_flags & 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;
- }
-