/*	arg_values

This module contains a set of routines for aquiring values associated with
argument strings in command lines.

Usage:

    #include	<argvalues.h>
    #include	<qioret.h>	For IO_ERROR symbols

    int
    arg_values (type, argument, values, maximum);

    int		type;		The type of value(s) to get (in argvalues.h):
				    STRING_VALUE
				    INTEGER_VALUE
				    LONG_VALUE
				    FLOAT_VALUE
    char	*argument;	The argument string to search
    <value type> *values;	The values vector to receive the values
    int		maximum;	The maximum number of values allowed


    char *
    chk_syntax (type, string)

    int		type;		As above
    char	*string;	The string to have its syntax checked

Description:

arg_values -

The possible value types are:

    STRING (char *)
    INTEGER (int)
    LONG (long)
    FLOAT (float)

The value(s) for an argument are always located immediately after the 
argument keyword, and separated from the keyword by a colon (:) character.

There may be more than one value per argument.  The value list is comma (,) 
or colon (:) separated in the argument string.  The maximum number of values
that will be obtained is indicated by maximum.  Each value found is placed
sequentially in the values vector.

>>> WARNING <<< The argument string is chopped up into the value list strings 
by the replacement of trailing separator characters with EOS (0) values.  If 
the original argument string is needed, a copy MUST be saved by the user.

>>> NOTE <<< An argument with a list of strings, any one of which contains
white space, must be enclosed in double quote (") characters (the entire
argument string must be contained within the double quotes), and the
individual strings in the list that contain whitespace must be enclosed in
single quote (') characters.  For example: 

    "keyword:string_1,'string 2'"

The values vector for STRINGS is an array of pointers to characters (char *). 
Each of these pointers refers to a location in the orginal argument string.

The function returns the number of values actually found (0 if none found).

If a syntax error occurs during value conversion, the search for values is 
halted and the following global variables have their values set:

    IO_ERROR is set to IE_BAD (Bad parameters)
    arg_string points to the bad character in the original argument string

If no error occurs, IO_ERROR is FALSE (0) and arg_string points to the beginning
of the values list in the original argument string. 

If dynamic memory cannot be allocated as needed, IO_ERROR is set to IE_NBF
(No buffer space), and arg_string is NULL.


chk_syntax -

The string is checked to confirm that it is the character representation of a 
number of the specified type.  For INTEGER_VALUE and LONG_VALUE only digits 
optionally preceeded by a sign (+ or -) are allowed.  For FLOAT_VALUE the 
function atod declared as type int is used to return a pointer to the first 
invalid character in the string (which is EOS if the syntax is correct).

A pointer to the first illegal character is returned for a string with bad 
syntax; otherwise NULL.

******************************************************************************/

#include	<std.h>
#include	<qioret.h>

IMPORTED STRING	white_space;
LOCAL STRING	default_white;
LOCAL STRING	separators = " \t:,";

#define COLON	':'
#define	QUOTE	'\''

STRING	arg_string;	/* Points to current value string */
INTEGER	arg_index;	/* Index to bad character in original arg string */

#define	_ARG_VALUES_
#include	<argvalues.h>

#ifdef DEBUG
LOCAL STRING	type_names[] =
    {
    "STRING",
    "INTEGER",
    "LONG",
    "FLOAT"
    };
#endif

GLOBAL STRING	chk_syntax ();



INTEGER
arg_values (type, argument, values, maximum)

COUNT	type;
STRING	argument;
union
    {
    STRING	*are_strings;
    INTEGER	*are_ints;
    LONG	*are_longs;
    FLOAT	*are_floats;
    } values;
INTEGER	maximum;
{
REGISTER STRING	*strings;
REGISTER STRING	location;
STRING		pointers;

IMPORTED LONG	atol ();
IMPORTED REAL	atof ();

#ifdef DEBUG
COUNT	count;

printf ("arg_values: Searching for %d %s values\n\t%s\n",
    maximum, type_name[type], argument);
#endif

IO_ERROR = 0;

if (maximum && (arg_string = search (argument, COLON)))
    {
    /*
    Parse the argument's value list into strings:
    */
#ifdef DEBUG
if (type != STRING_VALUE)
printf ("\t\tAllocate %d bytes of STRING array memory.\n",
(sizeof (STRING) * (maximum + 1)));
#endif
    if (type == STRING_VALUE)
	strings = values.are_strings;
    else if (! (strings = malloc (sizeof (STRING) * (maximum + 1))))
	{
	arg_string = NULL;
	IO_ERROR = IE_NBF;
	return (NULL);
	}
    default_white = white_space;
    white_space = separators;
#ifdef DEBUG
printf ("Parse the argument list\n");
#endif
    maximum = arg_parse (++arg_string, strings, maximum);
    white_space = default_white;
#ifdef DEBUG
printf ("\t%d value strings:\n", maximum);
for (count = 0; count < maximum; count++)
    printf ("\t\t%s\n", strings[count]);
#endif

    if (type != STRING_VALUE)
	{
	/*
	Convert the value strings into the appropriate value vector:
	*/
	strings[maximum] = NULL;	/* Mark end of values strings */
	pointers = strings;
	for (maximum = 0; *strings; maximum++, strings++)
	    {
	    if (location = chk_syntax (type, *strings))
		{
		arg_index = location - argument;
		arg_string = *strings;
#ifdef DEBUG
printf ("\tBad number syntax for %s\n\t(character %d of argument)\n",
*strings, arg_index);
#endif
		IO_ERROR = IE_BAD;
		break;
		}
	    switch (type)
		{
		case INTEGER_VALUE:
		    *values.are_ints++ = atoi (*strings);
		    break;
		case LONG_VALUE:
		    *values.are_longs++ = atol (*strings);
		    break;
		case FLOAT_VALUE:
		    *values.are_floats = atof (*strings);
		    /*
		    This kluge gets around a compiler bug in which pointers
		    to float are not correctly autoincremented when used with
		    indirection!
		    */
		    values.are_floats++;
		    break;
		}
	    }
	mfree (pointers);
#ifdef DEBUG
printf ("\t\tPointers memory freed.\n");
#endif
	}
    }
else
    maximum = 0;	/* No values in this argument string */

return (maximum);
}


/*
The string is checked to see if it has any illegal characters.  If it does, a
pointer to the bad character is returned; otherwise NULL is returned. 
*/

STRING
chk_syntax (type, string)

COUNT	type;
STRING	string;
{
IMPORTED INTEGER	atod ();	/* This way gives pntr to non-valid */

switch (type)
    {
    case INTEGER_VALUE:
    case LONG_VALUE:

	if (*string == '+' || *string == '-')
	    string++;
	while (isdigit (*string++));
	string--;
	break;

    case FLOAT_VALUE:

	string = (STRING)atod (string);
	break;

    default:

	return (NULL);
    }
if (*string)
    return (string);
else
    return (NULL);
}
