/* getargs.c -- get command switch arguments	*/
/* by Jim Mackraz, 1987.  Placed in the public domain by the author */

/*
 *		getargs() -- parses switch arguements in argv[]
 *	(a "switch argument" starts with SWITCHCHAR, normally '-')
 *
 *	SYNOPSIS:
 *  getargs( argv, argspec, numargs, prognameptr)
 *	 char	**argv;			-- as passed to main(), NULL terminated
 *	 struct Arg argspec[];	-- array specifying arguments expected
 *	 int		numargs;	-- elements in argspec[]
 *	 char	**prognameptr	-- if non-NULL, will put value of argv[0] here
 *
 *	DESCRIPTION:
 *	Parses argv for arguments of type: "-<char>" where 'char' is single
 *		character followed by data
 *	Types of arguments include integer, boolean, and string.
 *	Boolean args, by there presence, are true, so their default should be
 *		FALSE.  They have no data part
 *	Arguments with data can be provided with or without whitespace:
 *		-i8 equiv. to -i 8	(integer)
 *		-sfoo equiv. to -s foo	(string)
 *
 *	getargs() will call its internal argUsage() function to print
 *	switch arguments usage, with descriptions gotten from argspec[].
 *
 *	RETURNS:
 *	If successful, returns amount argv incremented to get past "switch"
 *	arguments.  Errors are negative numbers, and defined in "getargs.h"
 *
 *	WARNING:
 *	For strings, this function only changes pointers to point to correct
 *	places in **argv, so if argv storage is messed with, the strings
 *  (including argv[0]) should be copied first.  Also, the pointers
 *	in argv[] are changed in the course of processing and so argv
 *	cannot be reused.  The arguments following the switch arguments
 *	are not affected.
 */

#include "getargs.h"

#define D(x) ;

/* this array synced to arg_type defines	*/
char	*argtypes[] = {
	"<integer>",
	"         ",
	"<string>",
};

#define TEST 0
#if TEST

char	*myprogname;		/* set to argv[0]	*/
int		myint1 = 5;
int		mybool1 = 0;	
char	*mystring = "Default String";

struct Arg	myargs[] = {
	{'i', ARG_TINT, &myint1, "generic integer argument"},
	{'b', ARG_TBOOL, &mybool1, "generic bool argument"},
	{'s', ARG_TSTRING, &mystring, "generic string argument"}
};


main(argc, argv)
char	**argv;
{
	int		argb;

	argb = getargs( argv, myargs, NUMARGS( myargs ), &myprogname);

	if (argb >= 0) argv += argb;		/* bump argv to following arg	*/
	else exit (-argb);

	/** ALL DONE ARGS	**/
	printf("progname %s, myint1 %d, mybool1 %d mystring  %s\n",
		myprogname, myint1, mybool1, mystring);

	printf("getargs returned %d\n", argb);
	printf("next argv = %s\n", *argv? *argv: "NULL");
}
#endif TEST

static char	*pname;

/*
 * returns number of times argv was incremented, else
 * negative number for error (see getargs.h)
 */
getargs( argv, argspec, numargs, prognameptr)
char	**argv;
struct Arg	*argspec;
int		numargs;
char	**prognameptr;			/* copy argv[0] here if non-null	*/
{
	register struct Arg	*aptr;
	char 	achar;
	int		i;
	int		argfound;
	int		argbump = 0;		/* count how many times ++argv		*/

	/* give him the command name (argv[0])	*/
	pname = *argv;
	if (prognameptr) *prognameptr = pname;

	/* check argv until arg is not a switchchar	*/
	for (++argv, argbump++; *argv && (**argv == SWITCHCHAR); ++argv, argbump++)
	{
		achar = *(++(*argv));	/* first char following SWITCHCHAR	*/
		(*argv)++;				/* second char after SWITCHCHAR		*/

		D( printf("checking for arg_char: 0x%x <%c>\n", achar, achar) );
		argfound = 0;

		/* run through args for (first) match	*/
		for (aptr = argspec, i = 0; i < numargs; ++i, ++aptr)
		{
			if ( aptr->arg_char == achar )
			{
				argfound = 1;

				/* all but bool args have arg data	*/
				/* check if data follows white space */
				if ( aptr->arg_type != ARG_TBOOL && **argv == '\0' )
				{
					argv++;		/* point *argv to beginning of next arg	*/
					argbump++;

					if (!(*argv)) 	/* no more args, one expected	*/
					{
						D( printf("arg data not found\n") );
						argUsage( argspec, numargs );
						return (ARG_ENODATA);
					}
				}

				switch (aptr->arg_type)
				{
				case ARG_TINT:
					if (sscanf( *argv, "%d", aptr->arg_var) != 1)
					{
						D( printf("bad int: %s\n", *argv) );
						argUsage( argspec, numargs );
						return (ARG_EBADINT);
					}
					break;

				case ARG_TBOOL:		/* arg exists => TRUE	*/
					*((int *) aptr->arg_var) = 1;
					break;

				case ARG_TSTRING:
					*( (char **) aptr->arg_var) = *argv;
					break;

				default:
					D( printf("unknown arg type\n") );
					argUsage( argspec, numargs );
					return (ARG_EBADTYPE);
				}
			}
		}
		if (!argfound)
		{
			D( printf("unknown argument\n") );
			argUsage( argspec, numargs );
			return (ARG_EUNKNOWN);
		}
	}
	return (argbump);
}


static argUsage( argspec, numargs)
struct	Arg	argspec[];
{
	int		i;

	printf("%s argument usage:\n", pname);
	for (i = 0; i < numargs; ++i)
	{
		printf("\t%c%c %s\t - %s\n", SWITCHCHAR, argspec[i].arg_char,
			argtypes[argspec[i].arg_type], argspec[i].arg_descr);
	}
}

