[comp.sources.amiga] v02i082: stevie - vi editor clone, Part02/04

page@swan.ulowell.edu (Bob Page) (12/02/88)

Submitted-by: grwalter@watcgl.waterloo.edu
Posting-number: Volume 2, Issue 82
Archive-name: editors/stevie.2

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	keymap.h
#	linefunc.c
#	macros.h
#	main.c
#	mark.c
#	misccmds.c
#	param.c
#	param.h
#	raw.c
# This archive created: Thu Dec  1 20:37:28 1988
cat << \SHAR_EOF > keymap.h
/*
 * 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 
 */

/*
 * Keycode definitions for special keys 
 *
 * On systems that have any of these keys, the routine 'inchar' in the
 * machine-dependent code should return one of the codes here. 
 */

#define	K_CGRAVE	0x1e	/* control grave accent */

#define	K_HELP		0x80
#define	K_UNDO		0x81
#define	K_INSERT	0x82
#define	K_HOME		0x83
#define	K_UARROW	0x84
#define	K_DARROW	0x85
#define	K_LARROW	0x86
#define	K_RARROW	0x87
#define	K_SUARROW	0x88
#define	K_SDARROW	0x89
#define	K_SLARROW	0x8a
#define	K_SRARROW	0x8b

#define	K_F1		0x8c	/* function keys */
#define	K_F2		0x8d
#define	K_F3		0x8e
#define	K_F4		0x8f
#define	K_F5		0x90
#define	K_F6		0x91
#define	K_F7		0x92
#define	K_F8		0x93
#define	K_F9		0x94
#define	K_F10		0x95

#define	K_SF1		0x96	/* shifted function keys */
#define	K_SF2		0x97
#define	K_SF3		0x98
#define	K_SF4		0x99
#define	K_SF5		0x9a
#define	K_SF6		0x9b
#define	K_SF7		0x9c
#define	K_SF8		0x9d
#define	K_SF9		0x9e
#define	K_SF10		0x9f
SHAR_EOF
cat << \SHAR_EOF > linefunc.c
/*
 * 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"

/*
 * nextline(curr) 
 *
 * Return a pointer to the beginning of the next line after the one referenced
 * by 'curr'. Return NULL if there is no next line (at EOF). 
 */

LPTR           *
nextline(curr)
    LPTR           *curr;
{
    static LPTR     next;

    if (curr != NULL) {
	if (curr->linep->next != Fileend->linep) {
	    next.index = 0;
	    next.linep = curr->linep->next;
	    return &next;
	}
    }
    return (LPTR *) NULL;
}

/*
 * prevline(curr) 
 *
 * Return a pointer to the beginning of the line before the one referenced by
 * 'curr'. Return NULL if there is no prior line. 
 */

LPTR           *
prevline(curr)
    LPTR           *curr;
{
    static LPTR     prev;

    if (curr != NULL) {
	if (curr->linep->prev != NULL) {
	    prev.index = 0;
	    prev.linep = curr->linep->prev;
	    return &prev;
	}
    }
    return (LPTR *) NULL;
}

/*
 * coladvance(p,col) 
 *
 * Try to advance to the specified column, starting at p. 
 */

LPTR           *
coladvance(p, col)
    LPTR           *p;
    int             col;
{
    static LPTR     lp;
    int             c, in;

    lp.linep = p->linep;
    lp.index = p->index;

    /* If we're on a blank ('\n' only) line, we can't do anything */
    if (lp.linep->s[lp.index] == '\0')
	return &lp;
    /* try to advance to the specified column */
    for (c = 0; col-- > 0; c++) {
	/* Count a tab for what it's worth (if list mode not on) */
	if (gchar(&lp) == TAB && !P(P_LS)) {
	    in = ((P(P_TS) - 1) - c % P(P_TS));
	    col -= in;
	    c += in;
	}
	/* Don't go past the end of */
	/* the file or the line. */
	if (inc(&lp)) {
	    dec(&lp);
	    break;
	}
    }
    return &lp;
}
SHAR_EOF
cat << \SHAR_EOF > macros.h
/*
 * 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 
 */

/*
 * gchar(lp) - get the character at position "lp" 
 */
#define gchar(lp) ((lp)->linep->s[(lp)->index])

/*
 * pchar(lp, c) - put character 'c' at position 'lp' 
 */
#define pchar(lp, c) ((lp)->linep->s[(lp)->index] = (c))

/*
 * pswap(a, b) - swap two position pointers 
 */
#define pswap(a, b) { LPTR pswaptmp; pswaptmp = a; a = b; b = pswaptmp; }

/*
 * Position comparisons 
 */
#define lt(a, b) ((LINEOF(a) != LINEOF(b)) \
                   ? (LINEOF(a) < LINEOF(b)) : ((a)->index < (b)->index))

#define ltoreq(a, b) ((LINEOF(a) != LINEOF(b)) \
                   ? (LINEOF(a) < LINEOF(b)) : ((a)->index <= (b)->index))

#define gt(a, b) ((LINEOF(a) != LINEOF(b)) \
                   ? (LINEOF(a) > LINEOF(b)) : ((a)->index > (b)->index))

#define gtoreq(a, b) ((LINEOF(a) != LINEOF(b)) \
                   ? (LINEOF(a) > LINEOF(b)) : ((a)->index >= (b)->index))

#define equal(a, b) (((a)->linep == (b)->linep) && ((a)->index == (b)->index))

/*
 * anyinput
 *
 * Return non-zero if input is pending.
 */
#define anyinput() (Readbuffptr != NULL)

/*
 * buf1line() - return TRUE if there is only one line in file buffer
 */
#define buf1line() (Filemem->linep->next == Fileend->linep)

/*
 * bufempty() - return TRUE if the file buffer is empty 
 */
#define bufempty() (buf1line() && Filemem->linep->s[0] == NUL)

/*
 * lineempty() - return TRUE if the line is empty 
 */
#define lineempty(p) ((p)->linep->s[0] == NUL)

/*
 * startofline() - return TRUE if the given position is at start of line 
 */
#define startofline(p) ((p)->index == 0)

/*
 * endofline() - return TRUE if the given position is at end of line 
 *
 * This routine will probably never be called with a position resting on the NUL
 * byte, but handle it correctly in case it happens. 
 */
#define endofline(p) \
     ((p)->linep->s[(p)->index] == NUL || (p)->linep->s[(p)->index + 1] == NUL)

/*
 * RowNumber() - return the row number (if no UndoInProgress)
 */
#define RowNumber(p) (UndoInProgress ? 0 : cntllines(Filemem, (p)))
SHAR_EOF
cat << \SHAR_EOF > main.c
/*
 * 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. */

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

LPTR           *Filemem;	/* The contents of the file, as a single
				 * array. */

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 *) malloc((unsigned) sizeof(LPTR))) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Fileend = (LPTR *) malloc((unsigned) sizeof(LPTR))) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Topchar = (LPTR *) malloc((unsigned) sizeof(LPTR))) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Botchar = (LPTR *) malloc((unsigned) sizeof(LPTR))) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Curschar = (LPTR *) malloc((unsigned) sizeof(LPTR))) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Insstart = (LPTR *) malloc((unsigned) sizeof(LPTR))) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    /*
     * Allocate space for the many buffers 
     */
    if ((IObuff = malloc(IOSIZE)) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Insbuff = malloc(INSERT_SIZE)) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Readbuff = malloc(READSIZE)) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Redobuff = malloc(REDO_UNDO_SIZE)) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Undobuff = malloc(REDO_UNDO_SIZE)) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((UndoUndobuff = malloc(REDO_UNDO_SIZE)) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    if ((Yankbuff = malloc(YANKSIZE)) == NULL) {
	fprintf(stderr, "Can't allocate data structures\n");
	windexit(0);
    }
    screenalloc();
    filealloc();		/* Initialize Filemem & 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();

    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;
	} else
	    emsg("Couldn't stuffReadbuff() - should never happen");
    } else if ((strlen(Readbuff) + (strlen(s) + 1)) < READSIZE)
	strcat(Readbuff, s);
    else
	emsg("Couldn't stuffReadbuff() - should never happen");
}

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

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

/* OPTRESULT */
char
vgetc()
{
    if (Readbuffptr != NULL) {
	char            nextc = *Readbuffptr++;
	if (*Readbuffptr == NUL) {
	    *Readbuff = NUL;
	    Readbuffptr = NULL;
	}
	return (nextc);
    }
    return (inchar());
}

char
vpeekc()
{
    if (Readbuffptr != NULL)
	return (*Readbuffptr);
    return (NUL);
}
SHAR_EOF
cat << \SHAR_EOF > mark.c
/*
 * 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"

#ifdef	MEGAMAX
overlay "mark"
#endif

/*
 * This file contains routines to maintain and manipulate marks. 
 */

#define	NMARKS	10		/* max. # of marks that can be saved */

struct mark {
    char            name;
    LPTR            pos;
};

static struct mark mlist[NMARKS];
static struct mark pcmark;	/* previous context mark */
static bool_t   pcvalid = FALSE;/* true if pcmark is valid */

/*
 * setmark(c) - set mark 'c' at current cursor position 
 *
 * Returns TRUE on success, FALSE if no room for mark or bad name given. 
 */
bool_t
setmark(c)
    char            c;
{
    int             i;

    if (!isalpha(c))
	return FALSE;

    /*
     * If there is already a mark of this name, then just use the existing
     * mark entry. 
     */
    for (i = 0; i < NMARKS; i++) {
	if (mlist[i].name == c) {
	    mlist[i].pos = *Curschar;
	    return TRUE;
	}
    }

    /*
     * There wasn't a mark of the given name, so find a free slot 
     */
    for (i = 0; i < NMARKS; i++) {
	if (mlist[i].name == NUL) {	/* got a free one */
	    mlist[i].name = c;
	    mlist[i].pos = *Curschar;
	    return TRUE;
	}
    }
    return FALSE;
}

/*
 * setpcmark() - set the previous context mark to the current position 
 */
void
setpcmark()
{
    pcmark.pos = *Curschar;
    pcvalid = TRUE;
}

/*
 * getmark(c) - find mark for char 'c' 
 *
 * Return pointer to LPTR or NULL if no such mark. 
 */
LPTR           *
getmark(c)
    char            c;
{
    int             i;

    if (c == '\'' || c == '`')	/* previous context mark */
	return pcvalid ? &(pcmark.pos) : (LPTR *) NULL;

    for (i = 0; i < NMARKS; i++) {
	if (mlist[i].name == c)
	    return &(mlist[i].pos);
    }
    return (LPTR *) NULL;
}

/*
 * clrall() - clear all marks 
 *
 * Used mainly when trashing the entire buffer during ":e" type commands 
 */
void
clrall()
{
    int             i;

    for (i = 0; i < NMARKS; i++)
	mlist[i].name = NUL;
    pcvalid = FALSE;
}

/*
 * clrmark(line) - clear any marks for 'line' 
 *
 * Used any time a line is deleted so we don't have marks pointing to
 * non-existent lines. 
 */
void
clrmark(line)
    LINE           *line;
{
    int             i;

    for (i = 0; i < NMARKS; i++) {
	if (mlist[i].pos.linep == line)
	    mlist[i].name = NUL;
    }
    if (pcvalid && (pcmark.pos.linep == line))
	pcvalid = FALSE;
}
SHAR_EOF
cat << \SHAR_EOF > misccmds.c
/*
 * 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"

static          bool_t
openfwd(), openbwd();

extern int      did_ai;

/*
 * opencmd 
 *
 * Add a blank line above or below the current line. 
 */

bool_t
opencmd(dir, can_ai)
    int             dir;
    int             can_ai;	/* if true, consider auto-indent */
{
    if (dir == FORWARD)
	return (openfwd(can_ai));
    else
	return (openbwd(can_ai));
}

static          bool_t
openfwd(can_ai)
    int             can_ai;
{
    LINE           *l;
    LPTR           *next;
    char           *s;		/* string to be moved to new line, if any */

    /*
     * If we're in insert mode, we need to move the remainder of the current
     * line onto the new line. Otherwise the new line is left blank. 
     */
    if (State == INSERT)
	s = &Curschar->linep->s[Curschar->index];
    else
	s = "";

    if ((next = nextline(Curschar)) == NULL)	/* open on last line */
	next = Fileend;

    /*
     * By asking for as much space as the prior line had we make sure that
     * we'll have enough space for any auto-indenting. 
     */
    if ((l = newline(strlen(Curschar->linep->s) + SLOP)) == NULL) {
	emsg("out of memory");
	beep();
	sleep(2);
	return (FALSE);
    }
    if (*s != NUL)
	strcpy(l->s, s);	/* copy string to new line */
#ifdef AUTO_INDENT
    else if (can_ai && P(P_AI)) {
	/*
	 * Auto-indent removed due to buggy implementation... 
	 */
	did_ai = TRUE;
    }
#endif

    if (State == INSERT)	/* truncate current line at cursor */
	*s = NUL;

    Curschar->linep->next = l;	/* link neighbors to new line */
    next->linep->prev = l;

    l->prev = Curschar->linep;	/* link new line to neighbors */
    l->next = next->linep;

    if (next == Fileend)	/* new line at end */
	l->num = Curschar->linep->num + LINEINC;

    else if ((l->prev->num) + 1 == l->next->num)	/* no gap, renumber */
	renum();

    else {			/* stick it in the middle */
	long            lnum;
	lnum = (l->prev->num + l->next->num) / 2;
	l->num = lnum;
    }

    *Curschar = *nextline(Curschar);	/* cursor moves down */
    Curschar->index = 0;

    s_ins(Cursrow + 1, 1);	/* insert a physical line */
    updateNextscreen();		/* because Botchar is now invalid */
    cursupdate();		/* update Cursrow before insert */

    return (TRUE);
}

static          bool_t
openbwd(can_ai)
    int             can_ai;
{
    LINE           *l;
    LPTR           *prev;

    prev = prevline(Curschar);

    if ((l = newline(strlen(Curschar->linep->s) + SLOP)) == NULL) {
	emsg("out of memory");
	beep();
	sleep(2);
	return (FALSE);
    }
    Curschar->linep->prev = l;	/* link neighbors to new line */
    if (prev != NULL)
	prev->linep->next = l;

    l->next = Curschar->linep;	/* link new line to neighbors */
    if (prev != NULL)
	l->prev = prev->linep;

#ifdef AUTO_INDENT
    if (can_ai && P(P_AI)) {
	/*
	 * Auto-indent removed due to buggy implementation... 
	 */
	did_ai = TRUE;
    }
#endif

    *Curschar = *prevline(Curschar);	/* cursor moves up */
    Curschar->index = 0;

    if (prev == NULL)		/* new start of file */
	Filemem->linep = l;

    renum();			/* keep it simple - we don't do this often */

    cursupdate();		/* update Cursrow before insert */
    if (Cursrow != 0)
	s_ins(Cursrow, 1);	/* insert a physical line */
    updateNextscreen();

    return (TRUE);
}

int
cntllines(pbegin, pend)
    LPTR           *pbegin, *pend;
{
    register LINE  *lp;
    register int    lnum = 1;

    for (lp = pbegin->linep; lp != pend->linep; lp = lp->next)
	lnum++;

    return (lnum);
}

/*
 * plines(p) - return the number of physical screen lines taken by line 'p' 
 */
int
plines(p)
    LPTR           *p;
{
    int             col;
    char           *s;

    if (p == NULL) {
	fprintf(stderr, "plines(p) : p == NULL ????");
	return (0);
    }
    s = p->linep->s;

    if (*s == NUL)		/* empty line */
	return 1;

    /*
     * If list mode is on, then the '$' at the end of the line takes up one
     * extra column. 
     */
    col = P(P_LS) ? 1 : 0;

    for (; *s != NUL; s++) {
	if (*s == TAB && !P(P_LS))
	    col += P(P_TS) - (col % P(P_TS));
	else
	    col += chars[*s].ch_size;
    }
    return ((col + (Columns - 1)) / Columns);
}

void
fileinfo()
{
    long            l1, l2;
    char            buf[MAX_COLUMNS + 1];

    if (bufempty()) {
	msg("Buffer Empty");
	return;
    }
    l1 = cntllines(Filemem, Curschar);
    l2 = cntllines(Filemem, Fileend) - 1;
    sprintf(buf, "\"%s\"%s line %ld of %ld -- %ld %% --",
	    (Filename != NULL) ? Filename : "No File",
	    Changed ? " [Modified]" : "",
	    l1, l2, (l1 * 100) / l2);
    msg(buf);
}

/*
 * gotoline(n) - return a pointer to line 'n' 
 *
 * Returns a pointer to the last line of the file if n is zero, or beyond the
 * end of the file. 
 */
LPTR           *
gotoline(n)
    int             n;
{
    static LPTR     l;

    l.index = 0;

    if (n == 0)
	l = *prevline(Fileend);
    else {
	LPTR           *p;

	for (l = *Filemem; --n > 0; l = *p)
	    if ((p = nextline(&l)) == NULL)
		break;
    }
    return &l;
}

void
inschar(c)
    char            c;
{
    char           *p;
    char           *pend;

    /* make room for the new char. */
    if (!canincrease(1))
	return;

    p = &Curschar->linep->s[strlen(Curschar->linep->s) + 1];
    pend = &Curschar->linep->s[Curschar->index];

    for (; p > pend; p--)
	*p = *(p - 1);

    *p = c;

    if (RedrawingDisabled) {
	inc(Curschar);
	return;
    }
    /*
     * If we're in insert mode and showmatch mode is set, then check for
     * right parens and braces. If there isn't a match, then beep. If there
     * is a match AND it's on the screen, then flash to it briefly. If it
     * isn't on the screen, don't do anything. 
     */
    if (P(P_SM) && State == INSERT && (c == ')' || c == '}' || c == ']')) {
	LPTR           *lpos, csave;

	if ((lpos = showmatch()) == NULL)	/* no match, so beep */
	    beep();
	else if (LINEOF(lpos) >= LINEOF(Topchar)) {
	    updateNextscreen();	/* show the new char first */
	    updateRealscreen();
	    csave = *Curschar;
	    *Curschar = *lpos;	/* move to matching char */
	    cursupdate();
	    windgoto(Cursrow, Curscol);
	    delay();		/* brief pause */
	    *Curschar = csave;	/* restore cursor position */
	    cursupdate();
	}
    }
    inc(Curschar);
}

void
insstr(s)
    char           *s;
{
    char           *p, *endp;
    int             k, n = strlen(s);

    /* Move everything in the file over to make */
    /* room for the new string. */
    if (!canincrease(n))
	return;

    endp = &Curschar->linep->s[Curschar->index];
    p = Curschar->linep->s + strlen(Curschar->linep->s) + 1 + n;

    for (; p > endp; p--)
	*p = *(p - n);

    p = &Curschar->linep->s[Curschar->index];
    for (k = 0; k < n; k++) {
	*p++ = *s++;
	inc(Curschar);
    }
}

bool_t
delchar(fixpos, undo)
    bool_t          fixpos;	/* if TRUE fix the cursor position when done */
    bool_t          undo;	/* if TRUE put char deleted into Undo buffer */
{
    int             i;

    /* Check for degenerate case; there's nothing in the file. */
    if (bufempty())
	return FALSE;

    if (lineempty(Curschar))	/* can't do anything */
	return FALSE;

    if (undo)
	AppendToUndobuff(mkstr(gchar(Curschar)));

    /* Delete the char. at Curschar by shifting everything in the line down. */
    for (i = Curschar->index + 1; i < Curschar->linep->size; i++)
	Curschar->linep->s[i - 1] = Curschar->linep->s[i];

    /*
     * If we just took off the last character of a non-blank line, we don't
     * want to end up positioned at the newline. 
     */
    if (fixpos) {
	if (gchar(Curschar) == NUL && Curschar->index > 0 && State != INSERT)
	    Curschar->index--;
    }
    return TRUE;
}

void
delline(nlines)
{
    LINE           *p, *q;
    int             doscreen = TRUE;	/* if true, update the screen */

    /*
     * There's no point in keeping the screen updated if we're deleting more
     * than a screen's worth of lines. 
     */
    if (nlines > (Rows - 1)) {
	doscreen = FALSE;
	s_del(Cursrow, Rows - 1);	/* flaky way to clear rest of screen */
    }
    while (nlines-- > 0) {

	if (bufempty())		/* nothing to delete */
	    break;

	if (buf1line()) {	/* just clear the line */
	    Curschar->linep->s[0] = NUL;
	    Curschar->index = 0;
	    break;
	}
	p = Curschar->linep->prev;
	q = Curschar->linep->next;

	if (p == NULL) {	/* first line of file so... */
	    Filemem->linep = q;	/* adjust start of file */
	    Topchar->linep = q;	/* and screen */
	} else
	    p->next = q;
	q->prev = p;

	clrmark(Curschar->linep);	/* clear marks for the line */

	/*
	 * Delete the correct number of physical lines on the screen 
	 */
	if (doscreen)
	    s_del(Cursrow, plines(Curschar));

	/*
	 * If deleting the top line on the screen, adjust Topchar 
	 */
	if (Topchar->linep == Curschar->linep)
	    Topchar->linep = q;

	free(Curschar->linep->s);
	free((char *) (Curschar->linep));

	Curschar->linep = q;
	Curschar->index = 0;	/* is this right? */

	/* If we delete the last line in the file, back up */
	if (Curschar->linep == Fileend->linep) {
	    Curschar->linep = Curschar->linep->prev;
	    /* and don't try to delete any more lines */
	    break;
	}
    }
}
SHAR_EOF
cat << \SHAR_EOF > param.c
/*
 * 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 
 */

/*
 * Code to handle user-settable parameters. This is all pretty much table-
 * driven. To add a new parameter, put it in the params array, and add a
 * macro for it in param.h. If it's a numeric parameter, add any necessary
 * bounds checks to doset(). String parameters aren't currently supported. 
 */

#include "stevie.h"

struct param    params[] = {

			    {"tabstop", "ts", 8, P_NUM},
			    {"scroll", "scroll", 12, P_NUM},
			    {"report", "report", 5, P_NUM},
			    {"lines", "lines", 25, P_NUM},

			    {"vbell", "vb", TRUE, P_BOOL},
			    {"showmatch", "sm", FALSE, P_BOOL},
			    {"wrapscan", "ws", TRUE, P_BOOL},
			    {"errorbells", "eb", FALSE, P_BOOL},
			    {"showmode", "mo", FALSE, P_BOOL},
			    {"backup", "bk", FALSE, P_BOOL},
			    {"return", "cr", TRUE, P_BOOL},
			    {"list", "list", FALSE, P_BOOL},
			    {"autoindent", "ai", FALSE, P_BOOL},

			    {"", "", 0, 0,}	/* end marker */
};

static void     showparms();

void
doset(arg, inter)
    char           *arg;	/* parameter string */
    bool_t          inter;	/* TRUE if called interactively */
{
    int             i;
    char           *s;
    bool_t          did_lines = FALSE;

    bool_t          state = TRUE;	/* new state of boolean parms. */

    if (arg == NULL) {
	showparms(FALSE);
	return;
    }
    if (strncmp(arg, "all", 3) == 0) {
	showparms(TRUE);
	return;
    }
    if (strncmp(arg, "no", 2) == 0) {
	state = FALSE;
	arg += 2;
    }
    for (i = 0; params[i].fullname[0] != NUL; i++) {
	s = params[i].fullname;
	if (strncmp(arg, s, strlen(s)) == 0)	/* matched full name */
	    break;
	s = params[i].shortname;
	if (strncmp(arg, s, strlen(s)) == 0)	/* matched short name */
	    break;
    }

    if (params[i].fullname[0] != NUL) {	/* found a match */
	if (params[i].flags & P_NUM) {
	    did_lines = (i == P_LI);
	    if (inter && (arg[strlen(s)] != '=' || state == FALSE))
		emsg("Invalid set of numeric parameter");
	    else {
		params[i].value = atoi(arg + strlen(s) + 1);
		params[i].flags |= P_CHANGED;
	    }
	} else {		/* boolean */
	    if (inter && (arg[strlen(s)] == '='))
		emsg("Invalid set of boolean parameter");
	    else {
		params[i].value = state;
		params[i].flags |= P_CHANGED;
	    }
	}
    } else {
	if (inter)
	    emsg("Unrecognized 'set' option");
    }

    /*
     * Update the screen in case we changed something like "tabstop" or
     * "list" that will change its appearance. 
     */
    if (inter)
	updateNextscreen();

    if (did_lines) {
	Rows = P(P_LI);
	screenalloc();		/* allocate new screen buffers */
	screenclear();
	updateNextscreen();
    }
    /*
     * Check the bounds for numeric parameters here 
     */
    if (P(P_TS) <= 0 || P(P_TS) > 32) {
	if (inter)
	    emsg("Invalid tab size specified");
	P(P_TS) = 8;
	return;
    }
    if (P(P_SS) <= 0 || P(P_SS) > Rows) {
	if (inter)
	    emsg("Invalid scroll size specified");
	P(P_SS) = 12;
	return;
    }
    /*
     * Check for another argument, and call doset() recursively, if found. If
     * any argument results in an error, no further parameters are processed. 
     */
    while (*arg != ' ' && *arg != '\t') {	/* skip to next white space */
	if (*arg == NUL)
	    return;		/* end of parameter list */
	arg++;
    }
    while (*arg == ' ' || *arg == '\t')	/* skip to next non-white */
	arg++;

    if (*arg)
	doset(arg, TRUE);	/* recurse on next parameter, if present */
}

static void
showparms(all)
    bool_t          all;	/* show ALL parameters */
{
    struct param   *p;
    char            buf[64];

    gotocmdline(YES, NUL);
    outstr("Parameters:\r\n");

    for (p = &params[0]; p->fullname[0] != NUL; p++) {
	if (!all && ((p->flags & P_CHANGED) == 0))
	    continue;
	if (p->flags & P_BOOL)
	    sprintf(buf, "\t%s%s\r\n",
		    (p->value ? "" : "no"), p->fullname);
	else
	    sprintf(buf, "\t%s=%d\r\n", p->fullname, p->value);

	outstr(buf);
    }
    wait_return();
}
SHAR_EOF
cat << \SHAR_EOF > param.h
/*
 * 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 
 */

/*
 * Settable parameters 
 */

struct param {
    char           *fullname;	/* full parameter name */
    char           *shortname;	/* permissible abbreviation */
    int             value;	/* parameter value */
    int             flags;
};

extern struct param params[];

/*
 * Flags 
 */
#define	P_BOOL		0x01	/* the parameter is boolean */
#define	P_NUM		0x02	/* the parameter is numeric */
#define	P_CHANGED	0x04	/* the parameter has been changed */

/*
 * The following are the indices in the params array for each parameter 
 */

/*
 * Numeric parameters 
 */
#define	P_TS		0	/* tab size */
#define	P_SS		1	/* scroll size */
#define	P_RP		2	/* report */
#define	P_LI		3	/* lines */

/*
 * Boolean parameters 
 */
#define	P_VB		4	/* visual bell */
#define	P_SM		5	/* showmatch */
#define	P_WS		6	/* wrap scan */
#define	P_EB		7	/* error bells */
#define	P_MO		8	/* show mode */
#define	P_BK		9	/* make backups when writing out files */
#define	P_CR		10	/* use cr-lf to terminate lines on writes */
#define	P_LS		11	/* show tabs and newlines graphically */

#define	P_AI		12	/* auto-indent */

/*
 * Macro to get the value of a parameter 
 */
#define	P(n)	(params[n].value)
SHAR_EOF
cat << \SHAR_EOF > raw.c
/*
 * raw.c 
 *
 * This is a routine for setting a given stream to raw or cooked mode on the
 * Amiga . This is useful when you are using Lattice C to produce programs
 * that want to read single characters with the "getch()" or "fgetc" call. 
 *
 * Written : 18-Jun-87 By Chuck McManis.
 */
#include <exec/types.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <stdio.h>
#include <ios1.h>
#include <error.h>

extern int      errno;		/* The error variable */

/*
 * Function raw() - Convert the specified file pointer to 'raw' mode. This
 * only works on TTY's and essentially keeps DOS from translating keys for
 * you, also (BIG WIN) it means getch() will return immediately rather than
 * wait for a return. You lose editing features though. 
 */
long
raw(fp)
    FILE           *fp;

{
    struct MsgPort *mp;		/* The File Handle message port */
    struct FileHandle *afh;
    struct UFB     *ufb;
    long            Arg[1], res;

    ufb = (struct UFB *) chkufb(fileno(fp));	/* Step one, get the file
						 * handle */
    afh = (struct FileHandle *) (ufb->ufbfh);

    if (!IsInteractive(afh)) {	/* Step two, check to see if it's a console */
	errno = ENOTTY;
	return (-1);
    }
    /* Step three, get it's message port. */
    mp = ((struct FileHandle *) (BADDR(afh)))->fh_Type;
    Arg[0] = -1L;
    res = SendPacket(mp, ACTION_SCREEN_MODE, Arg, 1);	/* Put it in RAW: mode */
    if (res == 0) {
	errno = ENXIO;
	return (-1);
    }
    return (0);
}

/*
 * Function - cooked() this function returns the designate file pointer to
 * it's normal, wait for a <CR> mode. This is exactly like raw() except that
 * it sends a 0 to the console to make it back into a CON: from a RAW: 
 */

long
cooked(fp)
    FILE           *fp;

{
    struct MsgPort *mp;		/* The File Handle message port */
    struct FileHandle *afh;
    struct UFB     *ufb;
    long            Arg[1], res;

    ufb = (struct UFB *) chkufb(fileno(fp));
    afh = (struct FileHandle *) (ufb->ufbfh);
    if (!IsInteractive(afh)) {
	errno = ENOTTY;
	return (-1);
    }
    mp = ((struct FileHandle *) (BADDR(afh)))->fh_Type;
    Arg[0] = 0;
    res = SendPacket(mp, ACTION_SCREEN_MODE, Arg, 1);
    if (res == 0) {
	errno = ENXIO;
	return (-1);
    }
    return (0);
}
SHAR_EOF
#	End of shell archive
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.