vi inlining
diff --git a/editors/vi.c b/editors/vi.c
index ad6dd5c..ce6c3d8 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -19,7 +19,7 @@
  */
 
 static const char vi_Version[] =
-	"$Id: vi.c,v 1.22 2002/07/31 21:22:21 sandman Exp $";
+	"$Id: vi.c,v 1.23 2002/08/21 13:02:24 aaronl Exp $";
 
 /*
  * To compile for standalone use:
@@ -202,7 +202,7 @@
 static void sync_cursor(Byte *, int *, int *);	// synchronize the screen cursor to dot
 static Byte *begin_line(Byte *);	// return pointer to cur line B-o-l
 static Byte *end_line(Byte *);	// return pointer to cur line E-o-l
-static Byte *dollar_line(Byte *);	// return pointer to just before NL
+extern inline Byte *dollar_line(Byte *);	// return pointer to just before NL
 static Byte *prev_line(Byte *);	// return pointer to prev line B-o-l
 static Byte *next_line(Byte *);	// return pointer to next line B-o-l
 static Byte *end_screen(void);	// get pointer to last char on screen
@@ -232,7 +232,7 @@
 static Byte *text_hole_make(Byte *, int);	// at "p", make a 'size' byte hole
 static Byte *yank_delete(Byte *, Byte *, int, int);	// yank text[] into register then delete
 static void show_help(void);	// display some help info
-static void print_literal(Byte *, Byte *);	// copy s to buf, convert unprintable
+extern inline void print_literal(Byte *, Byte *);	// copy s to buf, convert unprintable
 static void rawmode(void);	// set "raw" mode on tty
 static void cookmode(void);	// return to "cooked" mode on tty
 static int mysleep(int);	// sleep for 'h' 1/100 seconds
@@ -296,7 +296,7 @@
 static Byte *text_yank(Byte *, Byte *, int);	// save copy of "p" into a register
 static Byte what_reg(void);		// what is letter of current YDreg
 static void check_context(Byte);	// remember context for '' command
-static Byte *swap_context(Byte *);	// goto new context for '' command
+extern inline Byte *swap_context(Byte *);	// goto new context for '' command
 #endif							/* CONFIG_FEATURE_VI_YANKMARK */
 #ifdef CONFIG_FEATURE_VI_CRASHME
 static void crash_dummy();
@@ -758,6 +758,2340 @@
 }
 #endif							/* CONFIG_FEATURE_VI_CRASHME */
 
+//----- The Colon commands -------------------------------------
+#ifdef CONFIG_FEATURE_VI_COLON
+static Byte *get_one_address(Byte * p, int *addr)	// get colon addr, if present
+{
+	int st;
+	Byte *q;
+
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+	Byte c;
+#endif							/* CONFIG_FEATURE_VI_YANKMARK */
+#ifdef CONFIG_FEATURE_VI_SEARCH
+	Byte *pat, buf[BUFSIZ];
+#endif							/* CONFIG_FEATURE_VI_SEARCH */
+
+	*addr = -1;			// assume no addr
+	if (*p == '.') {	// the current line
+		p++;
+		q = begin_line(dot);
+		*addr = count_lines(text, q);
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+	} else if (*p == '\'') {	// is this a mark addr
+		p++;
+		c = tolower(*p);
+		p++;
+		if (c >= 'a' && c <= 'z') {
+			// we have a mark
+			c = c - 'a';
+			q = mark[(int) c];
+			if (q != NULL) {	// is mark valid
+				*addr = count_lines(text, q);	// count lines
+			}
+		}
+#endif							/* CONFIG_FEATURE_VI_YANKMARK */
+#ifdef CONFIG_FEATURE_VI_SEARCH
+	} else if (*p == '/') {	// a search pattern
+		q = buf;
+		for (p++; *p; p++) {
+			if (*p == '/')
+				break;
+			*q++ = *p;
+			*q = '\0';
+		}
+		pat = (Byte *) xstrdup((char *) buf);	// save copy of pattern
+		if (*p == '/')
+			p++;
+		q = char_search(dot, pat, FORWARD, FULL);
+		if (q != NULL) {
+			*addr = count_lines(text, q);
+		}
+		free(pat);
+#endif							/* CONFIG_FEATURE_VI_SEARCH */
+	} else if (*p == '$') {	// the last line in file
+		p++;
+		q = begin_line(end - 1);
+		*addr = count_lines(text, q);
+	} else if (isdigit(*p)) {	// specific line number
+		sscanf((char *) p, "%d%n", addr, &st);
+		p += st;
+	} else {			// I don't reconise this
+		// unrecognised address- assume -1
+		*addr = -1;
+	}
+	return (p);
+}
+
+static Byte *get_address(Byte *p, int *b, int *e)	// get two colon addrs, if present
+{
+	//----- get the address' i.e., 1,3   'a,'b  -----
+	// get FIRST addr, if present
+	while (isblnk(*p))
+		p++;				// skip over leading spaces
+	if (*p == '%') {			// alias for 1,$
+		p++;
+		*b = 1;
+		*e = count_lines(text, end-1);
+		goto ga0;
+	}
+	p = get_one_address(p, b);
+	while (isblnk(*p))
+		p++;
+	if (*p == ',') {			// is there a address seperator
+		p++;
+		while (isblnk(*p))
+			p++;
+		// get SECOND addr, if present
+		p = get_one_address(p, e);
+	}
+ga0:
+	while (isblnk(*p))
+		p++;				// skip over trailing spaces
+	return (p);
+}
+
+static void colon(Byte * buf)
+{
+	Byte c, *orig_buf, *buf1, *q, *r;
+	Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
+	int i, l, li, ch, st, b, e;
+	int useforce, forced;
+	struct stat st_buf;
+
+	// :3154	// if (-e line 3154) goto it  else stay put
+	// :4,33w! foo	// write a portion of buffer to file "foo"
+	// :w		// write all of buffer to current file
+	// :q		// quit
+	// :q!		// quit- dont care about modified file
+	// :'a,'z!sort -u   // filter block through sort
+	// :'f		// goto mark "f"
+	// :'fl		// list literal the mark "f" line
+	// :.r bar	// read file "bar" into buffer before dot
+	// :/123/,/abc/d    // delete lines from "123" line to "abc" line
+	// :/xyz/	// goto the "xyz" line
+	// :s/find/replace/ // substitute pattern "find" with "replace"
+	// :!<cmd>	// run <cmd> then return
+	//
+	if (strlen((char *) buf) <= 0)
+		goto vc1;
+	if (*buf == ':')
+		buf++;			// move past the ':'
+
+	forced = useforce = FALSE;
+	li = st = ch = i = 0;
+	b = e = -1;
+	q = text;			// assume 1,$ for the range
+	r = end - 1;
+	li = count_lines(text, end - 1);
+	fn = cfn;			// default to current file
+	memset(cmd, '\0', BUFSIZ);	// clear cmd[]
+	memset(args, '\0', BUFSIZ);	// clear args[]
+
+	// look for optional address(es)  :.  :1  :1,9   :'q,'a   :%
+	buf = get_address(buf, &b, &e);
+
+	// remember orig command line
+	orig_buf = buf;
+
+	// get the COMMAND into cmd[]
+	buf1 = cmd;
+	while (*buf != '\0') {
+		if (isspace(*buf))
+			break;
+		*buf1++ = *buf++;
+	}
+	// get any ARGuments
+	while (isblnk(*buf))
+		buf++;
+	strcpy((char *) args, (char *) buf);
+	buf1 = last_char_is((char *)cmd, '!');
+	if (buf1) {
+		useforce = TRUE;
+		*buf1 = '\0';   // get rid of !
+	}
+	if (b >= 0) {
+		// if there is only one addr, then the addr
+		// is the line number of the single line the
+		// user wants. So, reset the end
+		// pointer to point at end of the "b" line
+		q = find_line(b);	// what line is #b
+		r = end_line(q);
+		li = 1;
+	}
+	if (e >= 0) {
+		// we were given two addrs.  change the
+		// end pointer to the addr given by user.
+		r = find_line(e);	// what line is #e
+		r = end_line(r);
+		li = e - b + 1;
+	}
+	// ------------ now look for the command ------------
+	i = strlen((char *) cmd);
+	if (i == 0) {		// :123CR goto line #123
+		if (b >= 0) {
+			dot = find_line(b);	// what line is #b
+			dot_skip_over_ws();
+		}
+	} else if (strncmp((char *) cmd, "!", 1) == 0) {	// run a cmd
+		// :!ls   run the <cmd>
+		(void) alarm(0);		// wait for input- no alarms
+		place_cursor(rows - 1, 0, FALSE);	// go to Status line
+		clear_to_eol();			// clear the line
+		cookmode();
+		system(orig_buf+1);		// run the cmd
+		rawmode();
+		Hit_Return();			// let user see results
+		(void) alarm(3);		// done waiting for input
+	} else if (strncmp((char *) cmd, "=", i) == 0) {	// where is the address
+		if (b < 0) {	// no addr given- use defaults
+			b = e = count_lines(text, dot);
+		}
+		psb("%d", b);
+	} else if (strncasecmp((char *) cmd, "delete", i) == 0) {	// delete lines
+		if (b < 0) {	// no addr given- use defaults
+			q = begin_line(dot);	// assume .,. for the range
+			r = end_line(dot);
+		}
+		dot = yank_delete(q, r, 1, YANKDEL);	// save, then delete lines
+		dot_skip_over_ws();
+	} else if (strncasecmp((char *) cmd, "edit", i) == 0) {	// Edit a file
+		int sr;
+		sr= 0;
+		// don't edit, if the current file has been modified
+		if (file_modified && ! useforce) {
+			psbs("No write since last change (:edit! overrides)");
+			goto vc1;
+		}
+		if (strlen(args) > 0) {
+			// the user supplied a file name
+			fn= args;
+		} else if (cfn != 0 && strlen(cfn) > 0) {
+			// no user supplied name- use the current filename
+			fn= cfn;
+			goto vc5;
+		} else {
+			// no user file name, no current name- punt
+			psbs("No current filename");
+			goto vc1;
+		}
+
+		// see if file exists- if not, its just a new file request
+		if ((sr=stat((char*)fn, &st_buf)) < 0) {
+			// This is just a request for a new file creation.
+			// The file_insert below will fail but we get
+			// an empty buffer with a file name.  Then the "write"
+			// command can do the create.
+		} else {
+			if ((st_buf.st_mode & (S_IFREG)) == 0) {
+				// This is not a regular file
+				psbs("\"%s\" is not a regular file", fn);
+				goto vc1;
+			}
+			if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
+				// dont have any read permissions
+				psbs("\"%s\" is not readable", fn);
+				goto vc1;
+			}
+		}
+
+		// There is a read-able regular file
+		// make this the current file
+		q = (Byte *) xstrdup((char *) fn);	// save the cfn
+		if (cfn != 0)
+			free(cfn);		// free the old name
+		cfn = q;			// remember new cfn
+
+	  vc5:
+		// delete all the contents of text[]
+		new_text(2 * file_size(fn));
+		screenbegin = dot = end = text;
+
+		// insert new file
+		ch = file_insert(fn, text, file_size(fn));
+
+		if (ch < 1) {
+			// start empty buf with dummy line
+			(void) char_insert(text, '\n');
+			ch= 1;
+		}
+		file_modified = FALSE;
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+		if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
+			free(reg[Ureg]);	//   free orig line reg- for 'U'
+			reg[Ureg]= 0;
+		}
+		if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
+			free(reg[YDreg]);	//   free default yank/delete register
+			reg[YDreg]= 0;
+		}
+		for (li = 0; li < 28; li++) {
+			mark[li] = 0;
+		}				// init the marks
+#endif							/* CONFIG_FEATURE_VI_YANKMARK */
+		// how many lines in text[]?
+		li = count_lines(text, end - 1);
+		psb("\"%s\"%s"
+#ifdef CONFIG_FEATURE_VI_READONLY
+			"%s"
+#endif							/* CONFIG_FEATURE_VI_READONLY */
+			" %dL, %dC", cfn,
+			(sr < 0 ? " [New file]" : ""),
+#ifdef CONFIG_FEATURE_VI_READONLY
+			((vi_readonly || readonly) ? " [Read only]" : ""),
+#endif							/* CONFIG_FEATURE_VI_READONLY */
+			li, ch);
+	} else if (strncasecmp((char *) cmd, "file", i) == 0) {	// what File is this
+		if (b != -1 || e != -1) {
+			ni((Byte *) "No address allowed on this command");
+			goto vc1;
+		}
+		if (strlen((char *) args) > 0) {
+			// user wants a new filename
+			if (cfn != NULL)
+				free(cfn);
+			cfn = (Byte *) xstrdup((char *) args);
+		} else {
+			// user wants file status info
+			edit_status();
+		}
+	} else if (strncasecmp((char *) cmd, "features", i) == 0) {	// what features are available
+		// print out values of all features
+		place_cursor(rows - 1, 0, FALSE);	// go to Status line, bottom of screen
+		clear_to_eol();	// clear the line
+		cookmode();
+		show_help();
+		rawmode();
+		Hit_Return();
+	} else if (strncasecmp((char *) cmd, "list", i) == 0) {	// literal print line
+		if (b < 0) {	// no addr given- use defaults
+			q = begin_line(dot);	// assume .,. for the range
+			r = end_line(dot);
+		}
+		place_cursor(rows - 1, 0, FALSE);	// go to Status line, bottom of screen
+		clear_to_eol();	// clear the line
+		write(1, "\r\n", 2);
+		for (; q <= r; q++) {
+			c = *q;
+			if (c > '~')
+				standout_start();
+			if (c == '\n') {
+				write(1, "$\r", 2);
+			} else if (*q < ' ') {
+				write(1, "^", 1);
+				c += '@';
+			}
+			write(1, &c, 1);
+			if (c > '~')
+				standout_end();
+		}
+#ifdef CONFIG_FEATURE_VI_SET
+	  vc2:
+#endif							/* CONFIG_FEATURE_VI_SET */
+		Hit_Return();
+	} else if ((strncasecmp((char *) cmd, "quit", i) == 0) ||	// Quit
+			   (strncasecmp((char *) cmd, "next", i) == 0)) {	// edit next file
+		if (useforce) {
+			// force end of argv list
+			if (*cmd == 'q') {
+				optind = save_argc;
+			}
+			editing = 0;
+			goto vc1;
+		}
+		// don't exit if the file been modified
+		if (file_modified) {
+			psbs("No write since last change (:%s! overrides)",
+				 (*cmd == 'q' ? "quit" : "next"));
+			goto vc1;
+		}
+		// are there other file to edit
+		if (*cmd == 'q' && optind < save_argc - 1) {
+			psbs("%d more file to edit", (save_argc - optind - 1));
+			goto vc1;
+		}
+		if (*cmd == 'n' && optind >= save_argc - 1) {
+			psbs("No more files to edit");
+			goto vc1;
+		}
+		editing = 0;
+	} else if (strncasecmp((char *) cmd, "read", i) == 0) {	// read file into text[]
+		fn = args;
+		if (strlen((char *) fn) <= 0) {
+			psbs("No filename given");
+			goto vc1;
+		}
+		if (b < 0) {	// no addr given- use defaults
+			q = begin_line(dot);	// assume "dot"
+		}
+		// read after current line- unless user said ":0r foo"
+		if (b != 0)
+			q = next_line(q);
+#ifdef CONFIG_FEATURE_VI_READONLY
+		l= readonly;			// remember current files' status
+#endif
+		ch = file_insert(fn, q, file_size(fn));
+#ifdef CONFIG_FEATURE_VI_READONLY
+		readonly= l;
+#endif
+		if (ch < 0)
+			goto vc1;	// nothing was inserted
+		// how many lines in text[]?
+		li = count_lines(q, q + ch - 1);
+		psb("\"%s\""
+#ifdef CONFIG_FEATURE_VI_READONLY
+			"%s"
+#endif							/* CONFIG_FEATURE_VI_READONLY */
+			" %dL, %dC", fn,
+#ifdef CONFIG_FEATURE_VI_READONLY
+			((vi_readonly || readonly) ? " [Read only]" : ""),
+#endif							/* CONFIG_FEATURE_VI_READONLY */
+			li, ch);
+		if (ch > 0) {
+			// if the insert is before "dot" then we need to update
+			if (q <= dot)
+				dot += ch;
+			file_modified = TRUE;
+		}
+	} else if (strncasecmp((char *) cmd, "rewind", i) == 0) {	// rewind cmd line args
+		if (file_modified && ! useforce) {
+			psbs("No write since last change (:rewind! overrides)");
+		} else {
+			// reset the filenames to edit
+			optind = fn_start - 1;
+			editing = 0;
+		}
+#ifdef CONFIG_FEATURE_VI_SET
+	} else if (strncasecmp((char *) cmd, "set", i) == 0) {	// set or clear features
+		i = 0;			// offset into args
+		if (strlen((char *) args) == 0) {
+			// print out values of all options
+			place_cursor(rows - 1, 0, FALSE);	// go to Status line, bottom of screen
+			clear_to_eol();	// clear the line
+			printf("----------------------------------------\r\n");
+#ifdef CONFIG_FEATURE_VI_SETOPTS
+			if (!autoindent)
+				printf("no");
+			printf("autoindent ");
+			if (!err_method)
+				printf("no");
+			printf("flash ");
+			if (!ignorecase)
+				printf("no");
+			printf("ignorecase ");
+			if (!showmatch)
+				printf("no");
+			printf("showmatch ");
+			printf("tabstop=%d ", tabstop);
+#endif							/* CONFIG_FEATURE_VI_SETOPTS */
+			printf("\r\n");
+			goto vc2;
+		}
+		if (strncasecmp((char *) args, "no", 2) == 0)
+			i = 2;		// ":set noautoindent"
+#ifdef CONFIG_FEATURE_VI_SETOPTS
+		if (strncasecmp((char *) args + i, "autoindent", 10) == 0 ||
+			strncasecmp((char *) args + i, "ai", 2) == 0) {
+			autoindent = (i == 2) ? 0 : 1;
+		}
+		if (strncasecmp((char *) args + i, "flash", 5) == 0 ||
+			strncasecmp((char *) args + i, "fl", 2) == 0) {
+			err_method = (i == 2) ? 0 : 1;
+		}
+		if (strncasecmp((char *) args + i, "ignorecase", 10) == 0 ||
+			strncasecmp((char *) args + i, "ic", 2) == 0) {
+			ignorecase = (i == 2) ? 0 : 1;
+		}
+		if (strncasecmp((char *) args + i, "showmatch", 9) == 0 ||
+			strncasecmp((char *) args + i, "sm", 2) == 0) {
+			showmatch = (i == 2) ? 0 : 1;
+		}
+		if (strncasecmp((char *) args + i, "tabstop", 7) == 0) {
+			sscanf(strchr((char *) args + i, '='), "=%d", &ch);
+			if (ch > 0 && ch < columns - 1)
+				tabstop = ch;
+		}
+#endif							/* CONFIG_FEATURE_VI_SETOPTS */
+#endif							/* CONFIG_FEATURE_VI_SET */
+#ifdef CONFIG_FEATURE_VI_SEARCH
+	} else if (strncasecmp((char *) cmd, "s", 1) == 0) {	// substitute a pattern with a replacement pattern
+		Byte *ls, *F, *R;
+		int gflag;
+
+		// F points to the "find" pattern
+		// R points to the "replace" pattern
+		// replace the cmd line delimiters "/" with NULLs
+		gflag = 0;		// global replace flag
+		c = orig_buf[1];	// what is the delimiter
+		F = orig_buf + 2;	// start of "find"
+		R = (Byte *) strchr((char *) F, c);	// middle delimiter
+		if (!R) goto colon_s_fail;
+		*R++ = '\0';	// terminate "find"
+		buf1 = (Byte *) strchr((char *) R, c);
+		if (!buf1) goto colon_s_fail;
+		*buf1++ = '\0';	// terminate "replace"
+		if (*buf1 == 'g') {	// :s/foo/bar/g
+			buf1++;
+			gflag++;	// turn on gflag
+		}
+		q = begin_line(q);
+		if (b < 0) {	// maybe :s/foo/bar/
+			q = begin_line(dot);	// start with cur line
+			b = count_lines(text, q);	// cur line number
+		}
+		if (e < 0)
+			e = b;		// maybe :.s/foo/bar/
+		for (i = b; i <= e; i++) {	// so, :20,23 s \0 find \0 replace \0
+			ls = q;		// orig line start
+		  vc4:
+			buf1 = char_search(q, F, FORWARD, LIMITED);	// search cur line only for "find"
+			if (buf1 != NULL) {
+				// we found the "find" pattern- delete it
+				(void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
+				// inset the "replace" patern
+				(void) string_insert(buf1, R);	// insert the string
+				// check for "global"  :s/foo/bar/g
+				if (gflag == 1) {
+					if ((buf1 + strlen((char *) R)) < end_line(ls)) {
+						q = buf1 + strlen((char *) R);
+						goto vc4;	// don't let q move past cur line
+					}
+				}
+			}
+			q = next_line(ls);
+		}
+#endif							/* CONFIG_FEATURE_VI_SEARCH */
+	} else if (strncasecmp((char *) cmd, "version", i) == 0) {	// show software version
+		psb("%s", vi_Version);
+	} else if ((strncasecmp((char *) cmd, "write", i) == 0) ||	// write text to file
+			   (strncasecmp((char *) cmd, "wq", i) == 0) ||
+			   (strncasecmp((char *) cmd, "x", i) == 0)) {
+		// is there a file name to write to?
+		if (strlen((char *) args) > 0) {
+			fn = args;
+		}
+#ifdef CONFIG_FEATURE_VI_READONLY
+		if ((vi_readonly || readonly) && ! useforce) {
+			psbs("\"%s\" File is read only", fn);
+			goto vc3;
+		}
+#endif							/* CONFIG_FEATURE_VI_READONLY */
+		// how many lines in text[]?
+		li = count_lines(q, r);
+		ch = r - q + 1;
+		// see if file exists- if not, its just a new file request
+		if (useforce) {
+			// if "fn" is not write-able, chmod u+w
+			// sprintf(syscmd, "chmod u+w %s", fn);
+			// system(syscmd);
+			forced = TRUE;
+		}
+		l = file_write(fn, q, r);
+		if (useforce && forced) {
+			// chmod u-w
+			// sprintf(syscmd, "chmod u-w %s", fn);
+			// system(syscmd);
+			forced = FALSE;
+		}
+		psb("\"%s\" %dL, %dC", fn, li, l);
+		if (q == text && r == end - 1 && l == ch)
+			file_modified = FALSE;
+		if ((cmd[0] == 'x' || cmd[1] == 'q') && l == ch) {
+			editing = 0;
+		}
+#ifdef CONFIG_FEATURE_VI_READONLY
+	  vc3:;
+#endif							/* CONFIG_FEATURE_VI_READONLY */
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+	} else if (strncasecmp((char *) cmd, "yank", i) == 0) {	// yank lines
+		if (b < 0) {	// no addr given- use defaults
+			q = begin_line(dot);	// assume .,. for the range
+			r = end_line(dot);
+		}
+		text_yank(q, r, YDreg);
+		li = count_lines(q, r);
+		psb("Yank %d lines (%d chars) into [%c]",
+			li, strlen((char *) reg[YDreg]), what_reg());
+#endif							/* CONFIG_FEATURE_VI_YANKMARK */
+	} else {
+		// cmd unknown
+		ni((Byte *) cmd);
+	}
+  vc1:
+	dot = bound_dot(dot);	// make sure "dot" is valid
+	return;
+#ifdef CONFIG_FEATURE_VI_SEARCH
+colon_s_fail:
+	psb(":s expression missing delimiters");
+	return;
+#endif
+
+}
+
+static void Hit_Return(void)
+{
+	char c;
+
+	standout_start();	// start reverse video
+	write(1, "[Hit return to continue]", 24);
+	standout_end();		// end reverse video
+	while ((c = get_one_char()) != '\n' && c != '\r')	/*do nothing */
+		;
+	redraw(TRUE);		// force redraw all
+}
+#endif							/* CONFIG_FEATURE_VI_COLON */
+
+//----- Synchronize the cursor to Dot --------------------------
+static void sync_cursor(Byte * d, int *row, int *col)
+{
+	Byte *beg_cur, *end_cur;	// begin and end of "d" line
+	Byte *beg_scr, *end_scr;	// begin and end of screen
+	Byte *tp;
+	int cnt, ro, co;
+
+	beg_cur = begin_line(d);	// first char of cur line
+	end_cur = end_line(d);	// last char of cur line
+
+	beg_scr = end_scr = screenbegin;	// first char of screen
+	end_scr = end_screen();	// last char of screen
+
+	if (beg_cur < screenbegin) {
+		// "d" is before  top line on screen
+		// how many lines do we have to move
+		cnt = count_lines(beg_cur, screenbegin);
+	  sc1:
+		screenbegin = beg_cur;
+		if (cnt > (rows - 1) / 2) {
+			// we moved too many lines. put "dot" in middle of screen
+			for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
+				screenbegin = prev_line(screenbegin);
+			}
+		}
+	} else if (beg_cur > end_scr) {
+		// "d" is after bottom line on screen
+		// how many lines do we have to move
+		cnt = count_lines(end_scr, beg_cur);
+		if (cnt > (rows - 1) / 2)
+			goto sc1;	// too many lines
+		for (ro = 0; ro < cnt - 1; ro++) {
+			// move screen begin the same amount
+			screenbegin = next_line(screenbegin);
+			// now, move the end of screen
+			end_scr = next_line(end_scr);
+			end_scr = end_line(end_scr);
+		}
+	}
+	// "d" is on screen- find out which row
+	tp = screenbegin;
+	for (ro = 0; ro < rows - 1; ro++) {	// drive "ro" to correct row
+		if (tp == beg_cur)
+			break;
+		tp = next_line(tp);
+	}
+
+	// find out what col "d" is on
+	co = 0;
+	do {				// drive "co" to correct column
+		if (*tp == '\n' || *tp == '\0')
+			break;
+		if (*tp == '\t') {
+			//         7       - (co %    8  )
+			co += ((tabstop - 1) - (co % tabstop));
+		} else if (*tp < ' ') {
+			co++;		// display as ^X, use 2 columns
+		}
+	} while (tp++ < d && ++co);
+
+	// "co" is the column where "dot" is.
+	// The screen has "columns" columns.
+	// The currently displayed columns are  0+offset -- columns+ofset
+	// |-------------------------------------------------------------|
+	//               ^ ^                                ^
+	//        offset | |------- columns ----------------|
+	//
+	// If "co" is already in this range then we do not have to adjust offset
+	//      but, we do have to subtract the "offset" bias from "co".
+	// If "co" is outside this range then we have to change "offset".
+	// If the first char of a line is a tab the cursor will try to stay
+	//  in column 7, but we have to set offset to 0.
+
+	if (co < 0 + offset) {
+		offset = co;
+	}
+	if (co >= columns + offset) {
+		offset = co - columns + 1;
+	}
+	// if the first char of the line is a tab, and "dot" is sitting on it
+	//  force offset to 0.
+	if (d == beg_cur && *d == '\t') {
+		offset = 0;
+	}
+	co -= offset;
+
+	*row = ro;
+	*col = co;
+}
+
+//----- Text Movement Routines ---------------------------------
+static Byte *begin_line(Byte * p) // return pointer to first char cur line
+{
+	while (p > text && p[-1] != '\n')
+		p--;			// go to cur line B-o-l
+	return (p);
+}
+
+static Byte *end_line(Byte * p) // return pointer to NL of cur line line
+{
+	while (p < end - 1 && *p != '\n')
+		p++;			// go to cur line E-o-l
+	return (p);
+}
+
+extern inline Byte *dollar_line(Byte * p) // return pointer to just before NL line
+{
+	while (p < end - 1 && *p != '\n')
+		p++;			// go to cur line E-o-l
+	// Try to stay off of the Newline
+	if (*p == '\n' && (p - begin_line(p)) > 0)
+		p--;
+	return (p);
+}
+
+static Byte *prev_line(Byte * p) // return pointer first char prev line
+{
+	p = begin_line(p);	// goto begining of cur line
+	if (p[-1] == '\n' && p > text)
+		p--;			// step to prev line
+	p = begin_line(p);	// goto begining of prev line
+	return (p);
+}
+
+static Byte *next_line(Byte * p) // return pointer first char next line
+{
+	p = end_line(p);
+	if (*p == '\n' && p < end - 1)
+		p++;			// step to next line
+	return (p);
+}
+
+//----- Text Information Routines ------------------------------
+static Byte *end_screen(void)
+{
+	Byte *q;
+	int cnt;
+
+	// find new bottom line
+	q = screenbegin;
+	for (cnt = 0; cnt < rows - 2; cnt++)
+		q = next_line(q);
+	q = end_line(q);
+	return (q);
+}
+
+static int count_lines(Byte * start, Byte * stop) // count line from start to stop
+{
+	Byte *q;
+	int cnt;
+
+	if (stop < start) {	// start and stop are backwards- reverse them
+		q = start;
+		start = stop;
+		stop = q;
+	}
+	cnt = 0;
+	stop = end_line(stop);	// get to end of this line
+	for (q = start; q <= stop && q <= end - 1; q++) {
+		if (*q == '\n')
+			cnt++;
+	}
+	return (cnt);
+}
+
+static Byte *find_line(int li)	// find begining of line #li
+{
+	Byte *q;
+
+	for (q = text; li > 1; li--) {
+		q = next_line(q);
+	}
+	return (q);
+}
+
+//----- Dot Movement Routines ----------------------------------
+static void dot_left(void)
+{
+	if (dot > text && dot[-1] != '\n')
+		dot--;
+}
+
+static void dot_right(void)
+{
+	if (dot < end - 1 && *dot != '\n')
+		dot++;
+}
+
+static void dot_begin(void)
+{
+	dot = begin_line(dot);	// return pointer to first char cur line
+}
+
+static void dot_end(void)
+{
+	dot = end_line(dot);	// return pointer to last char cur line
+}
+
+static Byte *move_to_col(Byte * p, int l)
+{
+	int co;
+
+	p = begin_line(p);
+	co = 0;
+	do {
+		if (*p == '\n' || *p == '\0')
+			break;
+		if (*p == '\t') {
+			//         7       - (co %    8  )
+			co += ((tabstop - 1) - (co % tabstop));
+		} else if (*p < ' ') {
+			co++;		// display as ^X, use 2 columns
+		}
+	} while (++co <= l && p++ < end);
+	return (p);
+}
+
+static void dot_next(void)
+{
+	dot = next_line(dot);
+}
+
+static void dot_prev(void)
+{
+	dot = prev_line(dot);
+}
+
+static void dot_scroll(int cnt, int dir)
+{
+	Byte *q;
+
+	for (; cnt > 0; cnt--) {
+		if (dir < 0) {
+			// scroll Backwards
+			// ctrl-Y  scroll up one line
+			screenbegin = prev_line(screenbegin);
+		} else {
+			// scroll Forwards
+			// ctrl-E  scroll down one line
+			screenbegin = next_line(screenbegin);
+		}
+	}
+	// make sure "dot" stays on the screen so we dont scroll off
+	if (dot < screenbegin)
+		dot = screenbegin;
+	q = end_screen();	// find new bottom line
+	if (dot > q)
+		dot = begin_line(q);	// is dot is below bottom line?
+	dot_skip_over_ws();
+}
+
+static void dot_skip_over_ws(void)
+{
+	// skip WS
+	while (isspace(*dot) && *dot != '\n' && dot < end - 1)
+		dot++;
+}
+
+static void dot_delete(void)	// delete the char at 'dot'
+{
+	(void) text_hole_delete(dot, dot);
+}
+
+static Byte *bound_dot(Byte * p) // make sure  text[0] <= P < "end"
+{
+	if (p >= end && end > text) {
+		p = end - 1;
+		indicate_error('1');
+	}
+	if (p < text) {
+		p = text;
+		indicate_error('2');
+	}
+	return (p);
+}
+
+//----- Helper Utility Routines --------------------------------
+
+//----------------------------------------------------------------
+//----- Char Routines --------------------------------------------
+/* Chars that are part of a word-
+ *    0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+ * Chars that are Not part of a word (stoppers)
+ *    !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
+ * Chars that are WhiteSpace
+ *    TAB NEWLINE VT FF RETURN SPACE
+ * DO NOT COUNT NEWLINE AS WHITESPACE
+ */
+
+static Byte *new_screen(int ro, int co)
+{
+	int li;
+
+	if (screen != 0)
+		free(screen);
+	screensize = ro * co + 8;
+	screen = (Byte *) xmalloc(screensize);
+	// initialize the new screen. assume this will be a empty file.
+	screen_erase();
+	//   non-existant text[] lines start with a tilde (~).
+	for (li = 1; li < ro - 1; li++) {
+		screen[(li * co) + 0] = '~';
+	}
+	return (screen);
+}
+
+static Byte *new_text(int size)
+{
+	if (size < 10240)
+		size = 10240;	// have a minimum size for new files
+	if (text != 0) {
+		//text -= 4;
+		free(text);
+	}
+	text = (Byte *) xmalloc(size + 8);
+	memset(text, '\0', size);	// clear new text[]
+	//text += 4;		// leave some room for "oops"
+	textend = text + size - 1;
+	//textend -= 4;		// leave some root for "oops"
+	return (text);
+}
+
+#ifdef CONFIG_FEATURE_VI_SEARCH
+static int mycmp(Byte * s1, Byte * s2, int len)
+{
+	int i;
+
+	i = strncmp((char *) s1, (char *) s2, len);
+#ifdef CONFIG_FEATURE_VI_SETOPTS
+	if (ignorecase) {
+		i = strncasecmp((char *) s1, (char *) s2, len);
+	}
+#endif							/* CONFIG_FEATURE_VI_SETOPTS */
+	return (i);
+}
+
+static Byte *char_search(Byte * p, Byte * pat, int dir, int range)	// search for pattern starting at p
+{
+#ifndef REGEX_SEARCH
+	Byte *start, *stop;
+	int len;
+
+	len = strlen((char *) pat);
+	if (dir == FORWARD) {
+		stop = end - 1;	// assume range is p - end-1
+		if (range == LIMITED)
+			stop = next_line(p);	// range is to next line
+		for (start = p; start < stop; start++) {
+			if (mycmp(start, pat, len) == 0) {
+				return (start);
+			}
+		}
+	} else if (dir == BACK) {
+		stop = text;	// assume range is text - p
+		if (range == LIMITED)
+			stop = prev_line(p);	// range is to prev line
+		for (start = p - len; start >= stop; start--) {
+			if (mycmp(start, pat, len) == 0) {
+				return (start);
+			}
+		}
+	}
+	// pattern not found
+	return (NULL);
+#else							/*REGEX_SEARCH */
+	char *q;
+	struct re_pattern_buffer preg;
+	int i;
+	int size, range;
+
+	re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
+	preg.translate = 0;
+	preg.fastmap = 0;
+	preg.buffer = 0;
+	preg.allocated = 0;
+
+	// assume a LIMITED forward search
+	q = next_line(p);
+	q = end_line(q);
+	q = end - 1;
+	if (dir == BACK) {
+		q = prev_line(p);
+		q = text;
+	}
+	// count the number of chars to search over, forward or backward
+	size = q - p;
+	if (size < 0)
+		size = p - q;
+	// RANGE could be negative if we are searching backwards
+	range = q - p;
+
+	q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
+	if (q != 0) {
+		// The pattern was not compiled
+		psbs("bad search pattern: \"%s\": %s", pat, q);
+		i = 0;			// return p if pattern not compiled
+		goto cs1;
+	}
+
+	q = p;
+	if (range < 0) {
+		q = p - size;
+		if (q < text)
+			q = text;
+	}
+	// search for the compiled pattern, preg, in p[]
+	// range < 0-  search backward
+	// range > 0-  search forward
+	// 0 < start < size
+	// re_search() < 0  not found or error
+	// re_search() > 0  index of found pattern
+	//            struct pattern    char     int    int    int     struct reg
+	// re_search (*pattern_buffer,  *string, size,  start, range,  *regs)
+	i = re_search(&preg, q, size, 0, range, 0);
+	if (i == -1) {
+		p = 0;
+		i = 0;			// return NULL if pattern not found
+	}
+  cs1:
+	if (dir == FORWARD) {
+		p = p + i;
+	} else {
+		p = p - i;
+	}
+	return (p);
+#endif							/*REGEX_SEARCH */
+}
+#endif							/* CONFIG_FEATURE_VI_SEARCH */
+
+static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
+{
+	if (c == 22) {		// Is this an ctrl-V?
+		p = stupid_insert(p, '^');	// use ^ to indicate literal next
+		p--;			// backup onto ^
+		refresh(FALSE);	// show the ^
+		c = get_one_char();
+		*p = c;
+		p++;
+		file_modified = TRUE;	// has the file been modified
+	} else if (c == 27) {	// Is this an ESC?
+		cmd_mode = 0;
+		cmdcnt = 0;
+		end_cmd_q();	// stop adding to q
+		strcpy((char *) status_buffer, " ");	// clear the status buffer
+		if ((p[-1] != '\n') && (dot>text)) {
+			p--;
+		}
+	} else if (c == erase_char) {	// Is this a BS
+		//     123456789
+		if ((p[-1] != '\n') && (dot>text)) {
+			p--;
+			p = text_hole_delete(p, p);	// shrink buffer 1 char
+#ifdef CONFIG_FEATURE_VI_DOT_CMD
+			// also rmove char from last_modifying_cmd
+			if (strlen((char *) last_modifying_cmd) > 0) {
+				Byte *q;
+
+				q = last_modifying_cmd;
+				q[strlen((char *) q) - 1] = '\0';	// erase BS
+				q[strlen((char *) q) - 1] = '\0';	// erase prev char
+			}
+#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
+		}
+	} else {
+		// insert a char into text[]
+		Byte *sp;		// "save p"
+
+		if (c == 13)
+			c = '\n';	// translate \r to \n
+		sp = p;			// remember addr of insert
+		p = stupid_insert(p, c);	// insert the char
+#ifdef CONFIG_FEATURE_VI_SETOPTS
+		if (showmatch && strchr(")]}", *sp) != NULL) {
+			showmatching(sp);
+		}
+		if (autoindent && c == '\n') {	// auto indent the new line
+			Byte *q;
+
+			q = prev_line(p);	// use prev line as templet
+			for (; isblnk(*q); q++) {
+				p = stupid_insert(p, *q);	// insert the char
+			}
+		}
+#endif							/* CONFIG_FEATURE_VI_SETOPTS */
+	}
+	return (p);
+}
+
+static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
+{
+	p = text_hole_make(p, 1);
+	if (p != 0) {
+		*p = c;
+		file_modified = TRUE;	// has the file been modified
+		p++;
+	}
+	return (p);
+}
+
+static Byte find_range(Byte ** start, Byte ** stop, Byte c)
+{
+	Byte *save_dot, *p, *q;
+	int cnt;
+
+	save_dot = dot;
+	p = q = dot;
+
+	if (strchr("cdy><", c)) {
+		// these cmds operate on whole lines
+		p = q = begin_line(p);
+		for (cnt = 1; cnt < cmdcnt; cnt++) {
+			q = next_line(q);
+		}
+		q = end_line(q);
+	} else if (strchr("^%$0bBeEft", c)) {
+		// These cmds operate on char positions
+		do_cmd(c);		// execute movement cmd
+		q = dot;
+	} else if (strchr("wW", c)) {
+		do_cmd(c);		// execute movement cmd
+		if (dot > text)
+			dot--;		// move back off of next word
+		if (dot > text && *dot == '\n')
+			dot--;		// stay off NL
+		q = dot;
+	} else if (strchr("H-k{", c)) {
+		// these operate on multi-lines backwards
+		q = end_line(dot);	// find NL
+		do_cmd(c);		// execute movement cmd
+		dot_begin();
+		p = dot;
+	} else if (strchr("L+j}\r\n", c)) {
+		// these operate on multi-lines forwards
+		p = begin_line(dot);
+		do_cmd(c);		// execute movement cmd
+		dot_end();		// find NL
+		q = dot;
+	} else {
+		c = 27;			// error- return an ESC char
+		//break;
+	}
+	*start = p;
+	*stop = q;
+	if (q < p) {
+		*start = q;
+		*stop = p;
+	}
+	dot = save_dot;
+	return (c);
+}
+
+static int st_test(Byte * p, int type, int dir, Byte * tested)
+{
+	Byte c, c0, ci;
+	int test, inc;
+
+	inc = dir;
+	c = c0 = p[0];
+	ci = p[inc];
+	test = 0;
+
+	if (type == S_BEFORE_WS) {
+		c = ci;
+		test = ((!isspace(c)) || c == '\n');
+	}
+	if (type == S_TO_WS) {
+		c = c0;
+		test = ((!isspace(c)) || c == '\n');
+	}
+	if (type == S_OVER_WS) {
+		c = c0;
+		test = ((isspace(c)));
+	}
+	if (type == S_END_PUNCT) {
+		c = ci;
+		test = ((ispunct(c)));
+	}
+	if (type == S_END_ALNUM) {
+		c = ci;
+		test = ((isalnum(c)) || c == '_');
+	}
+	*tested = c;
+	return (test);
+}
+
+static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
+{
+	Byte c;
+
+	while (st_test(p, type, dir, &c)) {
+		// make sure we limit search to correct number of lines
+		if (c == '\n' && --linecnt < 1)
+			break;
+		if (dir >= 0 && p >= end - 1)
+			break;
+		if (dir < 0 && p <= text)
+			break;
+		p += dir;		// move to next char
+	}
+	return (p);
+}
+
+// find matching char of pair  ()  []  {}
+static Byte *find_pair(Byte * p, Byte c)
+{
+	Byte match, *q;
+	int dir, level;
+
+	match = ')';
+	level = 1;
+	dir = 1;			// assume forward
+	switch (c) {
+	case '(':
+		match = ')';
+		break;
+	case '[':
+		match = ']';
+		break;
+	case '{':
+		match = '}';
+		break;
+	case ')':
+		match = '(';
+		dir = -1;
+		break;
+	case ']':
+		match = '[';
+		dir = -1;
+		break;
+	case '}':
+		match = '{';
+		dir = -1;
+		break;
+	}
+	for (q = p + dir; text <= q && q < end; q += dir) {
+		// look for match, count levels of pairs  (( ))
+		if (*q == c)
+			level++;	// increase pair levels
+		if (*q == match)
+			level--;	// reduce pair level
+		if (level == 0)
+			break;		// found matching pair
+	}
+	if (level != 0)
+		q = NULL;		// indicate no match
+	return (q);
+}
+
+#ifdef CONFIG_FEATURE_VI_SETOPTS
+// show the matching char of a pair,  ()  []  {}
+static void showmatching(Byte * p)
+{
+	Byte *q, *save_dot;
+
+	// we found half of a pair
+	q = find_pair(p, *p);	// get loc of matching char
+	if (q == NULL) {
+		indicate_error('3');	// no matching char
+	} else {
+		// "q" now points to matching pair
+		save_dot = dot;	// remember where we are
+		dot = q;		// go to new loc
+		refresh(FALSE);	// let the user see it
+		(void) mysleep(40);	// give user some time
+		dot = save_dot;	// go back to old loc
+		refresh(FALSE);
+	}
+}
+#endif							/* CONFIG_FEATURE_VI_SETOPTS */
+
+//  open a hole in text[]
+static Byte *text_hole_make(Byte * p, int size)	// at "p", make a 'size' byte hole
+{
+	Byte *src, *dest;
+	int cnt;
+
+	if (size <= 0)
+		goto thm0;
+	src = p;
+	dest = p + size;
+	cnt = end - src;	// the rest of buffer
+	if (memmove(dest, src, cnt) != dest) {
+		psbs("can't create room for new characters");
+	}
+	memset(p, ' ', size);	// clear new hole
+	end = end + size;	// adjust the new END
+	file_modified = TRUE;	// has the file been modified
+  thm0:
+	return (p);
+}
+
+//  close a hole in text[]
+static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
+{
+	Byte *src, *dest;
+	int cnt, hole_size;
+
+	// move forwards, from beginning
+	// assume p <= q
+	src = q + 1;
+	dest = p;
+	if (q < p) {		// they are backward- swap them
+		src = p + 1;
+		dest = q;
+	}
+	hole_size = q - p + 1;
+	cnt = end - src;
+	if (src < text || src > end)
+		goto thd0;
+	if (dest < text || dest >= end)
+		goto thd0;
+	if (src >= end)
+		goto thd_atend;	// just delete the end of the buffer
+	if (memmove(dest, src, cnt) != dest) {
+		psbs("can't delete the character");
+	}
+  thd_atend:
+	end = end - hole_size;	// adjust the new END
+	if (dest >= end)
+		dest = end - 1;	// make sure dest in below end-1
+	if (end <= text)
+		dest = end = text;	// keep pointers valid
+	file_modified = TRUE;	// has the file been modified
+  thd0:
+	return (dest);
+}
+
+// copy text into register, then delete text.
+// if dist <= 0, do not include, or go past, a NewLine
+//
+static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
+{
+	Byte *p;
+
+	// make sure start <= stop
+	if (start > stop) {
+		// they are backwards, reverse them
+		p = start;
+		start = stop;
+		stop = p;
+	}
+	if (dist <= 0) {
+		// we can not cross NL boundaries
+		p = start;
+		if (*p == '\n')
+			return (p);
+		// dont go past a NewLine
+		for (; p + 1 <= stop; p++) {
+			if (p[1] == '\n') {
+				stop = p;	// "stop" just before NewLine
+				break;
+			}
+		}
+	}
+	p = start;
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+	text_yank(start, stop, YDreg);
+#endif							/* CONFIG_FEATURE_VI_YANKMARK */
+	if (yf == YANKDEL) {
+		p = text_hole_delete(start, stop);
+	}					// delete lines
+	return (p);
+}
+
+static void show_help(void)
+{
+	puts("These features are available:"
+#ifdef CONFIG_FEATURE_VI_SEARCH
+	"\n\tPattern searches with / and ?"
+#endif							/* CONFIG_FEATURE_VI_SEARCH */
+#ifdef CONFIG_FEATURE_VI_DOT_CMD
+	"\n\tLast command repeat with \'.\'"
+#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+	"\n\tLine marking with  'x"
+	"\n\tNamed buffers with  \"x"
+#endif							/* CONFIG_FEATURE_VI_YANKMARK */
+#ifdef CONFIG_FEATURE_VI_READONLY
+	"\n\tReadonly if vi is called as \"view\""
+	"\n\tReadonly with -R command line arg"
+#endif							/* CONFIG_FEATURE_VI_READONLY */
+#ifdef CONFIG_FEATURE_VI_SET
+	"\n\tSome colon mode commands with \':\'"
+#endif							/* CONFIG_FEATURE_VI_SET */
+#ifdef CONFIG_FEATURE_VI_SETOPTS
+	"\n\tSettable options with \":set\""
+#endif							/* CONFIG_FEATURE_VI_SETOPTS */
+#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
+	"\n\tSignal catching- ^C"
+	"\n\tJob suspend and resume with ^Z"
+#endif							/* CONFIG_FEATURE_VI_USE_SIGNALS */
+#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
+	"\n\tAdapt to window re-sizes"
+#endif							/* CONFIG_FEATURE_VI_WIN_RESIZE */
+	);
+}
+
+extern inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
+{
+	Byte c, b[2];
+
+	b[1] = '\0';
+	strcpy((char *) buf, "");	// init buf
+	if (strlen((char *) s) <= 0)
+		s = (Byte *) "(NULL)";
+	for (; *s > '\0'; s++) {
+		c = *s;
+		if (*s > '~') {
+			strcat((char *) buf, SOs);
+			c = *s - 128;
+		}
+		if (*s < ' ') {
+			strcat((char *) buf, "^");
+			c += '@';
+		}
+		b[0] = c;
+		strcat((char *) buf, (char *) b);
+		if (*s > '~')
+			strcat((char *) buf, SOn);
+		if (*s == '\n') {
+			strcat((char *) buf, "$");
+		}
+	}
+}
+
+#ifdef CONFIG_FEATURE_VI_DOT_CMD
+static void start_new_cmd_q(Byte c)
+{
+	// release old cmd
+	if (last_modifying_cmd != 0)
+		free(last_modifying_cmd);
+	// get buffer for new cmd
+	last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
+	memset(last_modifying_cmd, '\0', BUFSIZ);	// clear new cmd queue
+	// if there is a current cmd count put it in the buffer first
+	if (cmdcnt > 0)
+		sprintf((char *) last_modifying_cmd, "%d", cmdcnt);
+	// save char c onto queue
+	last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
+	adding2q = 1;
+	return;
+}
+
+static void end_cmd_q(void)
+{
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+	YDreg = 26;			// go back to default Yank/Delete reg
+#endif							/* CONFIG_FEATURE_VI_YANKMARK */
+	adding2q = 0;
+	return;
+}
+#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
+
+#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
+static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
+{
+	int cnt, i;
+
+	i = strlen((char *) s);
+	p = text_hole_make(p, i);
+	strncpy((char *) p, (char *) s, i);
+	for (cnt = 0; *s != '\0'; s++) {
+		if (*s == '\n')
+			cnt++;
+	}
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+	psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
+#endif							/* CONFIG_FEATURE_VI_YANKMARK */
+	return (p);
+}
+#endif							/* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
+
+#ifdef CONFIG_FEATURE_VI_YANKMARK
+static Byte *text_yank(Byte * p, Byte * q, int dest)	// copy text into a register
+{
+	Byte *t;
+	int cnt;
+
+	if (q < p) {		// they are backwards- reverse them
+		t = q;
+		q = p;
+		p = t;
+	}
+	cnt = q - p + 1;
+	t = reg[dest];
+	if (t != 0) {		// if already a yank register
+		free(t);		//   free it
+	}
+	t = (Byte *) xmalloc(cnt + 1);	// get a new register
+	memset(t, '\0', cnt + 1);	// clear new text[]
+	strncpy((char *) t, (char *) p, cnt);	// copy text[] into bufer
+	reg[dest] = t;
+	return (p);
+}
+
+static Byte what_reg(void)
+{
+	Byte c;
+	int i;
+
+	i = 0;
+	c = 'D';			// default to D-reg
+	if (0 <= YDreg && YDreg <= 25)
+		c = 'a' + (Byte) YDreg;
+	if (YDreg == 26)
+		c = 'D';
+	if (YDreg == 27)
+		c = 'U';
+	return (c);
+}
+
+static void check_context(Byte cmd)
+{
+	// A context is defined to be "modifying text"
+	// Any modifying command establishes a new context.
+
+	if (dot < context_start || dot > context_end) {
+		if (strchr((char *) modifying_cmds, cmd) != NULL) {
+			// we are trying to modify text[]- make this the current context
+			mark[27] = mark[26];	// move cur to prev
+			mark[26] = dot;	// move local to cur
+			context_start = prev_line(prev_line(dot));
+			context_end = next_line(next_line(dot));
+			//loiter= start_loiter= now;
+		}
+	}
+	return;
+}
+
+extern inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
+{
+	Byte *tmp;
+
+	// the current context is in mark[26]
+	// the previous context is in mark[27]
+	// only swap context if other context is valid
+	if (text <= mark[27] && mark[27] <= end - 1) {
+		tmp = mark[27];
+		mark[27] = mark[26];
+		mark[26] = tmp;
+		p = mark[26];	// where we are going- previous context
+		context_start = prev_line(prev_line(prev_line(p)));
+		context_end = next_line(next_line(next_line(p)));
+	}
+	return (p);
+}
+#endif							/* CONFIG_FEATURE_VI_YANKMARK */
+
+static int isblnk(Byte c) // is the char a blank or tab
+{
+	return (c == ' ' || c == '\t');
+}
+
+//----- Set terminal attributes --------------------------------
+static void rawmode(void)
+{
+	tcgetattr(0, &term_orig);
+	term_vi = term_orig;
+	term_vi.c_lflag &= (~ICANON & ~ECHO);	// leave ISIG ON- allow intr's
+	term_vi.c_iflag &= (~IXON & ~ICRNL);
+	term_vi.c_oflag &= (~ONLCR);
+#ifndef linux
+	term_vi.c_cc[VMIN] = 1;
+	term_vi.c_cc[VTIME] = 0;
+#endif
+	erase_char = term_vi.c_cc[VERASE];
+	tcsetattr(0, TCSANOW, &term_vi);
+}
+
+static void cookmode(void)
+{
+	tcsetattr(0, TCSANOW, &term_orig);
+}
+
+#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
+//----- See what the window size currently is --------------------
+static void window_size_get(int sig)
+{
+	int i;
+
+	i = ioctl(0, TIOCGWINSZ, &winsize);
+	if (i != 0) {
+		// force 24x80
+		winsize.ws_row = 24;
+		winsize.ws_col = 80;
+	}
+	if (winsize.ws_row <= 1) {
+		winsize.ws_row = 24;
+	}
+	if (winsize.ws_col <= 1) {
+		winsize.ws_col = 80;
+	}
+	rows = (int) winsize.ws_row;
+	columns = (int) winsize.ws_col;
+}
+#endif							/* CONFIG_FEATURE_VI_WIN_RESIZE */
+
+//----- Come here when we get a window resize signal ---------
+#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
+static void winch_sig(int sig)
+{
+	signal(SIGWINCH, winch_sig);
+#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
+	window_size_get(0);
+#endif							/* CONFIG_FEATURE_VI_WIN_RESIZE */
+	new_screen(rows, columns);	// get memory for virtual screen
+	redraw(TRUE);		// re-draw the screen
+}
+
+//----- Come here when we get a continue signal -------------------
+static void cont_sig(int sig)
+{
+	rawmode();			// terminal to "raw"
+	*status_buffer = '\0';	// clear the status buffer
+	redraw(TRUE);		// re-draw the screen
+
+	signal(SIGTSTP, suspend_sig);
+	signal(SIGCONT, SIG_DFL);
+	kill(getpid(), SIGCONT);
+}
+
+//----- Come here when we get a Suspend signal -------------------
+static void suspend_sig(int sig)
+{
+	place_cursor(rows - 1, 0, FALSE);	// go to bottom of screen
+	clear_to_eol();		// Erase to end of line
+	cookmode();			// terminal to "cooked"
+
+	signal(SIGCONT, cont_sig);
+	signal(SIGTSTP, SIG_DFL);
+	kill(getpid(), SIGTSTP);
+}
+
+//----- Come here when we get a signal ---------------------------
+static void catch_sig(int sig)
+{
+	signal(SIGHUP, catch_sig);
+	signal(SIGINT, catch_sig);
+	signal(SIGTERM, catch_sig);
+	longjmp(restart, sig);
+}
+
+static void alarm_sig(int sig)
+{
+	signal(SIGALRM, catch_sig);
+	longjmp(restart, sig);
+}
+
+//----- Come here when we get a core dump signal -----------------
+static void core_sig(int sig)
+{
+	signal(SIGQUIT, core_sig);
+	signal(SIGILL, core_sig);
+	signal(SIGTRAP, core_sig);
+	signal(SIGIOT, core_sig);
+	signal(SIGABRT, core_sig);
+	signal(SIGFPE, core_sig);
+	signal(SIGBUS, core_sig);
+	signal(SIGSEGV, core_sig);
+#ifdef SIGSYS
+	signal(SIGSYS, core_sig);
+#endif
+
+	dot = bound_dot(dot);	// make sure "dot" is valid
+
+	longjmp(restart, sig);
+}
+#endif							/* CONFIG_FEATURE_VI_USE_SIGNALS */
+
+static int mysleep(int hund)	// sleep for 'h' 1/100 seconds
+{
+	// Don't hang- Wait 5/100 seconds-  1 Sec= 1000000
+	FD_ZERO(&rfds);
+	FD_SET(0, &rfds);
+	tv.tv_sec = 0;
+	tv.tv_usec = hund * 10000;
+	select(1, &rfds, NULL, NULL, &tv);
+	return (FD_ISSET(0, &rfds));
+}
+
+//----- IO Routines --------------------------------------------
+static Byte readit(void)	// read (maybe cursor) key from stdin
+{
+	Byte c;
+	int i, bufsiz, cnt, cmdindex;
+	struct esc_cmds {
+		Byte *seq;
+		Byte val;
+	};
+
+	static struct esc_cmds esccmds[] = {
+		{(Byte *) "OA", (Byte) VI_K_UP},	// cursor key Up
+		{(Byte *) "OB", (Byte) VI_K_DOWN},	// cursor key Down
+		{(Byte *) "OC", (Byte) VI_K_RIGHT},	// Cursor Key Right
+		{(Byte *) "OD", (Byte) VI_K_LEFT},	// cursor key Left
+		{(Byte *) "OH", (Byte) VI_K_HOME},	// Cursor Key Home
+		{(Byte *) "OF", (Byte) VI_K_END},	// Cursor Key End
+		{(Byte *) "", (Byte) VI_K_UP},	// cursor key Up
+		{(Byte *) "", (Byte) VI_K_DOWN},	// cursor key Down
+		{(Byte *) "", (Byte) VI_K_RIGHT},	// Cursor Key Right
+		{(Byte *) "", (Byte) VI_K_LEFT},	// cursor key Left
+		{(Byte *) "", (Byte) VI_K_HOME},	// Cursor Key Home
+		{(Byte *) "", (Byte) VI_K_END},	// Cursor Key End
+		{(Byte *) "[2~", (Byte) VI_K_INSERT},	// Cursor Key Insert
+		{(Byte *) "[5~", (Byte) VI_K_PAGEUP},	// Cursor Key Page Up
+		{(Byte *) "[6~", (Byte) VI_K_PAGEDOWN},	// Cursor Key Page Down
+		{(Byte *) "OP", (Byte) VI_K_FUN1},	// Function Key F1
+		{(Byte *) "OQ", (Byte) VI_K_FUN2},	// Function Key F2
+		{(Byte *) "OR", (Byte) VI_K_FUN3},	// Function Key F3
+		{(Byte *) "OS", (Byte) VI_K_FUN4},	// Function Key F4
+		{(Byte *) "[15~", (Byte) VI_K_FUN5},	// Function Key F5
+		{(Byte *) "[17~", (Byte) VI_K_FUN6},	// Function Key F6
+		{(Byte *) "[18~", (Byte) VI_K_FUN7},	// Function Key F7
+		{(Byte *) "[19~", (Byte) VI_K_FUN8},	// Function Key F8
+		{(Byte *) "[20~", (Byte) VI_K_FUN9},	// Function Key F9
+		{(Byte *) "[21~", (Byte) VI_K_FUN10},	// Function Key F10
+		{(Byte *) "[23~", (Byte) VI_K_FUN11},	// Function Key F11
+		{(Byte *) "[24~", (Byte) VI_K_FUN12},	// Function Key F12
+		{(Byte *) "[11~", (Byte) VI_K_FUN1},	// Function Key F1
+		{(Byte *) "[12~", (Byte) VI_K_FUN2},	// Function Key F2
+		{(Byte *) "[13~", (Byte) VI_K_FUN3},	// Function Key F3
+		{(Byte *) "[14~", (Byte) VI_K_FUN4},	// Function Key F4
+	};
+
+#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
+
+	(void) alarm(0);	// turn alarm OFF while we wait for input
+	// get input from User- are there already input chars in Q?
+	bufsiz = strlen((char *) readbuffer);
+	if (bufsiz <= 0) {
+	  ri0:
+		// the Q is empty, wait for a typed char
+		bufsiz = read(0, readbuffer, BUFSIZ - 1);
+		if (bufsiz < 0) {
+			if (errno == EINTR)
+				goto ri0;	// interrupted sys call
+			if (errno == EBADF)
+				editing = 0;
+			if (errno == EFAULT)
+				editing = 0;
+			if (errno == EINVAL)
+				editing = 0;
+			if (errno == EIO)
+				editing = 0;
+			errno = 0;
+			bufsiz = 0;
+		}
+		readbuffer[bufsiz] = '\0';
+	}
+	// return char if it is not part of ESC sequence
+	if (readbuffer[0] != 27)
+		goto ri1;
+
+	// This is an ESC char. Is this Esc sequence?
+	// Could be bare Esc key. See if there are any
+	// more chars to read after the ESC. This would
+	// be a Function or Cursor Key sequence.
+	FD_ZERO(&rfds);
+	FD_SET(0, &rfds);
+	tv.tv_sec = 0;
+	tv.tv_usec = 50000;	// Wait 5/100 seconds- 1 Sec=1000000
+
+	// keep reading while there are input chars and room in buffer
+	while (select(1, &rfds, NULL, NULL, &tv) > 0 && bufsiz <= (BUFSIZ - 5)) {
+		// read the rest of the ESC string
+		i = read(0, (void *) (readbuffer + bufsiz), BUFSIZ - bufsiz);
+		if (i > 0) {
+			bufsiz += i;
+			readbuffer[bufsiz] = '\0';	// Terminate the string
+		}
+	}
+	// Maybe cursor or function key?
+	for (cmdindex = 0; cmdindex < ESCCMDS_COUNT; cmdindex++) {
+		cnt = strlen((char *) esccmds[cmdindex].seq);
+		i = strncmp((char *) esccmds[cmdindex].seq, (char *) readbuffer, cnt);
+		if (i == 0) {
+			// is a Cursor key- put derived value back into Q
+			readbuffer[0] = esccmds[cmdindex].val;
+			// squeeze out the ESC sequence
+			for (i = 1; i < cnt; i++) {
+				memmove(readbuffer + 1, readbuffer + 2, BUFSIZ - 2);
+				readbuffer[BUFSIZ - 1] = '\0';
+			}
+			break;
+		}
+	}
+  ri1:
+	c = readbuffer[0];
+	// remove one char from Q
+	memmove(readbuffer, readbuffer + 1, BUFSIZ - 1);
+	readbuffer[BUFSIZ - 1] = '\0';
+	(void) alarm(3);	// we are done waiting for input, turn alarm ON
+	return (c);
+}
+
+//----- IO Routines --------------------------------------------
+static Byte get_one_char()
+{
+	static Byte c;
+
+#ifdef CONFIG_FEATURE_VI_DOT_CMD
+	// ! adding2q  && ioq == 0  read()
+	// ! adding2q  && ioq != 0  *ioq
+	// adding2q         *last_modifying_cmd= read()
+	if (!adding2q) {
+		// we are not adding to the q.
+		// but, we may be reading from a q
+		if (ioq == 0) {
+			// there is no current q, read from STDIN
+			c = readit();	// get the users input
+		} else {
+			// there is a queue to get chars from first
+			c = *ioq++;
+			if (c == '\0') {
+				// the end of the q, read from STDIN
+				free(ioq_start);
+				ioq_start = ioq = 0;
+				c = readit();	// get the users input
+			}
+		}
+	} else {
+		// adding STDIN chars to q
+		c = readit();	// get the users input
+		if (last_modifying_cmd != 0) {
+			// add new char to q
+			last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
+		}
+	}
+#else							/* CONFIG_FEATURE_VI_DOT_CMD */
+	c = readit();		// get the users input
+#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
+	return (c);			// return the char, where ever it came from
+}
+
+static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
+{
+	Byte buf[BUFSIZ];
+	Byte c;
+	int i;
+	static Byte *obufp = NULL;
+
+	strcpy((char *) buf, (char *) prompt);
+	*status_buffer = '\0';	// clear the status buffer
+	place_cursor(rows - 1, 0, FALSE);	// go to Status line, bottom of screen
+	clear_to_eol();		// clear the line
+	write(1, prompt, strlen((char *) prompt));	// write out the :, /, or ? prompt
+
+	for (i = strlen((char *) buf); i < BUFSIZ;) {
+		c = get_one_char();	// read user input
+		if (c == '\n' || c == '\r' || c == 27)
+			break;		// is this end of input
+		if (c == erase_char) {	// user wants to erase prev char
+			i--;		// backup to prev char
+			buf[i] = '\0';	// erase the char
+			buf[i + 1] = '\0';	// null terminate buffer
+			write(1, " ", 3);	// erase char on screen
+			if (i <= 0) {	// user backs up before b-o-l, exit
+				break;
+			}
+		} else {
+			buf[i] = c;	// save char in buffer
+			buf[i + 1] = '\0';	// make sure buffer is null terminated
+			write(1, buf + i, 1);	// echo the char back to user
+			i++;
+		}
+	}
+	refresh(FALSE);
+	if (obufp != NULL)
+		free(obufp);
+	obufp = (Byte *) xstrdup((char *) buf);
+	return (obufp);
+}
+
+static int file_size(Byte * fn) // what is the byte size of "fn"
+{
+	struct stat st_buf;
+	int cnt, sr;
+
+	if (fn == 0 || strlen(fn) <= 0)
+		return (-1);
+	cnt = -1;
+	sr = stat((char *) fn, &st_buf);	// see if file exists
+	if (sr >= 0) {
+		cnt = (int) st_buf.st_size;
+	}
+	return (cnt);
+}
+
+static int file_insert(Byte * fn, Byte * p, int size)
+{
+	int fd, cnt;
+
+	cnt = -1;
+#ifdef CONFIG_FEATURE_VI_READONLY
+	readonly = FALSE;
+#endif							/* CONFIG_FEATURE_VI_READONLY */
+	if (fn == 0 || strlen((char*) fn) <= 0) {
+		psbs("No filename given");
+		goto fi0;
+	}
+	if (size == 0) {
+		// OK- this is just a no-op
+		cnt = 0;
+		goto fi0;
+	}
+	if (size < 0) {
+		psbs("Trying to insert a negative number (%d) of characters", size);
+		goto fi0;
+	}
+	if (p < text || p > end) {
+		psbs("Trying to insert file outside of memory");
+		goto fi0;
+	}
+
+	// see if we can open the file
+#ifdef CONFIG_FEATURE_VI_READONLY
+	if (vi_readonly) goto fi1;		// do not try write-mode
+#endif
+	fd = open((char *) fn, O_RDWR);			// assume read & write
+	if (fd < 0) {
+		// could not open for writing- maybe file is read only
+#ifdef CONFIG_FEATURE_VI_READONLY
+  fi1:
+#endif
+		fd = open((char *) fn, O_RDONLY);	// try read-only
+		if (fd < 0) {
+			psbs("\"%s\" %s", fn, "could not open file");
+			goto fi0;
+		}
+#ifdef CONFIG_FEATURE_VI_READONLY
+		// got the file- read-only
+		readonly = TRUE;
+#endif							/* CONFIG_FEATURE_VI_READONLY */
+	}
+	p = text_hole_make(p, size);
+	cnt = read(fd, p, size);
+	close(fd);
+	if (cnt < 0) {
+		cnt = -1;
+		p = text_hole_delete(p, p + size - 1);	// un-do buffer insert
+		psbs("could not read file \"%s\"", fn);
+	} else if (cnt < size) {
+		// There was a partial read, shrink unused space text[]
+		p = text_hole_delete(p + cnt, p + (size - cnt) - 1);	// un-do buffer insert
+		psbs("could not read all of file \"%s\"", fn);
+	}
+	if (cnt >= size)
+		file_modified = TRUE;
+  fi0:
+	return (cnt);
+}
+
+static int file_write(Byte * fn, Byte * first, Byte * last)
+{
+	int fd, cnt, charcnt;
+
+	if (fn == 0) {
+		psbs("No current filename");
+		return (-1);
+	}
+	charcnt = 0;
+	// FIXIT- use the correct umask()
+	fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
+	if (fd < 0)
+		return (-1);
+	cnt = last - first + 1;
+	charcnt = write(fd, first, cnt);
+	if (charcnt == cnt) {
+		// good write
+		//file_modified= FALSE; // the file has not been modified
+	} else {
+		charcnt = 0;
+	}
+	close(fd);
+	return (charcnt);
+}
+
+//----- Terminal Drawing ---------------------------------------
+// The terminal is made up of 'rows' line of 'columns' columns.
+// classicly this would be 24 x 80.
+//  screen coordinates
+//  0,0     ...     0,79
+//  1,0     ...     1,79
+//  .       ...     .
+//  .       ...     .
+//  22,0    ...     22,79
+//  23,0    ...     23,79   status line
+//
+
+//----- Move the cursor to row x col (count from 0, not 1) -------
+static void place_cursor(int row, int col, int opti)
+{
+	char cm1[BUFSIZ];
+	char *cm;
+	int l;
+#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
+	char cm2[BUFSIZ];
+	Byte *screenp;
+	// char cm3[BUFSIZ];
+	int Rrow= last_row;
+#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
+	
+	memset(cm1, '\0', BUFSIZ - 1);  // clear the buffer
+
+	if (row < 0) row = 0;
+	if (row >= rows) row = rows - 1;
+	if (col < 0) col = 0;
+	if (col >= columns) col = columns - 1;
+	
+	//----- 1.  Try the standard terminal ESC sequence
+	sprintf((char *) cm1, CMrc, row + 1, col + 1);
+	cm= cm1;
+	if (! opti) goto pc0;
+
+#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
+	//----- find the minimum # of chars to move cursor -------------
+	//----- 2.  Try moving with discreet chars (Newline, [back]space, ...)
+	memset(cm2, '\0', BUFSIZ - 1);  // clear the buffer
+	
+	// move to the correct row
+	while (row < Rrow) {
+		// the cursor has to move up
+		strcat(cm2, CMup);
+		Rrow--;
+	}
+	while (row > Rrow) {
+		// the cursor has to move down
+		strcat(cm2, CMdown);
+		Rrow++;
+	}
+	
+	// now move to the correct column
+	strcat(cm2, "\r");			// start at col 0
+	// just send out orignal source char to get to correct place
+	screenp = &screen[row * columns];	// start of screen line
+	strncat(cm2, screenp, col);
+
+	//----- 3.  Try some other way of moving cursor
+	//---------------------------------------------
+
+	// pick the shortest cursor motion to send out
+	cm= cm1;
+	if (strlen(cm2) < strlen(cm)) {
+		cm= cm2;
+	}  /* else if (strlen(cm3) < strlen(cm)) {
+		cm= cm3;
+	} */
+#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
+  pc0:
+	l= strlen(cm);
+	if (l) write(1, cm, l);			// move the cursor
+}
+
+//----- Erase from cursor to end of line -----------------------
+static void clear_to_eol()
+{
+	write(1, Ceol, strlen(Ceol));	// Erase from cursor to end of line
+}
+
+//----- Erase from cursor to end of screen -----------------------
+static void clear_to_eos()
+{
+	write(1, Ceos, strlen(Ceos));	// Erase from cursor to end of screen
+}
+
+//----- Start standout mode ------------------------------------
+static void standout_start() // send "start reverse video" sequence
+{
+	write(1, SOs, strlen(SOs));	// Start reverse video mode
+}
+
+//----- End standout mode --------------------------------------
+static void standout_end() // send "end reverse video" sequence
+{
+	write(1, SOn, strlen(SOn));	// End reverse video mode
+}
+
+//----- Flash the screen  --------------------------------------
+static void flash(int h)
+{
+	standout_start();	// send "start reverse video" sequence
+	redraw(TRUE);
+	(void) mysleep(h);
+	standout_end();		// send "end reverse video" sequence
+	redraw(TRUE);
+}
+
+static void beep()
+{
+	write(1, bell, strlen(bell));	// send out a bell character
+}
+
+static void indicate_error(char c)
+{
+#ifdef CONFIG_FEATURE_VI_CRASHME
+	if (crashme > 0)
+		return;			// generate a random command
+#endif							/* CONFIG_FEATURE_VI_CRASHME */
+	if (err_method == 0) {
+		beep();
+	} else {
+		flash(10);
+	}
+}
+
+//----- Screen[] Routines --------------------------------------
+//----- Erase the Screen[] memory ------------------------------
+static void screen_erase()
+{
+	memset(screen, ' ', screensize);	// clear new screen
+}
+
+//----- Draw the status line at bottom of the screen -------------
+static void show_status_line(void)
+{
+	static int last_cksum;
+	int l, cnt, cksum;
+
+	cnt = strlen((char *) status_buffer);
+	for (cksum= l= 0; l < cnt; l++) { cksum += (int)(status_buffer[l]); }
+	// don't write the status line unless it changes
+	if (cnt > 0 && last_cksum != cksum) {
+		last_cksum= cksum;		// remember if we have seen this line
+		place_cursor(rows - 1, 0, FALSE);	// put cursor on status line
+		write(1, status_buffer, cnt);
+		clear_to_eol();
+		place_cursor(crow, ccol, FALSE);	// put cursor back in correct place
+	}
+}
+
+//----- format the status buffer, the bottom line of screen ------
+// print status buffer, with STANDOUT mode
+static void psbs(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	strcpy((char *) status_buffer, SOs);	// Terminal standout mode on
+	vsprintf((char *) status_buffer + strlen((char *) status_buffer), format,
+			 args);
+	strcat((char *) status_buffer, SOn);	// Terminal standout mode off
+	va_end(args);
+
+	return;
+}
+
+// print status buffer
+static void psb(char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	vsprintf((char *) status_buffer, format, args);
+	va_end(args);
+	return;
+}
+
+static void ni(Byte * s) // display messages
+{
+	Byte buf[BUFSIZ];
+
+	print_literal(buf, s);
+	psbs("\'%s\' is not implemented", buf);
+}
+
+static void edit_status(void)	// show file status on status line
+{
+	int cur, tot, percent;
+
+	cur = count_lines(text, dot);
+	tot = count_lines(text, end - 1);
+	//    current line         percent
+	//   -------------    ~~ ----------
+	//    total lines            100
+	if (tot > 0) {
+		percent = (100 * cur) / tot;
+	} else {
+		cur = tot = 0;
+		percent = 100;
+	}
+	psb("\"%s\""
+#ifdef CONFIG_FEATURE_VI_READONLY
+		"%s"
+#endif							/* CONFIG_FEATURE_VI_READONLY */
+		"%s line %d of %d --%d%%--",
+		(cfn != 0 ? (char *) cfn : "No file"),
+#ifdef CONFIG_FEATURE_VI_READONLY
+		((vi_readonly || readonly) ? " [Read only]" : ""),
+#endif							/* CONFIG_FEATURE_VI_READONLY */
+		(file_modified ? " [modified]" : ""),
+		cur, tot, percent);
+}
+
+//----- Force refresh of all Lines -----------------------------
+static void redraw(int full_screen)
+{
+	place_cursor(0, 0, FALSE);	// put cursor in correct place
+	clear_to_eos();		// tel terminal to erase display
+	screen_erase();		// erase the internal screen buffer
+	refresh(full_screen);	// this will redraw the entire display
+}
+
+//----- Format a text[] line into a buffer ---------------------
+static void format_line(Byte *dest, Byte *src, int li)
+{
+	int co;
+	Byte c;
+	
+	for (co= 0; co < MAX_SCR_COLS; co++) {
+		c= ' ';		// assume blank
+		if (li > 0 && co == 0) {
+			c = '~';        // not first line, assume Tilde
+		}
+		// are there chars in text[] and have we gone past the end
+		if (text < end && src < end) {
+			c = *src++;
+		}
+		if (c == '\n')
+			break;
+		if (c < ' ' || c > '~') {
+			if (c == '\t') {
+				c = ' ';
+				//       co %    8     !=     7
+				for (; (co % tabstop) != (tabstop - 1); co++) {
+					dest[co] = c;
+				}
+			} else {
+				dest[co++] = '^';
+				c |= '@';       // make it visible
+				c &= 0x7f;      // get rid of hi bit
+			}
+		}
+		// the co++ is done here so that the column will
+		// not be overwritten when we blank-out the rest of line
+		dest[co] = c;
+		if (src >= end)
+			break;
+	}
+}
+
+//----- Refresh the changed screen lines -----------------------
+// Copy the source line from text[] into the buffer and note
+// if the current screenline is different from the new buffer.
+// If they differ then that line needs redrawing on the terminal.
+//
+static void refresh(int full_screen)
+{
+	static int old_offset;
+	int li, changed;
+	Byte buf[MAX_SCR_COLS];
+	Byte *tp, *sp;		// pointer into text[] and screen[]
+#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
+	int last_li= -2;				// last line that changed- for optimizing cursor movement
+#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
+
+#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
+	window_size_get(0);
+#endif							/* CONFIG_FEATURE_VI_WIN_RESIZE */
+	sync_cursor(dot, &crow, &ccol);	// where cursor will be (on "dot")
+	tp = screenbegin;	// index into text[] of top line
+
+	// compare text[] to screen[] and mark screen[] lines that need updating
+	for (li = 0; li < rows - 1; li++) {
+		int cs, ce;				// column start & end
+		memset(buf, ' ', MAX_SCR_COLS);		// blank-out the buffer
+		buf[MAX_SCR_COLS-1] = 0;		// NULL terminate the buffer
+		// format current text line into buf
+		format_line(buf, tp, li);
+
+		// skip to the end of the current text[] line
+		while (tp < end && *tp++ != '\n') /*no-op*/ ;
+
+		// see if there are any changes between vitual screen and buf
+		changed = FALSE;	// assume no change
+		cs= 0;
+		ce= columns-1;
+		sp = &screen[li * columns];	// start of screen line
+		if (full_screen) {
+			// force re-draw of every single column from 0 - columns-1
+			goto re0;
+		}
+		// compare newly formatted buffer with virtual screen
+		// look forward for first difference between buf and screen
+		for ( ; cs <= ce; cs++) {
+			if (buf[cs + offset] != sp[cs]) {
+				changed = TRUE;	// mark for redraw
+				break;
+			}
+		}
+
+		// look backward for last difference between buf and screen
+		for ( ; ce >= cs; ce--) {
+			if (buf[ce + offset] != sp[ce]) {
+				changed = TRUE;	// mark for redraw
+				break;
+			}
+		}
+		// now, cs is index of first diff, and ce is index of last diff
+
+		// if horz offset has changed, force a redraw
+		if (offset != old_offset) {
+  re0:
+			changed = TRUE;
+		}
+
+		// make a sanity check of columns indexes
+		if (cs < 0) cs= 0;
+		if (ce > columns-1) ce= columns-1;
+		if (cs > ce) {  cs= 0;  ce= columns-1;  }
+		// is there a change between vitual screen and buf
+		if (changed) {
+			//  copy changed part of buffer to virtual screen
+			memmove(sp+cs, buf+(cs+offset), ce-cs+1);
+
+			// move cursor to column of first change
+			if (offset != old_offset) {
+				// opti_cur_move is still too stupid
+				// to handle offsets correctly
+				place_cursor(li, cs, FALSE);
+			} else {
+#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
+				// if this just the next line
+				//  try to optimize cursor movement
+				//  otherwise, use standard ESC sequence
+				place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
+				last_li= li;
+#else							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
+				place_cursor(li, cs, FALSE);	// use standard ESC sequence
+#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
+			}
+
+			// write line out to terminal
+			write(1, sp+cs, ce-cs+1);
+#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
+			last_row = li;
+#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
+		}
+	}
+
+#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
+	place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
+	last_row = crow;
+#else
+	place_cursor(crow, ccol, FALSE);
+#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
+	
+	if (offset != old_offset)
+		old_offset = offset;
+}
+
 //---------------------------------------------------------------------
 //----- the Ascii Chart -----------------------------------------------
 //
@@ -1628,2337 +3962,3 @@
 	if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
 		dot--;
 }
-
-//----- The Colon commands -------------------------------------
-#ifdef CONFIG_FEATURE_VI_COLON
-static Byte *get_one_address(Byte * p, int *addr)	// get colon addr, if present
-{
-	int st;
-	Byte *q;
-
-#ifdef CONFIG_FEATURE_VI_YANKMARK
-	Byte c;
-#endif							/* CONFIG_FEATURE_VI_YANKMARK */
-#ifdef CONFIG_FEATURE_VI_SEARCH
-	Byte *pat, buf[BUFSIZ];
-#endif							/* CONFIG_FEATURE_VI_SEARCH */
-
-	*addr = -1;			// assume no addr
-	if (*p == '.') {	// the current line
-		p++;
-		q = begin_line(dot);
-		*addr = count_lines(text, q);
-#ifdef CONFIG_FEATURE_VI_YANKMARK
-	} else if (*p == '\'') {	// is this a mark addr
-		p++;
-		c = tolower(*p);
-		p++;
-		if (c >= 'a' && c <= 'z') {
-			// we have a mark
-			c = c - 'a';
-			q = mark[(int) c];
-			if (q != NULL) {	// is mark valid
-				*addr = count_lines(text, q);	// count lines
-			}
-		}
-#endif							/* CONFIG_FEATURE_VI_YANKMARK */
-#ifdef CONFIG_FEATURE_VI_SEARCH
-	} else if (*p == '/') {	// a search pattern
-		q = buf;
-		for (p++; *p; p++) {
-			if (*p == '/')
-				break;
-			*q++ = *p;
-			*q = '\0';
-		}
-		pat = (Byte *) xstrdup((char *) buf);	// save copy of pattern
-		if (*p == '/')
-			p++;
-		q = char_search(dot, pat, FORWARD, FULL);
-		if (q != NULL) {
-			*addr = count_lines(text, q);
-		}
-		free(pat);
-#endif							/* CONFIG_FEATURE_VI_SEARCH */
-	} else if (*p == '$') {	// the last line in file
-		p++;
-		q = begin_line(end - 1);
-		*addr = count_lines(text, q);
-	} else if (isdigit(*p)) {	// specific line number
-		sscanf((char *) p, "%d%n", addr, &st);
-		p += st;
-	} else {			// I don't reconise this
-		// unrecognised address- assume -1
-		*addr = -1;
-	}
-	return (p);
-}
-
-static Byte *get_address(Byte *p, int *b, int *e)	// get two colon addrs, if present
-{
-	//----- get the address' i.e., 1,3   'a,'b  -----
-	// get FIRST addr, if present
-	while (isblnk(*p))
-		p++;				// skip over leading spaces
-	if (*p == '%') {			// alias for 1,$
-		p++;
-		*b = 1;
-		*e = count_lines(text, end-1);
-		goto ga0;
-	}
-	p = get_one_address(p, b);
-	while (isblnk(*p))
-		p++;
-	if (*p == ',') {			// is there a address seperator
-		p++;
-		while (isblnk(*p))
-			p++;
-		// get SECOND addr, if present
-		p = get_one_address(p, e);
-	}
-ga0:
-	while (isblnk(*p))
-		p++;				// skip over trailing spaces
-	return (p);
-}
-
-static void colon(Byte * buf)
-{
-	Byte c, *orig_buf, *buf1, *q, *r;
-	Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
-	int i, l, li, ch, st, b, e;
-	int useforce, forced;
-	struct stat st_buf;
-
-	// :3154	// if (-e line 3154) goto it  else stay put
-	// :4,33w! foo	// write a portion of buffer to file "foo"
-	// :w		// write all of buffer to current file
-	// :q		// quit
-	// :q!		// quit- dont care about modified file
-	// :'a,'z!sort -u   // filter block through sort
-	// :'f		// goto mark "f"
-	// :'fl		// list literal the mark "f" line
-	// :.r bar	// read file "bar" into buffer before dot
-	// :/123/,/abc/d    // delete lines from "123" line to "abc" line
-	// :/xyz/	// goto the "xyz" line
-	// :s/find/replace/ // substitute pattern "find" with "replace"
-	// :!<cmd>	// run <cmd> then return
-	//
-	if (strlen((char *) buf) <= 0)
-		goto vc1;
-	if (*buf == ':')
-		buf++;			// move past the ':'
-
-	forced = useforce = FALSE;
-	li = st = ch = i = 0;
-	b = e = -1;
-	q = text;			// assume 1,$ for the range
-	r = end - 1;
-	li = count_lines(text, end - 1);
-	fn = cfn;			// default to current file
-	memset(cmd, '\0', BUFSIZ);	// clear cmd[]
-	memset(args, '\0', BUFSIZ);	// clear args[]
-
-	// look for optional address(es)  :.  :1  :1,9   :'q,'a   :%
-	buf = get_address(buf, &b, &e);
-
-	// remember orig command line
-	orig_buf = buf;
-
-	// get the COMMAND into cmd[]
-	buf1 = cmd;
-	while (*buf != '\0') {
-		if (isspace(*buf))
-			break;
-		*buf1++ = *buf++;
-	}
-	// get any ARGuments
-	while (isblnk(*buf))
-		buf++;
-	strcpy((char *) args, (char *) buf);
-	buf1 = last_char_is((char *)cmd, '!');
-	if (buf1) {
-		useforce = TRUE;
-		*buf1 = '\0';   // get rid of !
-	}
-	if (b >= 0) {
-		// if there is only one addr, then the addr
-		// is the line number of the single line the
-		// user wants. So, reset the end
-		// pointer to point at end of the "b" line
-		q = find_line(b);	// what line is #b
-		r = end_line(q);
-		li = 1;
-	}
-	if (e >= 0) {
-		// we were given two addrs.  change the
-		// end pointer to the addr given by user.
-		r = find_line(e);	// what line is #e
-		r = end_line(r);
-		li = e - b + 1;
-	}
-	// ------------ now look for the command ------------
-	i = strlen((char *) cmd);
-	if (i == 0) {		// :123CR goto line #123
-		if (b >= 0) {
-			dot = find_line(b);	// what line is #b
-			dot_skip_over_ws();
-		}
-	} else if (strncmp((char *) cmd, "!", 1) == 0) {	// run a cmd
-		// :!ls   run the <cmd>
-		(void) alarm(0);		// wait for input- no alarms
-		place_cursor(rows - 1, 0, FALSE);	// go to Status line
-		clear_to_eol();			// clear the line
-		cookmode();
-		system(orig_buf+1);		// run the cmd
-		rawmode();
-		Hit_Return();			// let user see results
-		(void) alarm(3);		// done waiting for input
-	} else if (strncmp((char *) cmd, "=", i) == 0) {	// where is the address
-		if (b < 0) {	// no addr given- use defaults
-			b = e = count_lines(text, dot);
-		}
-		psb("%d", b);
-	} else if (strncasecmp((char *) cmd, "delete", i) == 0) {	// delete lines
-		if (b < 0) {	// no addr given- use defaults
-			q = begin_line(dot);	// assume .,. for the range
-			r = end_line(dot);
-		}
-		dot = yank_delete(q, r, 1, YANKDEL);	// save, then delete lines
-		dot_skip_over_ws();
-	} else if (strncasecmp((char *) cmd, "edit", i) == 0) {	// Edit a file
-		int sr;
-		sr= 0;
-		// don't edit, if the current file has been modified
-		if (file_modified && ! useforce) {
-			psbs("No write since last change (:edit! overrides)");
-			goto vc1;
-		}
-		if (strlen(args) > 0) {
-			// the user supplied a file name
-			fn= args;
-		} else if (cfn != 0 && strlen(cfn) > 0) {
-			// no user supplied name- use the current filename
-			fn= cfn;
-			goto vc5;
-		} else {
-			// no user file name, no current name- punt
-			psbs("No current filename");
-			goto vc1;
-		}
-
-		// see if file exists- if not, its just a new file request
-		if ((sr=stat((char*)fn, &st_buf)) < 0) {
-			// This is just a request for a new file creation.
-			// The file_insert below will fail but we get
-			// an empty buffer with a file name.  Then the "write"
-			// command can do the create.
-		} else {
-			if ((st_buf.st_mode & (S_IFREG)) == 0) {
-				// This is not a regular file
-				psbs("\"%s\" is not a regular file", fn);
-				goto vc1;
-			}
-			if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
-				// dont have any read permissions
-				psbs("\"%s\" is not readable", fn);
-				goto vc1;
-			}
-		}
-
-		// There is a read-able regular file
-		// make this the current file
-		q = (Byte *) xstrdup((char *) fn);	// save the cfn
-		if (cfn != 0)
-			free(cfn);		// free the old name
-		cfn = q;			// remember new cfn
-
-	  vc5:
-		// delete all the contents of text[]
-		new_text(2 * file_size(fn));
-		screenbegin = dot = end = text;
-
-		// insert new file
-		ch = file_insert(fn, text, file_size(fn));
-
-		if (ch < 1) {
-			// start empty buf with dummy line
-			(void) char_insert(text, '\n');
-			ch= 1;
-		}
-		file_modified = FALSE;
-#ifdef CONFIG_FEATURE_VI_YANKMARK
-		if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
-			free(reg[Ureg]);	//   free orig line reg- for 'U'
-			reg[Ureg]= 0;
-		}
-		if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
-			free(reg[YDreg]);	//   free default yank/delete register
-			reg[YDreg]= 0;
-		}
-		for (li = 0; li < 28; li++) {
-			mark[li] = 0;
-		}				// init the marks
-#endif							/* CONFIG_FEATURE_VI_YANKMARK */
-		// how many lines in text[]?
-		li = count_lines(text, end - 1);
-		psb("\"%s\"%s"
-#ifdef CONFIG_FEATURE_VI_READONLY
-			"%s"
-#endif							/* CONFIG_FEATURE_VI_READONLY */
-			" %dL, %dC", cfn,
-			(sr < 0 ? " [New file]" : ""),
-#ifdef CONFIG_FEATURE_VI_READONLY
-			((vi_readonly || readonly) ? " [Read only]" : ""),
-#endif							/* CONFIG_FEATURE_VI_READONLY */
-			li, ch);
-	} else if (strncasecmp((char *) cmd, "file", i) == 0) {	// what File is this
-		if (b != -1 || e != -1) {
-			ni((Byte *) "No address allowed on this command");
-			goto vc1;
-		}
-		if (strlen((char *) args) > 0) {
-			// user wants a new filename
-			if (cfn != NULL)
-				free(cfn);
-			cfn = (Byte *) xstrdup((char *) args);
-		} else {
-			// user wants file status info
-			edit_status();
-		}
-	} else if (strncasecmp((char *) cmd, "features", i) == 0) {	// what features are available
-		// print out values of all features
-		place_cursor(rows - 1, 0, FALSE);	// go to Status line, bottom of screen
-		clear_to_eol();	// clear the line
-		cookmode();
-		show_help();
-		rawmode();
-		Hit_Return();
-	} else if (strncasecmp((char *) cmd, "list", i) == 0) {	// literal print line
-		if (b < 0) {	// no addr given- use defaults
-			q = begin_line(dot);	// assume .,. for the range
-			r = end_line(dot);
-		}
-		place_cursor(rows - 1, 0, FALSE);	// go to Status line, bottom of screen
-		clear_to_eol();	// clear the line
-		write(1, "\r\n", 2);
-		for (; q <= r; q++) {
-			c = *q;
-			if (c > '~')
-				standout_start();
-			if (c == '\n') {
-				write(1, "$\r", 2);
-			} else if (*q < ' ') {
-				write(1, "^", 1);
-				c += '@';
-			}
-			write(1, &c, 1);
-			if (c > '~')
-				standout_end();
-		}
-#ifdef CONFIG_FEATURE_VI_SET
-	  vc2:
-#endif							/* CONFIG_FEATURE_VI_SET */
-		Hit_Return();
-	} else if ((strncasecmp((char *) cmd, "quit", i) == 0) ||	// Quit
-			   (strncasecmp((char *) cmd, "next", i) == 0)) {	// edit next file
-		if (useforce) {
-			// force end of argv list
-			if (*cmd == 'q') {
-				optind = save_argc;
-			}
-			editing = 0;
-			goto vc1;
-		}
-		// don't exit if the file been modified
-		if (file_modified) {
-			psbs("No write since last change (:%s! overrides)",
-				 (*cmd == 'q' ? "quit" : "next"));
-			goto vc1;
-		}
-		// are there other file to edit
-		if (*cmd == 'q' && optind < save_argc - 1) {
-			psbs("%d more file to edit", (save_argc - optind - 1));
-			goto vc1;
-		}
-		if (*cmd == 'n' && optind >= save_argc - 1) {
-			psbs("No more files to edit");
-			goto vc1;
-		}
-		editing = 0;
-	} else if (strncasecmp((char *) cmd, "read", i) == 0) {	// read file into text[]
-		fn = args;
-		if (strlen((char *) fn) <= 0) {
-			psbs("No filename given");
-			goto vc1;
-		}
-		if (b < 0) {	// no addr given- use defaults
-			q = begin_line(dot);	// assume "dot"
-		}
-		// read after current line- unless user said ":0r foo"
-		if (b != 0)
-			q = next_line(q);
-#ifdef CONFIG_FEATURE_VI_READONLY
-		l= readonly;			// remember current files' status
-#endif
-		ch = file_insert(fn, q, file_size(fn));
-#ifdef CONFIG_FEATURE_VI_READONLY
-		readonly= l;
-#endif
-		if (ch < 0)
-			goto vc1;	// nothing was inserted
-		// how many lines in text[]?
-		li = count_lines(q, q + ch - 1);
-		psb("\"%s\""
-#ifdef CONFIG_FEATURE_VI_READONLY
-			"%s"
-#endif							/* CONFIG_FEATURE_VI_READONLY */
-			" %dL, %dC", fn,
-#ifdef CONFIG_FEATURE_VI_READONLY
-			((vi_readonly || readonly) ? " [Read only]" : ""),
-#endif							/* CONFIG_FEATURE_VI_READONLY */
-			li, ch);
-		if (ch > 0) {
-			// if the insert is before "dot" then we need to update
-			if (q <= dot)
-				dot += ch;
-			file_modified = TRUE;
-		}
-	} else if (strncasecmp((char *) cmd, "rewind", i) == 0) {	// rewind cmd line args
-		if (file_modified && ! useforce) {
-			psbs("No write since last change (:rewind! overrides)");
-		} else {
-			// reset the filenames to edit
-			optind = fn_start - 1;
-			editing = 0;
-		}
-#ifdef CONFIG_FEATURE_VI_SET
-	} else if (strncasecmp((char *) cmd, "set", i) == 0) {	// set or clear features
-		i = 0;			// offset into args
-		if (strlen((char *) args) == 0) {
-			// print out values of all options
-			place_cursor(rows - 1, 0, FALSE);	// go to Status line, bottom of screen
-			clear_to_eol();	// clear the line
-			printf("----------------------------------------\r\n");
-#ifdef CONFIG_FEATURE_VI_SETOPTS
-			if (!autoindent)
-				printf("no");
-			printf("autoindent ");
-			if (!err_method)
-				printf("no");
-			printf("flash ");
-			if (!ignorecase)
-				printf("no");
-			printf("ignorecase ");
-			if (!showmatch)
-				printf("no");
-			printf("showmatch ");
-			printf("tabstop=%d ", tabstop);
-#endif							/* CONFIG_FEATURE_VI_SETOPTS */
-			printf("\r\n");
-			goto vc2;
-		}
-		if (strncasecmp((char *) args, "no", 2) == 0)
-			i = 2;		// ":set noautoindent"
-#ifdef CONFIG_FEATURE_VI_SETOPTS
-		if (strncasecmp((char *) args + i, "autoindent", 10) == 0 ||
-			strncasecmp((char *) args + i, "ai", 2) == 0) {
-			autoindent = (i == 2) ? 0 : 1;
-		}
-		if (strncasecmp((char *) args + i, "flash", 5) == 0 ||
-			strncasecmp((char *) args + i, "fl", 2) == 0) {
-			err_method = (i == 2) ? 0 : 1;
-		}
-		if (strncasecmp((char *) args + i, "ignorecase", 10) == 0 ||
-			strncasecmp((char *) args + i, "ic", 2) == 0) {
-			ignorecase = (i == 2) ? 0 : 1;
-		}
-		if (strncasecmp((char *) args + i, "showmatch", 9) == 0 ||
-			strncasecmp((char *) args + i, "sm", 2) == 0) {
-			showmatch = (i == 2) ? 0 : 1;
-		}
-		if (strncasecmp((char *) args + i, "tabstop", 7) == 0) {
-			sscanf(strchr((char *) args + i, '='), "=%d", &ch);
-			if (ch > 0 && ch < columns - 1)
-				tabstop = ch;
-		}
-#endif							/* CONFIG_FEATURE_VI_SETOPTS */
-#endif							/* CONFIG_FEATURE_VI_SET */
-#ifdef CONFIG_FEATURE_VI_SEARCH
-	} else if (strncasecmp((char *) cmd, "s", 1) == 0) {	// substitute a pattern with a replacement pattern
-		Byte *ls, *F, *R;
-		int gflag;
-
-		// F points to the "find" pattern
-		// R points to the "replace" pattern
-		// replace the cmd line delimiters "/" with NULLs
-		gflag = 0;		// global replace flag
-		c = orig_buf[1];	// what is the delimiter
-		F = orig_buf + 2;	// start of "find"
-		R = (Byte *) strchr((char *) F, c);	// middle delimiter
-		if (!R) goto colon_s_fail;
-		*R++ = '\0';	// terminate "find"
-		buf1 = (Byte *) strchr((char *) R, c);
-		if (!buf1) goto colon_s_fail;
-		*buf1++ = '\0';	// terminate "replace"
-		if (*buf1 == 'g') {	// :s/foo/bar/g
-			buf1++;
-			gflag++;	// turn on gflag
-		}
-		q = begin_line(q);
-		if (b < 0) {	// maybe :s/foo/bar/
-			q = begin_line(dot);	// start with cur line
-			b = count_lines(text, q);	// cur line number
-		}
-		if (e < 0)
-			e = b;		// maybe :.s/foo/bar/
-		for (i = b; i <= e; i++) {	// so, :20,23 s \0 find \0 replace \0
-			ls = q;		// orig line start
-		  vc4:
-			buf1 = char_search(q, F, FORWARD, LIMITED);	// search cur line only for "find"
-			if (buf1 != NULL) {
-				// we found the "find" pattern- delete it
-				(void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
-				// inset the "replace" patern
-				(void) string_insert(buf1, R);	// insert the string
-				// check for "global"  :s/foo/bar/g
-				if (gflag == 1) {
-					if ((buf1 + strlen((char *) R)) < end_line(ls)) {
-						q = buf1 + strlen((char *) R);
-						goto vc4;	// don't let q move past cur line
-					}
-				}
-			}
-			q = next_line(ls);
-		}
-#endif							/* CONFIG_FEATURE_VI_SEARCH */
-	} else if (strncasecmp((char *) cmd, "version", i) == 0) {	// show software version
-		psb("%s", vi_Version);
-	} else if ((strncasecmp((char *) cmd, "write", i) == 0) ||	// write text to file
-			   (strncasecmp((char *) cmd, "wq", i) == 0) ||
-			   (strncasecmp((char *) cmd, "x", i) == 0)) {
-		// is there a file name to write to?
-		if (strlen((char *) args) > 0) {
-			fn = args;
-		}
-#ifdef CONFIG_FEATURE_VI_READONLY
-		if ((vi_readonly || readonly) && ! useforce) {
-			psbs("\"%s\" File is read only", fn);
-			goto vc3;
-		}
-#endif							/* CONFIG_FEATURE_VI_READONLY */
-		// how many lines in text[]?
-		li = count_lines(q, r);
-		ch = r - q + 1;
-		// see if file exists- if not, its just a new file request
-		if (useforce) {
-			// if "fn" is not write-able, chmod u+w
-			// sprintf(syscmd, "chmod u+w %s", fn);
-			// system(syscmd);
-			forced = TRUE;
-		}
-		l = file_write(fn, q, r);
-		if (useforce && forced) {
-			// chmod u-w
-			// sprintf(syscmd, "chmod u-w %s", fn);
-			// system(syscmd);
-			forced = FALSE;
-		}
-		psb("\"%s\" %dL, %dC", fn, li, l);
-		if (q == text && r == end - 1 && l == ch)
-			file_modified = FALSE;
-		if ((cmd[0] == 'x' || cmd[1] == 'q') && l == ch) {
-			editing = 0;
-		}
-#ifdef CONFIG_FEATURE_VI_READONLY
-	  vc3:;
-#endif							/* CONFIG_FEATURE_VI_READONLY */
-#ifdef CONFIG_FEATURE_VI_YANKMARK
-	} else if (strncasecmp((char *) cmd, "yank", i) == 0) {	// yank lines
-		if (b < 0) {	// no addr given- use defaults
-			q = begin_line(dot);	// assume .,. for the range
-			r = end_line(dot);
-		}
-		text_yank(q, r, YDreg);
-		li = count_lines(q, r);
-		psb("Yank %d lines (%d chars) into [%c]",
-			li, strlen((char *) reg[YDreg]), what_reg());
-#endif							/* CONFIG_FEATURE_VI_YANKMARK */
-	} else {
-		// cmd unknown
-		ni((Byte *) cmd);
-	}
-  vc1:
-	dot = bound_dot(dot);	// make sure "dot" is valid
-	return;
-#ifdef CONFIG_FEATURE_VI_SEARCH
-colon_s_fail:
-	psb(":s expression missing delimiters");
-	return;
-#endif
-
-}
-
-static void Hit_Return(void)
-{
-	char c;
-
-	standout_start();	// start reverse video
-	write(1, "[Hit return to continue]", 24);
-	standout_end();		// end reverse video
-	while ((c = get_one_char()) != '\n' && c != '\r')	/*do nothing */
-		;
-	redraw(TRUE);		// force redraw all
-}
-#endif							/* CONFIG_FEATURE_VI_COLON */
-
-//----- Synchronize the cursor to Dot --------------------------
-static void sync_cursor(Byte * d, int *row, int *col)
-{
-	Byte *beg_cur, *end_cur;	// begin and end of "d" line
-	Byte *beg_scr, *end_scr;	// begin and end of screen
-	Byte *tp;
-	int cnt, ro, co;
-
-	beg_cur = begin_line(d);	// first char of cur line
-	end_cur = end_line(d);	// last char of cur line
-
-	beg_scr = end_scr = screenbegin;	// first char of screen
-	end_scr = end_screen();	// last char of screen
-
-	if (beg_cur < screenbegin) {
-		// "d" is before  top line on screen
-		// how many lines do we have to move
-		cnt = count_lines(beg_cur, screenbegin);
-	  sc1:
-		screenbegin = beg_cur;
-		if (cnt > (rows - 1) / 2) {
-			// we moved too many lines. put "dot" in middle of screen
-			for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
-				screenbegin = prev_line(screenbegin);
-			}
-		}
-	} else if (beg_cur > end_scr) {
-		// "d" is after bottom line on screen
-		// how many lines do we have to move
-		cnt = count_lines(end_scr, beg_cur);
-		if (cnt > (rows - 1) / 2)
-			goto sc1;	// too many lines
-		for (ro = 0; ro < cnt - 1; ro++) {
-			// move screen begin the same amount
-			screenbegin = next_line(screenbegin);
-			// now, move the end of screen
-			end_scr = next_line(end_scr);
-			end_scr = end_line(end_scr);
-		}
-	}
-	// "d" is on screen- find out which row
-	tp = screenbegin;
-	for (ro = 0; ro < rows - 1; ro++) {	// drive "ro" to correct row
-		if (tp == beg_cur)
-			break;
-		tp = next_line(tp);
-	}
-
-	// find out what col "d" is on
-	co = 0;
-	do {				// drive "co" to correct column
-		if (*tp == '\n' || *tp == '\0')
-			break;
-		if (*tp == '\t') {
-			//         7       - (co %    8  )
-			co += ((tabstop - 1) - (co % tabstop));
-		} else if (*tp < ' ') {
-			co++;		// display as ^X, use 2 columns
-		}
-	} while (tp++ < d && ++co);
-
-	// "co" is the column where "dot" is.
-	// The screen has "columns" columns.
-	// The currently displayed columns are  0+offset -- columns+ofset
-	// |-------------------------------------------------------------|
-	//               ^ ^                                ^
-	//        offset | |------- columns ----------------|
-	//
-	// If "co" is already in this range then we do not have to adjust offset
-	//      but, we do have to subtract the "offset" bias from "co".
-	// If "co" is outside this range then we have to change "offset".
-	// If the first char of a line is a tab the cursor will try to stay
-	//  in column 7, but we have to set offset to 0.
-
-	if (co < 0 + offset) {
-		offset = co;
-	}
-	if (co >= columns + offset) {
-		offset = co - columns + 1;
-	}
-	// if the first char of the line is a tab, and "dot" is sitting on it
-	//  force offset to 0.
-	if (d == beg_cur && *d == '\t') {
-		offset = 0;
-	}
-	co -= offset;
-
-	*row = ro;
-	*col = co;
-}
-
-//----- Text Movement Routines ---------------------------------
-static Byte *begin_line(Byte * p) // return pointer to first char cur line
-{
-	while (p > text && p[-1] != '\n')
-		p--;			// go to cur line B-o-l
-	return (p);
-}
-
-static Byte *end_line(Byte * p) // return pointer to NL of cur line line
-{
-	while (p < end - 1 && *p != '\n')
-		p++;			// go to cur line E-o-l
-	return (p);
-}
-
-static Byte *dollar_line(Byte * p) // return pointer to just before NL line
-{
-	while (p < end - 1 && *p != '\n')
-		p++;			// go to cur line E-o-l
-	// Try to stay off of the Newline
-	if (*p == '\n' && (p - begin_line(p)) > 0)
-		p--;
-	return (p);
-}
-
-static Byte *prev_line(Byte * p) // return pointer first char prev line
-{
-	p = begin_line(p);	// goto begining of cur line
-	if (p[-1] == '\n' && p > text)
-		p--;			// step to prev line
-	p = begin_line(p);	// goto begining of prev line
-	return (p);
-}
-
-static Byte *next_line(Byte * p) // return pointer first char next line
-{
-	p = end_line(p);
-	if (*p == '\n' && p < end - 1)
-		p++;			// step to next line
-	return (p);
-}
-
-//----- Text Information Routines ------------------------------
-static Byte *end_screen(void)
-{
-	Byte *q;
-	int cnt;
-
-	// find new bottom line
-	q = screenbegin;
-	for (cnt = 0; cnt < rows - 2; cnt++)
-		q = next_line(q);
-	q = end_line(q);
-	return (q);
-}
-
-static int count_lines(Byte * start, Byte * stop) // count line from start to stop
-{
-	Byte *q;
-	int cnt;
-
-	if (stop < start) {	// start and stop are backwards- reverse them
-		q = start;
-		start = stop;
-		stop = q;
-	}
-	cnt = 0;
-	stop = end_line(stop);	// get to end of this line
-	for (q = start; q <= stop && q <= end - 1; q++) {
-		if (*q == '\n')
-			cnt++;
-	}
-	return (cnt);
-}
-
-static Byte *find_line(int li)	// find begining of line #li
-{
-	Byte *q;
-
-	for (q = text; li > 1; li--) {
-		q = next_line(q);
-	}
-	return (q);
-}
-
-//----- Dot Movement Routines ----------------------------------
-static void dot_left(void)
-{
-	if (dot > text && dot[-1] != '\n')
-		dot--;
-}
-
-static void dot_right(void)
-{
-	if (dot < end - 1 && *dot != '\n')
-		dot++;
-}
-
-static void dot_begin(void)
-{
-	dot = begin_line(dot);	// return pointer to first char cur line
-}
-
-static void dot_end(void)
-{
-	dot = end_line(dot);	// return pointer to last char cur line
-}
-
-static Byte *move_to_col(Byte * p, int l)
-{
-	int co;
-
-	p = begin_line(p);
-	co = 0;
-	do {
-		if (*p == '\n' || *p == '\0')
-			break;
-		if (*p == '\t') {
-			//         7       - (co %    8  )
-			co += ((tabstop - 1) - (co % tabstop));
-		} else if (*p < ' ') {
-			co++;		// display as ^X, use 2 columns
-		}
-	} while (++co <= l && p++ < end);
-	return (p);
-}
-
-static void dot_next(void)
-{
-	dot = next_line(dot);
-}
-
-static void dot_prev(void)
-{
-	dot = prev_line(dot);
-}
-
-static void dot_scroll(int cnt, int dir)
-{
-	Byte *q;
-
-	for (; cnt > 0; cnt--) {
-		if (dir < 0) {
-			// scroll Backwards
-			// ctrl-Y  scroll up one line
-			screenbegin = prev_line(screenbegin);
-		} else {
-			// scroll Forwards
-			// ctrl-E  scroll down one line
-			screenbegin = next_line(screenbegin);
-		}
-	}
-	// make sure "dot" stays on the screen so we dont scroll off
-	if (dot < screenbegin)
-		dot = screenbegin;
-	q = end_screen();	// find new bottom line
-	if (dot > q)
-		dot = begin_line(q);	// is dot is below bottom line?
-	dot_skip_over_ws();
-}
-
-static void dot_skip_over_ws(void)
-{
-	// skip WS
-	while (isspace(*dot) && *dot != '\n' && dot < end - 1)
-		dot++;
-}
-
-static void dot_delete(void)	// delete the char at 'dot'
-{
-	(void) text_hole_delete(dot, dot);
-}
-
-static Byte *bound_dot(Byte * p) // make sure  text[0] <= P < "end"
-{
-	if (p >= end && end > text) {
-		p = end - 1;
-		indicate_error('1');
-	}
-	if (p < text) {
-		p = text;
-		indicate_error('2');
-	}
-	return (p);
-}
-
-//----- Helper Utility Routines --------------------------------
-
-//----------------------------------------------------------------
-//----- Char Routines --------------------------------------------
-/* Chars that are part of a word-
- *    0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
- * Chars that are Not part of a word (stoppers)
- *    !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
- * Chars that are WhiteSpace
- *    TAB NEWLINE VT FF RETURN SPACE
- * DO NOT COUNT NEWLINE AS WHITESPACE
- */
-
-static Byte *new_screen(int ro, int co)
-{
-	int li;
-
-	if (screen != 0)
-		free(screen);
-	screensize = ro * co + 8;
-	screen = (Byte *) xmalloc(screensize);
-	// initialize the new screen. assume this will be a empty file.
-	screen_erase();
-	//   non-existant text[] lines start with a tilde (~).
-	for (li = 1; li < ro - 1; li++) {
-		screen[(li * co) + 0] = '~';
-	}
-	return (screen);
-}
-
-static Byte *new_text(int size)
-{
-	if (size < 10240)
-		size = 10240;	// have a minimum size for new files
-	if (text != 0) {
-		//text -= 4;
-		free(text);
-	}
-	text = (Byte *) xmalloc(size + 8);
-	memset(text, '\0', size);	// clear new text[]
-	//text += 4;		// leave some room for "oops"
-	textend = text + size - 1;
-	//textend -= 4;		// leave some root for "oops"
-	return (text);
-}
-
-#ifdef CONFIG_FEATURE_VI_SEARCH
-static int mycmp(Byte * s1, Byte * s2, int len)
-{
-	int i;
-
-	i = strncmp((char *) s1, (char *) s2, len);
-#ifdef CONFIG_FEATURE_VI_SETOPTS
-	if (ignorecase) {
-		i = strncasecmp((char *) s1, (char *) s2, len);
-	}
-#endif							/* CONFIG_FEATURE_VI_SETOPTS */
-	return (i);
-}
-
-static Byte *char_search(Byte * p, Byte * pat, int dir, int range)	// search for pattern starting at p
-{
-#ifndef REGEX_SEARCH
-	Byte *start, *stop;
-	int len;
-
-	len = strlen((char *) pat);
-	if (dir == FORWARD) {
-		stop = end - 1;	// assume range is p - end-1
-		if (range == LIMITED)
-			stop = next_line(p);	// range is to next line
-		for (start = p; start < stop; start++) {
-			if (mycmp(start, pat, len) == 0) {
-				return (start);
-			}
-		}
-	} else if (dir == BACK) {
-		stop = text;	// assume range is text - p
-		if (range == LIMITED)
-			stop = prev_line(p);	// range is to prev line
-		for (start = p - len; start >= stop; start--) {
-			if (mycmp(start, pat, len) == 0) {
-				return (start);
-			}
-		}
-	}
-	// pattern not found
-	return (NULL);
-#else							/*REGEX_SEARCH */
-	char *q;
-	struct re_pattern_buffer preg;
-	int i;
-	int size, range;
-
-	re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
-	preg.translate = 0;
-	preg.fastmap = 0;
-	preg.buffer = 0;
-	preg.allocated = 0;
-
-	// assume a LIMITED forward search
-	q = next_line(p);
-	q = end_line(q);
-	q = end - 1;
-	if (dir == BACK) {
-		q = prev_line(p);
-		q = text;
-	}
-	// count the number of chars to search over, forward or backward
-	size = q - p;
-	if (size < 0)
-		size = p - q;
-	// RANGE could be negative if we are searching backwards
-	range = q - p;
-
-	q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
-	if (q != 0) {
-		// The pattern was not compiled
-		psbs("bad search pattern: \"%s\": %s", pat, q);
-		i = 0;			// return p if pattern not compiled
-		goto cs1;
-	}
-
-	q = p;
-	if (range < 0) {
-		q = p - size;
-		if (q < text)
-			q = text;
-	}
-	// search for the compiled pattern, preg, in p[]
-	// range < 0-  search backward
-	// range > 0-  search forward
-	// 0 < start < size
-	// re_search() < 0  not found or error
-	// re_search() > 0  index of found pattern
-	//            struct pattern    char     int    int    int     struct reg
-	// re_search (*pattern_buffer,  *string, size,  start, range,  *regs)
-	i = re_search(&preg, q, size, 0, range, 0);
-	if (i == -1) {
-		p = 0;
-		i = 0;			// return NULL if pattern not found
-	}
-  cs1:
-	if (dir == FORWARD) {
-		p = p + i;
-	} else {
-		p = p - i;
-	}
-	return (p);
-#endif							/*REGEX_SEARCH */
-}
-#endif							/* CONFIG_FEATURE_VI_SEARCH */
-
-static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
-{
-	if (c == 22) {		// Is this an ctrl-V?
-		p = stupid_insert(p, '^');	// use ^ to indicate literal next
-		p--;			// backup onto ^
-		refresh(FALSE);	// show the ^
-		c = get_one_char();
-		*p = c;
-		p++;
-		file_modified = TRUE;	// has the file been modified
-	} else if (c == 27) {	// Is this an ESC?
-		cmd_mode = 0;
-		cmdcnt = 0;
-		end_cmd_q();	// stop adding to q
-		strcpy((char *) status_buffer, " ");	// clear the status buffer
-		if ((p[-1] != '\n') && (dot>text)) {
-			p--;
-		}
-	} else if (c == erase_char) {	// Is this a BS
-		//     123456789
-		if ((p[-1] != '\n') && (dot>text)) {
-			p--;
-			p = text_hole_delete(p, p);	// shrink buffer 1 char
-#ifdef CONFIG_FEATURE_VI_DOT_CMD
-			// also rmove char from last_modifying_cmd
-			if (strlen((char *) last_modifying_cmd) > 0) {
-				Byte *q;
-
-				q = last_modifying_cmd;
-				q[strlen((char *) q) - 1] = '\0';	// erase BS
-				q[strlen((char *) q) - 1] = '\0';	// erase prev char
-			}
-#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
-		}
-	} else {
-		// insert a char into text[]
-		Byte *sp;		// "save p"
-
-		if (c == 13)
-			c = '\n';	// translate \r to \n
-		sp = p;			// remember addr of insert
-		p = stupid_insert(p, c);	// insert the char
-#ifdef CONFIG_FEATURE_VI_SETOPTS
-		if (showmatch && strchr(")]}", *sp) != NULL) {
-			showmatching(sp);
-		}
-		if (autoindent && c == '\n') {	// auto indent the new line
-			Byte *q;
-
-			q = prev_line(p);	// use prev line as templet
-			for (; isblnk(*q); q++) {
-				p = stupid_insert(p, *q);	// insert the char
-			}
-		}
-#endif							/* CONFIG_FEATURE_VI_SETOPTS */
-	}
-	return (p);
-}
-
-static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
-{
-	p = text_hole_make(p, 1);
-	if (p != 0) {
-		*p = c;
-		file_modified = TRUE;	// has the file been modified
-		p++;
-	}
-	return (p);
-}
-
-static Byte find_range(Byte ** start, Byte ** stop, Byte c)
-{
-	Byte *save_dot, *p, *q;
-	int cnt;
-
-	save_dot = dot;
-	p = q = dot;
-
-	if (strchr("cdy><", c)) {
-		// these cmds operate on whole lines
-		p = q = begin_line(p);
-		for (cnt = 1; cnt < cmdcnt; cnt++) {
-			q = next_line(q);
-		}
-		q = end_line(q);
-	} else if (strchr("^%$0bBeEft", c)) {
-		// These cmds operate on char positions
-		do_cmd(c);		// execute movement cmd
-		q = dot;
-	} else if (strchr("wW", c)) {
-		do_cmd(c);		// execute movement cmd
-		if (dot > text)
-			dot--;		// move back off of next word
-		if (dot > text && *dot == '\n')
-			dot--;		// stay off NL
-		q = dot;
-	} else if (strchr("H-k{", c)) {
-		// these operate on multi-lines backwards
-		q = end_line(dot);	// find NL
-		do_cmd(c);		// execute movement cmd
-		dot_begin();
-		p = dot;
-	} else if (strchr("L+j}\r\n", c)) {
-		// these operate on multi-lines forwards
-		p = begin_line(dot);
-		do_cmd(c);		// execute movement cmd
-		dot_end();		// find NL
-		q = dot;
-	} else {
-		c = 27;			// error- return an ESC char
-		//break;
-	}
-	*start = p;
-	*stop = q;
-	if (q < p) {
-		*start = q;
-		*stop = p;
-	}
-	dot = save_dot;
-	return (c);
-}
-
-static int st_test(Byte * p, int type, int dir, Byte * tested)
-{
-	Byte c, c0, ci;
-	int test, inc;
-
-	inc = dir;
-	c = c0 = p[0];
-	ci = p[inc];
-	test = 0;
-
-	if (type == S_BEFORE_WS) {
-		c = ci;
-		test = ((!isspace(c)) || c == '\n');
-	}
-	if (type == S_TO_WS) {
-		c = c0;
-		test = ((!isspace(c)) || c == '\n');
-	}
-	if (type == S_OVER_WS) {
-		c = c0;
-		test = ((isspace(c)));
-	}
-	if (type == S_END_PUNCT) {
-		c = ci;
-		test = ((ispunct(c)));
-	}
-	if (type == S_END_ALNUM) {
-		c = ci;
-		test = ((isalnum(c)) || c == '_');
-	}
-	*tested = c;
-	return (test);
-}
-
-static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
-{
-	Byte c;
-
-	while (st_test(p, type, dir, &c)) {
-		// make sure we limit search to correct number of lines
-		if (c == '\n' && --linecnt < 1)
-			break;
-		if (dir >= 0 && p >= end - 1)
-			break;
-		if (dir < 0 && p <= text)
-			break;
-		p += dir;		// move to next char
-	}
-	return (p);
-}
-
-// find matching char of pair  ()  []  {}
-static Byte *find_pair(Byte * p, Byte c)
-{
-	Byte match, *q;
-	int dir, level;
-
-	match = ')';
-	level = 1;
-	dir = 1;			// assume forward
-	switch (c) {
-	case '(':
-		match = ')';
-		break;
-	case '[':
-		match = ']';
-		break;
-	case '{':
-		match = '}';
-		break;
-	case ')':
-		match = '(';
-		dir = -1;
-		break;
-	case ']':
-		match = '[';
-		dir = -1;
-		break;
-	case '}':
-		match = '{';
-		dir = -1;
-		break;
-	}
-	for (q = p + dir; text <= q && q < end; q += dir) {
-		// look for match, count levels of pairs  (( ))
-		if (*q == c)
-			level++;	// increase pair levels
-		if (*q == match)
-			level--;	// reduce pair level
-		if (level == 0)
-			break;		// found matching pair
-	}
-	if (level != 0)
-		q = NULL;		// indicate no match
-	return (q);
-}
-
-#ifdef CONFIG_FEATURE_VI_SETOPTS
-// show the matching char of a pair,  ()  []  {}
-static void showmatching(Byte * p)
-{
-	Byte *q, *save_dot;
-
-	// we found half of a pair
-	q = find_pair(p, *p);	// get loc of matching char
-	if (q == NULL) {
-		indicate_error('3');	// no matching char
-	} else {
-		// "q" now points to matching pair
-		save_dot = dot;	// remember where we are
-		dot = q;		// go to new loc
-		refresh(FALSE);	// let the user see it
-		(void) mysleep(40);	// give user some time
-		dot = save_dot;	// go back to old loc
-		refresh(FALSE);
-	}
-}
-#endif							/* CONFIG_FEATURE_VI_SETOPTS */
-
-//  open a hole in text[]
-static Byte *text_hole_make(Byte * p, int size)	// at "p", make a 'size' byte hole
-{
-	Byte *src, *dest;
-	int cnt;
-
-	if (size <= 0)
-		goto thm0;
-	src = p;
-	dest = p + size;
-	cnt = end - src;	// the rest of buffer
-	if (memmove(dest, src, cnt) != dest) {
-		psbs("can't create room for new characters");
-	}
-	memset(p, ' ', size);	// clear new hole
-	end = end + size;	// adjust the new END
-	file_modified = TRUE;	// has the file been modified
-  thm0:
-	return (p);
-}
-
-//  close a hole in text[]
-static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
-{
-	Byte *src, *dest;
-	int cnt, hole_size;
-
-	// move forwards, from beginning
-	// assume p <= q
-	src = q + 1;
-	dest = p;
-	if (q < p) {		// they are backward- swap them
-		src = p + 1;
-		dest = q;
-	}
-	hole_size = q - p + 1;
-	cnt = end - src;
-	if (src < text || src > end)
-		goto thd0;
-	if (dest < text || dest >= end)
-		goto thd0;
-	if (src >= end)
-		goto thd_atend;	// just delete the end of the buffer
-	if (memmove(dest, src, cnt) != dest) {
-		psbs("can't delete the character");
-	}
-  thd_atend:
-	end = end - hole_size;	// adjust the new END
-	if (dest >= end)
-		dest = end - 1;	// make sure dest in below end-1
-	if (end <= text)
-		dest = end = text;	// keep pointers valid
-	file_modified = TRUE;	// has the file been modified
-  thd0:
-	return (dest);
-}
-
-// copy text into register, then delete text.
-// if dist <= 0, do not include, or go past, a NewLine
-//
-static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
-{
-	Byte *p;
-
-	// make sure start <= stop
-	if (start > stop) {
-		// they are backwards, reverse them
-		p = start;
-		start = stop;
-		stop = p;
-	}
-	if (dist <= 0) {
-		// we can not cross NL boundaries
-		p = start;
-		if (*p == '\n')
-			return (p);
-		// dont go past a NewLine
-		for (; p + 1 <= stop; p++) {
-			if (p[1] == '\n') {
-				stop = p;	// "stop" just before NewLine
-				break;
-			}
-		}
-	}
-	p = start;
-#ifdef CONFIG_FEATURE_VI_YANKMARK
-	text_yank(start, stop, YDreg);
-#endif							/* CONFIG_FEATURE_VI_YANKMARK */
-	if (yf == YANKDEL) {
-		p = text_hole_delete(start, stop);
-	}					// delete lines
-	return (p);
-}
-
-static void show_help(void)
-{
-	puts("These features are available:"
-#ifdef CONFIG_FEATURE_VI_SEARCH
-	"\n\tPattern searches with / and ?"
-#endif							/* CONFIG_FEATURE_VI_SEARCH */
-#ifdef CONFIG_FEATURE_VI_DOT_CMD
-	"\n\tLast command repeat with \'.\'"
-#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
-#ifdef CONFIG_FEATURE_VI_YANKMARK
-	"\n\tLine marking with  'x"
-	"\n\tNamed buffers with  \"x"
-#endif							/* CONFIG_FEATURE_VI_YANKMARK */
-#ifdef CONFIG_FEATURE_VI_READONLY
-	"\n\tReadonly if vi is called as \"view\""
-	"\n\tReadonly with -R command line arg"
-#endif							/* CONFIG_FEATURE_VI_READONLY */
-#ifdef CONFIG_FEATURE_VI_SET
-	"\n\tSome colon mode commands with \':\'"
-#endif							/* CONFIG_FEATURE_VI_SET */
-#ifdef CONFIG_FEATURE_VI_SETOPTS
-	"\n\tSettable options with \":set\""
-#endif							/* CONFIG_FEATURE_VI_SETOPTS */
-#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
-	"\n\tSignal catching- ^C"
-	"\n\tJob suspend and resume with ^Z"
-#endif							/* CONFIG_FEATURE_VI_USE_SIGNALS */
-#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
-	"\n\tAdapt to window re-sizes"
-#endif							/* CONFIG_FEATURE_VI_WIN_RESIZE */
-	);
-}
-
-static void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
-{
-	Byte c, b[2];
-
-	b[1] = '\0';
-	strcpy((char *) buf, "");	// init buf
-	if (strlen((char *) s) <= 0)
-		s = (Byte *) "(NULL)";
-	for (; *s > '\0'; s++) {
-		c = *s;
-		if (*s > '~') {
-			strcat((char *) buf, SOs);
-			c = *s - 128;
-		}
-		if (*s < ' ') {
-			strcat((char *) buf, "^");
-			c += '@';
-		}
-		b[0] = c;
-		strcat((char *) buf, (char *) b);
-		if (*s > '~')
-			strcat((char *) buf, SOn);
-		if (*s == '\n') {
-			strcat((char *) buf, "$");
-		}
-	}
-}
-
-#ifdef CONFIG_FEATURE_VI_DOT_CMD
-static void start_new_cmd_q(Byte c)
-{
-	// release old cmd
-	if (last_modifying_cmd != 0)
-		free(last_modifying_cmd);
-	// get buffer for new cmd
-	last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
-	memset(last_modifying_cmd, '\0', BUFSIZ);	// clear new cmd queue
-	// if there is a current cmd count put it in the buffer first
-	if (cmdcnt > 0)
-		sprintf((char *) last_modifying_cmd, "%d", cmdcnt);
-	// save char c onto queue
-	last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
-	adding2q = 1;
-	return;
-}
-
-static void end_cmd_q(void)
-{
-#ifdef CONFIG_FEATURE_VI_YANKMARK
-	YDreg = 26;			// go back to default Yank/Delete reg
-#endif							/* CONFIG_FEATURE_VI_YANKMARK */
-	adding2q = 0;
-	return;
-}
-#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
-
-#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
-static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
-{
-	int cnt, i;
-
-	i = strlen((char *) s);
-	p = text_hole_make(p, i);
-	strncpy((char *) p, (char *) s, i);
-	for (cnt = 0; *s != '\0'; s++) {
-		if (*s == '\n')
-			cnt++;
-	}
-#ifdef CONFIG_FEATURE_VI_YANKMARK
-	psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
-#endif							/* CONFIG_FEATURE_VI_YANKMARK */
-	return (p);
-}
-#endif							/* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
-
-#ifdef CONFIG_FEATURE_VI_YANKMARK
-static Byte *text_yank(Byte * p, Byte * q, int dest)	// copy text into a register
-{
-	Byte *t;
-	int cnt;
-
-	if (q < p) {		// they are backwards- reverse them
-		t = q;
-		q = p;
-		p = t;
-	}
-	cnt = q - p + 1;
-	t = reg[dest];
-	if (t != 0) {		// if already a yank register
-		free(t);		//   free it
-	}
-	t = (Byte *) xmalloc(cnt + 1);	// get a new register
-	memset(t, '\0', cnt + 1);	// clear new text[]
-	strncpy((char *) t, (char *) p, cnt);	// copy text[] into bufer
-	reg[dest] = t;
-	return (p);
-}
-
-static Byte what_reg(void)
-{
-	Byte c;
-	int i;
-
-	i = 0;
-	c = 'D';			// default to D-reg
-	if (0 <= YDreg && YDreg <= 25)
-		c = 'a' + (Byte) YDreg;
-	if (YDreg == 26)
-		c = 'D';
-	if (YDreg == 27)
-		c = 'U';
-	return (c);
-}
-
-static void check_context(Byte cmd)
-{
-	// A context is defined to be "modifying text"
-	// Any modifying command establishes a new context.
-
-	if (dot < context_start || dot > context_end) {
-		if (strchr((char *) modifying_cmds, cmd) != NULL) {
-			// we are trying to modify text[]- make this the current context
-			mark[27] = mark[26];	// move cur to prev
-			mark[26] = dot;	// move local to cur
-			context_start = prev_line(prev_line(dot));
-			context_end = next_line(next_line(dot));
-			//loiter= start_loiter= now;
-		}
-	}
-	return;
-}
-
-static Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
-{
-	Byte *tmp;
-
-	// the current context is in mark[26]
-	// the previous context is in mark[27]
-	// only swap context if other context is valid
-	if (text <= mark[27] && mark[27] <= end - 1) {
-		tmp = mark[27];
-		mark[27] = mark[26];
-		mark[26] = tmp;
-		p = mark[26];	// where we are going- previous context
-		context_start = prev_line(prev_line(prev_line(p)));
-		context_end = next_line(next_line(next_line(p)));
-	}
-	return (p);
-}
-#endif							/* CONFIG_FEATURE_VI_YANKMARK */
-
-static int isblnk(Byte c) // is the char a blank or tab
-{
-	return (c == ' ' || c == '\t');
-}
-
-//----- Set terminal attributes --------------------------------
-static void rawmode(void)
-{
-	tcgetattr(0, &term_orig);
-	term_vi = term_orig;
-	term_vi.c_lflag &= (~ICANON & ~ECHO);	// leave ISIG ON- allow intr's
-	term_vi.c_iflag &= (~IXON & ~ICRNL);
-	term_vi.c_oflag &= (~ONLCR);
-#ifndef linux
-	term_vi.c_cc[VMIN] = 1;
-	term_vi.c_cc[VTIME] = 0;
-#endif
-	erase_char = term_vi.c_cc[VERASE];
-	tcsetattr(0, TCSANOW, &term_vi);
-}
-
-static void cookmode(void)
-{
-	tcsetattr(0, TCSANOW, &term_orig);
-}
-
-#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
-//----- See what the window size currently is --------------------
-static void window_size_get(int sig)
-{
-	int i;
-
-	i = ioctl(0, TIOCGWINSZ, &winsize);
-	if (i != 0) {
-		// force 24x80
-		winsize.ws_row = 24;
-		winsize.ws_col = 80;
-	}
-	if (winsize.ws_row <= 1) {
-		winsize.ws_row = 24;
-	}
-	if (winsize.ws_col <= 1) {
-		winsize.ws_col = 80;
-	}
-	rows = (int) winsize.ws_row;
-	columns = (int) winsize.ws_col;
-}
-#endif							/* CONFIG_FEATURE_VI_WIN_RESIZE */
-
-//----- Come here when we get a window resize signal ---------
-#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
-static void winch_sig(int sig)
-{
-	signal(SIGWINCH, winch_sig);
-#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
-	window_size_get(0);
-#endif							/* CONFIG_FEATURE_VI_WIN_RESIZE */
-	new_screen(rows, columns);	// get memory for virtual screen
-	redraw(TRUE);		// re-draw the screen
-}
-
-//----- Come here when we get a continue signal -------------------
-static void cont_sig(int sig)
-{
-	rawmode();			// terminal to "raw"
-	*status_buffer = '\0';	// clear the status buffer
-	redraw(TRUE);		// re-draw the screen
-
-	signal(SIGTSTP, suspend_sig);
-	signal(SIGCONT, SIG_DFL);
-	kill(getpid(), SIGCONT);
-}
-
-//----- Come here when we get a Suspend signal -------------------
-static void suspend_sig(int sig)
-{
-	place_cursor(rows - 1, 0, FALSE);	// go to bottom of screen
-	clear_to_eol();		// Erase to end of line
-	cookmode();			// terminal to "cooked"
-
-	signal(SIGCONT, cont_sig);
-	signal(SIGTSTP, SIG_DFL);
-	kill(getpid(), SIGTSTP);
-}
-
-//----- Come here when we get a signal ---------------------------
-static void catch_sig(int sig)
-{
-	signal(SIGHUP, catch_sig);
-	signal(SIGINT, catch_sig);
-	signal(SIGTERM, catch_sig);
-	longjmp(restart, sig);
-}
-
-static void alarm_sig(int sig)
-{
-	signal(SIGALRM, catch_sig);
-	longjmp(restart, sig);
-}
-
-//----- Come here when we get a core dump signal -----------------
-static void core_sig(int sig)
-{
-	signal(SIGQUIT, core_sig);
-	signal(SIGILL, core_sig);
-	signal(SIGTRAP, core_sig);
-	signal(SIGIOT, core_sig);
-	signal(SIGABRT, core_sig);
-	signal(SIGFPE, core_sig);
-	signal(SIGBUS, core_sig);
-	signal(SIGSEGV, core_sig);
-#ifdef SIGSYS
-	signal(SIGSYS, core_sig);
-#endif
-
-	dot = bound_dot(dot);	// make sure "dot" is valid
-
-	longjmp(restart, sig);
-}
-#endif							/* CONFIG_FEATURE_VI_USE_SIGNALS */
-
-static int mysleep(int hund)	// sleep for 'h' 1/100 seconds
-{
-	// Don't hang- Wait 5/100 seconds-  1 Sec= 1000000
-	FD_ZERO(&rfds);
-	FD_SET(0, &rfds);
-	tv.tv_sec = 0;
-	tv.tv_usec = hund * 10000;
-	select(1, &rfds, NULL, NULL, &tv);
-	return (FD_ISSET(0, &rfds));
-}
-
-//----- IO Routines --------------------------------------------
-static Byte readit(void)	// read (maybe cursor) key from stdin
-{
-	Byte c;
-	int i, bufsiz, cnt, cmdindex;
-	struct esc_cmds {
-		Byte *seq;
-		Byte val;
-	};
-
-	static struct esc_cmds esccmds[] = {
-		{(Byte *) "OA", (Byte) VI_K_UP},	// cursor key Up
-		{(Byte *) "OB", (Byte) VI_K_DOWN},	// cursor key Down
-		{(Byte *) "OC", (Byte) VI_K_RIGHT},	// Cursor Key Right
-		{(Byte *) "OD", (Byte) VI_K_LEFT},	// cursor key Left
-		{(Byte *) "OH", (Byte) VI_K_HOME},	// Cursor Key Home
-		{(Byte *) "OF", (Byte) VI_K_END},	// Cursor Key End
-		{(Byte *) "", (Byte) VI_K_UP},	// cursor key Up
-		{(Byte *) "", (Byte) VI_K_DOWN},	// cursor key Down
-		{(Byte *) "", (Byte) VI_K_RIGHT},	// Cursor Key Right
-		{(Byte *) "", (Byte) VI_K_LEFT},	// cursor key Left
-		{(Byte *) "", (Byte) VI_K_HOME},	// Cursor Key Home
-		{(Byte *) "", (Byte) VI_K_END},	// Cursor Key End
-		{(Byte *) "[2~", (Byte) VI_K_INSERT},	// Cursor Key Insert
-		{(Byte *) "[5~", (Byte) VI_K_PAGEUP},	// Cursor Key Page Up
-		{(Byte *) "[6~", (Byte) VI_K_PAGEDOWN},	// Cursor Key Page Down
-		{(Byte *) "OP", (Byte) VI_K_FUN1},	// Function Key F1
-		{(Byte *) "OQ", (Byte) VI_K_FUN2},	// Function Key F2
-		{(Byte *) "OR", (Byte) VI_K_FUN3},	// Function Key F3
-		{(Byte *) "OS", (Byte) VI_K_FUN4},	// Function Key F4
-		{(Byte *) "[15~", (Byte) VI_K_FUN5},	// Function Key F5
-		{(Byte *) "[17~", (Byte) VI_K_FUN6},	// Function Key F6
-		{(Byte *) "[18~", (Byte) VI_K_FUN7},	// Function Key F7
-		{(Byte *) "[19~", (Byte) VI_K_FUN8},	// Function Key F8
-		{(Byte *) "[20~", (Byte) VI_K_FUN9},	// Function Key F9
-		{(Byte *) "[21~", (Byte) VI_K_FUN10},	// Function Key F10
-		{(Byte *) "[23~", (Byte) VI_K_FUN11},	// Function Key F11
-		{(Byte *) "[24~", (Byte) VI_K_FUN12},	// Function Key F12
-		{(Byte *) "[11~", (Byte) VI_K_FUN1},	// Function Key F1
-		{(Byte *) "[12~", (Byte) VI_K_FUN2},	// Function Key F2
-		{(Byte *) "[13~", (Byte) VI_K_FUN3},	// Function Key F3
-		{(Byte *) "[14~", (Byte) VI_K_FUN4},	// Function Key F4
-	};
-
-#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
-
-	(void) alarm(0);	// turn alarm OFF while we wait for input
-	// get input from User- are there already input chars in Q?
-	bufsiz = strlen((char *) readbuffer);
-	if (bufsiz <= 0) {
-	  ri0:
-		// the Q is empty, wait for a typed char
-		bufsiz = read(0, readbuffer, BUFSIZ - 1);
-		if (bufsiz < 0) {
-			if (errno == EINTR)
-				goto ri0;	// interrupted sys call
-			if (errno == EBADF)
-				editing = 0;
-			if (errno == EFAULT)
-				editing = 0;
-			if (errno == EINVAL)
-				editing = 0;
-			if (errno == EIO)
-				editing = 0;
-			errno = 0;
-			bufsiz = 0;
-		}
-		readbuffer[bufsiz] = '\0';
-	}
-	// return char if it is not part of ESC sequence
-	if (readbuffer[0] != 27)
-		goto ri1;
-
-	// This is an ESC char. Is this Esc sequence?
-	// Could be bare Esc key. See if there are any
-	// more chars to read after the ESC. This would
-	// be a Function or Cursor Key sequence.
-	FD_ZERO(&rfds);
-	FD_SET(0, &rfds);
-	tv.tv_sec = 0;
-	tv.tv_usec = 50000;	// Wait 5/100 seconds- 1 Sec=1000000
-
-	// keep reading while there are input chars and room in buffer
-	while (select(1, &rfds, NULL, NULL, &tv) > 0 && bufsiz <= (BUFSIZ - 5)) {
-		// read the rest of the ESC string
-		i = read(0, (void *) (readbuffer + bufsiz), BUFSIZ - bufsiz);
-		if (i > 0) {
-			bufsiz += i;
-			readbuffer[bufsiz] = '\0';	// Terminate the string
-		}
-	}
-	// Maybe cursor or function key?
-	for (cmdindex = 0; cmdindex < ESCCMDS_COUNT; cmdindex++) {
-		cnt = strlen((char *) esccmds[cmdindex].seq);
-		i = strncmp((char *) esccmds[cmdindex].seq, (char *) readbuffer, cnt);
-		if (i == 0) {
-			// is a Cursor key- put derived value back into Q
-			readbuffer[0] = esccmds[cmdindex].val;
-			// squeeze out the ESC sequence
-			for (i = 1; i < cnt; i++) {
-				memmove(readbuffer + 1, readbuffer + 2, BUFSIZ - 2);
-				readbuffer[BUFSIZ - 1] = '\0';
-			}
-			break;
-		}
-	}
-  ri1:
-	c = readbuffer[0];
-	// remove one char from Q
-	memmove(readbuffer, readbuffer + 1, BUFSIZ - 1);
-	readbuffer[BUFSIZ - 1] = '\0';
-	(void) alarm(3);	// we are done waiting for input, turn alarm ON
-	return (c);
-}
-
-//----- IO Routines --------------------------------------------
-static Byte get_one_char()
-{
-	static Byte c;
-
-#ifdef CONFIG_FEATURE_VI_DOT_CMD
-	// ! adding2q  && ioq == 0  read()
-	// ! adding2q  && ioq != 0  *ioq
-	// adding2q         *last_modifying_cmd= read()
-	if (!adding2q) {
-		// we are not adding to the q.
-		// but, we may be reading from a q
-		if (ioq == 0) {
-			// there is no current q, read from STDIN
-			c = readit();	// get the users input
-		} else {
-			// there is a queue to get chars from first
-			c = *ioq++;
-			if (c == '\0') {
-				// the end of the q, read from STDIN
-				free(ioq_start);
-				ioq_start = ioq = 0;
-				c = readit();	// get the users input
-			}
-		}
-	} else {
-		// adding STDIN chars to q
-		c = readit();	// get the users input
-		if (last_modifying_cmd != 0) {
-			// add new char to q
-			last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
-		}
-	}
-#else							/* CONFIG_FEATURE_VI_DOT_CMD */
-	c = readit();		// get the users input
-#endif							/* CONFIG_FEATURE_VI_DOT_CMD */
-	return (c);			// return the char, where ever it came from
-}
-
-static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
-{
-	Byte buf[BUFSIZ];
-	Byte c;
-	int i;
-	static Byte *obufp = NULL;
-
-	strcpy((char *) buf, (char *) prompt);
-	*status_buffer = '\0';	// clear the status buffer
-	place_cursor(rows - 1, 0, FALSE);	// go to Status line, bottom of screen
-	clear_to_eol();		// clear the line
-	write(1, prompt, strlen((char *) prompt));	// write out the :, /, or ? prompt
-
-	for (i = strlen((char *) buf); i < BUFSIZ;) {
-		c = get_one_char();	// read user input
-		if (c == '\n' || c == '\r' || c == 27)
-			break;		// is this end of input
-		if (c == erase_char) {	// user wants to erase prev char
-			i--;		// backup to prev char
-			buf[i] = '\0';	// erase the char
-			buf[i + 1] = '\0';	// null terminate buffer
-			write(1, " ", 3);	// erase char on screen
-			if (i <= 0) {	// user backs up before b-o-l, exit
-				break;
-			}
-		} else {
-			buf[i] = c;	// save char in buffer
-			buf[i + 1] = '\0';	// make sure buffer is null terminated
-			write(1, buf + i, 1);	// echo the char back to user
-			i++;
-		}
-	}
-	refresh(FALSE);
-	if (obufp != NULL)
-		free(obufp);
-	obufp = (Byte *) xstrdup((char *) buf);
-	return (obufp);
-}
-
-static int file_size(Byte * fn) // what is the byte size of "fn"
-{
-	struct stat st_buf;
-	int cnt, sr;
-
-	if (fn == 0 || strlen(fn) <= 0)
-		return (-1);
-	cnt = -1;
-	sr = stat((char *) fn, &st_buf);	// see if file exists
-	if (sr >= 0) {
-		cnt = (int) st_buf.st_size;
-	}
-	return (cnt);
-}
-
-static int file_insert(Byte * fn, Byte * p, int size)
-{
-	int fd, cnt;
-
-	cnt = -1;
-#ifdef CONFIG_FEATURE_VI_READONLY
-	readonly = FALSE;
-#endif							/* CONFIG_FEATURE_VI_READONLY */
-	if (fn == 0 || strlen((char*) fn) <= 0) {
-		psbs("No filename given");
-		goto fi0;
-	}
-	if (size == 0) {
-		// OK- this is just a no-op
-		cnt = 0;
-		goto fi0;
-	}
-	if (size < 0) {
-		psbs("Trying to insert a negative number (%d) of characters", size);
-		goto fi0;
-	}
-	if (p < text || p > end) {
-		psbs("Trying to insert file outside of memory");
-		goto fi0;
-	}
-
-	// see if we can open the file
-#ifdef CONFIG_FEATURE_VI_READONLY
-	if (vi_readonly) goto fi1;		// do not try write-mode
-#endif
-	fd = open((char *) fn, O_RDWR);			// assume read & write
-	if (fd < 0) {
-		// could not open for writing- maybe file is read only
-#ifdef CONFIG_FEATURE_VI_READONLY
-  fi1:
-#endif
-		fd = open((char *) fn, O_RDONLY);	// try read-only
-		if (fd < 0) {
-			psbs("\"%s\" %s", fn, "could not open file");
-			goto fi0;
-		}
-#ifdef CONFIG_FEATURE_VI_READONLY
-		// got the file- read-only
-		readonly = TRUE;
-#endif							/* CONFIG_FEATURE_VI_READONLY */
-	}
-	p = text_hole_make(p, size);
-	cnt = read(fd, p, size);
-	close(fd);
-	if (cnt < 0) {
-		cnt = -1;
-		p = text_hole_delete(p, p + size - 1);	// un-do buffer insert
-		psbs("could not read file \"%s\"", fn);
-	} else if (cnt < size) {
-		// There was a partial read, shrink unused space text[]
-		p = text_hole_delete(p + cnt, p + (size - cnt) - 1);	// un-do buffer insert
-		psbs("could not read all of file \"%s\"", fn);
-	}
-	if (cnt >= size)
-		file_modified = TRUE;
-  fi0:
-	return (cnt);
-}
-
-static int file_write(Byte * fn, Byte * first, Byte * last)
-{
-	int fd, cnt, charcnt;
-
-	if (fn == 0) {
-		psbs("No current filename");
-		return (-1);
-	}
-	charcnt = 0;
-	// FIXIT- use the correct umask()
-	fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
-	if (fd < 0)
-		return (-1);
-	cnt = last - first + 1;
-	charcnt = write(fd, first, cnt);
-	if (charcnt == cnt) {
-		// good write
-		//file_modified= FALSE; // the file has not been modified
-	} else {
-		charcnt = 0;
-	}
-	close(fd);
-	return (charcnt);
-}
-
-//----- Terminal Drawing ---------------------------------------
-// The terminal is made up of 'rows' line of 'columns' columns.
-// classicly this would be 24 x 80.
-//  screen coordinates
-//  0,0     ...     0,79
-//  1,0     ...     1,79
-//  .       ...     .
-//  .       ...     .
-//  22,0    ...     22,79
-//  23,0    ...     23,79   status line
-//
-
-//----- Move the cursor to row x col (count from 0, not 1) -------
-static void place_cursor(int row, int col, int opti)
-{
-	char cm1[BUFSIZ];
-	char *cm;
-	int l;
-#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
-	char cm2[BUFSIZ];
-	Byte *screenp;
-	// char cm3[BUFSIZ];
-	int Rrow= last_row;
-#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
-	
-	memset(cm1, '\0', BUFSIZ - 1);  // clear the buffer
-
-	if (row < 0) row = 0;
-	if (row >= rows) row = rows - 1;
-	if (col < 0) col = 0;
-	if (col >= columns) col = columns - 1;
-	
-	//----- 1.  Try the standard terminal ESC sequence
-	sprintf((char *) cm1, CMrc, row + 1, col + 1);
-	cm= cm1;
-	if (! opti) goto pc0;
-
-#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
-	//----- find the minimum # of chars to move cursor -------------
-	//----- 2.  Try moving with discreet chars (Newline, [back]space, ...)
-	memset(cm2, '\0', BUFSIZ - 1);  // clear the buffer
-	
-	// move to the correct row
-	while (row < Rrow) {
-		// the cursor has to move up
-		strcat(cm2, CMup);
-		Rrow--;
-	}
-	while (row > Rrow) {
-		// the cursor has to move down
-		strcat(cm2, CMdown);
-		Rrow++;
-	}
-	
-	// now move to the correct column
-	strcat(cm2, "\r");			// start at col 0
-	// just send out orignal source char to get to correct place
-	screenp = &screen[row * columns];	// start of screen line
-	strncat(cm2, screenp, col);
-
-	//----- 3.  Try some other way of moving cursor
-	//---------------------------------------------
-
-	// pick the shortest cursor motion to send out
-	cm= cm1;
-	if (strlen(cm2) < strlen(cm)) {
-		cm= cm2;
-	}  /* else if (strlen(cm3) < strlen(cm)) {
-		cm= cm3;
-	} */
-#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
-  pc0:
-	l= strlen(cm);
-	if (l) write(1, cm, l);			// move the cursor
-}
-
-//----- Erase from cursor to end of line -----------------------
-static void clear_to_eol()
-{
-	write(1, Ceol, strlen(Ceol));	// Erase from cursor to end of line
-}
-
-//----- Erase from cursor to end of screen -----------------------
-static void clear_to_eos()
-{
-	write(1, Ceos, strlen(Ceos));	// Erase from cursor to end of screen
-}
-
-//----- Start standout mode ------------------------------------
-static void standout_start() // send "start reverse video" sequence
-{
-	write(1, SOs, strlen(SOs));	// Start reverse video mode
-}
-
-//----- End standout mode --------------------------------------
-static void standout_end() // send "end reverse video" sequence
-{
-	write(1, SOn, strlen(SOn));	// End reverse video mode
-}
-
-//----- Flash the screen  --------------------------------------
-static void flash(int h)
-{
-	standout_start();	// send "start reverse video" sequence
-	redraw(TRUE);
-	(void) mysleep(h);
-	standout_end();		// send "end reverse video" sequence
-	redraw(TRUE);
-}
-
-static void beep()
-{
-	write(1, bell, strlen(bell));	// send out a bell character
-}
-
-static void indicate_error(char c)
-{
-#ifdef CONFIG_FEATURE_VI_CRASHME
-	if (crashme > 0)
-		return;			// generate a random command
-#endif							/* CONFIG_FEATURE_VI_CRASHME */
-	if (err_method == 0) {
-		beep();
-	} else {
-		flash(10);
-	}
-}
-
-//----- Screen[] Routines --------------------------------------
-//----- Erase the Screen[] memory ------------------------------
-static void screen_erase()
-{
-	memset(screen, ' ', screensize);	// clear new screen
-}
-
-//----- Draw the status line at bottom of the screen -------------
-static void show_status_line(void)
-{
-	static int last_cksum;
-	int l, cnt, cksum;
-
-	cnt = strlen((char *) status_buffer);
-	for (cksum= l= 0; l < cnt; l++) { cksum += (int)(status_buffer[l]); }
-	// don't write the status line unless it changes
-	if (cnt > 0 && last_cksum != cksum) {
-		last_cksum= cksum;		// remember if we have seen this line
-		place_cursor(rows - 1, 0, FALSE);	// put cursor on status line
-		write(1, status_buffer, cnt);
-		clear_to_eol();
-		place_cursor(crow, ccol, FALSE);	// put cursor back in correct place
-	}
-}
-
-//----- format the status buffer, the bottom line of screen ------
-// print status buffer, with STANDOUT mode
-static void psbs(char *format, ...)
-{
-	va_list args;
-
-	va_start(args, format);
-	strcpy((char *) status_buffer, SOs);	// Terminal standout mode on
-	vsprintf((char *) status_buffer + strlen((char *) status_buffer), format,
-			 args);
-	strcat((char *) status_buffer, SOn);	// Terminal standout mode off
-	va_end(args);
-
-	return;
-}
-
-// print status buffer
-static void psb(char *format, ...)
-{
-	va_list args;
-
-	va_start(args, format);
-	vsprintf((char *) status_buffer, format, args);
-	va_end(args);
-	return;
-}
-
-static void ni(Byte * s) // display messages
-{
-	Byte buf[BUFSIZ];
-
-	print_literal(buf, s);
-	psbs("\'%s\' is not implemented", buf);
-}
-
-static void edit_status(void)	// show file status on status line
-{
-	int cur, tot, percent;
-
-	cur = count_lines(text, dot);
-	tot = count_lines(text, end - 1);
-	//    current line         percent
-	//   -------------    ~~ ----------
-	//    total lines            100
-	if (tot > 0) {
-		percent = (100 * cur) / tot;
-	} else {
-		cur = tot = 0;
-		percent = 100;
-	}
-	psb("\"%s\""
-#ifdef CONFIG_FEATURE_VI_READONLY
-		"%s"
-#endif							/* CONFIG_FEATURE_VI_READONLY */
-		"%s line %d of %d --%d%%--",
-		(cfn != 0 ? (char *) cfn : "No file"),
-#ifdef CONFIG_FEATURE_VI_READONLY
-		((vi_readonly || readonly) ? " [Read only]" : ""),
-#endif							/* CONFIG_FEATURE_VI_READONLY */
-		(file_modified ? " [modified]" : ""),
-		cur, tot, percent);
-}
-
-//----- Force refresh of all Lines -----------------------------
-static void redraw(int full_screen)
-{
-	place_cursor(0, 0, FALSE);	// put cursor in correct place
-	clear_to_eos();		// tel terminal to erase display
-	screen_erase();		// erase the internal screen buffer
-	refresh(full_screen);	// this will redraw the entire display
-}
-
-//----- Format a text[] line into a buffer ---------------------
-static void format_line(Byte *dest, Byte *src, int li)
-{
-	int co;
-	Byte c;
-	
-	for (co= 0; co < MAX_SCR_COLS; co++) {
-		c= ' ';		// assume blank
-		if (li > 0 && co == 0) {
-			c = '~';        // not first line, assume Tilde
-		}
-		// are there chars in text[] and have we gone past the end
-		if (text < end && src < end) {
-			c = *src++;
-		}
-		if (c == '\n')
-			break;
-		if (c < ' ' || c > '~') {
-			if (c == '\t') {
-				c = ' ';
-				//       co %    8     !=     7
-				for (; (co % tabstop) != (tabstop - 1); co++) {
-					dest[co] = c;
-				}
-			} else {
-				dest[co++] = '^';
-				c |= '@';       // make it visible
-				c &= 0x7f;      // get rid of hi bit
-			}
-		}
-		// the co++ is done here so that the column will
-		// not be overwritten when we blank-out the rest of line
-		dest[co] = c;
-		if (src >= end)
-			break;
-	}
-}
-
-//----- Refresh the changed screen lines -----------------------
-// Copy the source line from text[] into the buffer and note
-// if the current screenline is different from the new buffer.
-// If they differ then that line needs redrawing on the terminal.
-//
-static void refresh(int full_screen)
-{
-	static int old_offset;
-	int li, changed;
-	Byte buf[MAX_SCR_COLS];
-	Byte *tp, *sp;		// pointer into text[] and screen[]
-#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
-	int last_li= -2;				// last line that changed- for optimizing cursor movement
-#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
-
-#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
-	window_size_get(0);
-#endif							/* CONFIG_FEATURE_VI_WIN_RESIZE */
-	sync_cursor(dot, &crow, &ccol);	// where cursor will be (on "dot")
-	tp = screenbegin;	// index into text[] of top line
-
-	// compare text[] to screen[] and mark screen[] lines that need updating
-	for (li = 0; li < rows - 1; li++) {
-		int cs, ce;				// column start & end
-		memset(buf, ' ', MAX_SCR_COLS);		// blank-out the buffer
-		buf[MAX_SCR_COLS-1] = 0;		// NULL terminate the buffer
-		// format current text line into buf
-		format_line(buf, tp, li);
-
-		// skip to the end of the current text[] line
-		while (tp < end && *tp++ != '\n') /*no-op*/ ;
-
-		// see if there are any changes between vitual screen and buf
-		changed = FALSE;	// assume no change
-		cs= 0;
-		ce= columns-1;
-		sp = &screen[li * columns];	// start of screen line
-		if (full_screen) {
-			// force re-draw of every single column from 0 - columns-1
-			goto re0;
-		}
-		// compare newly formatted buffer with virtual screen
-		// look forward for first difference between buf and screen
-		for ( ; cs <= ce; cs++) {
-			if (buf[cs + offset] != sp[cs]) {
-				changed = TRUE;	// mark for redraw
-				break;
-			}
-		}
-
-		// look backward for last difference between buf and screen
-		for ( ; ce >= cs; ce--) {
-			if (buf[ce + offset] != sp[ce]) {
-				changed = TRUE;	// mark for redraw
-				break;
-			}
-		}
-		// now, cs is index of first diff, and ce is index of last diff
-
-		// if horz offset has changed, force a redraw
-		if (offset != old_offset) {
-  re0:
-			changed = TRUE;
-		}
-
-		// make a sanity check of columns indexes
-		if (cs < 0) cs= 0;
-		if (ce > columns-1) ce= columns-1;
-		if (cs > ce) {  cs= 0;  ce= columns-1;  }
-		// is there a change between vitual screen and buf
-		if (changed) {
-			//  copy changed part of buffer to virtual screen
-			memmove(sp+cs, buf+(cs+offset), ce-cs+1);
-
-			// move cursor to column of first change
-			if (offset != old_offset) {
-				// opti_cur_move is still too stupid
-				// to handle offsets correctly
-				place_cursor(li, cs, FALSE);
-			} else {
-#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
-				// if this just the next line
-				//  try to optimize cursor movement
-				//  otherwise, use standard ESC sequence
-				place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
-				last_li= li;
-#else							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
-				place_cursor(li, cs, FALSE);	// use standard ESC sequence
-#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
-			}
-
-			// write line out to terminal
-			write(1, sp+cs, ce-cs+1);
-#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
-			last_row = li;
-#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
-		}
-	}
-
-#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
-	place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
-	last_row = crow;
-#else
-	place_cursor(crow, ccol, FALSE);
-#endif							/* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
-	
-	if (offset != old_offset)
-		old_offset = offset;
-}