/*
 *	(c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
 *
 *	Generic option parsing
 */

#include "config.h"
#include "options.h"

/* options.c */

static void dump_options __APROTO((int type, char *tail));
static void error __APROTO((char *message, int option_letter));

static char **save_argv, *usage_mesg;
static struct option_descr *save_optd;

extern int atoi();


char *program_name(av)
char **av;
{
    char *cp;

    /* skip "/path/" part of program name */
    if ((cp = strrchr(*av, '/')))
	return cp + 1;
    else
	return *av;
}

static void
dump_options(type, tail)
int type;
char *tail;
{
    register struct option_descr *od;
    int any = 0;

    for (od = save_optd; od->option_letter; od++) {
	if (od->option_type != type) continue;
	fprintf(stderr, any ? "%c" : " -%c", od->option_letter );
	any++;
    }

    if (any && tail && tail[0]) {
	fprintf(stderr, "%s", tail);
    }
}

static void error(message, option_letter)
char *message, option_letter;
{
    char *prog_name = program_name(save_argv);

    fprintf(stderr, "%s: ", prog_name);
    fprintf(stderr, message, option_letter);
    fputc('\n', stderr);

    fprintf(stderr, "usage: %s", prog_name);

    dump_options(1, "");
    dump_options(2, " STR");
    dump_options(3, " [STR]");
    dump_options(4, " NUM");
    dump_options(5, " [NUM]");

    if (usage_mesg) fprintf(stderr, usage_mesg);
    fputc(NL, stderr);

    nn_exit(9);
}

/* parse variables set on command line seperately from everything
 * else, very early.  This is necessary because open_master is done
 * so early for nntp; don't alter the arg, so we can skip it later.
 */
void
parseargv_variables(char **av, int ac)
{
    char *value;

    while(--ac > 0) {
        if(**++av != '-' && (value = strchr(*av, '='))) {
            *value++ = NUL;
            set_variable(*av, 1, value);
            *--value = '='; /* restore for later parsing to skip it */
        }
        /* OLSON gross hack; see comments at call in nn.c; because this
         * is so gross, I'm not doing it cleanly or "right" */
        else if(**av == '-' && (strchr(*av, 'x') || strchr(*av, 'N'))) {
                extern int no_update;
            no_update++;
        }
    }
}

int
parse_options(ac, av, envname, options, usage)
int ac;
char **av, *envname;
struct option_descr options[];
char *usage;
{
    register char *cp, opt;
    register struct option_descr *od;
    int files;
    char **names;
    char *envinit;

    save_argv = av;
    save_optd = options;

    if (options == NULL) return 0;

    usage_mesg = usage;

    --ac;
    names = ++av;
    files = 0;

    envinit = envname ? getenv(envname) : NULL;
    cp = envinit;

 next_option:

    if (envinit) {
	while (*cp && isspace(*cp)) cp++;
	if (*cp == '-') {
	    cp++;
	    goto next_option;
	}
	if (*cp == NUL) {
	    envinit = NULL;
	    goto next_option;
	}
    } else
	if (cp == NULL || *cp == NUL) {
	    if ((cp = *av++) == NULL) {
		*names = NULL;
		return files;
	    }
	    ac--;

	    if (*cp != '-') {
		if (!strchr(cp, '=')) {
		    *names++ = cp;
		    cp = NULL;
		    files++;
		}
                /* else it's a variable that was handled in
		 * parseargv_variables() earlier, so nothing to do. */
		cp = NULL;
		goto next_option;
	    }

	    cp++; /* skip - */
	}

    opt = *cp++;

    for (od = options; od->option_letter; od++) {
	if (od->option_letter != opt) continue;

	switch (od->option_type) {

	 case 1:	/* BOOL_OPTION */

	    *((int *)(od->option_address)) = !*((int *)(od->option_address));
	    goto next_option;

	 case 2:	/* STRING_OPTION */
	 case 3:	/* OPTIONAL_STRING */

	    /* -oSTR or -o STR */

	    while (*cp && isspace(*cp)) cp++;

	    if (*cp == NUL) {
		if (envinit || ac == 0) {
		    if (od->option_type == 3) goto opt_str;
		    error("missing string argumet to -%c", opt);
		}
		cp = *av++;
		ac--;
	    }

	    if (od->option_type == 3 && *cp == '-') {
		cp++;
		goto opt_str;
	    }

	    *(od->option_address) = cp;

	    if (envinit) {
		while (*cp && !isspace(*cp)) cp++;
		if (*cp) *cp++ = NUL;
	    } else
		cp = NULL;

	    goto next_option;

	 opt_str:
	    *(od->option_address) = od->option_default;
	    goto next_option;

	 case 4:
	 case 5:

	    /* -oN or -o N */

	    while (*cp && isspace(*cp)) cp++;

	    if (*cp) {
		if (!isdigit(*cp)) {
		    if (od->option_type == 5) goto opt_int;
		    error("non-numeric argument to -%c", opt);
		}
	    } else {
		if (envinit || ac == 0 || !isdigit(**av)) {
		    if (od->option_type == 5) goto opt_int;
		    error("missing argument to -%c", opt);
		}

		cp = *av++;
		ac--;
	    }
	    *((int *)(od->option_address)) = atoi(cp);
	    while (isdigit(*cp)) cp++;
	    goto next_option;

	 opt_int:
	    *((int *)(od->option_address)) = (int)(od->option_default);
	    goto next_option;
	}
    }

    error("unknown option '-%c'", opt);
    /*NOTREACHED*/
    return 0;
}
