/*
	getlopt: command line option/parameter parsing

	copyright ?-2008 by the mpg123 project - free software under the terms of the LGPL 2.1
	see COPYING and AUTHORS files in distribution or http://mpg123.org
	initially written Oliver Fromme
	old timestamp: Tue Apr  8 07:15:13 MET DST 1997
*/

#include <stdio.h>
#include "config.h"
#include "getlopt.h"
#include "compat.h"
#include "debug.h"

int loptind = 1;	/* index in argv[] */
int loptchr = 0;	/* index in argv[loptind] */
char *loptarg;		/* points to argument if present, else to option */

topt *findopt (int islong, char *opt, topt *opts)
{
	if (!opts)
		return (0);
	while (opts->lname) {
		if (islong) {
			if (!strcmp(opts->lname, opt))
				return (opts);
		}
		else
			if (opts->sname == *opt)
				return (opts);
		opts++;
	}
	return (0);
}

int performoption (int argc, char *argv[], topt *opt)
{
	int result = GLO_CONTINUE;
	/* this really is not supposed to happen, so the exit may be justified to create asap ficing pressure */
	#define prog_error() \
	{ \
		fprintf(stderr, __FILE__ ":%i Option without type flag! This is a programming error! Developer: fix this ASAP to regain your honor.\n", __LINE__); \
		exit(1); \
	}

	if (!(opt->flags & GLO_ARG)) { /* doesn't take argument */
		if (opt->var) {
			if (opt->flags & GLO_CHAR) /* var is *char */
			{
				debug1("char at %p", opt->var);
				*((char *) opt->var) = (char) opt->value;\
			}
			else if(opt->flags & GLO_LONG)
			{
				debug1("long at %p", opt->var);
				*( (long *) opt->var ) = opt->value;
			}
			else if(opt->flags & GLO_INT)
			{
				debug1("int at %p", opt->var);
				*( (int *) opt->var ) = (int) opt->value;
			}
			/* GLO_DOUBLE is not supported here */
			else prog_error();
								
			debug("casting assignment done");
		}
		else
			result = opt->value ? opt->value : opt->sname;
	}
	else { /* requires argument */
		if (loptind >= argc)
			return (GLO_NOARG);
		loptarg = argv[loptind++]+loptchr;
		loptchr = 0;
		if (opt->var) {
			if (opt->flags & GLO_CHAR) /* var is *char */
				*((char **) opt->var) = strdup(loptarg); /* valgrind claims lost memory here */
			else if(opt->flags & GLO_LONG)
				*((long *) opt->var) = atol(loptarg);
			else if(opt->flags & GLO_INT)
				*((int *) opt->var) = atoi(loptarg);
			else if(opt->flags & GLO_DOUBLE)
				*((double *) opt->var) = atof(loptarg);
			else prog_error();
		}
		else
			result = opt->value ? opt->value : opt->sname;
	}
	if (opt->func)
		opt->func(loptarg);
	return (result);
}

int getsingleopt (int argc, char *argv[], topt *opts)
{
	char *thisopt;
	topt *opt;
	static char shortopt[2] = {0, 0};

	if (loptind >= argc)
		return (GLO_END);
	thisopt = argv[loptind];
	debug1("getsingleopt: %s", thisopt);
	if (!loptchr) { /* start new option string */
		if (thisopt[0] != '-' || !thisopt[1]) /* no more options */
			return (GLO_END);
		if (thisopt[1] == '-') { /* "--" */
			if (thisopt[2]) { /* long option */
				loptarg = thisopt+2;
				loptind++;
				if (!(opt = findopt(1, thisopt+2, opts)))
					return (GLO_UNKNOWN);
				else
					return (performoption(argc, argv, opt));
			}
			else { /* "--" == end of options */
				loptind++;
				return (GLO_END);
			}
		}
		else /* start short option(s) */
			loptchr = 1;
	}
	shortopt[0] = thisopt[loptchr];
	loptarg = shortopt;
	opt = findopt(0, thisopt+(loptchr++), opts);
	if (!thisopt[loptchr]) {
		loptind++;
		loptchr = 0;
	}
	if (!opt)
		return (GLO_UNKNOWN);
	else
		return (performoption(argc, argv, opt));
}

int getlopt (int argc, char *argv[], topt *opts)
{
	
	int result;
	
	while ((result = getsingleopt(argc, argv, opts)) == GLO_CONTINUE);
	return (result);
}

/* EOF */
