/* Agent Tcl
 * Bob Gray
 * 10 Spetember 1995
 *
 * tclMain.cc --
 *
 *	Main program for the Tcl shell
 *
 * Copyright (c) 1995-1996 Bob Gray, Dartmouth College
 * Copyright (c) 1988-1994 The Regents of the University of California.
 * Copyright (c) 1994 Sun Microsystems, Inc.
 *
 * See the file "agent.terms" for information on usage and redistribution
 * of this file and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "platPorting.h"
#include "tcl.h"
#include "genFile.h"
#include "agentTcl.h"
#include "agentTclInt.h"

/*
 * Declarations for various library procedures and variables (don't want
 * to include tclPort.h here, because people might copy this file out of
 * the Tcl source directory to make their own modified versions).
 * Note:  "exit" should really be declared here, but there's no way to
 * declare it without causing conflicts with other definitions elsewher
 * on some systems, so it's better just to leave it out.
 */

extern int		isatty _ANSI_ARGS_((int fd));
extern char *		strcpy _ANSI_ARGS_((char *dst, CONST char *src));

static Tcl_Interp *interp;	/* Interpreter for application. */
static Tcl_DString command;	/* Used to buffer incomplete commands being
				 * read from stdin. */

/*
 *----------------------------------------------------------------------
 *
 * Tcl_Main --
 *
 *	Main program for tclsh and most other Tcl-based applications.
 *
 * Results:
 *	None. This procedure never returns (it exits the process when
 *	it's done.
 *
 * Side effects:
 *	This procedure initializes the Tcl world and then starts
 *	interpreting commands;  almost anything could happen, depending
 *	on the script being interpreted.
 *
 *----------------------------------------------------------------------
 */

void Agent_Main (int argc, char **argv, Tcl_AppInitProc *appInitProc)
{
    char buffer[1000], *cmd, *args, *fileName;
    int code, gotPartial, tty, length;
    int exitCode = 0;
    Tcl_Channel inChannel, outChannel, errChannel;
    Tcl_DString temp;

    /*  
     * Handle agents that do NOT arrive through the server
     *
     * First make command-line arguments available in the Tcl variables "argc"
     * and "argv".  If the first argument does not * start with a "-" then it 
     * is the name of a script file.
     */

    interp = Tcl_CreateInterp();
    Tcl_FindExecutable (argv[0]);

    fileName = NULL;
    if ((argc > 1) && (argv[1][0] != '-')) {
	fileName = argv[1];
	argc--;
	argv++;
    }

    args = Tcl_Merge(argc-1, argv+1);
    Tcl_SetVar(interp, "argv", args, TCL_GLOBAL_ONLY);
    ckfree(args);
    sprintf(buffer, "%d", argc-1);
    Tcl_SetVar(interp, "argc", buffer, TCL_GLOBAL_ONLY);
    Tcl_SetVar(interp, "argv0", (fileName != NULL) ? fileName : argv[0],
	    TCL_GLOBAL_ONLY);

    /*
     * Set the "tcl_interactive" variable.
     */

    tty = isatty(0);
#ifdef TCL8
    char value[8];
    sprintf (value, "%d", (fileName == NULL) && tty);
    Tcl_SetVar2 (interp, "tcl_interactive", (char *) NULL, value, TCL_GLOBAL_ONLY);
#else
    Tcl_SetInteractive (interp, (fileName == NULL) && tty);
#endif

    /*
     * Invoke application-specific initialization.
     */

    if ((*appInitProc)(interp) != TCL_OK) {
	errChannel = Tcl_GetStdChannel (TCL_STDERR);
	if (errChannel) {
	    Tcl_Write(errChannel,
		    "application-specific initialization failed: ", -1);
	    Tcl_Write(errChannel, interp->result, -1);
	    Tcl_Write(errChannel, "\n", 1);
	}
    }

    /*
     * If a script file was specified then just source that file and quit.
     */

    if (fileName != NULL) {
#ifndef TCL8
	Tcl_AllowStateCapture(interp);
#endif
	code = Tcl_EvalFile(interp, fileName);
	if (code != TCL_OK) {
	    errChannel = Tcl_GetStdChannel(TCL_STDERR);
	    if (errChannel) {
		/*
		 * The following statement guarantees that the errorInfo
		 * variable is set properly.
		 */

		Tcl_AddErrorInfo(interp, "");
		Tcl_Write(errChannel,
			Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY), -1);
		Tcl_Write(errChannel, "\n", 1);
	    }
	    exitCode = 1;
	}
	goto done;
    }

    /*
     * We're running interactively.  Source a user-specific startup
     * file if Tcl_AppInit specified one and if the file exists.
     */

    fileName = Tcl_GetVar (interp, "tcl_rcFileName", TCL_GLOBAL_ONLY);

    if (fileName != NULL) {
	Tcl_Channel c;
	char *fullName;

	Tcl_DStringInit (&temp);
	fullName = Tcl_TildeSubst(interp, fileName, &temp);
	if (fullName == NULL) {
	    errChannel = Tcl_GetStdChannel(TCL_STDERR);
	    if (errChannel) {
		Tcl_Write(errChannel, interp->result, -1);
		Tcl_Write(errChannel, "\n", 1);
	    }
	} else {

	    /*
	     * Test for the existence of the rc file before trying to read it.
	     */

            c = Tcl_OpenFileChannel(NULL, fullName, "r", 0);
            if (c != (Tcl_Channel) NULL) {
                Tcl_Close(NULL, c);
		if (Tcl_EvalFile(interp, fullName) != TCL_OK) {
		    errChannel = Tcl_GetStdChannel(TCL_STDERR);
		    if (errChannel) {
			Tcl_Write(errChannel, interp->result, -1);
			Tcl_Write(errChannel, "\n", 1);
		    }
		}
	    }
	}
	Tcl_DStringFree(&temp);
    }

    /*
     * Process commands from stdin until there's an end-of-file.
     */

    gotPartial = 0;
    Tcl_DStringInit(&command);
    inChannel = Tcl_GetStdChannel(TCL_STDIN);
    outChannel = Tcl_GetStdChannel(TCL_STDOUT);

    while (1) {

            /* display the prompt if we are on a tty */

    if (tty) {

      char *promptCmd;
      promptCmd = Tcl_GetVar(interp, (char *) (gotPartial ? "tcl_prompt2" : "tcl_prompt1"), TCL_GLOBAL_ONLY);

      if (promptCmd == NULL) {

defaultPrompt:

        if (!gotPartial && outChannel) {
	  Tcl_Write (outChannel, "agent-tcl> ", 11);
        }

      } else {

	code = Tcl_Eval(interp, promptCmd);
	inChannel = Tcl_GetStdChannel(TCL_STDIN);
	outChannel = Tcl_GetStdChannel(TCL_STDOUT);
	errChannel = Tcl_GetStdChannel(TCL_STDERR);
	if (code != TCL_OK) {
	    if (errChannel) {
		Tcl_Write(errChannel, interp->result, -1);
		Tcl_Write(errChannel, "\n", 1);
	    }
	    Tcl_AddErrorInfo(interp,
		    "\n    (script that generates prompt)");
	    goto defaultPrompt;
	}
      }
      if (outChannel) {
	  Tcl_Flush(outChannel);
      }
    }
    
    if (!inChannel) {
	goto done;
    }

    do {
	length = Tcl_Gets (inChannel, &command);
    } while ((length < 0) && !Tcl_Eof(inChannel));
	
    if (length < 0) {
	goto done;
    }

    if ((length == 0) && Tcl_Eof(inChannel) && (!gotPartial)) {
	goto done;
    }
    
    /*
     * Add the newline removed by Tcl_Gets back to the string.
     */
        
    (void) Tcl_DStringAppend(&command, "\n", -1);

    cmd = Tcl_DStringValue(&command);
    if (!Tcl_CommandComplete(cmd)) {
	gotPartial = 1;
	continue;
    }

    gotPartial = 0;
#ifndef TCL8
    Tcl_AllowStateCapture(interp);
#endif
    code = Tcl_RecordAndEval(interp, cmd, 0);
    inChannel = Tcl_GetStdChannel(TCL_STDIN);
    outChannel = Tcl_GetStdChannel(TCL_STDOUT);
    errChannel = Tcl_GetStdChannel(TCL_STDERR);
    Tcl_DStringFree(&command);
    if (code != TCL_OK) {
	if (errChannel) {
	    Tcl_Write(errChannel, interp->result, -1);
	    Tcl_Write(errChannel, "\n", 1);
	}
    } else if (tty && (*interp->result != 0)) {
	if (outChannel) {
	    Tcl_Write(outChannel, interp->result, -1);
	    Tcl_Write(outChannel, "\n", 1);
	}  
    }
    }

    /*
     * Rather than calling exit, invoke the "exit" command so that
     * users can replace "exit" with some other command to do additional
     * cleanup on exit.  The Tcl_Eval call should never return.
     */

    done:
    Tcl_DeleteInterp (interp);
    Tcl_Exit (exitCode);
}
