/* parse_options.c 
	vi:ts=3 sw=3:
 */

/* $Id: parse_options.c,v 1.6 1995/10/21 00:41:59 espie Exp espie $
 * $Log: parse_options.c,v $
 * Revision 1.6  1995/10/21 00:41:59  espie
 * *** empty log message ***
 *
 * Revision 1.5  1995/09/17 23:27:17  espie
 * *** empty log message ***
 *
 * Revision 1.4  1995/09/16 15:33:00  espie
 * Multiple options handling.
 *
 * Revision 1.3  1995/09/07 14:33:27  espie
 * *** empty log message ***
 *
 * Revision 1.2  1995/09/05 19:21:09  espie
 * *** empty log message ***
 *
 * Revision 1.1  1995/09/02 23:14:47  espie
 * Initial revision
 *
 */

/* stuff to put options into argc/argv form
 * (and parse them in the future)
 * not used yet...
 */
#include "defs.h"
#include "parse_options.h"
#include "extern.h"
#include <ctype.h>


#define LOOK_FOR_ARG 0			/* beginning of an argument, interspace */
#define QUOTED_ARG 1				/* argument with quote, looking for corresponding
                               * quote */
#define IN_ARG 2              /* inside normal argument */

int string2args(s, v)
char *s;
char *v[];
	{
	int j;
	char c;
	int mode;

	mode = LOOK_FOR_ARG;
	j = 0;

	while (*s)
		{
		switch(mode)
			{
		case LOOK_FOR_ARG:
			switch(*s)
				{
			case ' ': case '\t':
				break;
			case '\\':
				if (v)
					v[j] = s;
				j++;
				if (! *++s)			/* check for last char */
					return j;
				mode = IN_ARG;
				break;
			case '"': case '\'':
				c = *s;
				if (v)
					v[j] = s+1;
				j++;
				mode = QUOTED_ARG;
				break;
			default:
				if (v)
					v[j] = s;
				j++;
				mode = IN_ARG;
				break;
				}
			break;
		case IN_ARG:
			switch(*s)
				{
			case ' ': case '\t':
				if (v)
					*s = 0;
				mode = LOOK_FOR_ARG;
				break;
			case '\\':
				if (! *++s)
					return j;
				break;
			default:
				break;
				}
			break;
		case QUOTED_ARG:
			if (*s == '\\')
				{
				if (! *++s)
					return j;
				}
			else if (*s == c)
				{
				if (v)
					*s = 0;
				mode = LOOK_FOR_ARG;
				}
			break;
			}
		s++;
		}
	return j;
	}

LOCAL struct option_set_list
	{
	struct option_set_list *next;
	struct option_set set;
	} *first, *current;

LOCAL void set_up_args(set)
struct option_set *set;
	{
	int i; 
	int j;

	for (i = 0; i < set->number; i++)
		{
		switch (set->options[i].type) 
			{
		case 's':
		case 'n':
			set->args[i].scalar = set->options[i].def_scalar;
			break;
		case 'a':
			set->args[i].pointer = set->options[i].def_string;
			break;
		case 'm':
			for (j = 0; j < i; j++)
				{
			  	if (strcmp(set->options[i].def_string, 
						set->options[j].optiontext) == 0)
					{
					set->options[i].multi = j;
					break;
					}
				}
			break;
		default:
			notice("Internal problem with option:");
			notice(set->options[i].optiontext);
			break;
			}
		}
	}

void add_option_set(set)
struct option_set *set;
	{
	struct option_set_list *new;

	new = malloc(sizeof(struct option_set_list));
	if (!new)
		end_all("Error: out of memory");			
	new->next = 0;
	new->set.options = set->options;
	new->set.number = set->number;
	new->set.args = set->args;
	set_up_args(set);
	if (current)
		current->next = new;
	else
		first = new;
	current = new;
	}

LOCAL int do_option(text, arg)
char *text;
char *arg;
	{
	struct option_set_list *sweep;
	struct option_set *set;
	int i, j;
	char *check;
	int argindex;
	int type;

	for (sweep = first; sweep; sweep = sweep->next)
		{
		set = &(sweep->set);
		for (i = 0; i < set->number; i++)
			{
			check = set->options[i].optiontext;

			for (j = 0; check[j] && (check[j] == tolower(text[j])); j++)
						;
			if (set->options[i].type == 'm')
				argindex = set->options[i].multi;
			else
				argindex = i;
			type = set->options[argindex].type;
			if (text[j])
				{
					/* last chance for switches */
				if (type == 's'
					&& tolower(text[0]) == 'n'
					&& tolower(text[1]) == 'o')
					{
					for (j = 0; check[j] && check[j] == tolower(text[j+2]); j++)
						;
					if (!text[j+2])
						{
						if (i == argindex)
							set->args[i].scalar = 0;
						else
							set->args[argindex].scalar = 1; 
						return 0;
						}
					}
				}
			else
				{
				/* found option */
				switch(type)
					{
				case 's':
					if (i == argindex)
						set->args[argindex].scalar = 1;
					else
						set->args[i].scalar = 0;
					return 0;
				case 'n':
						{
						static int d;

						if (arg && sscanf(arg, "%d", &d) == 1)
							{
							set->args[argindex].scalar = d;
							return 1;
							}
						else
							{
							set->args[argindex].scalar = set->options[i].def_scalar;
							return 0;
							}
						}
				case 'a':
					if (arg && (arg[0] != '-')) 
						{
						set->args[argindex].pointer = arg;
						return 1;
						}
					else
						{
						set->args[argindex].pointer = set->options[i].def_string;
						return 0;
						}
					}
				}
			}
		}
	fprintf(stderr, "Unknown option: %s\n", text);
	return 0;
	}
					

					
void parse_options(argc, argv, what_to_do)
int argc;
char *argv[];
void (*what_to_do) P((char *arg));
	{
	int i;
	char *arg;
	for (i = 0; i < argc; i++)
		{
		if (argv[i][0] == '-' && argv[i][1] != 0)
			{
			if (i+1 < argc)
				arg = argv[i+1];
			else
				arg = 0;
			if (argv[i][1] == '-')
				{
				(*what_to_do)(argv[i]);
				(*what_to_do)(arg);
				i++;
				}
			else
				i += do_option(argv[i]+1, arg);
			}
		else
			(*what_to_do)(argv[i]);
		}
	}

#if 0
struct option opts[] =
	{
	{"coucou", 's', 0, 0},
	{"randomize", 's', 0, 0}
	};

VALUE args[3];

void pouet(s)
char *s;
	{
	printf("??? %s\n", s);
	}

main()
	{
	char buffer[50];
	int i, k, p;
	char **v;

	add_options(opts, sizeof(opts)/sizeof(struct option), args);
	while(1)
		{
		gets(buffer);
		k = do_string2args(buffer, 0);
		v = malloc(sizeof(char *) * k);
		(void)do_string2args(buffer, v);
		for (i = 0; i < k; i++)
			printf("%s\n", v[i]);
		args[0].scalar = args[1].scalar = args[2].scalar = -1;
		parse_options(k, v, pouet);
		printf("%d %d %d\n", args[0].scalar, args[1].scalar, args[2].scalar);
		free(v);
		}
	}
			
#endif
