[comp.sources.amiga] v02i081: stevie - vi editor clone, Part01/04

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

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

This is a source release of the STEVIE editor, a public domain clone
of the UNIX editor 'vi'. The program was originally developed for the
Atari ST, but has been ported to UNIX, OS/2, BSD 4.3 and the Amiga.

#	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:
#	Makefile
#	README
#	alloc.c
#	amiga.c
#	amiga.h
#	ascii.h
#	charset.c
#	cmdline.c
#	dec.c
#	edit.c
#	fileio.c
#	help.c
#	inc.c
# This archive created: Thu Dec  1 20:37:03 1988
cat << \SHAR_EOF > Makefile
#
# Makefile for Lattice C on Amiga
#

.c.o:
	lc $(CFLAGS) $<

LIBS = /regexp/regexp.lib
LDFLAGS=

CFLAGS = -cu -dAUTO_INDENT
LINKFLAGS = NODEBUG

MACH=	amiga.o raw.o sendpacket.o

OBJ=	main.o edit.o linefunc.o normal.o cmdline.o charset.o \
	updateRealscreen.o misccmds.o help.o dec.o inc.o search.o alloc.o \
	updateNextscreen.o mark.o screen.o fileio.o param.o $(MACH)

all : stevie

stevie : $(OBJ)
	BLINK TO stevie FROM lib:c.o $(OBJ) \
	    LIBRARY $(LIBS) lib:lc.lib lib:amiga.lib \
	    $(LINKFLAGS)

clean :
	delete $(OBJ)
SHAR_EOF
cat << \SHAR_EOF > README
STEVIE Source Release

This is a source release of the STEVIE editor, a public domain clone
of the UNIX editor 'vi'. The program was originally developed for the
Atari ST, but has been ported to UNIX, OS/2, BSD 4.3 and the Amiga as well.

To compile STEVIE, you'll also need Henry Spencer's regular expression
library.

The files included in this release are:

README
	This file.

stevie.doc
	Reference manual for STEVIE. Assumes familiarity with vi.

source.doc
	Quick overview of the major data structures used.

porting.doc
	Tips for porting STEVIE to other systems.

makefile.os2
makefile.usg
makefile.tos
makefile.bsd
makefile.amiga.lattice
	Makefiles for OS/2, UNIX System V, Atari ST, BSD 4.3 UNIX and the
Amiga respectively.

amiga.c
amiga.h
bsd.c
bsd.h
os2.c
os2.h
unix.c
unix.h
tos.c
tos.h
	System-dependent routines for the same.

alloc.c ascii.h cmdline.c edit.c fileio.c help.c charset.c
keymap.h linefunc.c main.c mark.c misccmds.c normal.c param.c
param.h ptrfunc.c screen.c search.c stevie.h term.h macros.h

	C source and header files for STEVIE.

To compile STEVIE for one of the provided systems:

	1. Compile the regular expression library and install as
	   appropriate for your system.

	2. Edit the file 'stevie.h' to set the system defines as needed.

	3. Check the makefile for your system, and modify as needed.

	4. Compile.

Good Luck...

Tony Andrews            March  12, 1988
G. R. (Fred) Walter     August 14, 1988
SHAR_EOF
cat << \SHAR_EOF > alloc.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"

/*
 * This file contains various routines dealing with allocation and
 * deallocation of data structures. 
 */

char           *
alloc(size)
    unsigned        size;
{
    char           *p;		/* pointer to new storage space */

    p = malloc(size);
    if (p == (char *) NULL) {	/* if there is no more room... */
	emsg("alloc() is unable to find memory!");
    }
    return (p);
}

char           *
strsave(string)
    char           *string;
{
    return (strcpy(alloc((unsigned) (strlen(string) + 1)), string));
}

void
screenalloc()
{
    /*
     * If we're changing the size of the screen, free the old arrays 
     */
    if (Realscreen != NULL)
	free(Realscreen);
    if (Nextscreen != NULL)
	free(Nextscreen);

    Realscreen = malloc((unsigned) (Rows * Columns));
    Nextscreen = malloc((unsigned) (Rows * Columns));
}

/*
 * Allocate and initialize a new line structure with room for 'nchars'
 * characters. 
 */
LINE           *
newline(nchars)
    int             nchars;
{
    LINE           *l;

    l = (LINE *) alloc((unsigned) sizeof(LINE));
    if (l == NULL)
	return (LINE *) NULL;

    l->s = alloc((unsigned) nchars);	/* the line is empty */
    l->s[0] = NUL;
    l->size = nchars;

    l->prev = (LINE *) NULL;	/* should be initialized by caller */
    l->next = (LINE *) NULL;

    return l;
}

/*
 * filealloc() - construct an initial empty file buffer 
 */
void
filealloc()
{
    if ((Filemem->linep = newline(1)) == NULL) {
	fprintf(stderr, "Unable to allocate file memory!\n");
	getout(1);
    }
    if ((Fileend->linep = newline(1)) == NULL) {
	fprintf(stderr, "Unable to allocate file memory!\n");
	getout(1);
    }
    Filemem->index = 0;
    Fileend->index = 0;

    Filemem->linep->next = Fileend->linep;
    Fileend->linep->prev = Filemem->linep;

    *Curschar = *Filemem;
    *Topchar = *Filemem;

    Filemem->linep->num = 0;
    Fileend->linep->num = 0xffff;

    clrall();			/* clear all marks */
}

/*
 * freeall() - free the current buffer 
 *
 * Free all lines in the current buffer. 
 */
void
freeall()
{
    LINE           *lp, *xlp;

    for (lp = Filemem->linep; lp != NULL; lp = xlp) {
	if (lp->s != NULL)
	    free(lp->s);
	xlp = lp->next;
	free((char *) lp);
    }

    Curschar->linep = NULL;	/* clear pointers */
    Filemem->linep = NULL;
    Fileend->linep = NULL;
}

/*
 * canincrease(n) - returns TRUE if the current line can be increased 'n'
 * bytes 
 *
 * This routine returns immediately if the requested space is available. If not,
 * it attempts to allocate the space and adjust the data structures
 * accordingly. If everything fails it returns FALSE. 
 */
bool_t
canincrease(n)
    int             n;
{
    int             nsize;
    char           *s;		/* pointer to new space */

    nsize = strlen(Curschar->linep->s) + 1 + n;	/* size required */

    if (nsize <= Curschar->linep->size)
	return TRUE;

    /*
     * Need to allocate more space for the string. Allow some extra space on
     * the assumption that we may need it soon. This avoids excessive numbers
     * of calls to malloc while entering new text. 
     */
    s = alloc((unsigned) (nsize + SLOP));
    if (s == NULL) {
	emsg("Can't add anything, file is too big!");
	State = NORMAL;
	return FALSE;
    }
    Curschar->linep->size = nsize + SLOP;
    strcpy(s, Curschar->linep->s);
    free(Curschar->linep->s);
    Curschar->linep->s = s;

    return TRUE;
}
SHAR_EOF
cat << \SHAR_EOF > amiga.c
/*
 * Amiga system-dependent routines. 
 */

#include "stevie.h"

long            stdin_file_handle = 0;

int
GetCharacter()
{
    char            c;

    Read(stdin_file_handle, &c, sizeof(c));
    return ((int) c);
}

/*
 * getCSIsequence - get a CSI sequence
 *                - either cursor keys, help, functionkeys, or some
 *                  other sequence (if other, check window size)
 */
int
getCSIsequence()
{
    int             c;
    int             param1;
    int             param2;

    c = GetCharacter();
    if (isdigit(c)) {
	param1 = 0;
	while (isdigit(c)) {
	    param1 = param1 * 10 + c - '0';
	    c = GetCharacter();
	}
	if (c == '~')		/* function key */
	    return ((char) (K_F1 + param1));

	/* must be an event of some sort or a window bound report */
	if (c == ';') {
	    param2 = 0;
	    c = GetCharacter();
	    while (isdigit(c)) {
		param2 = param2 * 10 + c - '0';
		c = GetCharacter();
	    }
	    if (c == ';') {
		param1 = 0;
		c = GetCharacter();
		while (isdigit(c)) {
		    param1 = param1 * 10 + c - '0';
		    c = GetCharacter();
		}
		if (c == ';') {
		    param2 = 0;
		    c = GetCharacter();
		    while (isdigit(c)) {
			param2 = param2 * 10 + c - '0';
			c = GetCharacter();
		    }
		    if (c == ' ') {
			c = GetCharacter();
			if (c == 'r') {
			    if (param1 < 2)
				param1 = 2;
			    if (param2 < 5)
				param2 = 5;
			    if (Columns != param2 || Rows != param1) {
				Columns = param2;
				Rows = param1;
				P(P_LI) = Rows;
				return (-1);
			    } else
				return 0;
			}
		    }
		}
	    }
	}
	while ((c != '|') && (c != '~'))
	    c = GetCharacter();

	outstr("\033[0 q");
    fflush(stdout); /* flush out the window size request */
	return 0;
    }
    switch (c) {
      case 'A':		/* cursor up */
	return K_UARROW;
      case 'B':		/* cursor down */
	return K_DARROW;
      case 'C':		/* cursor right */
	return K_RARROW;
      case 'D':		/* cursor left */
	return K_LARROW;
      case 'T':		/* shift cursor up */
	return K_SUARROW;
      case 'S':		/* shift cursor down */
	return K_SDARROW;
      case ' ':		/* shift cursor left or right */
	c = GetCharacter();
	if (c == 'A')		/* shift cursor left */
	    return K_SLARROW;
	if (c == '@')		/* shift cursor right */
	    return K_SRARROW;
	break;
      case '?':		/* help */
	c = GetCharacter();
	if (c == '~')
	    return K_HELP;
	break;
    }
    return 0;			/* some other control code */
}

/*
 * inchar() - get a character from the keyboard 
 */
char
inchar()
{
    int             c;

    fflush(stdout); /* flush any pending output */

    for (;;) {
	c = GetCharacter();
	if (c != 0x9b)
	    break;
	c = getCSIsequence();
	if (c > 0)
	    break;
	if (c == -1) {
	    screenalloc();
	    screenclear();
	    updateNextscreen();
	    updateRealscreen();
	    msg("");
	    cursupdate();
	    windgoto(Cursrow, Curscol);
	    fflush(stdout);
	}
    }
    return (char) c;
}

void
outstr(s)
    char           *s;
{
    while (*s)
	outchar(*s++);
}

void
beep()
{
    if (RedrawingDisabled)
	return;

    outchar('\007');
}

void
sleep(n)
    int             n;
{
    void            Delay();

    if (n > 0)
	Delay(50L * n);
}

void
delay()
{
    void            Delay();

    Delay(25L);
}

void
windinit()
{
    stdin_file_handle = Input();
    if (!IsInteractive(stdin_file_handle)) {
	fprintf(stderr, "stdin is not interactive ?!?!?!?");
	exit(2);
    }

    Columns = 80;
    P(P_LI) = Rows = 24;
    if (raw(stdin) != 0)
	perror("raw");

    outstr("\033[12{");		/* window resize events activated */
    outstr("\033[0 q");		/* get window size */
    fflush(stdout);

    for (;;) {
	if (GetCharacter() == 0x9b)
	    if (getCSIsequence() == -1)
		break;
    }
}

void
windexit(r)
    int             r;
{
    outstr("\033[12}");		/* window resize events de-activated */
	fflush(stdout); 		/* flush any pending output */


    if (cooked(stdin) != 0)
	perror("cooked");
    exit(r);
}

void
windgoto(r, c)
    int             c;
    int             r;
{
    r++;
    c++;

    outstr("\033[");
    if (r >= 10)
	outchar((char) (r / 10 + '0'));
    outchar((char) (r % 10 + '0'));
    outchar(';');
    if (c >= 10)
	outchar((char) (c / 10 + '0'));
    outchar((char) (c % 10 + '0'));
    outchar('H');
}

FILE           *
fopenb(fname, mode)
    char           *fname;
    char           *mode;
{
    FILE           *fopen();
    char            modestr[16];

    sprintf(modestr, "%sb", mode);
    return fopen(fname, modestr);
}
SHAR_EOF
cat << \SHAR_EOF > amiga.h
/*
 * Amiga Machine-dependent routines. 
 */
char inchar();
#define outchar(c) putchar(c)
void outstr();
void beep();
void windinit(), windexit(), windgoto();
void delay();
void sleep();
SHAR_EOF
cat << \SHAR_EOF > ascii.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 
 */

/*
 * Definitions of various common control characters 
 */

#define	NUL		'\000'
#define	BS		'\010'
#define	TAB		'\011'
#define	NL		'\012'
#define	NL_STR		"\012"
#define	CR		'\015'
#define	ESC		'\033'
#define	ESC_STR		"\033"

#define	UNDO_SHIFTJ	'\333'
#define	UNDO_SHIFTJ_STR	"\333"

#define	CTRL(x)	((x) & 0x1f)
SHAR_EOF
cat << \SHAR_EOF > charset.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"

/*
 * This file shows how to display characters on the screen. This is approach
 * is something of an overkill. It's a remnant from the original code that
 * isn't worth messing with for now. TABS are special-cased depending on the
 * value of the "list" parameter. 
 */

struct charinfo chars[] = {
			    /* 0 */ 1, NUL,
			    /* 1 */ 2, "^A",
			    /* 2 */ 2, "^B",
			    /* 3 */ 2, "^C",
			    /* 4 */ 2, "^D",
			    /* 5 */ 2, "^E",
			    /* 6 */ 2, "^F",
			    /* 7 */ 2, "^G",
			    /* 8 */ 2, "^H",
			    /* 9 */ 2, "^I",
			    /* 10 */ 7, "[ERROR]",	/* shouldn't happen */
			    /* 11 */ 2, "^K",
			    /* 12 */ 2, "^L",
			    /* 13 */ 2, "^M",
			    /* 14 */ 2, "^N",
			    /* 15 */ 2, "^O",
			    /* 16 */ 2, "^P",
			    /* 17 */ 2, "^Q",
			    /* 18 */ 2, "^R",
			    /* 19 */ 2, "^S",
			    /* 20 */ 2, "^T",
			    /* 21 */ 2, "^U",
			    /* 22 */ 2, "^V",
			    /* 23 */ 2, "^W",
			    /* 24 */ 2, "^X",
			    /* 25 */ 2, "^Y",
			    /* 26 */ 2, "^Z",
			    /* 27 */ 2, "^[",
			    /* 28 */ 2, "^\\",
			    /* 29 */ 2, "^]",
			    /* 30 */ 2, "^^",
			    /* 31 */ 2, "^_",
			    /* 32 */ 1, " ",
			    /* 33 */ 1, "!",
			    /* 34 */ 1, "\"",
			    /* 35 */ 1, "#",
			    /* 36 */ 1, "$",
			    /* 37 */ 1, "%",
			    /* 38 */ 1, "&",
			    /* 39 */ 1, "'",
			    /* 40 */ 1, "(",
			    /* 41 */ 1, ")",
			    /* 42 */ 1, "*",
			    /* 43 */ 1, "+",
			    /* 44 */ 1, ",",
			    /* 45 */ 1, "-",
			    /* 46 */ 1, ".",
			    /* 47 */ 1, "/",
			    /* 48 */ 1, "0",
			    /* 49 */ 1, "1",
			    /* 50 */ 1, "2",
			    /* 51 */ 1, "3",
			    /* 52 */ 1, "4",
			    /* 53 */ 1, "5",
			    /* 54 */ 1, "6",
			    /* 55 */ 1, "7",
			    /* 56 */ 1, "8",
			    /* 57 */ 1, "9",
			    /* 58 */ 1, ":",
			    /* 59 */ 1, ";",
			    /* 60 */ 1, "<",
			    /* 61 */ 1, "=",
			    /* 62 */ 1, ">",
			    /* 63 */ 1, "?",
			    /* 64 */ 1, "@",
			    /* 65 */ 1, "A",
			    /* 66 */ 1, "B",
			    /* 67 */ 1, "C",
			    /* 68 */ 1, "D",
			    /* 69 */ 1, "E",
			    /* 70 */ 1, "F",
			    /* 71 */ 1, "G",
			    /* 72 */ 1, "H",
			    /* 73 */ 1, "I",
			    /* 74 */ 1, "J",
			    /* 75 */ 1, "K",
			    /* 76 */ 1, "L",
			    /* 77 */ 1, "M",
			    /* 78 */ 1, "N",
			    /* 79 */ 1, "O",
			    /* 80 */ 1, "P",
			    /* 81 */ 1, "Q",
			    /* 82 */ 1, "R",
			    /* 83 */ 1, "S",
			    /* 84 */ 1, "T",
			    /* 85 */ 1, "U",
			    /* 86 */ 1, "V",
			    /* 87 */ 1, "W",
			    /* 88 */ 1, "X",
			    /* 89 */ 1, "Y",
			    /* 90 */ 1, "Z",
			    /* 91 */ 1, "[",
			    /* 92 */ 1, "\\",
			    /* 93 */ 1, "]",
			    /* 94 */ 1, "^",
			    /* 95 */ 1, "_",
			    /* 96 */ 1, "`",
			    /* 97 */ 1, "a",
			    /* 98 */ 1, "b",
			    /* 99 */ 1, "c",
			    /* 100 */ 1, "d",
			    /* 101 */ 1, "e",
			    /* 102 */ 1, "f",
			    /* 103 */ 1, "g",
			    /* 104 */ 1, "h",
			    /* 105 */ 1, "i",
			    /* 106 */ 1, "j",
			    /* 107 */ 1, "k",
			    /* 108 */ 1, "l",
			    /* 109 */ 1, "m",
			    /* 110 */ 1, "n",
			    /* 111 */ 1, "o",
			    /* 112 */ 1, "p",
			    /* 113 */ 1, "q",
			    /* 114 */ 1, "r",
			    /* 115 */ 1, "s",
			    /* 116 */ 1, "t",
			    /* 117 */ 1, "u",
			    /* 118 */ 1, "v",
			    /* 119 */ 1, "w",
			    /* 120 */ 1, "x",
			    /* 121 */ 1, "y",
			    /* 122 */ 1, "z",
			    /* 123 */ 1, "{",
			    /* 124 */ 1, "|",
			    /* 125 */ 1, "}",
			    /* 126 */ 1, "~",
			    /* 127 */ 2, "^?",
			    /* 128 */ 5, "[128]",
			    /* 129 */ 5, "[129]",
			    /* 130 */ 5, "[130]",
			    /* 131 */ 5, "[131]",
			    /* 132 */ 5, "[132]",
			    /* 133 */ 5, "[133]",
			    /* 134 */ 5, "[134]",
			    /* 135 */ 5, "[135]",
			    /* 136 */ 5, "[136]",
			    /* 137 */ 5, "[137]",
			    /* 138 */ 5, "[138]",
			    /* 139 */ 5, "[139]",
			    /* 140 */ 5, "[140]",
			    /* 141 */ 5, "[141]",
			    /* 142 */ 5, "[142]",
			    /* 143 */ 5, "[143]",
			    /* 144 */ 5, "[144]",
			    /* 145 */ 5, "[145]",
			    /* 146 */ 5, "[146]",
			    /* 147 */ 5, "[147]",
			    /* 148 */ 5, "[148]",
			    /* 149 */ 5, "[149]",
			    /* 150 */ 5, "[150]",
			    /* 151 */ 5, "[151]",
			    /* 152 */ 5, "[152]",
			    /* 153 */ 5, "[153]",
			    /* 154 */ 5, "[154]",
			    /* 155 */ 5, "[155]",
			    /* 156 */ 5, "[156]",
			    /* 157 */ 5, "[157]",
			    /* 158 */ 5, "[158]",
			    /* 159 */ 5, "[159]",
#ifdef AMIGA
			    /* 160 */ 1, "\240",
			    /* 161 */ 1, "\241",
			    /* 162 */ 1, "\242",
			    /* 163 */ 1, "\243",
			    /* 164 */ 1, "\244",
			    /* 165 */ 1, "\245",
			    /* 166 */ 1, "\246",
			    /* 167 */ 1, "\247",
			    /* 168 */ 1, "\250",
			    /* 169 */ 1, "\251",
			    /* 170 */ 1, "\252",
			    /* 171 */ 1, "\253",
			    /* 172 */ 1, "\254",
			    /* 173 */ 1, "\255",
			    /* 174 */ 1, "\256",
			    /* 175 */ 1, "\257",
			    /* 176 */ 1, "\260",
			    /* 177 */ 1, "\261",
			    /* 178 */ 1, "\262",
			    /* 179 */ 1, "\263",
			    /* 180 */ 1, "\264",
			    /* 181 */ 1, "\265",
			    /* 182 */ 1, "\266",
			    /* 183 */ 1, "\267",
			    /* 184 */ 1, "\270",
			    /* 185 */ 1, "\271",
			    /* 186 */ 1, "\272",
			    /* 187 */ 1, "\273",
			    /* 188 */ 1, "\274",
			    /* 189 */ 1, "\275",
			    /* 190 */ 1, "\276",
			    /* 191 */ 1, "\277",
			    /* 192 */ 1, "\300",
			    /* 193 */ 1, "\301",
			    /* 194 */ 1, "\302",
			    /* 195 */ 1, "\303",
			    /* 196 */ 1, "\304",
			    /* 197 */ 1, "\305",
			    /* 198 */ 1, "\306",
			    /* 199 */ 1, "\307",
			    /* 200 */ 1, "\310",
			    /* 201 */ 1, "\311",
			    /* 202 */ 1, "\312",
			    /* 203 */ 1, "\313",
			    /* 204 */ 1, "\314",
			    /* 205 */ 1, "\315",
			    /* 206 */ 1, "\316",
			    /* 207 */ 1, "\317",
			    /* 208 */ 1, "\320",
			    /* 209 */ 1, "\321",
			    /* 210 */ 1, "\322",
			    /* 211 */ 1, "\323",
			    /* 212 */ 1, "\324",
			    /* 213 */ 1, "\325",
			    /* 214 */ 1, "\326",
			    /* 215 */ 1, "\327",
			    /* 216 */ 1, "\330",
			    /* 217 */ 1, "\331",
			    /* 218 */ 1, "\332",
			    /* 219 */ 1, "\333",
			    /* 220 */ 1, "\334",
			    /* 221 */ 1, "\335",
			    /* 222 */ 1, "\336",
			    /* 223 */ 1, "\337",
			    /* 224 */ 1, "\340",
			    /* 225 */ 1, "\341",
			    /* 226 */ 1, "\342",
			    /* 227 */ 1, "\343",
			    /* 228 */ 1, "\344",
			    /* 229 */ 1, "\345",
			    /* 230 */ 1, "\346",
			    /* 231 */ 1, "\347",
			    /* 232 */ 1, "\350",
			    /* 233 */ 1, "\351",
			    /* 234 */ 1, "\352",
			    /* 235 */ 1, "\353",
			    /* 236 */ 1, "\354",
			    /* 237 */ 1, "\355",
			    /* 238 */ 1, "\356",
			    /* 239 */ 1, "\357",
			    /* 240 */ 1, "\360",
			    /* 241 */ 1, "\361",
			    /* 242 */ 1, "\362",
			    /* 243 */ 1, "\363",
			    /* 244 */ 1, "\364",
			    /* 245 */ 1, "\365",
			    /* 246 */ 1, "\366",
			    /* 247 */ 1, "\367",
			    /* 248 */ 1, "\370",
			    /* 249 */ 1, "\371",
			    /* 250 */ 1, "\372",
			    /* 251 */ 1, "\373",
			    /* 252 */ 1, "\374",
			    /* 253 */ 1, "\375",
			    /* 254 */ 1, "\376",
			    /* 255 */ 1, "\377"
#else
			    /* 160 */ 5, "[160]",
			    /* 161 */ 5, "[161]",
			    /* 162 */ 5, "[162]",
			    /* 163 */ 5, "[163]",
			    /* 164 */ 5, "[164]",
			    /* 165 */ 5, "[165]",
			    /* 166 */ 5, "[166]",
			    /* 167 */ 5, "[167]",
			    /* 168 */ 5, "[168]",
			    /* 169 */ 5, "[169]",
			    /* 170 */ 5, "[170]",
			    /* 171 */ 5, "[171]",
			    /* 172 */ 5, "[172]",
			    /* 173 */ 5, "[173]",
			    /* 174 */ 5, "[174]",
			    /* 175 */ 5, "[175]",
			    /* 176 */ 5, "[176]",
			    /* 177 */ 5, "[177]",
			    /* 178 */ 5, "[178]",
			    /* 179 */ 5, "[179]",
			    /* 180 */ 5, "[180]",
			    /* 181 */ 5, "[181]",
			    /* 182 */ 5, "[182]",
			    /* 183 */ 5, "[183]",
			    /* 184 */ 5, "[184]",
			    /* 185 */ 5, "[185]",
			    /* 186 */ 5, "[186]",
			    /* 187 */ 5, "[187]",
			    /* 188 */ 5, "[188]",
			    /* 189 */ 5, "[189]",
			    /* 190 */ 5, "[190]",
			    /* 191 */ 5, "[191]",
			    /* 192 */ 5, "[192]",
			    /* 193 */ 5, "[193]",
			    /* 194 */ 5, "[194]",
			    /* 195 */ 5, "[195]",
			    /* 196 */ 5, "[196]",
			    /* 197 */ 5, "[197]",
			    /* 198 */ 5, "[198]",
			    /* 199 */ 5, "[199]",
			    /* 200 */ 5, "[200]",
			    /* 201 */ 5, "[201]",
			    /* 202 */ 5, "[202]",
			    /* 203 */ 5, "[203]",
			    /* 204 */ 5, "[204]",
			    /* 205 */ 5, "[205]",
			    /* 206 */ 5, "[206]",
			    /* 207 */ 5, "[207]",
			    /* 208 */ 5, "[208]",
			    /* 209 */ 5, "[209]",
			    /* 210 */ 5, "[210]",
			    /* 211 */ 5, "[211]",
			    /* 212 */ 5, "[212]",
			    /* 213 */ 5, "[213]",
			    /* 214 */ 5, "[214]",
			    /* 215 */ 5, "[215]",
			    /* 216 */ 5, "[216]",
			    /* 217 */ 5, "[217]",
			    /* 218 */ 5, "[218]",
			    /* 219 */ 5, "[219]",
			    /* 220 */ 5, "[220]",
			    /* 221 */ 5, "[221]",
			    /* 222 */ 5, "[222]",
			    /* 223 */ 5, "[223]",
			    /* 224 */ 5, "[224]",
			    /* 225 */ 5, "[225]",
			    /* 226 */ 5, "[226]",
			    /* 227 */ 5, "[227]",
			    /* 228 */ 5, "[228]",
			    /* 229 */ 5, "[229]",
			    /* 230 */ 5, "[230]",
			    /* 231 */ 5, "[231]",
			    /* 232 */ 5, "[232]",
			    /* 233 */ 5, "[233]",
			    /* 234 */ 5, "[234]",
			    /* 235 */ 5, "[235]",
			    /* 236 */ 5, "[236]",
			    /* 237 */ 5, "[237]",
			    /* 238 */ 5, "[238]",
			    /* 239 */ 5, "[239]",
			    /* 240 */ 5, "[240]",
			    /* 241 */ 5, "[241]",
			    /* 242 */ 5, "[242]",
			    /* 243 */ 5, "[243]",
			    /* 244 */ 5, "[244]",
			    /* 245 */ 5, "[245]",
			    /* 246 */ 5, "[246]",
			    /* 247 */ 5, "[247]",
			    /* 248 */ 5, "[248]",
			    /* 249 */ 5, "[249]",
			    /* 250 */ 5, "[250]",
			    /* 251 */ 5, "[251]",
			    /* 252 */ 5, "[252]",
			    /* 253 */ 5, "[253]",
			    /* 254 */ 5, "[254]",
			    /* 255 */ 5, "[255]"
#endif
};
SHAR_EOF
cat << \SHAR_EOF > cmdline.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 char    *altfile = NULL;	/* alternate file */
static int      altline;	/* line # in alternate file */

static char    *nowrtmsg = "No write since last change (use ! to override)";

extern char   **files;		/* used for "n" and "rew" */
extern int      curfile;
extern int      numfiles;

/*
 * The next two variables contain the bounds of any range given in a command.
 * If no range was given, both contain null line pointers. If only a single
 * line was given, u_pos will contain a null line pointer. 
 */
static LPTR     l_pos, u_pos;

static bool_t   interactive;	/* TRUE if we're reading a real command line */

static bool_t   doecmd();
static void
badcmd(), doshell(), get_range();
static LPTR    *get_line();

#ifdef	MEGAMAX
overlay "cmdline"
#endif

/*
 * readcmdline() - accept a command line starting with ':', '/', or '?' 
 *
 * readcmdline() accepts and processes colon commands and searches. If 'cmdline'
 * is null, the command line is read here. Otherwise, cmdline points to a
 * complete command line that should be used. This is used in main() to
 * handle initialization commands in the environment variable "EXINIT". 
 */
void
readcmdline(firstc, cmdline)
    char            firstc;	/* either ':', '/', or '?' */
    char           *cmdline;	/* optional command string */
{
    char            c;
    char            buff[CMDBUFFSIZE];
    char           *p, *q, *cmd, *arg;
    bool_t          literal_next_flag = FALSE;

    /*
     * Clear the range variables. 
     */
    l_pos.linep = (LINE *) NULL;
    u_pos.linep = (LINE *) NULL;

    interactive = (cmdline == NULL);

    if (interactive)
	gotocmdline(YES, firstc);
    p = buff;
    if (firstc != ':')
	*p++ = firstc;

    if (interactive) {
	/* collect the command string, handling '\b' and @ */
	for (;;) {
	    c = vgetc();
	    if (c == CTRL('V') && !literal_next_flag) {
		literal_next_flag = TRUE;
		outchar('^');
		continue;
	    }
	    if (c == '\n' || ((c == '\r' || c == ESC) && (!literal_next_flag)))
		break;
	    if ((c == '\b') && (!literal_next_flag)) {
		if (p > buff) {
		    p--;
		    /*
		     * this is gross, but it relies only on 'gotocmdline' 
		     */
		    gotocmdline(YES, firstc == ':' ? ':' : NUL);
		    for (q = buff; q < p; q++)
			outstr(chars[*q].ch_str);
		} else {
		    msg("");
		    return;	/* back to cmd mode */
		}
		continue;
	    }
	    if ((c == '@') && (!literal_next_flag)) {
		p = buff;
		gotocmdline(YES, firstc);
		continue;
	    }
	    if (literal_next_flag) {
		literal_next_flag = FALSE;
		outchar('\b');
	    }
	    outstr(chars[c].ch_str);
	    *p++ = c;
	}
	*p = '\0';
    } else {
	if (strlen(cmdline) > CMDBUFFSIZE - 2)	/* should really do something
						 * better here... */
	    return;
	strcpy(p, cmdline);
    }

    /* skip any initial white space */
    for (cmd = buff; *cmd != NUL && isspace(*cmd); cmd++);

    /* search commands */
    c = *cmd;
    if (c == '/' || c == '?') {
	cmd++;
	/* was the command was '//' or '??' (I.E. repeat last search) */
	if ((*cmd == c) || (*cmd == NUL)) {
	    if (c == '/')
		searchagain(FORWARD);
	    else
		searchagain(BACKWARD);
	    return;
	}
	/* If there is a matching '/' or '?' at the end, toss it */
	p = strchr(cmd, NUL);
	if (*(p - 1) == c && *(p - 2) != '\\')
	    *(p - 1) = NUL;
	dosearch((c == '/') ? FORWARD : BACKWARD, cmd);
	return;
    }
    /*
     * Parse a range, if present (and update the cmd pointer). 
     */
    get_range(&cmd);

    /* isolate the command and find any argument */
    for (p = cmd; *p != NUL && !isspace(*p); p++);
    if (*p == NUL)
	arg = NULL;
    else {
	*p = NUL;
	for (p++; *p != NUL && isspace(*p); p++);
	arg = p;
	if (*arg == NUL)
	    arg = NULL;
    }
    if (strcmp(cmd, "q!") == 0)
	getout(0);
    if (strcmp(cmd, "q") == 0) {
	if (Changed)
	    emsg(nowrtmsg);
	else
	    getout(0);
	return;
    }
    if (strcmp(cmd, "w") == 0) {
	if (arg == NULL) {
	    if (Filename != NULL) {
		writeit(Filename, &l_pos, &u_pos);
		UNCHANGED;
	    } else
		emsg("No output file");
	} else
	    writeit(arg, &l_pos, &u_pos);
	return;
    }
    if (strcmp(cmd, "wq") == 0) {
	if (Filename != NULL) {
	    if (writeit(Filename, (LPTR *) NULL, (LPTR *) NULL))
		getout(0);
	} else
	    emsg("No output file");
	return;
    }
    if (strcmp(cmd, "x") == 0) {
	if (Changed) {
	    if (Filename != NULL) {
		if (!writeit(Filename, (LPTR *) NULL, (LPTR *) NULL))
		    return;
	    } else {
		emsg("No output file");
		return;
	    }
	}
	getout(0);
    }
    if (strcmp(cmd, "f") == 0 && arg == NULL) {
	fileinfo();
	return;
    }
    if (*cmd == 'n') {
	if ((curfile + 1) < numfiles) {
	    /*
	     * stuff ":e[!] FILE\n" 
	     */
	    stuffReadbuff(":e");
	    if (cmd[1] == '!')
		stuffReadbuff("!");
	    stuffReadbuff(" ");
	    stuffReadbuff(files[++curfile]);
	    stuffReadbuff("\n");
	} else
	    emsg("No more files!");
	return;
    }
    if (*cmd == 'p') {
	if (curfile > 0) {
	    /*
	     * stuff ":e[!] FILE\n" 
	     */
	    stuffReadbuff(":e");
	    if (cmd[1] == '!')
		stuffReadbuff("!");
	    stuffReadbuff(" ");
	    stuffReadbuff(files[--curfile]);
	    stuffReadbuff("\n");
	} else
	    emsg("No more files!");
	return;
    }
    if (strncmp(cmd, "rew", 3) == 0) {
	if (numfiles <= 1)	/* nothing to rewind */
	    return;
	curfile = 0;
	/*
	 * stuff ":e[!] FILE\n" 
	 */
	stuffReadbuff(":e");
	if (cmd[3] == '!')
	    stuffReadbuff("!");
	stuffReadbuff(" ");
	stuffReadbuff(files[0]);
	stuffReadbuff("\n");
	return;
    }
    if (strcmp(cmd, "e") == 0 || strcmp(cmd, "e!") == 0) {
	doecmd(arg, cmd[1] == '!');
	return;
    }
    if (strcmp(cmd, "f") == 0) {
	Filename = strsave(arg);
	filemess("");
	return;
    }
    if (strcmp(cmd, "r") == 0 || strcmp(cmd, ".r") == 0) {
	if (arg == NULL) {
	    badcmd();
	    return;
	}
	if (readfile(arg, Curschar, 1)) {
	    emsg("Can't open file");
	    return;
	}
	updateNextscreen();
	CHANGED;
	return;
    }
    if (strcmp(cmd, ".=") == 0) {
	smsg("line %d", cntllines(Filemem, Curschar));
	return;
    }
    if (strcmp(cmd, "$=") == 0) {
	smsg("%d", cntllines(Filemem, Fileend) - 1);
	return;
    }
    if (strncmp(cmd, "ta", 2) == 0) {
	dotag(arg, cmd[2] == '!');
	return;
    }
    if (strcmp(cmd, "set") == 0) {
	doset(arg, interactive);
	return;
    }
    if (strcmp(cmd, "help") == 0) {
	if (help()) {
	    screenclear();
	    updateNextscreen();
	}
	return;
    }
    if (strcmp(cmd, "version") == 0) {
	extern char    *Version;

	msg(Version);
	return;
    }
    if (strcmp(cmd, "sh") == 0) {
	doshell();
	return;
    }
    /*
     * If we got a line, but no command, then go to the line. 
     */
    if (*cmd == NUL && l_pos.linep != NULL) {
	*Curschar = l_pos;
	cursupdate();
	return;
    }
    badcmd();
}

/*
 * get_range - parse a range specifier 
 *
 * Ranges are of the form: 
 *
 * addr[,addr] 
 *
 * where 'addr' is: 
 *
 * $  [+-NUM] 'x [+-NUM] (where x denotes a currently defined mark)
 * .  [+-NUM] NUM 
 *
 * The pointer *cp is updated to point to the first character following the
 * range spec. If an initial address is found, but no second, the upper bound
 * is equal to the lower. 
 */
static void
get_range(cp)
    char          **cp;
{
    LPTR           *l;
    char           *p;

    if ((l = get_line(cp)) == NULL)
	return;

    l_pos = *l;

    for (p = *cp; *p != NUL && isspace(*p); p++);

    *cp = p;

    if (*p != ',') {		/* is there another line spec ? */
	u_pos = l_pos;
	return;
    }
    *cp = ++p;

    if ((l = get_line(cp)) == NULL) {
	u_pos = l_pos;
	return;
    }
    u_pos = *l;
}

static LPTR    *
get_line(cp)
    char          **cp;
{
    static LPTR     pos;
    LPTR           *lp;
    char           *p, c;
    int             lnum;

    pos.index = 0;		/* shouldn't matter... check back later */

    p = *cp;
    /*
     * Determine the basic form, if present. 
     */
    switch (c = *p++) {

      case '$':
	pos.linep = Fileend->linep->prev;
	break;

      case '.':
	pos.linep = Curschar->linep;
	break;

      case '\'':
	if ((lp = getmark(*p++)) == NULL) {
	    emsg("Unknown mark");
	    return (LPTR *) NULL;
	}
	pos = *lp;
	break;

      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
	for (lnum = c - '0'; isdigit(*p); p++)
	    lnum = (lnum * 10) + (*p - '0');

	if (lnum == 0)
	    lnum = 1;

	pos = *gotoline(lnum);
	break;

      default:
	return (LPTR *) NULL;
    }

    while (*p != NUL && isspace(*p))
	p++;

    if (*p == '-' || *p == '+') {
	bool_t          neg = (*p++ == '-');

	for (lnum = 0; isdigit(*p); p++)
	    lnum = (lnum * 10) + (*p - '0');

	if (neg)
	    lnum = -lnum;

	pos = *gotoline(cntllines(Filemem, &pos) + lnum);
    }
    *cp = p;
    return &pos;
}

static void
badcmd()
{
    if (interactive)
	emsg("Unrecognized command");
}

/*
 * dotag(tag, force) - goto tag 
 */
void
dotag(tag, force)
    char           *tag;
    bool_t          force;
{
    FILE           *tp, *fopen();
    char            lbuf[LSIZE];
    char           *fname, *str;

    if ((tp = fopen("tags", "r")) == NULL) {
	emsg("Can't open tags file");
	return;
    }
    while (fgets(lbuf, LSIZE, tp) != NULL) {

	if ((fname = strchr(lbuf, TAB)) == NULL) {
	    emsg("Format error in tags file");
	    return;
	}
	*fname++ = '\0';
	if ((str = strchr(fname, TAB)) == NULL) {
	    emsg("Format error in tags file");
	    return;
	}
	*str++ = '\0';

	if (strcmp(lbuf, tag) == 0) {
	    if (doecmd(fname, force)) {
		stuffReadbuff(str);	/* str has \n at end */
		stuffReadbuff("\007");	/* CTRL('G') */
		fclose(tp);
		return;
	    }
	}
    }
    emsg("tag not found");
    fclose(tp);
}

static          bool_t
doecmd(arg, force)
    char           *arg;
    bool_t          force;
{
    int             line = 1;	/* line # to go to in new file */

    if (!force && Changed) {
	emsg(nowrtmsg);
	return FALSE;
    }
    if (arg != NULL) {
	/*
	 * First detect a ":e" on the current file. This is mainly for ":ta"
	 * commands where the destination is within the current file. 
	 */
	if (Filename != NULL && strcmp(arg, Filename) == 0) {
	    if (!Changed || (Changed && !force))
		return TRUE;
	}
	if (strcmp(arg, "#") == 0) {	/* alternate */
	    char           *s = Filename;

	    if (altfile == NULL) {
		emsg("No alternate file");
		return FALSE;
	    }
	    Filename = altfile;
	    altfile = s;
	    line = altline;
	    altline = cntllines(Filemem, Curschar);
	} else {
	    altfile = Filename;
	    altline = cntllines(Filemem, Curschar);
	    Filename = strsave(arg);
	}
    }
    if (Filename == NULL) {
	emsg("No filename");
	return FALSE;
    }
    /* clear mem and read file */
    freeall();
    filealloc();
    UNCHANGED;

    readfile(Filename, Filemem, 0);
    *Topchar = *Curschar;
    if (line != 1) {
	stuffnumReadbuff(line);
	stuffReadbuff("G");
    }
    setpcmark();
    updateNextscreen();
    return TRUE;
}

static void
doshell()
{
    char           *sh, *getenv();

    sh = getenv("SHELL");
    if (sh == NULL) {
	emsg("Shell variable not set");
	return;
    }
    gotocmdline(YES, NUL);

    if (system(sh) < 0) {
	emsg("Exec failed");
	return;
    }
    wait_return();
}

void
gotocmdline(clr, firstc)
    bool_t          clr;
    char            firstc;
{
    windgoto(Rows - 1, 0);
    if (clr)
	outstr(T_EL);		/* clear the bottom line */
    if (firstc)
	outchar(firstc);
}

/*
 * msg(s) - displays the string 's' on the status line 
 */
void
msg(s)
    char           *s;
{
    gotocmdline(YES, NUL);
    outstr(s);
#ifdef AMIGA
    fflush(stdout);
#endif
#ifdef BSD
    fflush(stdout);
#endif
}

/* VARARGS */
void
smsg(s, a1, a2, a3, a4, a5, a6, a7, a8, a9)
    char           *s;
    int             a1, a2, a3, a4, a5, a6, a7, a8, a9;
{
    char            sbuf[MAX_COLUMNS + 1];

    sprintf(sbuf, s, a1, a2, a3, a4, a5, a6, a7, a8, a9);
    msg(sbuf);
}

/*
 * emsg() - display an error message 
 *
 * Rings the bell, if appropriate, and calls message() to do the real work 
 */
void
emsg(s)
    char           *s;
{
    UndoInProgress = FALSE;
    RedrawingDisabled = FALSE;
    ResetBuffers();

    if (P(P_EB))
	beep();
    outstr(T_TI);
    msg(s);
    outstr(T_TP);
#ifdef AMIGA
    fflush(stdout);
#endif
#ifdef BSD
    fflush(stdout);
#endif
}

void
wait_return()
{
    char            c;

    outstr("Press RETURN to continue");
    do {
	c = vgetc();
    } while (c != '\r' && c != '\n');

    screenclear();
    updateNextscreen();
}
SHAR_EOF
cat << \SHAR_EOF > dec.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"

/*
 * dec(p) 
 *
 * Decrement the line pointer 'p' crossing line boundaries as necessary. Return
 * 1 when crossing a line, -1 when at start of file, 0 otherwise. 
 */
int
dec(lp)
    LPTR           *lp;
{
    if (lp->index > 0) {	/* still within line */
	lp->index--;
	return 0;
    }
    if (lp->linep->prev != NULL) {	/* there is a prior line */
	lp->linep = lp->linep->prev;
	lp->index = strlen(lp->linep->s);
	return 1;
    }
    return -1;			/* at start of file */
}
SHAR_EOF
cat << \SHAR_EOF > edit.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"

/*
 * This flag is used to make auto-indent work right on lines where only a
 * <RETURN> or <ESC> is typed. It is set when an auto-indent is done, and
 * reset when any other editting is done on the line. If an <ESC> or <RETURN>
 * is received, and did_ai is TRUE, the line is truncated. 
 */
bool_t          did_ai = FALSE;

void
edit()
{
    char            c;
    bool_t          literal_next_flag = FALSE;

    Prenum = 0;

    /* position the display and the cursor at the top of the file. */
    *Topchar = *Filemem;
    *Curschar = *Filemem;
    Cursrow = Curscol = 0;

    for (;;) {

	if (!RedrawingDisabled) {
	    cursupdate();	/* Figure out where the cursor is based on
				 * Curschar. */
	    if (MustRedrawLine)
		redrawline();
	    if (MustRedrawScreen)
		updateRealscreen();

	    windgoto(Cursrow, Curscol);
	}
	c = vgetc();

	if (State == NORMAL) {
	    /* We're in the normal (non-insert) mode. */

	    /* Pick up any leading digits and compute 'Prenum' */
	    if ((Prenum > 0 && isdigit(c)) || (isdigit(c) && c != '0')) {
		Prenum = Prenum * 10 + (c - '0');
		continue;
	    }
	    /* execute the command */
	    normal(c);
	    Prenum = 0;

	} else {
	    if (c == CTRL('V') && !literal_next_flag) {
		literal_next_flag = TRUE;
		outchar('^');
		continue;
	    }
	    if (literal_next_flag) {
		literal_next_flag = FALSE;
		outchar('\b');
		if (c != NL) {
		    did_ai = FALSE;
		    insertchar(c);
		    continue;
		}
	    }
	    switch (c) {	/* We're in insert mode */

	      case CR:
	      case NL:
		*Insbuffptr++ = NL;
		*Insbuffptr = NUL;
		if (!opencmd(FORWARD, TRUE))
		    goto doESCkey;	/* out of memory */

		if (!RedrawingDisabled)
		    windgoto(Cursrow, Curscol);
		break;

	      case ESC:	/* an escape ends input mode */
	doESCkey:
		set_want_col = TRUE;

		/* Don't end up on a '\n' if you can help it. */
		if (gchar(Curschar) == NUL && Curschar->index != 0)
		    dec(Curschar);

		/*
		 * The cursor should end up on the last inserted character.
		 * This is an attempt to match the real 'vi', but it may not
		 * be quite right yet. 
		 */
		if (Curschar->index != 0 && !endofline(Curschar))
		    dec(Curschar);

		State = NORMAL;
		msg("");

		if (!UndoInProgress) {
		    int             n;
		    char           *p;

		    if (last_command == 'o')
			AppendToUndobuff(UNDO_SHIFTJ_STR);

		    if (Insbuff != Insbuffptr) {
			if (last_command == 'O')
			    AppendToUndobuff("0");
			AppendToRedobuff(Insbuff);
			AppendToUndoUndobuff(Insbuff);
			n = 0;
			for (p = Insbuff; p < Insbuffptr; p++) {
			    if (*p == NL) {
				if (n) {
				    AppendNumberToUndobuff(n);
				    AppendToUndobuff("dl");
				    n = 0;
				}
				AppendToUndobuff(UNDO_SHIFTJ_STR);
			    } else
				n++;
			}
			if (n) {
			    AppendNumberToUndobuff(n);
			    AppendToUndobuff("dl");
			}
		    }
		    if (last_command == 'c') {
			AppendToUndobuff(mkstr(last_command_char));
			AppendToUndobuff(Yankbuff);
			AppendToUndobuff(ESC_STR);
		    }
		    AppendToRedobuff(ESC_STR);
		    AppendToUndoUndobuff(ESC_STR);
		    if (last_command == 'O')
			AppendToUndobuff(UNDO_SHIFTJ_STR);
		}
		break;

	      case CTRL('D'):
		/*
		 * Control-D is treated as a backspace in insert mode to make
		 * auto-indent easier. This isn't completely compatible with
		 * vi, but it's a lot easier than doing it exactly right, and
		 * the difference isn't very noticeable. 
		 */
	      case BS:
		/* can't backup past starting point */
		if (Curschar->linep == Insstart->linep &&
		    Curschar->index <= Insstart->index) {
		    beep();
		    break;
		}
		/* can't backup to a previous line */
		if (Curschar->linep != Insstart->linep &&
		    Curschar->index <= 0) {
		    beep();
		    break;
		}
		did_ai = FALSE;
		dec(Curschar);
		delchar(TRUE, FALSE);
		Insbuffptr--;
		*Insbuffptr = NUL;
		cursupdate();
		updateline();
		break;

	      default:
		did_ai = FALSE;
		insertchar(c);
		break;
	    }
	}
    }
}

/*
 * Special characters in this context are those that need processing other
 * than the simple insertion that can be performed here. This includes ESC
 * which terminates the insert, and CR/NL which need special processing to
 * open up a new line. This routine tries to optimize insertions performed by
 * the "redo" command, so it needs to know when it should stop and defer
 * processing to the "normal" mechanism. 
 */
#define	ISSPECIAL(c)	((c) == NL || (c) == CR || (c) == ESC)

void
insertchar(c)
    char            c;
{
    if (anyinput()) {		/* If there's any pending input, grab it all
				 * at once. */
	char           *p;

	p = Insbuffptr;
	*Insbuffptr++ = c;
	for (c = vpeekc(); !ISSPECIAL(c) && anyinput(); c = vpeekc()) {
	    c = vgetc();
	    *Insbuffptr++ = c;
	    /*
	     * The following kludge avoids overflowing the insert buffer. 
	     */
	    if (Insbuffptr + 10 >= &Insbuff[INSERT_SIZE]) {
		int             n;

		*Insbuffptr = NUL;
		insstr(p);

		Insbuffptr = Insbuff;
		p = Insbuffptr;

		emsg("Insert buffer overflow - buffers flushed");
		sleep(2);

		n = cntllines(Filemem, Curschar);
		AppendPositionToUndobuff(Curschar->index, n);
		AppendPositionToUndoUndobuff(Curschar->index, n);
		if (endofline(Curschar)) {
		    AppendToRedobuff("a");
		    AppendToUndoUndobuff("a");
		} else {
		    AppendToRedobuff("i");
		    AppendToUndoUndobuff("i");
		}
	    }
	}
	*Insbuffptr = NUL;
	insstr(p);
    } else {
	inschar(c);
	*Insbuffptr++ = c;

	/*
	 * The following kludge avoids overflowing the insert buffer. 
	 */
	if (Insbuffptr + 10 >= &Insbuff[INSERT_SIZE]) {
	    int             n;

	    Insbuffptr = Insbuff;

	    emsg("Insert buffer overflow - buffers flushed");
	    sleep(2);

	    n = cntllines(Filemem, Curschar);
	    AppendPositionToUndobuff(Curschar->index, n);
	    AppendPositionToUndoUndobuff(Curschar->index, n);
	    if (endofline(Curschar)) {
		AppendToRedobuff("a");
		AppendToUndoUndobuff("a");
	    } else {
		AppendToRedobuff("i");
		AppendToUndoUndobuff("i");
	    }
	}
	*Insbuffptr = NUL;
    }

    updateline();
}

void
getout(r)
    int             r;
{
    windgoto(Rows - 1, 0);
    putchar('\r');
    putchar('\n');
    windexit(r);
}

void
scrolldown(nlines)
    int             nlines;
{
    LPTR           *p;
    int             done = 0;	/* total # of physical lines done */

    /* Scroll up 'nlines' lines. */
    while (nlines--) {
	if ((p = prevline(Topchar)) == NULL)
	    break;
	done += plines(p);
	*Topchar = *p;
	if (Curschar->linep == Botchar->linep->prev)
	    *Curschar = *prevline(Curschar);
    }
    s_ins(0, done);
}

void
scrollup(nlines)
    int             nlines;
{
    LPTR           *p;
    int             done = 0;	/* total # of physical lines done */
    int             pl;		/* # of plines for the current line */

    /* Scroll down 'nlines' lines. */
    while (nlines--) {
	pl = plines(Topchar);
	if ((p = nextline(Topchar)) == NULL)
	    break;
	done += pl;
	if (Curschar->linep == Topchar->linep)
	    *Curschar = *p;
	*Topchar = *p;

    }
    s_del(0, done);
}

/*
 * oneright oneleft onedown oneup 
 *
 * Move one char {right,left,down,up}.  Return TRUE when sucessful, FALSE when
 * we hit a boundary (of a line, or the file). 
 */

bool_t
oneright()
{
    set_want_col = TRUE;

    switch (inc(Curschar)) {

      case 0:
	return TRUE;

      case 1:
	dec(Curschar);		/* crossed a line, so back up */
	/* FALLTHROUGH */
      case -1:
	return FALSE;
    }

    return FALSE;		/* PARANOIA: should never reach here */
}

bool_t
oneleft()
{
    set_want_col = TRUE;

    switch (dec(Curschar)) {

      case 0:
	return TRUE;

      case 1:
	inc(Curschar);		/* crossed a line, so back up */
	/* FALLTHROUGH */
      case -1:
	return FALSE;
    }

    return FALSE;		/* PARANOIA: should never reach here */
}

void
beginline(flag)
    bool_t          flag;
{
    while (oneleft());
    if (flag) {
	while (isspace(gchar(Curschar)) && oneright());
    }
    set_want_col = TRUE;
}

bool_t
oneup(n)
{
    LPTR            p, *np;
    int             k;

    p = *Curschar;
    for (k = 0; k < n; k++) {
	/* Look for the previous line */
	if ((np = prevline(&p)) == NULL) {
	    /* If we've at least backed up a little .. */
	    if (k > 0)
		break;		/* to update the cursor, etc. */
	    else
		return FALSE;
	}
	p = *np;
    }
    *Curschar = p;

    cursupdate();		/* make sure Topchar is valid */

    /* try to advance to the column we want to be at */
    *Curschar = *coladvance(&p, Curswant);
    return TRUE;
}

bool_t
onedown(n)
{
    LPTR            p, *np;
    int             k;

    p = *Curschar;
    for (k = 0; k < n; k++) {
	/* Look for the next line */
	if ((np = nextline(&p)) == NULL) {
	    if (k > 0)
		break;
	    else
		return FALSE;
	}
	p = *np;
    }

    cursupdate();		/* make sure Topchar is valid */

    /* try to advance to the column we want to be at */
    *Curschar = *coladvance(&p, Curswant);
    return TRUE;
}
SHAR_EOF
cat << \SHAR_EOF > fileio.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"

void
filemess(s)
    char           *s;
{
    sprintf(IObuff, "\"%s\" %s", ((Filename == NULL) ? "" : Filename), s);
    msg(IObuff);
}

void
renum()
{
    LPTR           *p;
    unsigned int    l = 0;

    for (p = Filemem; p != NULL; p = nextline(p), l += LINEINC)
	p->linep->num = l;

    Fileend->linep->num = 0xffff;
}

#ifdef	MEGAMAX
overlay "fileio"
#endif

bool_t
readfile(fname, fromp, nochangename)
    char           *fname;
    LPTR           *fromp;
    bool_t          nochangename;	/* if TRUE, don't change the Filename */
{
    FILE           *f, *fopen();
    LINE           *curr;
    char           *p;
    int             i, c;
    long            nchars;
    int             unprint = 0;
    int             linecnt = 0;
    bool_t          wasempty = bufempty();

    curr = fromp->linep;

    if (!nochangename)
	Filename = strsave(fname);

    if ((f = fopen(fname, "r")) == NULL)
	return TRUE;

    filemess("");

    i = 0;
    for (nchars = 0; (c = getc(f)) != EOF; nchars++) {
	if (c >= 0x80) {
	    c -= 0x80;
	    unprint++;
	}
	/*
	 * Nulls are special, so they can't show up in the file. We should
	 * count nulls seperate from other nasties, but this is okay for now. 
	 */
	if (c == NUL) {
	    unprint++;
	    continue;
	}
	if (c == '\n') {	/* process the completed line */
	    int             len;
	    LINE           *lp;

	    IObuff[i] = NUL;
	    len = strlen(IObuff) + 1;
	    if ((lp = newline(len)) == NULL) {
		fprintf(stderr, "not enough memory - should never happen");
		getout(1);
	    }
	    strcpy(lp->s, IObuff);

	    curr->next->prev = lp;	/* new line to next one */
	    lp->next = curr->next;

	    curr->next = lp;	/* new line to prior one */
	    lp->prev = curr;

	    curr = lp;		/* new line becomes current */
	    i = 0;
	    linecnt++;
	} else
	    IObuff[i++] = (char) c;
    }
    fclose(f);

    /*
     * If the buffer was empty when we started, we have to go back and remove
     * the "dummy" line at Filemem and patch up the ptrs. 
     */
    if (wasempty) {
	LINE           *dummy = Filemem->linep;	/* dummy line ptr */

	free(dummy->s);		/* free string space */
	Filemem->linep = Filemem->linep->next;
	free((char *) dummy);	/* free LINE struct */
	Filemem->linep->prev = NULL;

	Curschar->linep = Filemem->linep;
	Topchar->linep = Filemem->linep;
    }
    if (unprint > 0)
	p = "\"%s\" %d lines, %ld characters (%d un-printable))";
    else
	p = "\"%s\" %d lines, %ld characters";

    sprintf(IObuff, p, fname, linecnt, nchars, unprint);
    msg(IObuff);
    renum();
    return FALSE;
}


/*
 * writeit - write to file 'fname' lines 'start' through 'end' 
 *
 * If either 'start' or 'end' contain null line pointers, the default is to use
 * the start or end of the file respectively. 
 */
bool_t
writeit(fname, start, end)
    char           *fname;
    LPTR           *start, *end;
{
    FILE           *f, *fopen();
    FILE           *fopenb();	/* open in binary mode, where needed */
    char            backup[16], *s;
    long            nchars;
    int             lines;
    LPTR           *p;

    sprintf(IObuff, "\"%s\"", fname);
    msg(IObuff);

    /*
     * Form the backup file name - change foo.* to foo.bak 
     */
    strcpy(backup, fname);
    for (s = backup; *s && *s != '.'; s++);
    *s = NUL;
    strcat(backup, ".bak");

    /*
     * Delete any existing backup and move the current version to the backup.
     * For safety, we don't remove the backup until the write has finished
     * successfully. And if the 'backup' option is set, leave it around. 
     */
    rename(fname, backup);


    f = P(P_CR) ? fopen(fname, "w") : fopenb(fname, "w");

    if (f == NULL) {
	emsg("Can't open file for writing!");
	return FALSE;
    }
    /*
     * If we were given a bound, start there. Otherwise just start at the
     * beginning of the file. 
     */
    if (start == NULL || start->linep == NULL)
	p = Filemem;
    else
	p = start;

    lines = 0;
    nchars = 0;
    do {
	fprintf(f, "%s\n", p->linep->s);
	nchars += strlen(p->linep->s) + 1;
	lines++;

	/*
	 * If we were given an upper bound, and we just did that line, then
	 * bag it now. 
	 */
	if (end != NULL && end->linep != NULL) {
	    if (end->linep == p->linep)
		break;
	}
    } while ((p = nextline(p)) != NULL);

    fclose(f);
    sprintf(IObuff, "\"%s\" %d lines, %ld characters", fname, lines, nchars);
    msg(IObuff);
    UNCHANGED;

    /*
     * Remove the backup unless they want it left around 
     */
    if (!P(P_BK))
	remove(backup);

    return TRUE;
}
SHAR_EOF
cat << \SHAR_EOF > help.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"

char           *Version = "STEVIE - Version 3.10a";

static int      helprow;

#ifdef	HELP

#ifdef	MEGAMAX
overlay "help"
#endif

static void     longline();

bool_t
help()
{

    /***********************************************************************
         * First Screen:   Positioning within file, Adjusting the Screen
         ***********************************************************************/

    outstr(T_ED);
    windgoto(helprow = 0, 0);

    longline("\
   Positioning within file\n\
   =======================\n\
      ^F             Forward screenfull             Worked on by:\n\
      ^B             Backward screenfull                Tim Thompson\n");
    longline("\
      ^D             scroll down half screen            Tony Andrews\n\
      ^U             scroll up half screen              G.R. (Fred) Walter\n");
    longline("\
      G              Goto line (end default)\n\
      ]]             next function\n\
      [[             previous function\n\
      /re            next occurence of regular expression 're'\n");
    longline("\
      ?re            prior occurence of regular expression 're'\n\
      n              repeat last / or ?\n\
      N              reverse last / or ?\n\
      %              find matching (, ), {, }, [, or ]\n");
    longline("\
\n\
   Adjusting the screen\n\
   ====================\n\
      ^L             Redraw the screen\n\
      ^E             scroll window down 1 line\n\
      ^Y             scroll window up 1 line\n");
    longline("\
      z<RETURN>      redraw, current line at top\n\
      z-             ... at bottom\n\
      z.             ... at center\n");

    windgoto(0, 52);
    longline(Version);

    windgoto(helprow = Rows - 2, 47);
    longline("<Press space bar to continue>\n");
    windgoto(helprow = Rows - 1, 47);
    longline("<Any other key will quit>");

    if (vgetc() != ' ')
	return TRUE;

    /***********************************************************************
         * Second Screen:   Character positioning
         ***********************************************************************/

    outstr(T_ED);
    windgoto(helprow = 0, 0);

    longline("\
   Character Positioning\n\
   =====================\n\
      ^              first non-white\n\
      0              beginning of line\n\
      $              end of line\n\
      h              backward\n");
    longline("\
      l              forward\n\
      ^H             same as h\n\
      space          same as l\n\
      fx             find 'x' forward\n");
    longline("\
      Fx             find 'x' backward\n\
      tx             upto 'x' forward\n\
      Tx             upto 'x' backward\n\
      ;              Repeat last f, F, t, or T\n");
    longline("\
      ,              inverse of ;\n\
      |              to specified column\n\
      %              find matching (, ), {, }, [, or ]\n");

    windgoto(helprow = Rows - 2, 47);
    longline("<Press space bar to continue>\n");
    windgoto(helprow = Rows - 1, 47);
    longline("<Any other key will quit>");

    if (vgetc() != ' ')
	return TRUE;

    /***********************************************************************
         * Third Screen:   Line Positioning, Marking and Returning
         ***********************************************************************/

    outstr(T_ED);
    windgoto(helprow = 0, 0);

    longline("\
    Line Positioning\n\
    =====================\n\
    H           home window line\n\
    L           last window line\n\
    M           middle window line\n");
    longline("\
    +           next line, at first non-white\n\
    -           previous line, at first non-white\n\
    CR          return, same as +\n\
    j           next line, same column\n\
    k           previous line, same column\n");

    longline("\
\n\
    Marking and Returning\n\
    =====================\n\
    ``          previous context\n\
    ''          ... at first non-white in line\n");
    longline("\
    mx          mark position with letter 'x'\n\
    `x          to mark 'x'\n\
    'x          ... at first non-white in line\n");

    windgoto(helprow = Rows - 2, 47);
    longline("<Press space bar to continue>\n");
    windgoto(helprow = Rows - 1, 47);
    longline("<Any other key will quit>");

    if (vgetc() != ' ')
	return TRUE;
    /***********************************************************************
         * Fourth Screen:   Insert & Replace, 
         ***********************************************************************/

    outstr(T_ED);
    windgoto(helprow = 0, 0);

    longline("\
    Insert and Replace\n\
    ==================\n\
    a           append after cursor\n\
    i           insert before cursor\n\
    A           append at end of line\n\
    I           insert before first non-blank\n");
    longline("\
    o           open line below\n\
    O           open line above\n\
    rx          replace single char with 'x'\n\
    R           replace characters (not yet)\n\
    ~           replace character under cursor with other case\n");

    longline("\
\n\
    Words, sentences, paragraphs\n\
    ============================\n\
    w           word forward\n\
    b           back word\n\
    e           end of word\n\
    )           to next sentence (not yet)\n\
    }           to next paragraph (not yet)\n");
    longline("\
    (           back sentence (not yet)\n\
    {           back paragraph (not yet)\n\
    W           blank delimited word\n\
    B           back W\n\
    E           to end of W");

    windgoto(helprow = Rows - 2, 47);
    longline("<Press space bar to continue>\n");
    windgoto(helprow = Rows - 1, 47);
    longline("<Any other key will quit>");

    if (vgetc() != ' ')
	return TRUE;

    /***********************************************************************
         * Fifth Screen:   Misc. operations, 
         ***********************************************************************/

    outstr(T_ED);
    windgoto(helprow = 0, 0);

    longline("\
    Undo  &  Redo\n\
    =============\n\
    u           undo last change\n\
    U           restore current line (not yet)\n\
    .           repeat last change\n");

    longline("\
\n\
    File manipulation\n\
    =================\n");
    longline("\
    :w          write back changes\n\
    :wq         write and quit\n\
    :x          write if modified, and quit\n\
    :q          quit\n\
    :q!         quit, discard changes\n\
    :e name     edit file 'name'\n");
    longline("\
    :e!         reedit, discard changes\n\
    :e #        edit alternate file\n\
    :w name     write file 'name'\n");
    longline("\
    :n          edit next file in arglist\n\
    :n args     specify new arglist (not yet)\n\
    :rew        rewind arglist\n\
    :f          show current file and lines\n");
    longline("\
    :f file     change current file name\n\
    :ta tag     to tag file entry 'tag'\n\
    ^]          :ta, current word is tag");

    windgoto(helprow = Rows - 2, 47);
    longline("<Press space bar to continue>\n");
    windgoto(helprow = Rows - 1, 47);
    longline("<Any other key will quit>");

    if (vgetc() != ' ')
	return TRUE;

    /***********************************************************************
         * Sixth Screen:   Operators, Misc. operations, Yank & Put
         ***********************************************************************/

    outstr(T_ED);
    windgoto(helprow = 0, 0);

    longline("\
    Operators (double to affect lines)\n\
    ==================================\n\
    d           delete\n\
    c           change\n");
    longline("\
    <           left shift\n\
    >           right shift\n\
    y           yank to buffer\n");

    longline("\n\
    Miscellaneous operations\n\
    ========================\n\
    C           change rest of line\n\
    D           delete rest of line\n\
    s           substitute chars\n");
    longline("\
    S           substitute lines (not yet)\n\
    J           join lines\n\
    x           delete characters\n\
    X           ... before cursor\n");

    longline("\n\
    Yank and Put\n\
    ============\n\
    p           put back text\n\
    P           put before\n\
    Y           yank lines");

    windgoto(helprow = Rows - 1, 47);
    longline("<Press any key>");

    vgetc();

    return TRUE;
}

static void
longline(p)
    char           *p;
{
# ifdef AMIGA
    outstr(p);
# else
    char           *s;

    for (s = p; *s; s++) {
	if (*s == '\n')
	    windgoto(++helprow, 0);
	else
	    outchar(*s);
    }
# endif
}
#else

bool_t
help()
{
    msg("Sorry, help not configured");
    return FALSE;
}
#endif
SHAR_EOF
cat << \SHAR_EOF > inc.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"

/*
 * inc(p) 
 *
 * Increment the line pointer 'p' crossing line boundaries as necessary. Return
 * 1 when crossing a line, -1 when at end of file, 0 otherwise. 
 */
int
inc(lp)
    LPTR           *lp;
{
    char           *p = &(lp->linep->s[lp->index]);

    if (*p != NUL) {		/* still within line */
	lp->index++;
	return ((p[1] != NUL) ? 0 : 1);
    }
    if (lp->linep->next != Fileend->linep) {	/* there is a next line */
	lp->index = 0;
	lp->linep = lp->linep->next;
	return 1;
    }
    return -1;
}
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.