/*
 *	(c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
 *
 *	.nn/init file handling
 */


#include "config.h"
#include "articles.h"
#include "keymap.h"
#include "nn_term.h"
#include "menu.h"
#ifdef USE_MALLOC_H
#include <malloc.h>
#endif

/* init.c */

static char *strip_str __APROTO((register char *cmd));
static split_command __APROTO((register char *cmd));
static char *argv __APROTO((int i));
static is_sequence __APROTO((char *cmd));
static void load_init_file __APROTO((char *name, FILE **seq_hook_ptr, int only_seq));
static void print_debug_info __APROTO((void));
static void print_command __APROTO((char *str));
static do_show __APROTO((char *table, int mode_arg));
static do_map __APROTO((FILE *initf));
static void parse_on_to_end __APROTO((FILE *f));


import char *help_directory, *lib_directory;

export int in_init = 0;		/* true when parsing init file */
export int alt_cmd_key;		/* K_ when parse_command returns AC_KEYCMD */

export long initial_memory_break;	/* for :debug statistics */

export int first_time_user = 0;

static int init_err = 0;	/* errors in init file */

extern char *term_name;
extern FILE *loc_seq_hook, *glob_seq_hook;
extern int list_offset;
#ifndef HAVE_UNISTD_H
extern char *sbrk();
#endif
	    extern in_menu_mode;
	    import char *dflt_enter_macro;
	    import char *dflt_exit_macro;
	    import int terminal_speed, slow_speed;
	    extern char *pname;
	    import char *start_up_macro;
	    extern char *newsrc_file;
	    extern in_menu_mode;
	    import int do_kill_handling;
	    import char printer[];
	    import char *mail_box;



void init_message(char *fmt,...)
{
    va_list	ap;

    va_start(ap, fmt);

    if (in_init) {
	visual_off();
	printf("init error: ");
	vprintf(fmt, ap);
	putchar(NL);
	init_err++;
    } else
	vmsg(fmt, ap);

    va_end(ap);
}


#define MAXARG 10

static char *argvec[MAXARG + 2];
static int argc;

static char *strip_str(cmd)
register char *cmd;
{
    if (cmd == NULL) return cmd;

    while (*cmd && isascii(*cmd) && isspace(*cmd)) cmd++;
    if (*cmd == NUL || *cmd == NL) return NULL;

    return cmd;
}


static int
split_command(cmd)
register char *cmd;
{
    /* split command string */

    for (argc = 0; argc < MAXARG + 2; argc++) argvec[argc] = NULL;
strip_more:
    if ((cmd = strip_str(cmd)) == NULL || *cmd == '#') return 0;
    if (*cmd == ':') {
	cmd++;
	goto strip_more;
    }

    argc = 0;
    argvec[0] = cmd;

    return 1;
}

static char *argv(i)
int i;
{
    register char *cmd;

    if (i > MAXARG) return NULL;

    if (argc <= i)
	if (argvec[argc]) {
	    cmd = argvec[argc];
	    while (argc <= i) {
		while (*cmd && (!isascii(*cmd) || !isspace(*cmd))) cmd++;
		if (*cmd == NUL) {
		    argc = MAXARG;
		    break;
		}

		*cmd++ = NUL;
		if ((cmd = strip_str(cmd)) == NULL) {
		    argc = MAXARG;
		    break;
		}
		argvec[++argc] = cmd;
	    }
	}
	else
	    argc = MAXARG;

    return argvec[i];
}

static int
is_sequence(cmd)
char *cmd;
{
    if (!split_command(cmd)) return 0;
    if ((cmd = argv(0)) == NULL) return 0;
    return strcmp(cmd, "sequence") == 0;
}

#define START_SEQUENCE 555
#define CHAIN_FILE 556		/* chain file */
#define STOP_FILE 557		/* stop */

static void load_init_file(name, seq_hook_ptr, only_seq)
char *name;
FILE **seq_hook_ptr;
int only_seq;
{
    FILE *init;
    char cmdbuf[1024], *cmd, *term;

    /* use cmdbuf temporarily (to handle @ expansion) */
    for (cmd = cmdbuf; *name; name++)
	if (*name == '@') {
	    term = term_name;
	    while (term && *term) *cmd++ = *term++;
	} else
	    *cmd++ = *name;
    *cmd = NUL;
    name = cmdbuf;

 chain_file:
    if (strchr(name, '/') == NULL)
	name = relative(nn_directory, name);

    init = open_file(name, OPEN_READ);
    if (init == NULL) return;

    while (fgets_multi(cmdbuf, 1024, init)) {
	if (only_seq) {
	    if (!is_sequence(cmdbuf)) continue;
	    *seq_hook_ptr = init;
	    return;
	}
	/* we use AC_REDRAW to avoid !-commands clear the screen */
	switch (parse_command(cmdbuf, AC_REDRAW, init)) {
	 case CHAIN_FILE:
	    fclose(init);
	    name = argvec[argc];	/* ARGTAIL */
	    if (name == NULL) return;
	    goto chain_file;

	 case STOP_FILE:
	    fclose(init);
	    return;

	 case START_SEQUENCE:
	    if (seq_hook_ptr) {
		*seq_hook_ptr = init;
		return;	/* no close !! */
	    } else {
		init_message("load file contains 'sequence'");
		fclose(init);
		return;
	    }
	}
    }

    fclose(init);
}

static char dflt_init_files[] = ",init";

void
visit_init_file(only_seq, first_arg)
int only_seq;
char *first_arg;
{
    char *next_arg;

    in_init = 1;
    load_init_file(relative(lib_directory, "setup"), (FILE **)NULL, 0);
    in_init = 0;

    if (first_arg && strncmp(first_arg, "-I", 2) == 0) {
	if (first_arg[2] == NUL) return;
	first_arg += 2;
    } else
	first_arg = dflt_init_files;

    in_init = 1;
    while (first_arg) {
	next_arg = strchr(first_arg, ',');
	if (next_arg) *next_arg++ = NUL;

	if (*first_arg == NUL) {
	    if (glob_seq_hook == NULL)
		load_init_file(relative(lib_directory, "init"), &glob_seq_hook, only_seq);
	} else {
	    if (loc_seq_hook != NULL) {
		fclose(loc_seq_hook);
		loc_seq_hook = NULL;
	    }
	    load_init_file(first_arg, &loc_seq_hook, only_seq);
	}
	first_arg = next_arg;
    }

    if (init_err) nn_exit(1);
    in_init = 0;
}


/*
 * parse a command (also :-commands)
 */

static char *sw_string;
static int sw_loop_once;

#define	SWITCH(str)	\
    for (sw_string = str, sw_loop_once = 1; --sw_loop_once == 0; )

#define CASE(str)	\
    if (strcmp(sw_string, str) == 0)


#define ARG(i, str)	(argv(i) && strcmp(argv(i), str) == 0)
#define ARGVAL(i)	atol(argv(i))
#define ARGTAIL		argvec[argc]

struct alt_commands {
    char *alt_name;
    int	 alt_len;
    int  alt_type;
} alt_commands[] = {
    "admin",			5,	0,
    "bug",			3,	0,
    "cd",			2,	1,
    "compile",			7,	0,
    "coredump",			8,	0,
    "cost",			4,	0,
    "decode",			6,	0,
    "define",			6,	0,
    "help",			4,	2,
    "load",			4,	0,
    "local",			5,	3,
    "lock",			4,	3,
    "make",			4,	-1,
    "make map ",		9,	-2,
    "man",			3,	0,
    "map",			3,	4,
    "mkdir",			5,	1,
    "motd",			4,	0,
    "patch",			5,	0, /* QUICK HACK */
    "post",			4,	0, /* QUICK HACK */
    "print",			5,	-3, /* QUICK HACK */
    "print-variables",		15,	0,
    "pwd",			3,	0,
    "rmail",			5,	0,
    "set",			3,	3,
    "show",			4,	-1,
    "show groups",		11,	-1,
    "show groups all",		15,	0,
    "show groups subscr",	18,	0,
    "show groups total",	17,	0,
    "show groups unsub",	17,	0,
    "show kill",		9,	0,
    "show map",			8,	-1,
    "show map #",		10,	0,
    "show map key",		12,	0,
    "show map menu",		13,	0,
    "show map show",		13,	0,
    "show rc ",			8,	0,
    "sort",			4,	-1,
    "sort arrival",		12,	0,
    "sort date",		9,	0,
    "sort lexical",		12,	0,
    "sort sender",		11,	0,
    "sort subject",		12,	0,
    "toggle",			6,	3,
    "unread",			6,	0,
    "unset",			5,	3,
    "unshar",			6,	0, /* QUICK HACK */
    NULL,			0,	0
};

int
alt_completion(buf, index)
char *buf;
int index;
{
    static char *head, *tail = NULL, buffer[FILENAME];
    static int len;
    static struct alt_commands *alt, *help_alt;
    static fct_type other_compl;
    int temp;
    register char *p, *q;

    if (other_compl) {
	temp = CALL(other_compl)(buf, index);
	if (index == 0 && temp == 1 && tail) strcpy(tail, head);
	if (index < 0 || (index == 0 && temp == 0)) {
	    other_compl = NULL;
	    list_offset = 0;
	}
	return temp;
    }

    if (index < 0) return 0;

    if (buf) {
	if (index >= 1 && buf[0] == '!') return -1;	/* :! is special */

	head = buf;
	tail = buf + index;
	alt = help_alt = alt_commands;
	len = tail - head;
	other_compl = NULL;

	for (; alt->alt_name; alt++) {
	    if (len <= alt->alt_len) continue;
	    if (head[alt->alt_len] != SP) {
		if (alt->alt_type != -2) continue;
		if (strncmp(alt->alt_name, head, alt->alt_len)) continue;
		return -1;
	    }
	    index = strncmp(alt->alt_name, head, alt->alt_len);
	    if (index < 0) continue;
	    if (index > 0) break;

	    if (alt->alt_type < 0) {
		if (len > alt->alt_len) continue;
		break;
	    }

	    if (alt->alt_type == 0) return -1; /* cannot be further compl */

	    head += alt->alt_len;
	    while (*head && *head == SP) head++;
	    len = tail - head;
	    temp = -1;

	    switch (alt->alt_type) {
	     case 1:
		other_compl = file_completion;
		tail = NULL;
		temp = file_completion(head, len);
		break;

	     case 2:
		other_compl = file_completion;
		sprintf(buffer, "%s.%s",
			relative(help_directory, "help"), head);
		len = strlen(buffer);
		head = buffer + len;
		list_offset = 5;
		temp = file_completion(buffer, len);
		break;

	     case 3:
		/* [set ]variable[ value] */
		for (p = head; *p; )
		    if (*p++ == SP) return -1;
		other_compl = var_completion;
		var_compl_opts(tail - buf);
		tail = NULL;
		temp = var_completion(head, len);
		break;

	     case 4:
		/* [map XXX ]Y command[ N] */
		if (*head == '#') return -1;
		for (p = head, temp = 0; *p; )
		    if (*p++ == SP) {
			while (*p && *p == SP) p++;
			head = p;
			temp++;
		    }
		if (temp == 0) 
		    other_compl = keymap_completion;
		else if (temp == 2)
		    other_compl = cmd_completion;
		else
		    return -1;

		tail = NULL;
		len = p - head;
		temp = CALL(other_compl)(head, len);
		break;
	    }
	    if (temp <= 0) other_compl = NULL;
	    return temp;
	}

	alt = alt_commands;
	return 1;
    }

    if (index) {
	list_completion((char *)NULL);
	if (help_alt->alt_name == NULL) help_alt = alt_commands;
	list_offset = 0;
	if ((p = strrchr(head, ' '))) list_offset = p - head;

	while (help_alt->alt_name) {
	    if (len > help_alt->alt_len ||
		(index = strncmp(help_alt->alt_name, head, len)) < 0) {
		help_alt++;
		continue;
	    }
	    if (index > 0) {
		help_alt = alt_commands;
		break;
	    }
	    p = help_alt->alt_name;
	    if (list_completion(p) == 0) break;
	    temp = help_alt->alt_len;

	    if (help_alt->alt_type == -3) {
		help_alt++;
		continue;
	    }
	
	    do help_alt++;
	    while ((q = help_alt->alt_name) && help_alt->alt_len > temp &&
		   strncmp(p, q, temp) == 0);
	}
	fl;
	list_offset = 0;
	return 1;
    }

    for (; alt->alt_name; alt++) {
	if (len == 0)
	    index = 0;
	else
	    index = strncmp(alt->alt_name, head, len);
	if (index < 0) continue;
	if (index > 0) break;

	p = alt->alt_name;
	sprintf(tail, "%s%s", p + len, alt->alt_type <= -2 ? "" : " ");
	temp = alt->alt_len;

	if (alt->alt_type == -3) {
	    alt++;
	    return 1;
	}
	
	do alt++;
	while ((q = alt->alt_name) && alt->alt_len > temp && 
	       strncmp(p, q, temp) == 0);

	return 1;
    }

    cmd_completion(head, len);
    if ((temp = cmd_completion((char *)NULL, 0))) {
	other_compl = cmd_completion;
	tail = NULL;
    }

    return temp;
}

static void
print_debug_info()
{
#ifdef USE_MALLOC_H
    struct mallinfo mallinfo(), mi;
#endif
    static long prev_mem = 0;
    long cur_mem;

    clrdisp();
    tprintf("group=%s, nart=%ld\n\r", current_group->group_name, n_articles);

    cur_mem = (((long)sbrk(0)) - initial_memory_break)/1024;

    tprintf("\nMemory usage: %ldk, previous: %ldk, change: %ldk\n\r",
	   cur_mem, prev_mem, cur_mem - prev_mem);
    prev_mem = cur_mem;

#ifdef USE_MALLOC_H
    mi = mallinfo();
    tprintf("\nMalloc info.  Total allocation: %d\n\r", mi.arena);
    tprintf("Ordinary blocks: %d, space in use: %d, space free: %d\n\r",
	   mi.ordblks, mi.uordblks, mi.fordblks);
    tprintf("Small blocks: %d, space in use: %d, space free: %d\n\r",
	   mi.smblks, mi.usmblks, mi.fsmblks);
    tprintf("Holding blocks: %d, space in headers: %d\n\r",
	   mi.hblks, mi.hblkhd);
#endif

    any_key(0);
}

static void
print_command(str)
char *str;
{
    char **av;
    char buf[1024];

    if (!in_init) {
	msg(str);
	return;
    }

    buf[0] = NUL;
    for (av = argvec; *av; av++) {
	strcat(buf, " ");
	strcat(buf, *av);
    }
    init_message("%s: %s", str, buf);
}


static int
do_show(table, mode_arg)
char *table;
int mode_arg;
{
    register group_header *gh;
    int ret = 1;

    if (in_init || table == NULL) return 0;

    no_raw();

    SWITCH( table ) {

	CASE( "kill" ) {
	    clrdisp();
	    dump_kill_list();
	    break;
	}

	CASE( "groups" ) {

	    clrdisp();
	    if ARG(mode_arg, "all")
		group_overview(1);
	    else
	    if ARG(mode_arg, "total")
		group_overview(2);
	    else
	    if ARG(mode_arg, "unsub")
		group_overview(3);
	    else
	    if ARG(mode_arg, "subscr")
		group_overview(4);
	    else
	    if ARG(mode_arg, "sequence")
		group_overview(-1);
	    else
		group_overview(0);

	    break;
	}

	CASE( "map" ) {
	    char *name;

	    if ((name = argv(mode_arg)) == NULL)
		name = in_menu_mode ? "menu" : "show";

	    if (name[0] == '#') {
		clrdisp();
		dump_multi_keys();
		break;
	    }

	    SWITCH( name ) {

		CASE( "key" ) {
		    clrdisp();
		    dump_global_map();
		    break;
		}
		if (dump_key_map(name) >= 0)
		    break;

		init_message("unknown map '%s'", argv(mode_arg));
		ret = 0;
		/* break;	goto err */
	    }

	    break;
	}

	CASE( "rc" ) {
	    if (argv(2)) {
		gh = lookup(argv(2));
		if (gh == NULL) {
		    msg("Unknown: %s", argv(2));
		    break;
		}
	    } else
		gh = current_group;
	    if (gh->group_flag & G_FAKED) break;

	    clrdisp();

	    tprintf("Available: %ld - %ld  (unread %ld)\n\n\r",
		   (long)(gh->first_db_article), (long)(gh->last_db_article),
		   (long)(gh->unread_count));
	    tprintf(".newsrc:\n\r>%s\r<%s\n\rselect:\n\r>%s\r<%s\n\r",
	       gh->newsrc_line ? gh->newsrc_line : "(null)\n",
	       gh->newsrc_orig == gh->newsrc_line ? "(same)\n" :
		   gh->newsrc_orig ? gh->newsrc_orig : "(null)\n",
	       gh->select_line ? gh->select_line : "(null)\n",
	       gh->select_orig == gh->select_line ? "(same)\n" :
		   gh->select_orig ? gh->select_orig : "(null)\n");
	    any_key(0);
	    break;
	}

	init_message("unknown table '%s'", table);
	ret = 0;
	/* break; */
	/* goto err; */
	/*NOTREACHED*/
    }

    nn_raw();
    return ret;
/* 
err:
    nn_raw();
    return 0;
*/
}


static int
do_map(initf)
FILE *initf;
{
    int code, map_menu, map_show, must_redraw = 0;
    key_type bind_to;
    register struct key_map_def *map_def;
    register int *map;
    
    code = lookup_keymap(argv(1));
    if (code < 0) {
	print_command("unknown map");
	goto out;
    }
    map_def = &keymaps[code];
    
    if (map_def->km_flag & K_GLOBAL_KEY_MAP) {
	if (argv(3) == NULL) goto mac_err;
	if (argv(2) == NULL) {
	    dump_global_map();
	    return 1;
	}
	global_key_map[parse_key(argv(2))] = parse_key(argv(3));
	return 0;
    }
    
    if (map_def->km_flag & K_MULTI_KEY_MAP) {
	key_type multi_buffer[16], *mb;
	int i;
	
	if (argv(1)[1] == NUL) {
	    dump_multi_keys();
	    return 1;
	}
	
	if (isdigit(argv(1)[1])) 
	    bind_to = K_function(argv(1)[1] - '0');
	else {
	    bind_to = parse_key(argv(1) + 1);
	    if (bind_to < K_up_arrow || bind_to > K_right_arrow) goto mac_err;
	}
	
	for (i = 2, mb = multi_buffer; argv(i); i++)
	    *mb++ = parse_key(argv(i));
	*mb = NUL;
	
	enter_multi_key(bind_to, (key_type *)copy_str((char *)multi_buffer));
	return 0;
    }
    
    code = K_UNBOUND;
    map = map_def->km_map;
    map_show = map_def->km_flag & K_BOTH_MAPS;
    map_menu = map_def->km_flag & K_BIND_ORIG;
    
    if (ARG(3, "(")) {
	code = (int)m_define("-2", initf);
	must_redraw = 1;
	if (code == K_UNBOUND) goto mac_err;
    }
    
    if (code == K_UNBOUND && argv(3))
	code = lookup_command(argv(3), map_def->km_flag & (K_ONLY_MENU|K_ONLY_MORE));
    
    switch (code) {
     case K_INVALID-1:
	init_message("Cannot bind '%s' in '%s' map", argv(3), argv(1));
	goto out;

     case K_EQUAL_KEY:
	if (argv(4) == NULL) goto mac_err;
	code = map[parse_key(argv(4))];
	break;
	
     case K_MACRO:
     case K_ARTICLE_ID:
	if (argv(4) == NULL) goto mac_err;
	code |= atoi(argv(4));
	break;
	
     case K_PREFIX_KEY: 
	if (argv(4) == NULL) goto mac_err;
	code = lookup_keymap(argv(4));
	if (code < 0) {
	    print_command("unknown prefix map");
	    goto out;
	}
	code |= K_PREFIX_KEY;
	break;
    }
    
    if (code == K_INVALID) {
	init_message("unknown key command: %s", argv(3));
	goto out;
    }
    
    bind_to = parse_key(argv(2));
    if (map_menu) {
	if (code & K_MACRO && orig_menu_map[bind_to] == 0)
	    orig_menu_map[bind_to] = map[bind_to];
    }
    map[bind_to] = code;
    if (map_show)
	more_key_map[bind_to] = code;
    goto out;
    
 mac_err:
    print_command("map argument missing");
 out:
    return must_redraw;
}

static void
parse_on_to_end(f)
FILE *f;
{
    register char *cp;
    char buf[1024];
    static char *last_cmd_res = NULL;
    int i;
    int err_flag = 0;

    if (ARGTAIL == NULL || *ARGTAIL == NUL) goto on_err;

    cp = NULL;
    switch (*ARGTAIL) {
     case '#':			/* on #... end: skipped (+hack for else) */
	goto skip_to_end;

     case '`':			/* on `shell command` str1 str2 ... */
	{
	    FILE *p;
	    char *cmd = ARGTAIL + 1, *t;

	    if ((cp = strrchr(cmd, '`')) == NULL) goto syntax_err;
	    if ((t = strip_str(cp + 1)) == NULL) goto syntax_err;
	    *cp = NUL;
	    ARGTAIL = t;

	    if (cmd[0]) {
		buf[0] = NUL;
		if ((p = popen(cmd, "r"))) {
		    if (fgets(buf, 1024, p))
			buf[strlen(buf) - 1] = NUL;
		    pclose(p);
		}
		if (last_cmd_res != NULL && strcmp(last_cmd_res, buf)) {
		    free(last_cmd_res);
		    last_cmd_res = NULL;
		}
		if (buf[0] == NUL) goto skip_to_end;
		last_cmd_res = copy_str(buf);
	    }
	    for (i = 1; argv(i) != NULL; i++)
		if (strcmp(argv(i), last_cmd_res) == 0) return;
	}
	goto skip_to_end;

     case '$':			/* on $VAR [ a b c ... ] */
	cp = argv(1);
	if ((cp = getenv(cp+1)) == NULL) goto skip_to_end;
	if (ARGTAIL == NULL) return;
	for (i = 2; argv(i) != NULL; i++)
	    if (strcmp(argv(i), cp) == 0) return;
	goto skip_to_end;
	
     case '!':			/* on !shell-command */
	cp = ARGTAIL + 1;
	break;

     case '[':			/* on [ test ] */
	cp = ARGTAIL + strlen(ARGTAIL) - 1;
	if (*cp != ']') goto syntax_err;
	cp = ARGTAIL;
	break;

     default:
	break;
    }

    if (cp) {
	if (run_shell(cp, -2, 1) == 0) return;
	goto skip_to_end;
    }

    if (argv(1) == NULL) goto on_err;

    SWITCH ( argv(1) ) {

	CASE( "entry" ) {
	    group_header *gh;
	    char *macro;
	    int ii;

	    macro = parse_enter_macro(f, NL);
	    if (ARGTAIL) {
		for (ii = 2; argv(ii); ii++) {
		    start_group_search(argv(ii));
		    while ((gh = get_group_search()))
			gh->enter_macro = macro;
		}
	    } else
		dflt_enter_macro = macro;
	    return;
	}

/*	CASE( "exit" ) {
	    dflt_exit_macro = parse_enter_macro(f, NL);
	    return;
	}
*/
	CASE( "slow" ) {
	    if (terminal_speed <= (slow_speed / 10)) return;
	    break;
	}

	CASE( "fast" ) {
	    if (terminal_speed > (slow_speed / 10)) return;
	    break;
	}

	CASE( "term" ) {
	    int ii;

	    for (ii = 2; argv(ii) != NULL; ii++)
		if (strcmp(argv(ii), term_name) == 0) return;
	    break;
	}

	CASE( "host" ) {
	    char local_host[100];
	    int ii;

	    nn_gethostname(local_host, 100);
	    for (ii = 2; argv(ii) != NULL; ii++)
		if (strcmp(argv(ii), local_host) == 0) return;
	    break;
	}

	CASE( "program" ) {
	    char *pname1;
	    int ii;

	    for (pname1 = pname; *pname1 == 'n'; pname1++);
	    
	    for (ii = 2; argv(ii) != NULL; ii++) {
		if (strcmp(argv(ii), pname) == 0) return;
		if (pname1[0] && strcmp(argv(ii), pname1) == 0) return;
	    }
	    break;
	}

	CASE( "start-up" ) {
	    start_up_macro = parse_enter_macro(f, NL);
	    return;
	}

	CASE( "first-use" ) { 
	    if (!first_time_user) break;
	    if (argv(2) == NULL || ARG(2, "all")) return;
	    if (newsrc_file == NULL) /* == code from visit_rc_file == */
		newsrc_file = home_relative(".newsrc");
	    if (ARG(2, "old") && file_exist(newsrc_file, (char *)NULL))
		return;
	    if (ARG(2, "new") && !file_exist(newsrc_file, (char *)NULL))
		return;
	    break;
	}

	err_flag = 1;
/*	goto on_err; */
    }
    if (err_flag == 1)
	goto on_err;

 skip_to_end:
    while (fgets_multi(buf, 1024, f)) {
	for (cp = buf; *cp && isascii(*cp) && isspace(*cp); cp++);
	if (*cp != 'e') continue;
	if (strncmp(cp, "end", 3) == 0) return;
	if (strncmp(cp, "else", 4) == 0) return;
    }
    init_message("end missing (on %s)", argv(1));
    return;
    
on_err:
    init_message("on `what'?");
    return;

syntax_err:
    init_message("syntax error: on %s", ARGTAIL);
}

int
parse_command(cmd, ok_val, initf)
char *cmd;
int ok_val;
FILE *initf;
{

    if (!split_command(cmd)) return ok_val;

    if (*ARGTAIL == '!') {
	if (ok_val == AC_UNCHANGED) { /* in macro */
	    if (ARGTAIL[1] == '!') /* !!cmd => guarantee no output! */
		run_shell(ARGTAIL+2, -2, 1);
	    else
		run_shell(ARGTAIL+1, -1, 1);
	    return ok_val;
	}
	if (run_shell(ARGTAIL+1, ok_val == AC_PROMPT ? 1 : 0, in_init) >= 0) {
	    any_key(0);
	    return AC_REDRAW;
	}
	return ok_val;
    }

    SWITCH( argv(0) ) {

	CASE( "unset" ) {
	    if (argv(1) == NULL) goto stx_err;

	    if (set_variable(argv(1), 0, (char *)NULL))
		return AC_REDRAW;
	    else
		return ok_val;
	}

	CASE( "local" ) {
	    if (ARGTAIL == NULL) goto stx_err;

	    cmd = argv(1);
	    if (!push_variable(cmd)) return ok_val;

	    if (ARGTAIL && set_variable(cmd, 1, ARGTAIL))
		return AC_REDRAW;
	    else
		return ok_val;
	}

	CASE( "set" ) {
	    if (ARGTAIL == NULL || ARGTAIL[0] == '/') {
		disp_variables(0, ARGTAIL);
		return AC_REDRAW;
	    }

	    cmd = argv(1);	/* get ARGTAIL right */
	    if (cmd != NULL && strcmp(cmd, "all") == 0) {
		disp_variables(1, (char *)NULL);
		return AC_REDRAW;
	    }

	    if (set_variable(cmd, 1, ARGTAIL))
		return AC_REDRAW;
	    else
		return ok_val;
	}

	CASE( "toggle" ) {
	    if (argv(1) == NULL) goto stx_err;
	    toggle_variable(argv(1));
	    break;
	}

	CASE( "lock" ) {
	    if (argv(1) == NULL) goto stx_err;
	    lock_variable(argv(1));
	    break;
	}

	CASE( "define" ) {
	    if (in_init) {
		if (argv(1) == NULL) {
		    init_message("macro number missing");
		    break;
		}
		m_define(argv(1), initf);
	    } else
		if (m_define(argv(1), (FILE *)NULL))
		    return AC_REDRAW;

	    break;
	}

	CASE( "map" ) {
	    if (argv(2) == NULL) {
		if (do_show("map", 1))
		    return AC_REDRAW;
		break;
	    }

	    if (do_map(initf))
		return AC_REDRAW;
	    break;
	}

	CASE("make") {
	    if (ARG(1, "map") && argv(2)) {
		switch (make_keymap(argv(2))) {
		 case -1:
		    init_message("map %s already exists", argv(2));
		    break;
		 case -2:
		    init_message("cannot make %s: too many maps", argv(2));
		    break;
		}
		break;
	    }

	    print_command("invalid make command");
	    break;
	}

	CASE( "cd" ) {
	    if (change_dir(argv(1), in_init))
		init_message("chdir %s FAILED", argv(1));

	    break;
	}

	CASE( "clear" ) {
	    clrdisp();
	    break;
	}

	if (in_init) {

	    CASE( "load" ) {
		if (argv(1)) load_init_file(argv(1), (FILE **)NULL, 0);
		break;
	    }

	    CASE( "on" ) {
		parse_on_to_end(initf);
		break;
	    }

	    CASE( "else" ) {
		ARGTAIL = "#";	/* skip to end */
		parse_on_to_end(initf);
		break;
	    }

	    CASE( "end" ) {
		break;
	    }

	    CASE( "echo" ) {
		tprintf("\r%s\n\r", ARGTAIL);
		break;
	    }

	    CASE( "error" ) {
		nn_exitmsg(1, "%s\n", ARGTAIL);
	    }

	    CASE( "exit" ) {
		nn_exit(ARGTAIL != NULL ? atoi(ARGTAIL) : 0);
	    }

	    CASE( "chain" ) {
		return CHAIN_FILE;
	    }

	    CASE( "stop" ) {
		return STOP_FILE;
	    }

	    CASE( "sequence" ) {
		return START_SEQUENCE;
	    }

	    CASE( "save-files" ) {
		parse_save_files(initf);
		break;
	    }

	    print_command("unknown command");
	    break;
	}

	/*
	 * commands only available from : command line
	 */

	if (ok_val != AC_REDRAW) {

	    alt_cmd_key = lookup_command(sw_string,
				 in_menu_mode ? K_ONLY_MENU : K_ONLY_MORE);
	    if (alt_cmd_key > K_INVALID && alt_cmd_key != K_HELP) {
		if (alt_cmd_key == K_MACRO) {
		    if (ARGTAIL == NULL) break;
		    alt_cmd_key |= atoi(ARGTAIL);
		}
		return AC_KEYCMD;
	    }
	}

	CASE( "load" ) {
	    clrdisp();
	    in_init = 1;
	    init_err = 0;
	    load_init_file(argv(1) ? argv(1) : "init", (FILE **)NULL, 0);
	    in_init = 0;
	    if (init_err) any_key(0);
	    return AC_REDRAW;
	}

	CASE( "q" ) {
	    break;
	}

	CASE( "Q" ) {
	    return AC_QUIT;
	}

	CASE( "q!" ) {
	    if (restore_bak())
		return AC_QUIT;
	    break;
	}

	CASE( "x" ) {
	    update_rc_all(current_group, 0);
	    return AC_QUIT;
	}

	CASE( "help" ) {
	    if (argv(1) == NULL)
		display_help("help");
	    else
		display_help(argv(1));
	    return AC_REDRAW;
	}

	CASE( "man" ) {
	    char *manual;
	    group_header *orig_group;

	    manual = relative(help_directory, "Manual");
	    if (!file_exist(manual, "fr")) {
		manual = relative(lib_directory, "Manual");
		if (!file_exist(manual, "fr")) {
		    msg("Online manual is not available");
		    break;
		}
	    }
	    orig_group = current_group;
	    folder_menu(manual, 1);
	    init_group(orig_group);

	    return AC_REDRAW;
	}

	CASE( "motd" ) {
	    if (display_motd(0)) return AC_REDRAW;
	    msg("no motd file");
	    break;
	}

	CASE( "sort" ) {
	    if (argv(1) == NULL)
		sort_articles(-1);
	    else if (ARG(1, "no") || ARG(1, "arrival"))
		sort_articles(0);
	    else if ARG(1, "subject")
		sort_articles(1);
	    else if ARG(1, "lexical")
		sort_articles(2);
	    else if (ARG(1, "date") || ARG(1, "age"))
		sort_articles(3);
	    else if (ARG(1, "sender") || ARG(1, "from"))
		sort_articles(4);
	    else {
		msg("Unknown sort mode '%s'", argv(1));
		break;
	    }

	    return AC_REORDER;
	}

	CASE( "unread" ) {
	    group_header *gh;
	    int ix;

	    if (argv(1) && (gh = lookup(argv(1))) != NULL)
		ix = 2;
	    else {
		ix = 1;
		gh = current_group;
	    }

	    if (gh == current_group) return AC_REENTER_GROUP;

	    if (argv(ix)) {
		if (!restore_rc(gh, gh->last_db_article - ARGVAL(ix)))
		    break;
	    } else
		if (!restore_unread(gh))
		    break;
	    break;
	}

	CASE( "dump" ) {
	    if (do_show(argv(1), 2))
		return AC_REDRAW;
	    break;
	}

	CASE( "show" ) {
	    if (do_show(argv(1), 2))
		return AC_REDRAW;
	    break;
	}

	CASE( "compile" ) {
	    clrdisp();
	    rm_kill_file();
	    free_kill_entries();
	    do_kill_handling = init_kill() && do_kill_handling;
	    return AC_REDRAW;
	}

	CASE( "print-variables" ) {
	    FILE *p;
	    if ((p = popen(ARGTAIL ? ARGTAIL : printer, "w"))) {
		print_variable_config(p, 1);
		pclose(p);
		msg("Variables printed");
	    }
	    break;
	}
	
	CASE( "pwd" ) {
	    FILE *p = popen("exec pwd", "r");
	    char dir[FILENAME];
	    if (p) {
		if (fgets(dir, FILENAME, p)) {
		    dir[strlen(dir) - 1] = NUL;
		    msg("%s", dir);
		}
		pclose(p);
	    }
	    break;
	}

	CASE( "rmail" ) {
	    group_header *orig_group;
	    int rv;

	    if (mail_box == NULL) {
		msg("'mail' path not defined");
		break;
	    }

	    orig_group = current_group;
	    rv = folder_menu(mail_box, 2);
	    init_group(orig_group);

	    return rv == ME_NO_REDRAW ? ok_val : AC_REDRAW;
	}

	CASE( "mkdir" ) {
	    char *dir;
	    char name_buf[FILENAME];

	    if ((dir = run_mkdir(argv(1), name_buf))) {
		prompt("Change to %s", dir);
		if (yes(0)) change_dir(dir, 0);
	    }
	    break;
	}

	CASE( "sh" ) {
	    suspend_nn();
	    s_redraw = 0;
	    return AC_REDRAW;
	}

	CASE( "admin" ) {
	    group_header *cur_group;

	    cur_group = current_group;
	    no_raw();
	    clrdisp();
	    tprintf("\n\n\n\rADMINISTRATION MODE\r\n\n\n");
	    admin_mode((char *)NULL);
	    clrdisp();
	    nn_raw();
	    init_group(cur_group);
	    return AC_REDRAW;
	}

	CASE( "cost" ) {
#ifdef ACCOUNTING
	    gotoxy(0, Lines-1); clrline();
	    account('C', 1);
#else
	    msg("No accounting");
#endif
	    break;
	}

	CASE( "bug" ) {
	    if (answer((article_header *)NULL, K_BUG_REPORT, 0))
		return AC_REDRAW;
	    break;
	}

	CASE( "debug" ) {
	    print_debug_info();
	    return AC_REDRAW;
	}

	CASE( "coredump" ) {
	    unset_raw();
	    abort();
	}

	msg("unknown command: \"%s\"", argv(0));
     }

    return ok_val;

 stx_err:
    print_command("syntax error");
    return ok_val;
}


void
display_help(subject)
char *subject;
{
    char file[FILENAME];

    strcpy(file, "help.");
    strcpy(file+5, subject);

    display_file(file, CLEAR_DISPLAY | CONFIRMATION);
}
