[comp.sources.amiga] v89i042: stevie - vi-like text editor v35a, Part03/06

page@swan.ulowell.edu (Bob Page) (03/15/89)

Submitted-by: grwalter@watcgl.waterloo.edu (Fred Walter)
Posting-number: Volume 89, Issue 42
Archive-name: editors/stevie35a.3

#	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:
#	charset.c
#	edit.c
#	help.c
#	main.c
#	misccmds.c
#	screen.c
# This archive created: Tue Mar 14 14:41:34 1989
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 > 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 ESC:	/* an escape ends input mode */
	doESCkey:
		/*
		 * If we just did an auto-indent, truncate the line, and put
		 * the cursor back. 
		 */
		if (did_ai) {
		    Curschar->linep->s[0] = NUL;
		    Curschar->index = 0;
		    did_ai = FALSE;
		}
		set_want_col = TRUE;

		/*
		 * 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) {
		    if (gchar(Curschar) == NUL)
			dec(Curschar);
		    else if (Insbuffptr != NULL)
			dec(Curschar);
		}
		State = NORMAL;
		msg("");

		if (RedrawingDisabled) {
		    updateNextscreen(NOT_VALID);	/* Update LineSizes. */
		    cursupdate();	/* Update Topchar and Botchar. */
		}
		if (!UndoInProgress) {
		    int             n;
		    char           *p;

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

		    if (Insbuffptr != NULL) {
			if (last_command == 'O')
			    AppendToUndobuff("0");
			AppendToRedobuff(Insbuff);
			AppendToUndoUndobuff(Insbuff);
			n = 0;
			for (p = Insbuff; *p != NUL; 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);
		/*
		 * It's a little strange to put backspaces into the redo
		 * buffer, but it makes auto-indent a lot easier to deal
		 * with. 
		 */
		AppendToInsbuff(BS_STR);
		if (!RedrawingDisabled) {
		    cursupdate();
		    updateline();
		}
		break;

	      case CR:
	      case NL:
		AppendToInsbuff(NL_STR);
		if (!OpenForward(!RedrawingDisabled))
		    goto doESCkey;	/* out of memory */

		if (!RedrawingDisabled)
		    windgoto(Cursrow, Curscol);
		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", "undo" or "put" commands, so it needs to know when it should
 * stop and defer processing to the "normal" mechanism. 
 */
#define	ISSPECIAL(c)	((c) == BS || (c) == NL || (c) == CR || (c) == ESC)

void
insertchar(c)
    char            c;
{
    if (anyinput()) {		/* If there's any pending input, grab up to
				 * MAX_COLUMNS at once. */
	char            p[MAX_COLUMNS + 1];
	int             i;

	p[0] = c;
	i = 1;
	c = vpeekc();
	while (!ISSPECIAL(c) && anyinput() && (i < MAX_COLUMNS)) {
	    p[i++] = vgetc();
	    c = vpeekc();
	}
	p[i] = '\0';
	insstr(p);
	AppendToInsbuff(p);
    } else {
	inschar(c);
	AppendToInsbuff(mkstr(c));
    }

    if (!RedrawingDisabled)
	updateline();
}

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

void
scrolldown(nlines)
    int             nlines;
{
    register LPtr  *p;
    register 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, Rows, Columns);
}

void
scrollup(nlines)
    int             nlines;
{
    register LPtr  *p;
    register int    done = 0;	/* total # of physical lines done */
    register 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, Rows, Columns);
}

/*
 * 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 > 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"

extern char    *Version;

static int      helprow;

#ifdef	HELP

#ifdef	MEGAMAX
overlay "help"
#endif

static void     longline();

bool_t
help()
{
    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, 32);
    longline(Version);
#ifdef AMIGA
    longline(" ");
    longline(__DATE__);
    longline(" ");
    longline(__TIME__);
#endif

    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;

    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;

    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;

    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;

    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;

    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\
    Yank and Put\n\
    ============\n\
    p           put back text\n\
    P           put before\n\
    Y           yank lines");

    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;

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

    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\
    :[range]s/search/replace/[g]\n\
    :[range]g/search[/p|/d]\n\
    :[range]d   delete range of lines\n");

    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 > 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. */
int             NumLineSizes = 0;	/* # of active LineSizes */
LINE          **LinePointers = NULL;	/* Pointer to the line for LineSizes */
char           *LineSizes = NULL;	/* Size of a line (pline output) */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    int             atoi();

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

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

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

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

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

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

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

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

	    break;

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

    windinit();

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

    screenclear();

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

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

    setpcmark();

    updateNextscreen(NOT_VALID);

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

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

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

    windexit(0);
}

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

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

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

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

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

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

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

    return (char) c;
}

char
vpeekc()
{
    if (Readbuffptr != NULL)
	return (*Readbuffptr);
    return (NUL);
}
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"

extern int      did_ai;

/*
 * OpenForward 
 *
 * Add a blank line below the current line. 
 */

bool_t
OpenForward(can_ai)
    int             can_ai;
{
    LINE           *l;
    LPtr           *next;
    char           *s;		/* string to be moved to new line, if any */
    int             newindex = 0;	/* index of the cursor on the new
					 * line */

    /*
     * 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 (can_ai && P(P_AI)) {
	char           *p;

	/*
	 * Copy prior line, and truncate after white space 
	 */
	strcpy(l->s, Curschar->linep->s);

	for (p = l->s; *p == ' ' || *p == TAB; p++);
	*p = NUL;
	newindex = p - l->s;
	AppendToInsbuff(l->s);
	if (*s != NUL)
	    strcat(l->s, s);

	/*
	 * If we just did an auto-indent, then we didn't type anything on the
	 * prior line, and it should be truncated. 
	 */
	if (did_ai)
	    Curschar->linep->s[0] = NUL;

	did_ai = TRUE;
    } else if (*s != NUL) {
	strcpy(l->s, s);	/* copy string to new line */
    }
    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;
    }

    if (!RedrawingDisabled) {
	/*
	 * Get the cursor to the start of the line, so that 'Cursrow' gets
	 * set to the right physical line number for the stuff that
	 * follows... 
	 */
	Curschar->index = 0;
	cursupdate();

	/*
	 * If we're doing an open on the last logical line, then go ahead and
	 * scroll the screen up. Otherwise, just insert a blank line at the
	 * right place. We use calls to plines() in case the cursor is
	 * resting on a long line. 
	 */
	if (Cursrow + plines(Curschar) == (Rows - 1))
	    scrollup(1);
	else
	    s_ins(Cursrow + plines(Curschar), 1, Rows, Columns);
    }
    *Curschar = *nextline(Curschar);	/* cursor moves down */
    Curschar->index = newindex;

    if (!RedrawingDisabled) {
	/* because Botchar is now invalid */
	updateNextscreen(VALID_TO_CURSCHAR);
	cursupdate();		/* update Cursrow before insert */
    }
    CHANGED;

    return (TRUE);
}

/*
 * OpenBackward 
 *
 * Add a blank line above the current line. 
 */

bool_t
OpenBackward(can_ai)
    int             can_ai;
{
    LINE           *l;
    LINE           *prev;
    int             newindex = 0;	/* index of the cursor on the new
					 * line */

    prev = Curschar->linep->prev;

    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 */
    prev->next = l;

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

    if (can_ai && P(P_AI)) {
	char           *p;

	/*
	 * Copy current line, and truncate after white space 
	 */
	strcpy(l->s, Curschar->linep->s);

	for (p = l->s; *p == ' ' || *p == TAB; p++);
	*p = NUL;
	newindex = p - l->s;
	AppendToInsbuff(l->s);

	did_ai = TRUE;
    }
    Curschar->linep = Curschar->linep->prev;
    Curschar->index = newindex;

    if (prev == Filetop->linep) {	/* new start of file */
	Filemem->linep = l;
	renum();
    } 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;
    }

    if (!RedrawingDisabled) {
	if (LINEOF(Curschar) < LINEOF(Topchar)) {
	    Topchar->linep = Curschar->linep;
	    updateNextscreen(NOT_VALID);
	} else {
	    updateNextscreen(VALID_TO_CURSCHAR);
	}
	cursupdate();		/* update Cursrow before insert */
	if (Cursrow != 0)
	    s_ins(Cursrow, 1, Rows, Columns);	/* insert a physical line */
    }
    CHANGED;

    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;
{
    register int    col = 0;
    register char  *s;

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

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

    for (; *s != NUL; s++) {
	if (*s == TAB && !P(P_LS))
	    col += P(P_TS) - (col % P(P_TS));
	else
	    col += chars[(unsigned) (*s & 0xff)].ch_size;
    }

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

    /*
     * If 'number' mode is on, add another 8. 
     */
    if (P(P_NU))
	col += 8;

    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;
{
    register char  *p;
    register 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) {
	Curschar->index++;
	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(VALID_TO_CURSCHAR);	/* 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);

    CHANGED;
}

void
insstr(s)
    register char  *s;
{
    register char  *p;
    register char  *pend;
    register int    n = strlen(s);

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

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

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

    for (; n > 0; n--) {
	*p++ = *s++;
	Curschar->index++;
    }
    CHANGED;
}

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--;
    }
    CHANGED;
    return TRUE;
}

void
delline(nlines, can_update)
    int             nlines;
    bool_t          can_update;
{
    register LINE  *p;
    register LINE  *q;
    int             doscreen;	/* if true, update the screen */
    int             num_plines = 0;

    doscreen = can_update;
    /*
     * 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) && can_update) {
	doscreen = FALSE;
	/* flaky way to clear rest of screen */
	s_del(Cursrow, Rows - 1, Rows, Columns);
    }
    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 == Filetop->linep) {	/* first line of file so... */
	    Filemem->linep = q;	/* adjust start of file */
	    Topchar->linep = q;	/* and screen */
	}
	p->next = q;
	q->prev = p;

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

	/*
	 * Set up to delete the correct number of physical lines on the
	 * screen 
	 */
	if (doscreen)
	    num_plines += 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? */

	CHANGED;

	/* 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;
	}
    }
    /*
     * Delete the correct number of physical lines on the screen 
     */
    if (doscreen && num_plines > 0)
	s_del(Cursrow, num_plines, Rows, Columns);
}
SHAR_EOF
cat << \SHAR_EOF > screen.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"

/*
 * The following variable is set (in cursupdate) to the number of physical
 * lines taken by the line the cursor is on. We use this to avoid extra calls
 * to plines(). The optimized routines updateline() and redrawline()
 * make sure that the size of the cursor line hasn't changed. If so, lines below
 * the cursor will move up or down and we need to call the routines
 * updateNextscreen() and updateRealscreen() to examine the entire screen. 
 */
static int      Cline_size;	/* size (in rows) of the cursor line */
static int      Cline_row;	/* starting row of the cursor line */

/*
 * updateline() - like updateNextscreen() but only for cursor line 
 *
 * This determines whether or not we need to call updateNextscreen() to examine
 * the entire screen for changes. This occurs if the size of the cursor line
 * (in rows) hasn't changed.
 */
void
updateline()
{
    register int    row;
    register int    col;
    register char  *screenp;
    register char   c;
    LPtr            memp;
    register char  *nextrow;
    char            extra[16];
    char           *p_extra;
    int             n_extra;
    int             n;
    bool_t          eof;
    int             lno;	/* number of the line we're doing */
    int             coff;	/* column offset */

    MustRedrawLine = TRUE;

    coff = P(P_NU) ? 8 : 0;

    /*
     * This should be done more efficiently. 
     */
    if (P(P_NU))
	lno = cntllines(Filemem, Curschar);

    screenp = Nextscreen + (Cline_row * Columns);

    memp = *Curschar;
    memp.index = 0;

    eof = FALSE;
    col = 0;
    row = Cline_row;

    p_extra = NULL;
    n_extra = 0;
    if (P(P_NU)) {
	strcpy(extra, mkline(lno));
	p_extra = extra;
	n_extra = 8;
    }
    while (!eof) {
	/* Get the next character to put on the screen. */

	/*
	 * The 'extra' array contains the extra stuff that is inserted to
	 * represent special characters (tabs, and other non-printable stuff.
	 * The order in the 'extra' array is reversed. 
	 */

	if (n_extra > 0) {
	    c = *p_extra++;
	    n_extra--;
	} else {
	    c = gchar(&memp);
	    if (inc(&memp) == -1)
		eof = TRUE;
	    /*
	     * when getting a character from the file, we may have to turn it
	     * into something else on the way to putting it into
	     * 'Nextscreen'. 
	     */
	    if (c == TAB && !P(P_LS)) {
		strcpy(extra, "               ");
		p_extra = extra;
		/* tab amount depends on current column */
		n_extra = ((P(P_TS) - 1) - (col - coff) % P(P_TS));
		c = ' ';
	    } else if (c == NUL && P(P_LS)) {
		extra[0] = NUL;
		p_extra = extra;
		n_extra = 1;
		c = '$';
	    } else if (c != NUL && (n = chars[c].ch_size) > 1) {
		p_extra = chars[c].ch_str;
		c = *p_extra++;
		n_extra = n - 1;
	    }
	}

	if (c == NUL) {
	    row++;
	    /* get pointer to start of next row */
	    nextrow = Nextscreen + (row * Columns);
	    /* blank out the rest of this row */
	    while (screenp < nextrow)
		*screenp++ = ' ';
	    break;
	}
	if (col >= Columns) {
	    row++;
	    col = 0;
	}
	/* store the character in Nextscreen */
	*screenp++ = c;
	col++;
    }
    if ((row - Cline_row) != Cline_size) {
	updateNextscreen(VALID_TO_CURSCHAR);
    }
}

/*
 * redrawline 
 *
 * Like updateRealscreen() but only for the cursor line. 
 */
void
redrawline()
{
    register char  *np = Nextscreen + (Cline_row * Columns);
    register char  *rp = Realscreen + (Cline_row * Columns);
    register char  *endline;
    register int    row, col;
    int             gorow = -1, gocol = -1;

    if (RedrawingDisabled)
	return;

    if (!MustRedrawLine && !MustRedrawScreen)
	return;

    if (MustRedrawScreen) {
	msg("STEVIE internal error: redrawline called");
	sleep(5);
    }
    endline = np + (Cline_size * Columns);

    row = Cline_row;
    col = 0;

    outstr(T_CI);		/* disable cursor */

    for (; np < endline; np++, rp++) {
	/* If desired screen (contents of Nextscreen) does not */
	/* match what's really there, put it there. */
	if (*np != *rp) {
	    /* if we are positioned at the right place, */
	    /* we don't have to use windgoto(). */
	    if (gocol != col || gorow != row) {
		/*
		 * If we're just off by one, don't send an entire esc. seq.
		 * (this happens a lot!) 
		 */
		if (gorow == row && gocol + 1 == col) {
		    outchar(*(np - 1));
		    gocol++;
		} else
		    windgoto(gorow = row, gocol = col);
	    }
	    outchar(*rp = *np);
	    gocol++;
	}
	if (++col >= Columns) {
	    col = 0;
	    row++;
	}
    }
    outstr(T_CV);		/* enable cursor again */

    MustRedrawScreen = FALSE;
}

/*
 * prt_line() - print the given line
 */
void
prt_line(s)
    char           *s;
{
    register int    si = 0;
    register int    c;
    register int    col = 0;

    char            extra[16];
    int             n_extra = 0;
    int             n;

    for (;;) {

	if (n_extra > 0)
	    c = extra[--n_extra];
	else {
	    c = s[si++];
	    if (c == TAB && !P(P_LS)) {
		strcpy(extra, "                ");
		/* tab amount depends on current column */
		n_extra = (P(P_TS) - 1) - col % P(P_TS);
		c = ' ';
	    } else if (c == NUL && P(P_LS)) {
		extra[0] = NUL;
		n_extra = 1;
		c = '$';
	    } else if (c != NUL && (n = chars[c].ch_size) > 1) {
		char           *p;

		n_extra = 0;
		p = chars[c].ch_str;
		/* copy 'ch-str'ing into 'extra' in reverse */
		while (n > 1)
		    extra[n_extra++] = p[--n];
		c = p[0];
	    }
	}

	if (c == NUL)
	    break;

	outchar(c);
	col++;
    }
}

void
screenclear()
{
    register char  *rp;
    register char  *np;
    register char  *end;
    register int    i;

    outstr(T_ED);		/* clear the display */

    rp = Realscreen;
    end = Realscreen + Rows * Columns;
    np = Nextscreen;

    /* blank out the stored screens */
    while (rp != end)
	*rp++ = *np++ = ' ';

    /* clear screen info */
    for (i = 0; i < Rows; i++) {
	LinePointers[i] = NULL;
	LineSizes[i] = '\0';
    }
    NumLineSizes = 0;
}

void
cursupdate()
{
    LPtr           *p;
    LPtr           *pp;
    char            c;
    int             incr, nlines;
    int             i;
    int             didincr;

    if (bufempty()) {		/* special case - file is empty */
	*Topchar = *Filemem;
	*Curschar = *Filemem;
	for (i = 0; i < Rows; i++)
	    LineSizes[i] = 0;
    } else if (LINEOF(Curschar) < LINEOF(Topchar)) {
	nlines = cntllines(Curschar, Topchar);
	/*
	 * if the cursor is above the top of the screen, put it at the top of
	 * the screen.. 
	 */
	*Topchar = *Curschar;
	Topchar->index = 0;
	/*
	 * ... and, if we weren't very close to begin with, we scroll so that
	 * the line is close to the middle. 
	 */
	if (nlines > Rows / 3) {
	    p = Topchar;
	    for (i = 0; i < Rows / 3; i += plines(p)) {
		pp = prevline(p);
		if (pp == NULL)
		    break;
		p = pp;
	    }
	    *Topchar = *p;
	} else
	    s_ins(0, nlines - 1, Rows, Columns);
	updateNextscreen(VALID);
    } else if (LINEOF(Curschar) >= LINEOF(Botchar)) {
	nlines = cntllines(Botchar, Curschar);
	/*
	 * If the cursor is off the bottom of the screen, put it at the top
	 * of the screen.. ... and back up 
	 */
	if (nlines > Rows / 3) {
	    p = Curschar;
	    for (i = 0; i < (2 * Rows) / 3; i += plines(p)) {
		pp = prevline(p);
		if (pp == NULL)
		    break;
		p = pp;
	    }
	    *Topchar = *p;
	} else {
	    scrollup(nlines);
	}
	updateNextscreen(VALID);
    }
    Cursrow = Curscol = Cursvcol = i = 0;
    for (p = Topchar; p->linep != Curschar->linep; p = nextline(p))
	Cursrow += LineSizes[i++];

    if (P(P_NU))
	Curscol = 8;

    Cline_row = Cursrow;
    if (i >= NumLineSizes) {	/* Should only happen with a line that is too
				 * long to fit on the last screen line. */
	Cline_size = 0;
    } else {
	Cline_size = LineSizes[i];

	for (i = 0; i <= Curschar->index; i++) {
	    c = Curschar->linep->s[i];
	    /* A tab gets expanded, depending on the current column */
	    if (c == TAB && !P(P_LS))
		incr = P(P_TS) - (Cursvcol % P(P_TS));
	    else
		incr = chars[c].ch_size;
	    Curscol += incr;
	    Cursvcol += incr;
	    if (Curscol >= Columns) {
		Curscol -= Columns;
		Cursrow++;
		didincr = TRUE;
	    } else
		didincr = FALSE;
	}
	if (didincr)
	    Cursrow--;

	if (c == TAB && State == NORMAL && !P(P_LS)) {
	    Curscol--;
	    Cursvcol--;
	} else {
	    Curscol -= incr;
	    Cursvcol -= incr;
	}
	if (Curscol < 0)
	    Curscol += Columns;
    }

    if (set_want_col) {
	Curswant = Cursvcol;
	set_want_col = FALSE;
    }
}

/*
 * The rest of the routines in this file perform screen manipulations. The
 * given operation is performed physically on the screen. The corresponding
 * change is also made to the internal screen image. In this way, the editor
 * anticipates the effect of editing changes on the appearance of the screen.
 * That way, when we call screenupdate a complete redraw isn't usually
 * necessary. Another advantage is that we can keep adding code to anticipate
 * screen changes, and in the meantime, everything still works. 
 */

/*
 * s_ins(row, nlines, total_rows, columns) - insert 'nlines' lines at 'row' 
 */
void
s_ins(row, nlines, total_rows, columns)
    int             row;
    int             nlines;
    int		    total_rows;
    int		    columns;
{
    register char  *s;	/* src for block copy */
    register char  *d;	/* dest for block copy */
    register char  *e;	/* end point for copy */
    int             i;

    if (nlines > (total_rows - 1 - row))
	nlines = total_rows - 1 - row;

    if ((T_IL[0] == NUL) || RedrawingDisabled || nlines <= 0)
	return;

    /*
     * It "looks" better if we do all the inserts at once 
     */
    outstr(T_SC);		/* save position */

    if (T_IL_B[0] == NUL) {
	for (i = 0; i < nlines; i++) {
	    windgoto(row, 0);
	    outstr(T_IL);
	}
    } else {
	windgoto(row, 0);
	outstr(T_IL);
	if (nlines >= 10)
	    outchar((char) (nlines / 10 + '0'));
	outchar((char) (nlines % 10 + '0'));
	outstr(T_IL_B);
    }

    windgoto(total_rows - 1, 0);	/* delete any garbage that may have */
    outstr(T_EL);		/* been shifted to the bottom line */

    outstr(T_RC);		/* restore the cursor position */

    /*
     * Now do a block move to update the internal screen image 
     */
    d = Realscreen + (columns * (total_rows - 1)) - 1;
    s = d - (columns * nlines);
    e = Realscreen + (columns * row);

    while (s >= e)
	*d-- = *s--;

    /*
     * Clear the inserted lines 
     */
    s = Realscreen + (row * columns);
    e = s + (nlines * columns);
    while (s < e)
	*s++ = ' ';
}

/*
 * s_del(row, nlines, total_rows, columns) - delete 'nlines' lines at 'row' 
 */
void
s_del(row, nlines, total_rows, columns)
    int             row;
    int             nlines;
    int		    total_rows;
    int		    columns;
{
    register char  *s;
    register char  *d;
    register char  *e;
    int             i;

    if (nlines > (total_rows - 1 - row))
	nlines = total_rows - 1 - row;

    if ((T_DL[0] == NUL) || RedrawingDisabled || nlines <= 0)
	return;

    outstr(T_SC);		/* save position */

    windgoto(total_rows - 1, 0);	/* delete any garbage that */
    outstr(T_EL);		/* was on the status line */

    /* delete the lines */
    if (T_DL_B[0] == NUL) {
	for (i = 0; i < nlines; i++) {
	    windgoto(row, 0);
	    outstr(T_DL);	/* delete a line */
	}
    } else {
	windgoto(row, 0);
	outstr(T_DL);
	if (nlines >= 10)
	    outchar((char) (nlines / 10 + '0'));
	outchar((char) (nlines % 10 + '0'));
	outstr(T_DL_B);
    }

    outstr(T_RC);		/* restore position */

    /*
     * do a block move to update the internal image 
     */
    d = Realscreen + (row * columns);
    s = d + (nlines * columns);
    e = Realscreen + ((total_rows - 1) * columns);

    while (s < e)
	*d++ = *s++;

    while (d < e)		/* clear the lines at the bottom */
	*d++ = ' ';
}
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.