/*
		Version 7 compatible
	Argument scanner, scans argv style argument list.

	Some stuff is a kludge because sscanf screws up

	Gary Newman - 10/4/1979 - Ampex Corp.

	Modified by Spencer W. Thomas, Univ. of Utah, 5/81 to
	add args introduced by 	a flag, add qscanargs call,
	allow empty flags.

	Compiling with QUICK defined generates 'qscanargs' ==
	scanargs w/o floating point support; avoids huge size
	of scanf.

	If you make improvements we'd like to get them too.
	Jay Lepreau	lepreau@utah-20, decvax!{harpo,randvax}!utah-cs!lepreau
	Spencer Thomas	thomas@utah-20, decvax!{harpo,randvax}!utah-cs!thomas

	(There seems to be a bug here in that if the last option you have
	is a flag, and the user enters args incorrectly, sometimes the usage
	message printed will miss the null and go flying off thru core...)
							--jay for spencer
*/
#include <stdio.h>
#include <ctype.h>

#define YES 1
#define NO 0
#define ERROR(msg)  {fprintf(stderr, "msg\n"); goto error; }

char	*prformat();

#ifndef	QUICK
scanargs (argc, argv, format, arglist)
#else
qscanargs (argc, argv, format, arglist)
#endif
int     argc;
char  **argv;
char   *format;
int     arglist[];
{
#ifndef	QUICK
    _scanargs (argc, argv, format, &arglist);
#else
    _qscanargs (argc, argv, format, &arglist);
#endif
}

#ifndef	QUICK
_scanargs (argc, argv, format, arglist)
#else
_qscanargs (argc, argv, format, arglist)
#endif
int     argc;
char  **argv;
char   *format;
int    *arglist[];
{
    register    check;			/* check counter to be sure all argvs
					   are processed */
    register char  *cp;
    register    cnt;
    char    tmpflg;			/* temp flag */
    char    c;
    char    numnum;			/* number of numbers already processed
					   */
    char    numstr;			/* count # of strings already
					   processed */
    char    tmpcnt;			/* temp count of # things already
					   processed */
    char    required;
    char    exflag;			/* when set, one of a set of exclusive
					   flags is set */
    char    excnt;			/* count of which exclusive flag is
					   being processed */
    char   *ncp;			/* remember cp during flag scanning */
#ifndef	QUICK
    char   *cntrl;			/* control string for scanf's */
    char    junk[2];			/* junk buffer for scanf's */

    cntrl = "% %1s";			/* control string initialization for
					   scanf's */
#endif
    check = numnum = numstr = 0;
    cp = format;
    while (*cp)
    {
	required = NO;
	switch (*(cp++))
	{
	    default: 			/* all other chars */
		break;
	    case '!': 			/* required argument */
		required = YES;
	    case '%': 			/* not required argument */
		switch (tmpflg = *(cp++))
		{
		    case '-': 		/* argument is flag */
		    /* go back to label */
			ncp = cp-1;	/* remember */
			cp -= 3;
			for (excnt = exflag = 0
				; *cp != ' ' && !(*cp=='-' &&(cp[-1]=='!'||cp[-1]=='%'));
				(--cp, excnt++))
			{
			    for (cnt = 1; cnt < argc; cnt++)
			    {
			    /* flags all start with - */
				if (*argv[cnt] == '-' && !isdigit(argv[cnt][1]))
				    if (*(argv[cnt] + 1) == *cp)
				    {
					if (*(argv[cnt] + 2) != 0)
					    ERROR (extra flags ignored);
					if (exflag)
					    ERROR (more than one exclusive flag chosen);
					exflag++;
					required = NO;
					check += cnt;
					**arglist |=
					    (1 << excnt);
					break;
				    }
			    }
			}
			if (required)
			    ERROR (flag argument missing);
			cp = ncp;
			if (!exflag)	/* if no flags scanned, skip */
			{
			    while (*++cp != ' ' && *cp)
				if (*cp == '!' || *cp == '%')
				    arglist++;
			}
			else
			    cp++;	/* skip over - */
			while (*cp == ' ')
			    cp++;
			arglist++;
			break;
		    case 's': 		/* char string */
		    case 'd': 		/* decimal # */
		    case 'o': 		/* octal # */
		    case 'x': 		/* hexadecimal # */
#ifndef	QUICK
		    case 'f': 		/* floating # */
#endif
		    case 'D': 		/* long decimal # */
		    case 'O': 		/* long octal # */
		    case 'X': 		/* long hexadecimal # */
#ifndef	QUICK
		    case 'F': 		/* double precision floating # */
#endif
			tmpcnt = tmpflg == 's' ? numstr : numnum;
			for (cnt = 1; cnt < argc; cnt++)
			{
			    if (tmpflg == 's')/* string */
			    {
				if ((c = *argv[cnt]) == '-')
				    continue;
				if (c >= '0' && c <= '9')
				    continue;
				if (tmpcnt-- != 0)
				    continue;
				**arglist = (int) argv[cnt];
				check += cnt;
				numstr++;
				required = NO;
				break;
			    }
			    if (*argv[cnt] == '-')
			    {
				if(!isdigit (*(argv[cnt] + 1)))
				    continue;
			    }
			    else if (!isdigit(*argv[cnt]))
				continue;
			    if (tmpcnt-- != 0)/* skip to new one */
				continue;
#ifndef	QUICK
			/* kludge for sscanf */
			    if ((tmpflg == 'o' || tmpflg == 'O')
				    && *argv[cnt] > '7')
				ERROR (Bad numeric argument);
			    cntrl[1] = tmpflg;/* put in conversion */
			    if (sscanf (argv[cnt], cntrl, *arglist
					,junk) != 1)
#else
			    if (numcvt(argv[cnt], tmpflg, *arglist) != 1)
#endif
				ERROR (Bad numeric argument);
			    check += cnt;
			    numnum++;
			    required = NO;
			    break;
			}
			if (required)
			    switch (tmpflg)
			    {
				case 'x':
				case 'X':
				    ERROR (missing hexadecimal argument);
				case 's':
				    ERROR (missing string argument);
				case 'o':
				case 'O':
				    ERROR (missing octal argument);
				case 'd':
				case 'D':
				    ERROR (missing decimal argument);
				case 'f':
				case 'F':
				    ERROR (missing floating argument);
			    }
			arglist++;
			while (*cp == ' ')
			    cp++;
			break;
		    default: 		/* error */
			fprintf (stderr, "error in call to scanargs\n");
			return (0);
		}
	}
    }
/*  Count up empty flags */
    for (cnt=1; cnt<argc; cnt++)
	if (argv[cnt][0] == '-' && argv[cnt][1] == 0)
	    check += cnt;
 /* sum from 1 to N = n*(n+1)/2 used to count up checks */
    if (check != (((argc - 1) * argc) / 2))
	ERROR (extra arguments not processed);
	    return (1);

error:
    fprintf (stderr, "usage : ");
    if (*(cp = format) != ' ')
	while (putc (*cp++, stderr) != ' ');
    else
	fprintf (stderr, "?? ");
    while (*cp == ' ')
	cp++;
    prformat (cp, NO);
    return 0;
}

char *
prformat (format, recurse)
char   *format;
{
    register char  *cp;
    char    required;

    cp = format;
    if (recurse)
	putc (' ', stderr);
    required = NO;
    while (*cp)
    {
	if (recurse && *cp == ' ')
	    break;
	switch (*cp)
	{
	    default:
		cp++;
		break;
	    case '!':
		required = YES;
	    case '%':
		switch (*++cp)
		{
		    case '-': 		/* flags */
			if (!required)
			{
			    putc ('[', stderr);
			    putc ('-', stderr);
			}
			else
			{
			    putc ('-', stderr);
			    putc ('{', stderr);
			}
			cp = format;
			while (*cp != '%' && *cp != '!')
			    putc (*cp++, stderr);
			if (required)
			    putc ('}', stderr);
			cp += 2;	/* skip !- or %- */
			if (*cp != ' ')
			    cp = prformat (cp, YES);
					/* this is a recursive call */
			if (!required)
			    putc (']', stderr);
			break;
		    case 's': 		/* char string */
		    case 'd': 		/* decimal # */
		    case 'o': 		/* octal # */
		    case 'x': 		/* hexadecimal # */
		    case 'f': 		/* floating # */
		    case 'D': 		/* long decimal # */
		    case 'O': 		/* long octal # */
		    case 'X': 		/* long hexadecimal # */
		    case 'F': 		/* double precision floating # */
			if (!required)
			    putc ('[', stderr);
			for (; format < cp - 1; format++)
			    putc (*format, stderr);
			if (!required)
			    putc (']', stderr);
			break;
		    default:
			break;
		}
		required = NO;
		format = ++cp;
		putc (' ', stderr);
	}
    }
    if (!recurse)
	putc ('\n', stderr);
    return (cp);
}

#ifdef	QUICK
numcvt(str, conv, val)
register char *str;
char conv;
int *val;
{
    int base, neg = 0;
    register unsigned int d;
    long retval = 0;
    register char *digits;
    extern char *index();
    if (conv == 'o' || conv == 'O')
	base = 8;
    else if (conv == 'd' || conv == 'D')
	base = 10;
    else if (conv == 'x' || conv == 'X')
	base = 16;
    else
	return 0;

    if (*str == '-')
    {
	neg = 1;
	str++;
    }
    while (*str)
    {
	if (*str >= '0' && *str < '0'+base)
	    d = *str - '0';
	else if (base == 16 && *str >= 'a' && *str <= 'f')
	    d = 10 + *str - 'a';
	else if (base == 16 && *str >= 'A' && *str <= 'F')
	    d = 10 + *str - 'A';
	else
	    return 0;
	retval = retval*base + d;
	str++;
    }
    if (neg)
	retval = -retval;
    if (conv == 'D' || conv == 'O' || conv == 'X')
	*(long *) val = retval;
    else
	*val = (int) retval;
    return 1;
}
#endif	QUICK

