/* xlisp.c - a small implementation of lisp with object-oriented programming */
/* Copyright (c) 1989, by David Michael Betz.                            */
/* You may give out copies of this software; for conditions see the file */
/* COPYING included with this distribution.                              */

/* For full credits see file xlisp.h */

#include "xlisp.h"

/* define the banner line string */
#define BANNER  "XLISP-PLUS version 3.04\n\
Portions Copyright (c) 1988, by David Betz.\n\
Modified by Thomas Almy and others."

/* global variables */
#ifdef SAVERESTORE
XL_JMP_BUF top_level;
#endif
char *progname;  /* used for reading the symbol table - L. Tierney */
#ifdef SAVERESTORE
char *resfile = "xlisp.wks"; /* make extern to allow setting elsewhere */
#endif

/* local variables */
XL_JMP_BUF exit_xlisp;

/* forward declarations */
#ifdef MACINTOSH
int main(void);
#else
int main _((int argc, char *argv[]));
#endif /* MACINTOSH */
LOCAL VOID toplevelloop(V);

/* main - the main routine */
#ifdef MACINTOSH
int main(void)
#else
int main(argc,argv)
     int argc; char *argv[];
#endif /* MACINTOSH */
{
    char *transcript;
    CONTEXT cntxt;
    int verbose,i, sts;
    struct { char *transcript; int verbose, i; } state;
#ifdef AMIGA
    char project[30],defdir[50];
#endif /* AMIGA */

    /* The way out on errors */
    i = XL_SETJMP(exit_xlisp);
    if (i != 0)
	return i-1;

    /* setup default argument values */
    transcript = NULL;
    verbose = FALSE;

#ifdef FILETABLE
    /* Initialize the file table values */
    filetab[0].fp = stdin;
    filetab[0].tname = "(stdin)";
    filetab[1].fp = stdout;
    filetab[1].tname = "(stdout)";
    filetab[2].fp = stderr;
    filetab[2].tname = "(console)";
    filetab[3].fp = NULL;
    filetab[3].tname = "";
#endif

    /* parse the argument list switches */
#ifndef MACINTOSH
#ifdef AMIGA
    FindStart(&argc,argv,deftool,project,defdir);
#endif /* AMIGA */
    progname = argv[0];  /* L. Tierney */
    for (i = 1; i < argc; ++i)
	if (argv[i][0] == '-')
	    switch(isupper(argv[i][1])?tolower(argv[i][1]):argv[i][1]) {
	    case 't':
		transcript = &argv[i][2];
		break;
	    case 'b':
		batchmode = TRUE;
		break;
	    case 'v':
		verbose = TRUE;
		break;
#ifdef SAVERESTORE
            case 'w':
                resfile = &argv[i][2];
                break;
#endif
#ifdef XLISP_STAT
	    case 'p':
		defaultpath = &argv[i][2];
		break;
#endif /* XLISP_STAT */
#ifndef _Windows
            default: /* Added to print bad switch message */
                fprintf(stderr,"Bad switch: %s\n",argv[i]);
#endif
	    }
#endif /* MACINTOSH */

    /* initialize and print the banner line */
    osinit(BANNER);

    /* setup initialization error handler */
    xlbegin(&cntxt,CF_TOPLEVEL|CF_CLEANUP|CF_BRKLEVEL,(LVAL)1);
    state.transcript = transcript; state.verbose = verbose; state.i = i;
    sts = XL_SETJMP(cntxt.c_jmpbuf);
    transcript = state.transcript; verbose = state.verbose; i = state.i;
    if (sts)
	xlfatal("fatal initialization error");
#ifdef SAVERESTORE
    state.transcript = transcript; state.verbose = verbose; state.i = i;
    sts = XL_SETJMP(top_level);
    transcript = state.transcript; verbose = state.verbose; i = state.i;
    if (sts)
	xlfatal("RESTORE not allowed during initialization");
#endif

    /* initialize xlisp */
#ifdef SAVERESTORE
#ifdef MACINTOSH
    i = macxlinit(resfile);
#else  
    i = xlinit(resfile);
#endif /* MACINTOSH */
#else
    i = xlinit(NULL);
#endif

    /* reset the error handler, since we know what "true" is */
    xlend(&cntxt);
    xlbegin(&cntxt, CF_TOPLEVEL|CF_CLEANUP|CF_BRKLEVEL, s_true);

    /* open the transcript file */
    if (transcript!=NULL && (tfp = OSAOPEN(transcript,CREATE_WR)) == CLOSED) {
	/* TAA Mod -- quote name so "-t foo" will indicate no file name */
	sprintf(buf,"error: can't open transcript file: \"%s\"",transcript);
	stdputstr(buf);
    }

#ifndef MACINTOSH
    /* enter the command line (L. Tierney 9/93) */
    state.transcript = transcript; state.verbose = verbose; state.i = i;
    sts = XL_SETJMP(cntxt.c_jmpbuf);
    transcript = state.transcript; verbose = state.verbose; i = state.i;
    if (sts == 0) {
      LVAL line;
      int j;
      xlsave1(line);
      line = NIL;
      for (j = argc - 1; j >= 0; j--)
	line = cons(cvstring(argv[j]), line);
      xlpop();
      setsvalue(s_command_line, line);
    }
#endif /* MACINTOSH */

    enable_interrupts();

    /* load "init.lsp" */
    if (i) {
      state.transcript = transcript; state.verbose = verbose; state.i = i;
      sts = XL_SETJMP(cntxt.c_jmpbuf);
      transcript = state.transcript; verbose = state.verbose; i = state.i;
      if (sts == 0)
	xsload("init.lsp",TRUE,FALSE);
    }

    /* run any startup functions (L. Tierney 9/93) */
    state.transcript = transcript; state.verbose = verbose; state.i = i;
    sts = XL_SETJMP(cntxt.c_jmpbuf);
    transcript = state.transcript; verbose = state.verbose; i = state.i;
    if (sts == 0) {
      LVAL funs = getvalue(s_startup_functions);
      FRAMEP newfp;

      for (; consp(funs); funs = cdr(funs)) {
	newfp = xlsp;
	pusharg(cvfixnum((FIXTYPE)(newfp - xlfp)));
	pusharg(car(funs));
	pusharg(cvfixnum((FIXTYPE) 0));
	xlfp = newfp;
	xlapply(0);
      }
    }

    /* load any files mentioned on the command line */
    if (! null(getvalue(s_loadfileargs))) {
      state.transcript = transcript; state.verbose = verbose; state.i = i;
      sts = XL_SETJMP(cntxt.c_jmpbuf);
      transcript = state.transcript; verbose = state.verbose; i = state.i;
      if (sts == 0) {
#ifdef MACINTOSH
        macloadinits();
#else
        for (i = 1; i < argc; i++)
	    if (argv[i][0] != '-' && !xsload(argv[i],TRUE,verbose))
		xlerror("can't load file",cvstring(argv[i]));
#endif /* MACINTOSH */
      }
    }

    /* target for restore */
#ifdef SAVERESTORE
    state.transcript = transcript; state.verbose = verbose; state.i = i;
    sts = XL_SETJMP(top_level);
    transcript = state.transcript; verbose = state.verbose; i = state.i;
    if (sts)
	xlbegin(&cntxt, CF_TOPLEVEL|CF_CLEANUP|CF_BRKLEVEL, s_true);
#endif

    /* main command processing loop */
    for (;;) {

	/* setup the error return */
	if (XL_SETJMP(cntxt.c_jmpbuf)) {
	    setvalue(s_evalhook,NIL);
	    setvalue(s_applyhook,NIL);
	    xltrcindent = 0;
	    xldebug = 0;
	    osreset();   /* L. Tierney */
	    xlflush();
	}

#ifdef STSZ
	stackwarn = FALSE;
#endif

	if (boundp(s_toplevelloop)) {
	  FRAMEP newfp;

	  newfp = xlsp;
	  pusharg(cvfixnum((FIXTYPE)(newfp - xlfp)));
	  pusharg(getvalue(s_toplevelloop));
	  pusharg(cvfixnum((FIXTYPE) 0));
	  xlfp = newfp;
	  xlapply(0);
	}
	else 
	  toplevelloop();
    }   /* never exit from here */
}

/* xtoplevelloop - lisp-callable top level loop */
/* Luke Tierney 9/93 */
LVAL xtoplevelloop(V)
{
  xllastarg();
  
  toplevelloop();
  return(NIL); /* doesn't return */
}

/* toplevelloop - the default command loop */
LOCAL VOID toplevelloop(V)
{
  LVAL expr;
#ifdef MULVALS
  int i;
#endif /* MULVALS */

  /* protect some pointers */
  xlsave1(expr);

  for(;;) {
    /* print a prompt */
#ifdef PACKAGES
    if (!redirectin) {
      LVAL pack = getvalue(s_package);
      if (pack != xluserpack && goodpackagep(pack)) {
	dbgputstr(getstring(xlpackagename(pack)));
      }
      dbgputstr("> ");
    }
#else
    if (!redirectin) dbgputstr("> ");
#endif /* PACKAGES */

    /* read an expression */
    if (!xlread(getvalue(s_stdin),&expr,FALSE,FALSE)) {
      /* clean up */
      wrapup();
      break;
    }

    /* save the input expression */
    xlrdsave(expr);

    /* evaluate the expression */
    expr = xleval(expr);

    /* save the result */
    xlevsave(expr);

    /* Show result on a new line -- TAA MOD to improve display */
    xlfreshline(getvalue(s_stdout));

    /* print it */
#ifdef MULVALS
    switch (xlnumresults) {
    case 0: break;
    case 1: stdprint(expr); break;
    default:
      {
	LVAL vals;
	xlsave1(vals);
	for (i = xlnumresults; i-- > 0; ) vals = cons(xlresults[i], vals);
	for (; consp(vals); vals = cdr(vals)) stdprint(car(vals));
	xlpop();
      }
    }
#else
    stdprint(expr);
#endif /* MULVALS */
  }
}

/* xlrdsave - save the last expression returned by the reader */
VOID xlrdsave P1C(LVAL, expr)
{
    setvalue(s_3plus,getvalue(s_2plus));
    setvalue(s_2plus,getvalue(s_1plus));
    setvalue(s_1plus,getvalue(s_minus));
    setvalue(s_minus,expr);
}

/* xlevsave - save the last expression returned by the evaluator */
VOID xlevsave P1C(LVAL, expr)
{
    setvalue(s_3star,getvalue(s_2star));
    setvalue(s_2star,getvalue(s_1star));
    setvalue(s_1star,expr);
}

/* xlfatal - print a fatal error message and exit */
VOID xlfatal P1C(char *, msg)
{
    xoserror(msg);
    wrapup();
}

/* do-exits - run user exit functions */
VOID do_exits(V)
{
  CONTEXT cntxt;

  xlbegin(&cntxt,CF_TOPLEVEL|CF_CLEANUP|CF_BRKLEVEL,s_true);
  XL_SETJMP(cntxt.c_jmpbuf);

  while (s_exit_functions != NULL && consp(getvalue(s_exit_functions))) {
    FRAMEP newfp;
    LVAL func = car(getvalue(s_exit_functions));
    setvalue(s_exit_functions, cdr(getvalue(s_exit_functions)));
    newfp = xlsp;
    pusharg(cvfixnum((FIXTYPE)(newfp - xlfp)));
    pusharg(func);
    pusharg(cvfixnum((FIXTYPE) 0));
    xlfp = newfp;
    xlapply(0);
  }
  xlend(&cntxt);
}

/* wrapup - clean up and exit to the operating system */
VOID wrapup(V)
{
  /* $putpatch.c$: "MODULE_XLISP_C_WRAPUP" */
  CONTEXT cntxt;

  do_exits();
  if (XL_SETJMP(cntxt.c_jmpbuf) == 0) {
    if (tfp != CLOSED)
      OSCLOSE(tfp);
    osfinish();
  }
  XL_LONGJMP(exit_xlisp, 1);
}

/* xresetsystem - reset system for user top-levels */
LVAL xresetsystem(V)
{
  osreset();   /* L. Tierney */
  xlflush();
  return(NIL);
}

/* new internal load function -- allows load to be redefined in workspace */
int xsload P3C(char *, name, int, vflag, int, pflag)
{
  if (fboundp(s_load)) {
    FRAMEP newfp;

    /* create the new call frame */
    newfp = xlsp;
    pusharg(cvfixnum((FIXTYPE)(newfp - xlfp)));
    pusharg(getfunction(s_load));
    pusharg(cvfixnum((FIXTYPE) 7));
    pusharg(cvstring(name));
    pusharg(k_print);
    pusharg(pflag ? s_true : NIL);
    pusharg(k_verbose);
    pusharg(vflag ? s_true : NIL);
    pusharg(k_nexist);
    pusharg(NIL);
    xlfp = newfp;

    /* return the result of applying the function */
    return null(xlapply(7)) ? FALSE : TRUE;
  }
  else
    return xlload(name, pflag, vflag);
}


syntax highlighted by Code2HTML, v. 0.9.1