/*
 * STEVIE - Simply Try this Editor for VI Enthusiasts
 *
 * Code Contributions By : Tim Thompson           twitch!tjt
 *                         Tony Andrews           onecom!wldrdg!tony 
 *                         G. R. (Fred) Walter    watmath!watcgl!grwalter 
 */

#include "stevie.h"

int             Rows;		/* Number of Rows and Columns */
int             Columns;	/* in the current window. */

char           *Realscreen = NULL;	/* What's currently on the screen, a
					 * single array of size Rows*Columns. */
char           *Nextscreen = NULL;	/* What's to be put on the screen. */
int             NumLineSizes = 0;	/* # of active LineSizes */
LINE          **LinePointers = NULL;	/* Pointer to the line for LineSizes */
char           *LineSizes = NULL;	/* Size of a line (pline output) */

char           *Filename = NULL;/* Current file name */

LPtr           *Filemem;	/* The contents of the file, as a single
				 * array. */
LPtr           *Filetop;	/* Line 'above' the start of the file */

LPtr           *Fileend;	/* Pointer to the end of the file in Filemem.
				 * (It points to the byte AFTER the last
				 * byte.) */

LPtr           *Topchar;	/* Pointer to the byte in Filemem which is in
				 * the upper left corner of the screen. */

LPtr           *Botchar;	/* Pointer to the byte in Filemem which is
				 * just off the bottom of the screen. */

LPtr           *Curschar;	/* Pointer to byte in Filemem at which the
				 * cursor is currently placed. */

int             Curscol;	/* Current position of cursor (column) */
int             Cursrow;	/* Current position of cursor (row) */

int             Cursvcol;	/* Current virtual column, the column number
				 * of the file's actual line, as opposed to
				 * the column number we're at on the screen.
				 * This makes a difference on lines that span
				 * more than one screen line. */

int             Curswant = 0;	/* The column we'd like to be at. This is
				 * used try to stay in the same column
				 * through up/down cursor motions. */

bool_t          set_want_col;	/* If set, then update Curswant the next time
				 * through cursupdate() to the current
				 * virtual column. */

int             State = NORMAL;	/* This is the current state of the command
				 * interpreter. */

int             Prenum = 0;	/* The (optional) number before a command. */

LPtr           *Insstart;	/* This is where the latest insert/append
				 * mode started. */

bool_t          Changed = FALSE;/* Set to TRUE if something in the file has
				 * been changed and not written out. */

char           *IObuff;		/* file reads are done, one line at a time,
				 * into this buffer; as well as sprintf's */

char           *Insbuffptr = NULL;
char           *Insbuff;	/* Each insertion gets stuffed into this
				 * buffer. */

char           *Readbuffptr = NULL;
char           *Readbuff;	/* Having this buffer allows STEVIE to easily
				 * make itself do commands */

char           *Redobuffptr = NULL;
char           *Redobuff;	/* Each command should stuff characters into
				 * this buffer that will re-execute itself. */

bool_t          UndoInProgress = FALSE;	/* Set to TRUE if undo'ing */
char           *Undobuffptr = NULL;
char           *Undobuff;	/* Each command should stuff characters into
				 * this buffer that will undo its effects. */

char           *UndoUndobuffptr = NULL;
char           *UndoUndobuff;	/* Each command should stuff characters into
				 * this buffer that will undo its undo. */

char           *Yankbuffptr = NULL;
char           *Yankbuff;	/* Yank buffer */

char            last_command = NUL;	/* last command */
char            last_command_char = NUL;	/* character needed to undo
						 * last command */

bool_t          RedrawingDisabled = FALSE;	/* Set to TRUE if undo'ing or
						 * put'ing */

bool_t          MustRedrawLine = FALSE;	/* Set to TRUE if we must redraw the
					 * current line */
bool_t          MustRedrawScreen = TRUE;	/* Set to TRUE if we must
						 * redraw the screen */

char          **files;		/* list of input files */
int             numfiles;	/* number of input files */
int             curfile;	/* number of the current file */

static void
usage()
{
    fprintf(stderr, "usage: stevie [file ...]\n");
    fprintf(stderr, "       stevie -t tag\n");
    fprintf(stderr, "       stevie +[num] file\n");
    fprintf(stderr, "       stevie +/pat  file\n");
    exit(1);
}

#ifdef AMIGA
void
#else
int
#endif
main(argc, argv)
    int             argc;
    char          **argv;
{
    char           *initstr, *getenv();	/* init string from the environment */
    char           *tag = NULL;	/* tag from command line */
    char           *pat = NULL;	/* pattern from command line */
    int             line = -1;	/* line number from command line */

    int             atoi();

#ifdef AMIGA
/*
 * This won't be needed if you have a version of Lattice 4.01 without broken
 * break signal handling.
 */
    (void) signal(SIGINT, SIG_IGN);
#endif

    /*
     * Process the command line arguments. 
     */
    if (argc > 1) {
	switch (argv[1][0]) {

	  case '-':		/* -t tag */
	    if (argv[1][1] != 't')
		usage();

	    if (argv[2] == NULL)
		usage();

	    Filename = NULL;
	    tag = argv[2];
	    numfiles = 1;
	    break;

	  case '+':		/* +n or +/pat */
	    if (argv[1][1] == '/') {
		if (argv[2] == NULL)
		    usage();
		Filename = strsave(argv[2]);
		pat = &(argv[1][1]);
		numfiles = 1;

	    } else if (isdigit(argv[1][1]) || argv[1][1] == NUL) {
		if (argv[2] == NULL)
		    usage();
		Filename = strsave(argv[2]);
		numfiles = 1;

		line = (isdigit(argv[1][1])) ?
		    atoi(&(argv[1][1])) : 0;
	    } else
		usage();

	    break;

	  default:		/* must be a file name */
	    Filename = strsave(argv[1]);
	    files = &(argv[1]);
	    numfiles = argc - 1;
	    break;
	}
    } else {
	Filename = NULL;
	numfiles = 1;
    }
    curfile = 0;

    windinit();

    /*
     * Allocate LPtr structures for all the various position pointers 
     */
    if ((Filemem = (LPtr *) alloc((unsigned) sizeof(LPtr))) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Filetop = (LPtr *) alloc((unsigned) sizeof(LPtr))) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Fileend = (LPtr *) alloc((unsigned) sizeof(LPtr))) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Topchar = (LPtr *) alloc((unsigned) sizeof(LPtr))) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Botchar = (LPtr *) alloc((unsigned) sizeof(LPtr))) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Curschar = (LPtr *) alloc((unsigned) sizeof(LPtr))) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Insstart = (LPtr *) alloc((unsigned) sizeof(LPtr))) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    /*
     * Allocate space for the many buffers 
     */
    if ((IObuff = alloc(IOSIZE)) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Insbuff = alloc(INSERT_SIZE)) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Readbuff = alloc(READSIZE)) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Redobuff = alloc(REDO_UNDO_SIZE)) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Undobuff = alloc(REDO_UNDO_SIZE)) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((UndoUndobuff = alloc(REDO_UNDO_SIZE)) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Yankbuff = alloc(YANKSIZE)) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    screenalloc();
    filealloc();		/* Initialize Filemem, Filetop & Fileend */

    screenclear();

    if ((initstr = getenv("EXINIT")) != NULL) {
	char           *lp, buf[128];

	if ((lp = getenv("LINES")) != NULL) {
	    sprintf(buf, "%s lines=%s", initstr, lp);
	    readcmdline(':', buf);
	} else
	    readcmdline(':', initstr);
    }
    if (Filename != NULL) {
	if (readfile(Filename, Filemem, FALSE))
	    filemess("[New File]");
    } else
	msg("Empty Buffer");

    setpcmark();

    updateNextscreen(NOT_VALID);

    if (tag) {
	stuffReadbuff(":ta ");
	stuffReadbuff(tag);
	stuffReadbuff("\n");

    } else if (pat) {
	stuffReadbuff(pat);
	stuffReadbuff("\n");

    } else if (line >= 0) {
	if (line > 0)
	    stuffnumReadbuff(line);
	stuffReadbuff("G");
    }
    edit();

    windexit(0);
}

void
stuffReadbuff(s)
    char           *s;
{
    if (strlen(s) == 0)
	return;

    if (Readbuffptr == NULL) {
	if ((strlen(s) + 1) < READSIZE) {
	    strcpy(Readbuff, s);
	    Readbuffptr = Readbuff;
	    return;
	}
    } else if ((strlen(Readbuff) + (strlen(s) + 1)) < READSIZE) {
	strcat(Readbuff, s);
	return;
    }
    emsg("Couldn't stuffReadbuff() - clearing Readbuff\n");
    *Readbuff = NUL;
    Readbuffptr = NULL;
}

void
stuffnumReadbuff(n)
    int             n;
{
    char            buf[32];

    sprintf(buf, "%d", n);
    stuffReadbuff(buf);
}

/* OPTRESULT */
char
vgetc()
{
    int             c;

    /*
     * inchar() may map special keys by using stuffReadbuff(). If it does so,
     * it returns -1 so we know to loop here to get a real char. 
     */
    do {
	if (Readbuffptr != NULL) {
	    char            nextc = *Readbuffptr++;

	    if (*Readbuffptr == NUL) {
		*Readbuff = NUL;
		Readbuffptr = NULL;
	    }
	    return (nextc);
	}
	c = inchar();
    } while (c == -1);

    return (char) c;
}

char
vpeekc()
{
    if (Readbuffptr != NULL)
	return (*Readbuffptr);
    return (NUL);
}
