/*
 * National Center for Supercomputing Applications, University of Illinois
 *
 * This NCSA software product is public domain software.  Permission
 * is hereby granted to do whatever you like with it. Should you wish
 * to make a contribution towards the production of this software, please
 * send us your comments about your experience with the software,  why
 * you liked or disliked it, how you use it, and most importantly, how it
 * helps your work. We will receive your comments at softdev@ncsa.uiuc.edu.
 *
 * Please send bug reports to bugs@ncsa.uiuc.edu
 *
 *	Author:		Brian Calvert, NCSA
 *			bcalvert@ncsa.uiuc.edu
 */

/*
 *      File:           args.c
 *      Contents:       Command-line arguments processing routines.
 */


/* INCLUDE FILES */

#include <stdio.h>
#include "args.h"


/**********************************************************************
*  Function	:	args_found_arg
*  Purpose	:	checks if a given argument is present
*  Parameters	:	args		table of arguments
*			switchname	name of argument to check for
*  Returns	:	TRUE if present, FALSE otherwise
*  Calls	:	none
*  Called by	:	none
**********************************************************************/
int
args_found_arg(args, switchname)
	args_t args[];
	char * switchname;
{
	args_t * arg;

	/* Search until we find the switch, then return its "found" value */
	for (arg=args; arg->switchname != NULL; arg++) {
		if (strcmp(switchname, arg->switchname) == 0)
			return (arg->found);
	}

	/* If we didn't find the switch, return FALSE */
	return FALSE;
}


/**********************************************************************
*  Function     :       args_usage
*  Purpose      :       prints a "usage" message using the args table data
*  Parameters   :       argv            command line arguments
*                       args		table of arguments
*  Returns      :       FALSE
*  Calls        :       none
*  Called by    :       args_parse
**********************************************************************/
int
args_usage(argv, args)
	char * argv[];
	args_t args[];
{
	int i, j;
	static char * argtype[] = {"", " <int>", " <float>", " <string>"};

	/* Print the usage header, name of the program, and then one */
	/* switch per line. */
	printf("usage:\t%s\n", argv[0]);
	for(i=0; args[i].switchname != NULL; i++) {
		/* Print the switchname.  Place a bracket in front of it */
		/* if it is optional. */
		printf("\t%c-%s",args[i].mandatory ? '\0' : '[',
				 args[i].switchname);

		for(j=args[i].size; j>0; j--) {
			printf(argtype[args[i].type]);
			if (args[i].type == STRING) break;
		}

		/* Optionally print a closing bracket. */
		printf("%c\n",args[i].mandatory ? '\0' : ']');
	}

	/* Print the descriptions of the switches */
	printf("where:");
	for(i=0; args[i].switchname != NULL; i++) {
		printf("\t\"%s\" %s\n", args[i].switchname,
					args[i].description);
	}

	return FALSE;
}


/**********************************************************************
*  Function     :       args_dump_args
*  Purpose      :       "dumps" all of the arguments in the table
*  Parameters   :	args		table of arguments
*  Returns      :       FALSE
*  Calls        :       args_dump_arg
*  Called by    :       none
**********************************************************************/
int
args_dump_args(args)
	args_t args[];
{
	int i;

	/* Dump the contents of the switch table */
	for (i=0; args[i].switchname != NULL; i++) {
		args_dump_arg(args,i);
	}

	return FALSE;
}


/**********************************************************************
*  Function     :       args_dump_arg
*  Purpose      :       prints the contents of one element of the args table
*  Parameters   :       args            table of arguments
*			i		the argument to print
*  Returns      :       TRUE if bad argument number, FALSE otherwise
*  Calls        :       none
*  Called by    :       args_dump_args
**********************************************************************/
static int
args_dump_arg(args,i)
	args_t args[];
	int i;
{
	static char * typename[] = {"BOOLEAN", "INT", "FLOAT", "STRING"};

	/* Quit if the switchname is null. */
	if (args[i].switchname == NULL) {
		printf("Bad switch table number.\n");
		return TRUE;
	}

	/* Print the information that can be done without much processing */
	printf("\nSwitchname:\t%s",args[i].switchname);
	printf("\n  Mandatory:\t%s",(args[i].mandatory ? "YES" : "NO"));
	printf("\n  Size:\t\t%d",args[i].size);
	printf("\n  Type:\t\t%s",typename[args[i].type]);
	printf("\n  Value:\t");

	/* Format the value of the switch appropriately */
	switch(args[i].type) {
	case BOOLEAN:
		printf("%s",(*((int *)args[i].value) ? "TRUE" : "FALSE"));
		break;
	case INT:
		printf("%d",*((int *)args[i].value));
		break;
	case FLOAT:
		printf("%f",*((float *)args[i].value));
		break;
	case STRING:
		printf("%s",args[i].value);
		break;
	}

	/* Finally, some more information that needs little processing */
	printf("\n  Found:\t%s",(args[i].found ? "YES" : "NO"));
	printf("\n  Description:\t%s\n",args[i].description);

	return FALSE;
}


/**********************************************************************
*  Function     :       args_parse
*  Purpose      :       prints the contents of one element of the args table
*  Parameters   :       argc		number of arguments
*			argv		command line arguments
*			args            table of arguments
*  Returns      :       TRUE if bad argument number, FALSE otherwise
*  Calls        :       args_parse_value, args_usage
*  Called by    :       args_dump_args
**********************************************************************/
int
args_parse(argc, argv, args)
	int argc;
	char * argv[];
	args_t args[];
{
	int i, j;
	int found, error;

	/* Parse all of the arguments on the command-line */
	for(i=1; i<argc; i++) {
		found = FALSE;
		error = FALSE;

		for (j=0; (!found && (args[j].switchname != NULL)); j++) {
			if (strcmp(args[j].switchname,&argv[i][1]) == 0) {
				/* We have a match.  Parse the following */
				/* arguments as required. */
				error = args_parse_value(argc,argv,args,&i,j);

				/* Set the "found" flag */
				found = TRUE;
			}
		}

		/* If an error occurred, or the flag was not found, print */
		/* the usage */
		if (!found || error) {
			if (!found)
				printf("ERROR:  %s not a valid switch.\n",
				       argv[i]);
			args_usage(argv,args);
			exit(-1);
		}
	}

	/* Confirm that all required switches were found */
	for (j=0; args[j].switchname != NULL; j++) {
		if ( (args[j].mandatory) && (!args[j].found) ) {
			printf("ERROR:  -%s is required.\n",args[j].switchname);
			args_usage(argv,args);
			exit(-1);
		}
	}

	return FALSE;
}



/**********************************************************************
*  Function     :       args_parse_value
*  Purpose      :       parses the given switch and updates the args table
*  Parameters   :       argc            number of arguments
*                       argv            command line arguments
*                       args            table of arguments
*			arg_num		argument number to parse (modified)
*			table_num	entry in args table to parse into
*  Returns      :       TRUE if error, FALSE otherwise
*  Calls        :       none
*  Called by    :       args_parse
**********************************************************************/
static int
args_parse_value(argc, argv, args, arg_num, table_num)
	int argc;
	char * argv[];
	args_t args[];
	int * arg_num;
	int table_num;
{
	int i, j;


	/* If the switch has already been processed, return an error */
	if (args[table_num].found)
		return TRUE;

	/* Determine which type of switch was encountered and parse */
	/* accordingly */
	switch (args[table_num].type) {
	case BOOLEAN:
		*(args[table_num].value) = !*(args[table_num].value);
		break;
	case INT:
		/* Read all of the elements we require into the array */
		for(j=args[table_num].size, i=0; j > 0; j--, i++) {
			(*arg_num)++;
			sscanf(argv[*arg_num], "%d", 
			       ((int *)args[table_num].value)+i);
		}
		break;
	case FLOAT:
		/* Read all of the elements we require into the array */
		for(j=args[table_num].size, i=0; j > 0; j--, i++) {
			(*arg_num)++;
			sscanf(argv[*arg_num], "%f", 
			       ((float *)args[table_num].value)+i);
		}
		break;
	case STRING:
		/* Copy the string into the data value area */
		(*arg_num)++;
		strncpy((char *)args[table_num].value,argv[*arg_num],
			args[table_num].size);
		*(((char *) args[table_num].value)+args[table_num].size-1) =
			'\0';
		break;
	default:
		printf("ERROR:  Switch type %d undefined.\n",
		       args[table_num].type);
		return TRUE;
	}

	/* If we got this far, everything went fine.  Set found to TRUE for */
	/* the current switch and return FALSE (no error) */
	args[table_num].found = TRUE;
	return FALSE;
}
