/*
 * Argument parsing utilies.
 *
 * Neil Hunt (Neil%Teleos.com@ai.sri.com).
 *
 * Copyright (c) 1989 Teleos Research, Inc 1989.
 * Copyright (c) 1989 Schlumberger Technologies, Inc 1989.
 *
 * Anyone can use this software in any manner they choose,
 * including modification and redistribution, provided they make
 * no charge for it, and these conditions remain unchanged.
 *
 * This program is distributed as is, with all faults (if any), and
 * without any wrranty.  No author or distributor accepts responsibility
 * to anyone for the consequences of using it, or for whether it serves any
 * particular purpose at all, or any other reason.
 *
 * $Log:	args.c,v $
 * Revision 1.2  89/02/20  11:21:31  neil
 * Removed sunview.h include.
 * 
 * Revision 1.1  89/02/10  18:40:00  neil
 * Initial revision
 * 
 * Taken from newlib library:
 * Revision 1.6  88/08/08  17:54:07  hunt
 * Removed prexit from a_ungetc in case where a_arg_ptr is NULL,
 * as this case is indestinguishable from the case where a_arg_ptr
 * pointed to the end of a word (to the application).
 * 
 * Revision 1.5  88/08/08  16:54:27  hunt
 * Made a_arg_ptr etc non static, for special purpose applications such
 * as parsing window arguments from the array.
 * 
 * Revision 1.4  88/05/11  16:44:11  hunt
 * Added a_getc and a_ungetc functions.
 * 
 * Revision 1.3  88/03/17  18:29:54  hunt
 * Completely rewritten; cleaner implementation.
 * Added a_scaled function and some scale tables.
 * 
 * Revision 1.2  88/01/12  18:33:54  hunt
 * Added a_arg(argc, argv) function for completeness and to hide a_arg_ptr.
 * Currently a_arg_ptr is not made static, so as not to break old code.
 * 
 * Revision 1.1  87/11/24  11:20:01  hunt
 * Initial revision
 */

#include <stdio.h>
#include <math.h>
#include <string.h>
#ifdef MSDOS
#define MAXPATHLEN 255
#include <stdlib.h>
#include <process.h>
#else
#include <sys/param.h>		/* For MAXPATHLEN */
#endif
#include "std.h"
#include "args.h"

static char *	a_arg_ptr = NULL;
static int	a_arg_index = 0;
static bool	a_escape_seen;
char *		a_prog_name = "Anonymous";

/*
 * a_next:
 *	Returns the next flag in the command line,
 *	or A_ARG if it is not a flag,
 *	or A_END if there are no more args.
 */

char
a_next(argc, argv)
int argc;
char **argv;
{
	char opt;

	/*
	 * Checks.
	 */
	if(argv == NULL || argc < 1)
	{
		fprintf(stderr, "a_arg: bad arguments\n");
		exit(-1);
	}

	/*
	 * Get program name on first call.
	 */
	if(a_arg_index == 0)
	{
		a_prog_name = argv[0];
		a_arg_index = 1;
	}

	/*
	 * If there is part of the previous word left, then return it.
	 */
	if(a_arg_ptr && *a_arg_ptr)
		return *a_arg_ptr++;

	/*
	 * Return A_END after the end of the list.
	 */
	if(a_arg_index >= argc)
		return A_END;

	/*
	 * Look at the next word.
	 */
	a_arg_ptr = argv[a_arg_index++];

	/*
	 * If we have seen the escape "--",
	 * or if the first char of the word * is not a '-',
	 * or if this is an isolated "-",
	 * then return ARG.
	 */
	if(a_escape_seen || a_arg_ptr[0] != '-' || a_arg_ptr[1] == '\0')
		return A_ARG;

	/*
	 * Look at the next char.
	 */
	a_arg_ptr++;
	opt = *a_arg_ptr++;

	/*
	 * If the next char is '-', then this is the escape.
	 * start over...
	 */
	if(opt == '-')
	{
		a_escape_seen = TRUE;
		return a_next(argc, argv);
	}

	/*
	 * Otherwise, return this option.
	 */
	return opt;
}

/*
 * a_arg:
 *	Returns the next argument in the command line,
 *	or NULL if there are no more args.
 */

char *
a_arg(argc, argv)
int argc;
char **argv;
{
	char *arg;

	/*
	 * Checks.
	 */
	if(argv == NULL || argc < 1)
	{
		fprintf(stderr, "a_arg: bad arguments\n");
		exit(-1);
	}

	/*
	 * Get program name on first call.
	 */
	if(a_arg_index == 0)
	{
		a_prog_name = argv[0];
		a_arg_index = 1;
	}

	/*
	 * If there is part of the previous word left, then return it.
	 */
	if(a_arg_ptr && *a_arg_ptr)
	{
		arg = a_arg_ptr;
		a_arg_ptr = NULL;
		return arg;
	}

	/*
	 * Return NULL after the end of the list.
	 */
	if(a_arg_index >= argc)
		return NULL;

	/*
	 * Return the next word.
	 */
	return argv[a_arg_index++];
}

/*
 * a_getc:
 *	Returns the next char in the current word,
 *	or NULL if no more chars in word.
 */

char
a_getc(argc, argv)
int argc;
char **argv;
{
	return a_arg_ptr ? *a_arg_ptr++ : '\0';
}

/*
 * a_ungetc:
 *	Pushes char back onto current word, if still available.
 */

void
a_ungetc(argc, argv ,c)
int argc;
char **argv;
char c;
{
	if(a_arg_ptr)
		*--a_arg_ptr = c;
}

/*
 * a_number:
 *	Interpret the next word or part word as a number.
 */

double
a_number(argc, argv)
int argc;
char **argv;
{
	char *arg;

	if((arg = a_arg(argc, argv)) == NULL)
		return 0.0;
	else
		return atof(arg);
}

/*
 * a_integer:
 *	Interpret the next word or part word as an integer.
 */

int
a_integer(argc, argv)
int argc;
char **argv;
{
	char *arg;

	if((arg = a_arg(argc, argv)) == NULL)
		return 0;
	else
		return atoi(arg);
}

/*
 * a_scale:
 *	Interpret the next word of part word as a number with scale factor.
 */

double
a_scaled(argc, argv, scale_table)
int argc;
char **argv;
struct scale_table *scale_table;
{
	char *arg;
	double val;
	char scale[100];
	int scale_len;

	val = 0.0;

	/*
	 * Get the word.
	 */
	if((arg = a_arg(argc, argv)) == NULL)
		return 0.0;

	/*
	 * Read a double value and a units specifier.
	 * If no units, just return the value.
	 */
	if(sscanf(arg, " %lf %s", &val, &scale[0]) < 2 || scale_table == NULL)
		return val;

	/*
	 * Get the length of the scale string.
	 */
	if((scale_len = strlen(scale)) <= 0)
		return val;

	/*
	 * Try to match the units specifier.
	 */
	for( ; scale_table->scale_name; scale_table++)
		if(strncmp(scale_table->scale_name, scale, scale_len) == 0)
			return val * scale_table->scale_factor;

	return val;
}

/*
 * Prepared scale tables.
 */

struct scale_table scale_units[] =
{
	{ "pico",	1e-12, },
	{ "p",		1e-12, },
	{ "nano",	1e-9, },
	{ "n",		1e-9, },
	{ "micro",	1e-6, },
	{ "u",		1e-6, },
	{ "milli",	1e-3, },
	{ "m",		1e-3, },

	{ "kilo",	1e3, },
	{ "k",		1e3, },
	{ "Mega",	1e6, },
	{ "mega",	1e6, },
	{ "M",		1e6, },
	{ "Giga",	1e9, },
	{ "giga",	1e9, },
	{ "G",		1e9, },
	{ "Tera",	1e12, },
	{ "tera",	1e12, },
	{ "T",		1e12, },

	{ (char *)NULL,	0.0, },
};

#define	IN_PER_M	39.37007874
#define M_PER_IN	0.0254

struct scale_table scale_inches[] =
{
	{ "meters",	1.0 * IN_PER_M, },	/* Must come first */
	{ "metres",	1.0 * IN_PER_M, },
	{ "angstroms",	1e-10 * IN_PER_M, },
	{ "nm",		1e-9 * IN_PER_M, },
	{ "microns",	1e-6 * IN_PER_M, },
	{ "um",		1e-6 * IN_PER_M, },
	{ "millimeters",1e-3 * IN_PER_M, },
	{ "millimetres",1e-3 * IN_PER_M, },
	{ "mm",		1e-3 * IN_PER_M, },
	{ "centimeters",1e-2 * IN_PER_M, },
	{ "centimetres",1e-2 * IN_PER_M, },
	{ "cms",	1e-2 * IN_PER_M, },
	{ "kilometers",	1e3 * IN_PER_M, },
	{ "kilometres",	1e3 * IN_PER_M, },
	{ "km",		1e3 * IN_PER_M, },
	{ "nmile",	1852 * IN_PER_M, },

	{ "mils",	0.001, },
	{ "inches", 	1.0, },
	{ "\"",		1.0, },
	{ "feet",	12.0, },
	{ "foot",	12.0, },
	{ "ft",		12.0, },
	{ "\'",		12.0, },
	{ "yards",	36.0, },
	{ "yd",		36.0, },
	{ "rod",	198, },
	{ "rd",		198, },
	{ "miles",	63360, },

	{ (char *)NULL,	0.0, },
};

struct scale_table scale_meters[] =
{
	{ "meters",	1.0, },
	{ "metres",	1.0, },
	{ "angstroms",	1e-10, },
	{ "nm",		1e-9, },
	{ "microns",	1e-6, },
	{ "um",		1e-6, },
	{ "millimeters",1e-3, },
	{ "millimetres",1e-3, },
	{ "mm",		1e-3, },
	{ "centimeters",1e-2, },
	{ "centimetres",1e-2, },
	{ "cms",	1e-2, },
	{ "kilometers",	1e3, },
	{ "kilometres",	1e3, },
	{ "km",		1e3, },
	{ "nmile",	1852, },

	{ "mils",	0.001 * M_PER_IN, },
	{ "inches", 	1.0 * M_PER_IN, },
	{ "\"",		1.0 * M_PER_IN, },
	{ "feet",	12.0 * M_PER_IN, },
	{ "foot",	12.0 * M_PER_IN, },
	{ "ft",		12.0 * M_PER_IN, },
	{ "\'",		12.0 * M_PER_IN, },
	{ "yards",	36.0 * M_PER_IN, },
	{ "yd",		36.0 * M_PER_IN, },
	{ "rod",	198 * M_PER_IN, },
	{ "rd",		198 * M_PER_IN, },
	{ "miles",	63360 * M_PER_IN, },

	{ (char *)NULL,	0.0, },
};

struct scale_table scale_seconds[] =
{
	{ "us",			1e-6, },
	{ "microseconds",	1e-6, },
	{ "ms",			1e-3, },
	{ "milliseconds",	1e-3, },
	{ "seconds",		1.0, },
	{ "secs",		1.0, },
	{ "minutes",		60.0, },
	{ "mins",		60.0, },
	{ "hours",		3600.0, },
	{ "hrs",		3600.0, },
	{ "days",		86400.0, },
	{ "weeks",		604800.0, },
	{ "wks",		604800.0, },
	{ "years",		31556925.97, },
	{ "yrs",		31556925.97, },

	{ (char *)NULL,	0.0, },
};

#define KG_PER_LB	0.45359237
#define LB_PER_KG	2.204622622

struct scale_table scale_kilograms[] =
{
	{ "ounces",		0.0625 * KG_PER_LB, },
	{ "ozs",		0.0625 * KG_PER_LB, },
	{ "pounds",		1.0 * KG_PER_LB, },
	{ "lbs",		1.0 * KG_PER_LB, },
	{ "stones",		14.0 * KG_PER_LB, },
	{ "quarters",		28.0 * KG_PER_LB, },
	{ "hundredweights",	112.0 * KG_PER_LB, },
	{ "longtons",		2240.0 * KG_PER_LB, },
	{ "shorttons",		2000 * KG_PER_LB, },
	{ "tons",		2000 * KG_PER_LB, },

	{ "milligrams",		1e-6, },
	{ "mgs",		1e-6, },
	{ "grams",		1e-3, },
	{ "gms",		1e-3, },
	{ "kilograms",		1.0, },
	{ "kgs",		1.0, },
	{ "tonnes",		1e3, },
	{ "metrictons",		1e3, },

	{ (char *)NULL,	0.0, },
};

struct scale_table scale_pounds[] =
{
	{ "ounces",		0.0625, },
	{ "ozs",		0.0625, },
	{ "pounds",		1.0, },
	{ "lbs",		1.0, },
	{ "stones",		14.0, },
	{ "quarters",		28.0, },
	{ "hundredweights",	112.0, },
	{ "longtons",		2240.0, },
	{ "shorttons",		2000, },
	{ "tons",		2000, },

	{ "milligrams",		1e-6 * LB_PER_KG, },
	{ "mgs",		1e-6 * LB_PER_KG, },
	{ "grams",		1e-3 * LB_PER_KG, },
	{ "gms",		1e-3 * LB_PER_KG, },
	{ "kilograms",		1.0 * LB_PER_KG, },
	{ "kgs",		1.0 * LB_PER_KG, },
	{ "tonnes",		1e3 * LB_PER_KG, },
	{ "metrictons",		1e3 * LB_PER_KG, },

	{ (char *)NULL,	0.0, },
};