/*
 * Copyright (c) 1986, 2014 by The Trustees of Columbia University in
 * the City of New York.  All rights reserved.
 *
 * Note that as of 2011, Columbia University no longer has any involvement
 * with this program (Columbia MM), it holds the copyright in
 * perpetuity.  As of MM 0.96.0, the license has changed from the
 * previous somewhat restrictive one to the Open Source Modified Berkeley
 * 3-clause license, text just below.  Effective 19 February 2014,
 * Columbia MM development and distribution is headquartered at the 
 * New Open Source Kermit Project:
 *
 *    http://www.kermitproject.org/mm/
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  
 *  + Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  
 *  + Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  
 *  + Neither the name of Columbia University nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *  
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 */

/*
 * mm.c - portable "mail manager" program
 */


#include "mm.h"
#include "parse.h"
#include "cmds.h"
#include "set.h"
#include "acttab.h"

extern int user_aborted;
extern string default_read_command;
extern string default_send_command;
extern variable set_variables[];

const char *progname;
int mode = MM_TOP_LEVEL;
int memdebug = false;
int debug = false;
int nocheck = 0;
#define dmsg(msg) if (debug) { printf (msg); fflush(stdout); } else debug = 0
static void shell_escape ARGS((void));

#define CMDBUFLEN (10*BUFLEN)
static int cmdbuf[CMDBUFLEN];		/* ccmd work buffers */
static buffer wrkbuf;
buffer atmbuf;				/* parsed field */
pval pv;				/* parse value */
fdb *used;				/* which fdb was used to parse field */
extern char cmcont;			/* line continuation character */

int use_pop = 0;
static int mail_check = false;
jmp_buf abortbuf;

static char **Argv;
static int Argc,doargs=false;
int gotargs=false;

int
#if HAVE_STDC
main(int argc, char **argv)
#else /* K&R style */
main(argc, argv)
int argc;
char *argv[];
#endif /* HAVE_STDC */
{
    int err;
    volatile int doget;

#ifdef USAGEFILE
    usage_start();
#endif
    Argv = argv;
    Argc = argc;
    doargs = false;
#ifdef MDEBUG
    m_init();
#endif /* MDEBUG */

    dmsg ("initialize...");
    initialize (argv);			/* set up some variables */

    mode = MM_TOP_LEVEL;

    cmcont = '\\';			/* lines are continued with \ */
    cmbufs (cmdbuf, sizeof (cmdbuf)/sizeof(*cmdbuf), /* set up buffers  */
	    atmbuf, sizeof (atmbuf)/sizeof(*atmbuf), /*   for ccmd */
	    wrkbuf, sizeof (wrkbuf)/sizeof(*wrkbuf));

    cmact(mmactini());
    cmcsb._cmntb = "#";			/* comments start with # */

    init_signals ();			/* see signals.c */

    printf ("%s\n", mm_version);

    dmsg ("crunch...");

    cmseti(NULL, NULL, NULL);	      /* no files needed while reading those */
    cmhst(0);				/* turn off cmd history */

    dmsg ("system init...");
    system_init ();			/* take system init file */

    dmsg ("group init...");		/* take group init file */
    group_init();

    clear_modified();			/* clear modified flags on variables */

    dmsg ("mailrc...");
    mailrc();
    if (isatty(0))
	cmseti(stdin, stdout, stderr);	/* DON'T TRY TO PARSE BEFORE HERE */
    else
	cmseti(stdin, NULL, stderr);	/* pipe or file redirect. */
					/* don't echo */
    dmsg ("user init...");
    user_init ();

    if (clear_screen) {			/* var is now appropriately set */
	cmcls();
	printf ("%s\n", mm_version);
    }
    printf ("\
Please report all problems using MM's BUG command, or send mail to BUG-MM.\n\
Suggestions are also welcome.\n");

    dmsg ("user rc...");
    user_rc ();
    if (editor == NULL && !set_variables[SET_EDITOR].changed) {
	char *ed;

	if ((ed = (char *) getenv ("EDITOR")) != NULL)
	    editor = split_args (ed);
	else
	    editor = split_args(EDITOR);
    }
    cmhst(100);				/* turn on command history */
    show_route(FALSE);			/* show mail routing if routed */

    dmsg ("\n");

    doget = true;
    cmseter();
    if (doget && cf == NULL && strlen(mail_file) != 0 && auto_startup_get &&
	Argc == 1) {
	doget = false;
	printf("Reading %s ...\n", mail_file);
	cf = getfile(mail_file,CMD_GET);
	if (cf != NULL)
	    do_flagged();		/* show flagged ones */
    }
    mail_check = true;
    doargs = true;
    while (true) {
	err = top_level_parser ();
	switch (err) {
	  case CMxEOF:
	    done (0);
	  default:
	    fprintf (stderr, "%s: main: fatal parser error %d\n",
		     progname, err);
	    done (1);
	}
    }
}

int
top_level_parser (VOID)
{
    fdb *fdbs;
    static int level = 0;		/* see if we are here in a "take" */
    volatile int doprev;

    level++;				/* count this entry */
    cmcsb._cmerr = CMxOK;		/* assume no error */
    while (true) {
	setjmp(abortbuf);
	allow_aborts = false;
#ifdef undef
	debug_validate_msgvec("top level loop");
#endif
	if (mail_check && cf)		/* fdc 4 Dec 2005 */
	    new_mail(true);		/* check for new mail. */
	if (cmseteof ()) {
	    level--;			/* count the return */
	    return CMxEOF;
	}
	doprev = false;
	if (cmseter ()) {		/* errors return here */
	    if (cmcsb._cmerr == CMxEOF) {
		level--;
		return CMxEOF;
	    }
	    else
		doprev = true;
	}
	if (user_aborted) {
	    mode &= ~(MM_SEND|MM_REPLY|MM_ANSWER);
	    user_aborted = false;
	}
	if (doargs && level <= 1) {
	    fdbs = fdbchn (&mm_top_fdb_abbr, &mm_top_fdb_1, &mm_top_fdb_2,
			   &mm_top_fdb_3, &mm_top_fdb_4, &mm_top_fdb_5,
			   &mm_top_fdb_6, &mm_top_fdb_7, &mm_top_fdb_inv,
			   &shell_fdb, nil);
	    if (cmargs(Argc,Argv))
		gotargs = true;
	}
	if (gotargs && doargs && level <= 1) {
	    cmsetrp();
	}
	else if (mode == MM_REPLY) {
	    cm_set_ind(TRUE);		/* allow indirections */
	    alarm(0);			/* we don't get to the parse */
	    do_reply_many();		/*   further down where alarm is */
	    continue;			/*   turned off so do it here */
	}
	else if (mode & MM_SEND) {
	    if (!doprev)
		novice_banner(MM_SEND);
	    prompt (send_prompt);
	    cmsetrp ();			/* reparse comes back here */
	    fdbs = fdbchn (&mm_send_fdb_abbr, &mm_send_fdb_1,
			   &mm_send_fdb_2, &mm_send_fdb_3, &mm_send_fdb_4,
			   &mm_send_fdb_5, &mm_send_fdb_inv,
			   &shell_fdb, nil);
	    if (default_send_command[0] == '\0')
		mm_send_fdb_abbr._cmdef = NULL;
	    else
		mm_send_fdb_abbr._cmdef = default_send_command;
	}
	else if (mode & MM_BROWSE) {
	    alarm(0);
	    browse_message();
	    continue;
	}
	else if (mode & MM_READ) {
	    if (!doprev)
		novice_banner(MM_READ);
	    prompt (read_prompt);
	    cmsetrp ();			/* reparse comes back here */
	    fdbs = fdbchn (&mm_read_fdb_abbr, &mm_read_fdb_1,
			   &mm_read_fdb_2, &mm_read_fdb_3, &mm_read_fdb_4,
			   &mm_read_fdb_5, &mm_read_fdb_6, &mm_read_fdb_7,
			   &mm_read_fdb_inv,
			   &shell_fdb, nil);
	    if (default_read_command[0] == '\0')
		mm_read_fdb_abbr._cmdef = NULL;
	    else
		mm_read_fdb_abbr._cmdef = default_read_command;
	}
	else {
	    if (gotargs && level <= 1) {
		if (cf && !update(&cf,UPD_SAVEMOD) && (cf->flags & MF_WRITERR))
		    gotargs = FALSE;	/* don't exit if write fails */
		else
		    done(0);
	    }
	    if (!doprev)
		novice_banner(MM_TOP_LEVEL);
	    prompt (top_level_prompt);
	    cmsetrp ();			/* reparse comes back here */
	    fdbs = fdbchn (&mm_top_fdb_abbr, &mm_top_fdb_1, &mm_top_fdb_2,
			   &mm_top_fdb_3, &mm_top_fdb_4, &mm_top_fdb_5,
			   &mm_top_fdb_6, &mm_top_fdb_7, &mm_top_fdb_inv,
			   &shell_fdb, nil);
	}
	if (doprev) {
	    doprev = false;
	    prevcmd();
	}
	cmcsb._cmerh = (int(*) ARGS((int, ...)))ccmd_error;
					/* reset in case of reparse */
	cm_set_ind(TRUE);		/* allow indirections */
	if (fdbs->_cmdef != nil)
	    cmcsb._cmflg |= CM_PRS;	/* XXX kludge! */
	doargs = false;
	parse (fdbs, &pv, &used);
	alarm(0);
	if (used == &shell_fdb) {
	    (void) shell_escape ();
	} else {
	    keywrd *k;
	    k = (keywrd *) pv._pvkey;
	    (*mm_cmds[k->_kwval]) (k->_kwval);
	}
    }
}

/*
 * parse an (optional) shell command and invoke the shell
 */

static void
shell_escape (VOID)
{
    static fdb cmdfdb = { _CMTXT };

    parse (&cmdfdb, &pv, &used);	/* parse a line of text */
    if (strlen (atmbuf) == 0)
	shell (nil);
    else
	shell (atmbuf);
}

void
#if HAVE_STDC
cmd_debug (int n)
#else /* K&R style */
cmd_debug (n)
int n;
#endif /* HAVE_STDC */
{
    debug = parse_yesnoask("yes");
}

void
#if HAVE_STDC
cmd_debug_memory (int n)
#else /* K&R style */
cmd_debug_memory (n)
int n;
#endif /* HAVE_STDC */
{
    memdebug = parse_yesnoask("yes");
}
