From d4f2e7ff71f253ee993e11cf7ce6a1244dec52e0 Mon Sep 17 00:00:00 2001
From: Denys Vlasenko <vda.linux@googlemail.com>
Date: Mon, 1 Apr 2019 14:18:02 +0200
Subject: vi: rearrange functions, no logic changes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
---
 editors/vi.c | 3437 ++++++++++++++++++++++++++++------------------------------
 1 file changed, 1668 insertions(+), 1769 deletions(-)

diff --git a/editors/vi.c b/editors/vi.c
index 9db763ccd..a0a2b7a82 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -483,123 +483,8 @@ struct globals {
 } while (0)
 
 
-static void do_cmd(int);	// execute a command
-static int next_tabstop(int);
-static void sync_cursor(char *, int *, int *);	// synchronize the screen cursor to dot
-static char *begin_line(char *);	// return pointer to cur line B-o-l
-static char *end_line(char *);	// return pointer to cur line E-o-l
-static char *prev_line(char *);	// return pointer to prev line B-o-l
-static char *next_line(char *);	// return pointer to next line B-o-l
-static char *end_screen(void);	// get pointer to last char on screen
-static int count_lines(char *, char *);	// count line from start to stop
-static char *find_line(int);	// find beginning of line #li
-static char *move_to_col(char *, int);	// move "p" to column l
-static void dot_left(void);	// move dot left- dont leave line
-static void dot_right(void);	// move dot right- dont leave line
-static void dot_begin(void);	// move dot to B-o-l
-static void dot_end(void);	// move dot to E-o-l
-static void dot_next(void);	// move dot to next line B-o-l
-static void dot_prev(void);	// move dot to prev line B-o-l
-static void dot_scroll(int, int);	// move the screen up or down
-static void dot_skip_over_ws(void);	// move dot pat WS
-static char *bound_dot(char *);	// make sure  text[0] <= P < "end"
-static char *new_screen(int, int);	// malloc virtual screen memory
-#if !ENABLE_FEATURE_VI_UNDO
-#define char_insert(a,b,c) char_insert(a,b)
-#endif
-static char *char_insert(char *, char, int);	// insert the char c at 'p'
-// might reallocate text[]! use p += stupid_insert(p, ...),
-// and be careful to not use pointers into potentially freed text[]!
-static uintptr_t stupid_insert(char *, char);	// stupidly insert the char c at 'p'
-static int st_test(char *, int, int, char *);	// helper for skip_thing()
-static char *skip_thing(char *, int, int, int);	// skip some object
-static char *find_pair(char *, char);	// find matching pair ()  []  {}
-#if !ENABLE_FEATURE_VI_UNDO
-#define text_hole_delete(a,b,c) text_hole_delete(a,b)
-#endif
-static char *text_hole_delete(char *, char *, int);	// at "p", delete a 'size' byte hole
-// might reallocate text[]! use p += text_hole_make(p, ...),
-// and be careful to not use pointers into potentially freed text[]!
-static uintptr_t text_hole_make(char *, int);	// at "p", make a 'size' byte hole
-#if !ENABLE_FEATURE_VI_UNDO
-#define yank_delete(a,b,c,d,e) yank_delete(a,b,c,d)
-#endif
-static char *yank_delete(char *, char *, int, int, int);	// yank text[] into register then delete
-static void rawmode(void);	// set "raw" mode on tty
-static void cookmode(void);	// return to "cooked" mode on tty
-// sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready)
-static int mysleep(int);
-static int get_one_char(void);	// read 1 char from stdin
-// file_insert might reallocate text[]!
-static int file_insert(const char *, char *, int);
-static int file_write(char *, char *, char *);
-static void screen_erase(void);
-static void go_bottom_and_clear_to_eol(void);
-static void standout_start(void);	// send "start reverse video" sequence
-static void standout_end(void);	// send "end reverse video" sequence
-static void flash(int);		// flash the terminal screen
 static void show_status_line(void);	// put a message on the bottom line
-static void status_line(const char *, ...);     // print to status buf
 static void status_line_bold(const char *, ...);
-static void status_line_bold_errno(const char *fn);
-static void not_implemented(const char *); // display "Not implemented" message
-static int format_edit_status(void);	// format file status on status line
-static void redraw(int);	// force a full screen refresh
-static char* format_line(char* /*, int*/);
-static void refresh(int);	// update the terminal from screen[]
-
-static void indicate_error(void);       // use flash or beep to indicate error
-static void Hit_Return(void);
-
-#if ENABLE_FEATURE_VI_SEARCH
-static char *char_search(char *, const char *, int);	// search for pattern starting at p
-#endif
-#if ENABLE_FEATURE_VI_COLON
-static char *get_one_address(char *, int *);	// get colon addr, if present
-static char *get_address(char *, int *, int *);	// get two colon addrs, if present
-#endif
-static void colon(char *);	// execute the "colon" mode cmds
-#if ENABLE_FEATURE_VI_USE_SIGNALS
-static void winch_handler(int);	// catch window size changes
-static void tstp_handler(int);	// catch ctrl-Z
-static void int_handler(int);	// catch ctrl-C
-#endif
-#if ENABLE_FEATURE_VI_DOT_CMD
-static void start_new_cmd_q(char);	// new queue for command
-static void end_cmd_q(void);	// stop saving input chars
-#else
-#define end_cmd_q() ((void)0)
-#endif
-#if ENABLE_FEATURE_VI_SETOPTS
-static void showmatching(char *);	// show the matching pair ()  []  {}
-#endif
-#if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME
-// might reallocate text[]! use p += string_insert(p, ...),
-// and be careful to not use pointers into potentially freed text[]!
-# if !ENABLE_FEATURE_VI_UNDO
-#define string_insert(a,b,c) string_insert(a,b)
-# endif
-static uintptr_t string_insert(char *, const char *, int);	// insert the string at 'p'
-#endif
-#if ENABLE_FEATURE_VI_YANKMARK
-static char *text_yank(char *, char *, int);	// save copy of "p" into a register
-static char what_reg(void);		// what is letter of current YDreg
-static void check_context(char);	// remember context for '' command
-#endif
-#if ENABLE_FEATURE_VI_UNDO
-static void flush_undo_data(void);
-static void undo_push(char *, unsigned, unsigned char);	// push an operation on the undo stack
-static void undo_push_insert(char *, int, int); // convenience function
-static void undo_pop(void);		// undo the last operation
-# if ENABLE_FEATURE_VI_UNDO_QUEUE
-static void undo_queue_commit(void);	// flush any queued objects to the undo stack
-# else
-# define undo_queue_commit() ((void)0)
-# endif
-#else
-#define flush_undo_data()   ((void)0)
-#define undo_queue_commit() ((void)0)
-#endif
 
 #if ENABLE_FEATURE_VI_CRASHME
 static void crash_dummy();
@@ -645,37 +530,6 @@ static void write1(const char *out)
 	fputs(out, stdout);
 }
 
-/* read text from file or create an empty buf */
-/* will also update current_filename */
-static int init_text_buffer(char *fn)
-{
-	int rc;
-
-	/* allocate/reallocate text buffer */
-	free(text);
-	text_size = 10240;
-	screenbegin = dot = end = text = xzalloc(text_size);
-
-	if (fn != current_filename) {
-		free(current_filename);
-		current_filename = xstrdup(fn);
-	}
-	rc = file_insert(fn, text, 1);
-	if (rc < 0) {
-		// file doesnt exist. Start empty buf with dummy line
-		char_insert(text, '\n', NO_UNDO);
-	}
-
-	flush_undo_data();
-	modified_count = 0;
-	last_modified_count = -1;
-#if ENABLE_FEATURE_VI_YANKMARK
-	/* init the marks */
-	memset(mark, 0, sizeof(mark));
-#endif
-	return rc;
-}
-
 #if ENABLE_FEATURE_VI_WIN_RESIZE
 static int query_screen_dimensions(void)
 {
@@ -969,654 +823,288 @@ static NOINLINE void sync_cursor(char *d, int *row, int *col)
 	*col = co;
 }
 
-//----- The Colon commands -------------------------------------
-#if ENABLE_FEATURE_VI_COLON
-static char *get_one_address(char *p, int *addr)	// get colon addr, if present
+//----- Format a text[] line into a buffer ---------------------
+static char* format_line(char *src /*, int li*/)
 {
-	int st;
-	char *q;
-	IF_FEATURE_VI_YANKMARK(char c;)
-	IF_FEATURE_VI_SEARCH(char *pat;)
+	unsigned char c;
+	int co;
+	int ofs = offset;
+	char *dest = scr_out_buf; // [MAX_SCR_COLS + MAX_TABSTOP * 2]
 
-	*addr = -1;			// assume no addr
-	if (*p == '.') {	// the current line
-		p++;
-		q = begin_line(dot);
-		*addr = count_lines(text, q);
-	}
-#if ENABLE_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[(unsigned char) c];
-			if (q != NULL) {	// is mark valid
-				*addr = count_lines(text, q);
+	c = '~'; // char in col 0 in non-existent lines is '~'
+	co = 0;
+	while (co < columns + tabstop) {
+		// have we gone past the end?
+		if (src < end) {
+			c = *src++;
+			if (c == '\n')
+				break;
+			if ((c & 0x80) && !Isprint(c)) {
+				c = '.';
+			}
+			if (c < ' ' || c == 0x7f) {
+				if (c == '\t') {
+					c = ' ';
+					//      co %    8     !=     7
+					while ((co % tabstop) != (tabstop - 1)) {
+						dest[co++] = c;
+					}
+				} else {
+					dest[co++] = '^';
+					if (c == 0x7f)
+						c = '?';
+					else
+						c += '@'; // Ctrl-X -> 'X'
+				}
 			}
 		}
-	}
-#endif
-#if ENABLE_FEATURE_VI_SEARCH
-	else if (*p == '/') {	// a search pattern
-		q = strchrnul(++p, '/');
-		pat = xstrndup(p, q - p); // save copy of pattern
-		p = q;
-		if (*p == '/')
-			p++;
-		q = char_search(dot, pat, (FORWARD << 1) | FULL);
-		if (q != NULL) {
-			*addr = count_lines(text, q);
+		dest[co++] = c;
+		// discard scrolled-off-to-the-left portion,
+		// in tabstop-sized pieces
+		if (ofs >= tabstop && co >= tabstop) {
+			memmove(dest, dest + tabstop, co);
+			co -= tabstop;
+			ofs -= tabstop;
 		}
-		free(pat);
-	}
-#endif
-	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(p, "%d%n", addr, &st);
-		p += st;
-	} else {
-		// unrecognized address - assume -1
-		*addr = -1;
+		if (src >= end)
+			break;
 	}
-	return p;
+	// check "short line, gigantic offset" case
+	if (co < ofs)
+		ofs = co;
+	// discard last scrolled off part
+	co -= ofs;
+	dest += ofs;
+	// fill the rest with spaces
+	if (co < columns)
+		memset(&dest[co], ' ', columns - co);
+	return dest;
 }
 
-static char *get_address(char *p, int *b, int *e)	// get two colon addrs, if present
+//----- 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)
 {
-	//----- get the address' i.e., 1,3   'a,'b  -----
-	// get FIRST addr, if present
-	while (isblank(*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 (isblank(*p))
-		p++;
-	if (*p == ',') {			// is there a address separator
-		p++;
-		while (isblank(*p))
-			p++;
-		// get SECOND addr, if present
-		p = get_one_address(p, e);
-	}
- ga0:
-	while (isblank(*p))
-		p++;				// skip over trailing spaces
-	return p;
-}
+#define old_offset refresh__old_offset
 
-#if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
-static void setops(const char *args, const char *opname, int flg_no,
-			const char *short_opname, int opt)
-{
-	const char *a = args + flg_no;
-	int l = strlen(opname) - 1; // opname have + ' '
+	int li, changed;
+	char *tp, *sp;		// pointer into text[] and screen[]
 
-	// maybe strncmp? we had tons of erroneous strncasecmp's...
-	if (strncasecmp(a, opname, l) == 0
-	 || strncasecmp(a, short_opname, 2) == 0
-	) {
-		if (flg_no)
-			vi_setops &= ~opt;
-		else
-			vi_setops |= opt;
-	}
-}
+	if (ENABLE_FEATURE_VI_WIN_RESIZE IF_FEATURE_VI_ASK_TERMINAL(&& !G.get_rowcol_error) ) {
+		unsigned c = columns, r = rows;
+		query_screen_dimensions();
+#if ENABLE_FEATURE_VI_USE_SIGNALS
+		full_screen |= (c - columns) | (r - rows);
+#else
+		if (c != columns || r != rows) {
+			full_screen = TRUE;
+			// update screen memory since SIGWINCH won't have done it
+			new_screen(rows, columns);
+		}
 #endif
+	}
+	sync_cursor(dot, &crow, &ccol);	// where cursor will be (on "dot")
+	tp = screenbegin;	// index into text[] of top line
 
-#endif /* FEATURE_VI_COLON */
+	// compare text[] to screen[] and mark screen[] lines that need updating
+	for (li = 0; li < rows - 1; li++) {
+		int cs, ce;				// column start & end
+		char *out_buf;
+		// format current text line
+		out_buf = format_line(tp /*, li*/);
 
-// buf must be no longer than MAX_INPUT_LEN!
-static void colon(char *buf)
-{
-#if !ENABLE_FEATURE_VI_COLON
-	// Simple ":cmd" handler with minimal set of commands
-	char *p = buf;
-	int cnt;
+		// skip to the end of the current text[] line
+		if (tp < end) {
+			char *t = memchr(tp, '\n', end - tp);
+			if (!t) t = end - 1;
+			tp = t + 1;
+		}
 
-	if (*p == ':')
-		p++;
-	cnt = strlen(p);
-	if (cnt == 0)
-		return;
-	if (strncmp(p, "quit", cnt) == 0
-	 || strncmp(p, "q!", cnt) == 0
-	) {
-		if (modified_count && p[1] != '!') {
-			status_line_bold("No write since last change (:%s! overrides)", p);
-		} else {
-			editing = 0;
+		// see if there are any changes between virtual screen and out_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;
 		}
-		return;
-	}
-	if (strncmp(p, "write", cnt) == 0
-	 || strncmp(p, "wq", cnt) == 0
-	 || strncmp(p, "wn", cnt) == 0
-	 || (p[0] == 'x' && !p[1])
-	) {
-		if (modified_count != 0 || p[0] != 'x') {
-			cnt = file_write(current_filename, text, end - 1);
+		// compare newly formatted buffer with virtual screen
+		// look forward for first difference between buf and screen
+		for (; cs <= ce; cs++) {
+			if (out_buf[cs] != sp[cs]) {
+				changed = TRUE;	// mark for redraw
+				break;
+			}
 		}
-		if (cnt < 0) {
-			if (cnt == -1)
-				status_line_bold("Write error: "STRERROR_FMT STRERROR_ERRNO);
-		} else {
-			modified_count = 0;
-			last_modified_count = -1;
-			status_line("'%s' %dL, %dC",
-				current_filename,
-				count_lines(text, end - 1), cnt
-			);
-			if (p[0] == 'x'
-			 || p[1] == 'q' || p[1] == 'n'
-			 || p[1] == 'Q' || p[1] == 'N'
-			) {
-				editing = 0;
+
+		// look backward for last difference between out_buf and screen
+		for (; ce >= cs; ce--) {
+			if (out_buf[ce] != sp[ce]) {
+				changed = TRUE;	// mark for redraw
+				break;
 			}
 		}
-		return;
-	}
-	if (strncmp(p, "file", cnt) == 0) {
-		last_status_cksum = 0;	// force status update
-		return;
-	}
-	if (sscanf(p, "%d", &cnt) > 0) {
-		dot = find_line(cnt);
-		dot_skip_over_ws();
-		return;
-	}
-	not_implemented(p);
-#else
+		// now, cs is index of first diff, and ce is index of last diff
 
-	char c, *buf1, *q, *r;
-	char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN];
-	int i, l, li, b, e;
-	int useforce;
-# if ENABLE_FEATURE_VI_SEARCH || ENABLE_FEATURE_ALLOW_EXEC
-	char *orig_buf;
-# endif
+		// if horz offset has changed, force a redraw
+		if (offset != old_offset) {
+ re0:
+			changed = TRUE;
+		}
 
-	// :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
-	//
+		// 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 virtual screen and out_buf
+		if (changed) {
+			// copy changed part of buffer to virtual screen
+			memcpy(sp+cs, out_buf+cs, ce-cs+1);
+			place_cursor(li, cs);
+			// write line out to terminal
+			fwrite(&sp[cs], ce - cs + 1, 1, stdout);
+		}
+	}
 
-	if (!buf[0])
-		goto ret;
-	if (*buf == ':')
-		buf++;			// move past the ':'
+	place_cursor(crow, ccol);
 
-	li = i = 0;
-	b = e = -1;
-	q = text;			// assume 1,$ for the range
-	r = end - 1;
-	li = count_lines(text, end - 1);
-	fn = current_filename;
+	old_offset = offset;
+#undef old_offset
+}
 
-	// look for optional address(es)  :.  :1  :1,9   :'q,'a   :%
-	buf = get_address(buf, &b, &e);
+//----- Force refresh of all Lines -----------------------------
+static void redraw(int full_screen)
+{
+	// cursor to top,left; clear to the end of screen
+	write1(ESC_SET_CURSOR_TOPLEFT ESC_CLEAR2EOS);
+	screen_erase();		// erase the internal screen buffer
+	last_status_cksum = 0;	// force status update
+	refresh(full_screen);	// this will redraw the entire display
+	show_status_line();
+}
 
-# if ENABLE_FEATURE_VI_SEARCH || ENABLE_FEATURE_ALLOW_EXEC
-	// remember orig command line
-	orig_buf = buf;
-# endif
+//----- Flash the screen  --------------------------------------
+static void flash(int h)
+{
+	standout_start();
+	redraw(TRUE);
+	mysleep(h);
+	standout_end();
+	redraw(TRUE);
+}
 
-	// get the COMMAND into cmd[]
-	buf1 = cmd;
-	while (*buf != '\0') {
-		if (isspace(*buf))
-			break;
-		*buf1++ = *buf++;
-	}
-	*buf1 = '\0';
-	// get any ARGuments
-	while (isblank(*buf))
-		buf++;
-	strcpy(args, buf);
-	useforce = FALSE;
-	buf1 = last_char_is(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(cmd);
-	if (i == 0) {		// :123CR goto line #123
-		if (b >= 0) {
-			dot = find_line(b);	// what line is #b
-			dot_skip_over_ws();
-		}
-	}
-# if ENABLE_FEATURE_ALLOW_EXEC
-	else if (cmd[0] == '!') {	// run a cmd
-		int retcode;
-		// :!ls   run the <cmd>
-		go_bottom_and_clear_to_eol();
-		cookmode();
-		retcode = system(orig_buf + 1);	// run the cmd
-		if (retcode)
-			printf("\nshell returned %i\n\n", retcode);
-		rawmode();
-		Hit_Return();			// let user see results
+static void indicate_error(void)
+{
+#if ENABLE_FEATURE_VI_CRASHME
+	if (crashme > 0)
+		return;
+#endif
+	if (!err_method) {
+		write1(ESC_BELL);
+	} else {
+		flash(10);
 	}
-# endif
-	else if (cmd[0] == '=' && !cmd[1]) {	// where is the address
-		if (b < 0) {	// no addr given- use defaults
-			b = e = count_lines(text, dot);
-		}
-		status_line("%d", b);
-	} else if (strncmp(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, ALLOW_UNDO);	// save, then delete lines
-		dot_skip_over_ws();
-	} else if (strncmp(cmd, "edit", i) == 0) {	// Edit a file
-		int size;
+}
 
-		// don't edit, if the current file has been modified
-		if (modified_count && !useforce) {
-			status_line_bold("No write since last change (:%s! overrides)", cmd);
-			goto ret;
-		}
-		if (args[0]) {
-			// the user supplied a file name
-			fn = args;
-		} else if (current_filename && current_filename[0]) {
-			// no user supplied name- use the current filename
-			// fn = current_filename;  was set by default
-		} else {
-			// no user file name, no current name- punt
-			status_line_bold("No current filename");
-			goto ret;
-		}
+//----- IO Routines --------------------------------------------
+static int readit(void) // read (maybe cursor) key from stdin
+{
+	int c;
 
-		size = init_text_buffer(fn);
+	fflush_all();
 
-# if ENABLE_FEATURE_VI_YANKMARK
-		if (Ureg >= 0 && Ureg < 28) {
-			free(reg[Ureg]);	//   free orig line reg- for 'U'
-			reg[Ureg] = NULL;
-		}
-		if (YDreg >= 0 && YDreg < 28) {
-			free(reg[YDreg]);	//   free default yank/delete register
-			reg[YDreg] = NULL;
-		}
-# endif
-		// how many lines in text[]?
-		li = count_lines(text, end - 1);
-		status_line("'%s'%s"
-			IF_FEATURE_VI_READONLY("%s")
-			" %dL, %dC",
-			current_filename,
-			(size < 0 ? " [New file]" : ""),
-			IF_FEATURE_VI_READONLY(
-				((readonly_mode) ? " [Readonly]" : ""),
-			)
-			li, (int)(end - text)
-		);
-	} else if (strncmp(cmd, "file", i) == 0) {	// what File is this
-		if (b != -1 || e != -1) {
-			status_line_bold("No address allowed on this command");
-			goto ret;
-		}
-		if (args[0]) {
-			// user wants a new filename
-			free(current_filename);
-			current_filename = xstrdup(args);
-		} else {
-			// user wants file status info
-			last_status_cksum = 0;	// force status update
-		}
-	} else if (strncmp(cmd, "features", i) == 0) {	// what features are available
-		// print out values of all features
-		go_bottom_and_clear_to_eol();
-		cookmode();
-		show_help();
-		rawmode();
-		Hit_Return();
-	} else if (strncmp(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);
-		}
+	// Wait for input. TIMEOUT = -1 makes read_key wait even
+	// on nonblocking stdin.
+	// Note: read_key sets errno to 0 on success.
+ again:
+	c = read_key(STDIN_FILENO, readbuffer, /*timeout:*/ -1);
+	if (c == -1) { // EOF/error
+		if (errno == EAGAIN) // paranoia
+			goto again;
 		go_bottom_and_clear_to_eol();
-		puts("\r");
-		for (; q <= r; q++) {
-			int c_is_no_print;
+		cookmode(); // terminal to "cooked"
+		bb_error_msg_and_die("can't read user input");
+	}
+	return c;
+}
 
-			c = *q;
-			c_is_no_print = (c & 0x80) && !Isprint(c);
-			if (c_is_no_print) {
-				c = '.';
-				standout_start();
-			}
-			if (c == '\n') {
-				write1("$\r");
-			} else if (c < ' ' || c == 127) {
-				bb_putchar('^');
-				if (c == 127)
-					c = '?';
-				else
-					c += '@';
-			}
-			bb_putchar(c);
-			if (c_is_no_print)
-				standout_end();
-		}
-		Hit_Return();
-	} else if (strncmp(cmd, "quit", i) == 0 // quit
-	        || strncmp(cmd, "next", i) == 0 // edit next file
-	        || strncmp(cmd, "prev", i) == 0 // edit previous file
-	) {
-		int n;
-		if (useforce) {
-			if (*cmd == 'q') {
-				// force end of argv list
-				optind = save_argc;
-			}
-			editing = 0;
-			goto ret;
-		}
-		// don't exit if the file been modified
-		if (modified_count) {
-			status_line_bold("No write since last change (:%s! overrides)", cmd);
-			goto ret;
-		}
-		// are there other file to edit
-		n = save_argc - optind - 1;
-		if (*cmd == 'q' && n > 0) {
-			status_line_bold("%d more file(s) to edit", n);
-			goto ret;
-		}
-		if (*cmd == 'n' && n <= 0) {
-			status_line_bold("No more files to edit");
-			goto ret;
-		}
-		if (*cmd == 'p') {
-			// are there previous files to edit
-			if (optind < 1) {
-				status_line_bold("No previous files to edit");
-				goto ret;
-			}
-			optind -= 2;
-		}
-		editing = 0;
-	} else if (strncmp(cmd, "read", i) == 0) {	// read file into text[]
-		int size;
+static int get_one_char(void)
+{
+	int c;
 
-		fn = args;
-		if (!fn[0]) {
-			status_line_bold("No filename given");
-			goto ret;
-		}
-		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);
-			// read after last line
-			if (q == end-1)
-				++q;
-		}
-		{ // dance around potentially-reallocated text[]
-			uintptr_t ofs = q - text;
-			size = file_insert(fn, q, 0);
-			q = text + ofs;
-		}
-		if (size < 0)
-			goto ret;	// nothing was inserted
-		// how many lines in text[]?
-		li = count_lines(q, q + size - 1);
-		status_line("'%s'"
-			IF_FEATURE_VI_READONLY("%s")
-			" %dL, %dC",
-			fn,
-			IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
-			li, size
-		);
-		if (size > 0) {
-			// if the insert is before "dot" then we need to update
-			if (q <= dot)
-				dot += size;
-		}
-	} else if (strncmp(cmd, "rewind", i) == 0) {	// rewind cmd line args
-		if (modified_count && !useforce) {
-			status_line_bold("No write since last change (:%s! overrides)", cmd);
+#if ENABLE_FEATURE_VI_DOT_CMD
+	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 {
-			// reset the filenames to edit
-			optind = -1; // start from 0th file
-			editing = 0;
-		}
-# if ENABLE_FEATURE_VI_SET
-	} else if (strncmp(cmd, "set", i) == 0) {	// set or clear features
-#  if ENABLE_FEATURE_VI_SETOPTS
-		char *argp;
-#  endif
-		i = 0;			// offset into args
-		// only blank is regarded as args delimiter. What about tab '\t'?
-		if (!args[0] || strcasecmp(args, "all") == 0) {
-			// print out values of all options
-#  if ENABLE_FEATURE_VI_SETOPTS
-			status_line_bold(
-				"%sautoindent "
-				"%sflash "
-				"%signorecase "
-				"%sshowmatch "
-				"tabstop=%u",
-				autoindent ? "" : "no",
-				err_method ? "" : "no",
-				ignorecase ? "" : "no",
-				showmatch ? "" : "no",
-				tabstop
-			);
-#  endif
-			goto ret;
-		}
-#  if ENABLE_FEATURE_VI_SETOPTS
-		argp = args;
-		while (*argp) {
-			if (strncmp(argp, "no", 2) == 0)
-				i = 2;		// ":set noautoindent"
-			setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT);
-			setops(argp, "flash "     , i, "fl", VI_ERR_METHOD);
-			setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE);
-			setops(argp, "showmatch " , i, "sm", VI_SHOWMATCH );
-			if (strncmp(argp + i, "tabstop=", 8) == 0) {
-				int t = 0;
-				sscanf(argp + i+8, "%u", &t);
-				if (t > 0 && t <= MAX_TABSTOP)
-					tabstop = t;
+			// there is a queue to get chars from first
+			// careful with correct sign expansion!
+			c = (unsigned char)*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
 			}
-			argp = skip_non_whitespace(argp);
-			argp = skip_whitespace(argp);
 		}
-#  endif /* FEATURE_VI_SETOPTS */
-# endif /* FEATURE_VI_SET */
+	} else {
+		// adding STDIN chars to q
+		c = readit();	// get the users input
+		if (lmc_len >= MAX_INPUT_LEN - 1) {
+			status_line_bold("last_modifying_cmd overrun");
+		} else {
+			// add new char to q
+			last_modifying_cmd[lmc_len++] = c;
+		}
+	}
+#else
+	c = readit();		// get the users input
+#endif /* FEATURE_VI_DOT_CMD */
+	return c;
+}
 
-# if ENABLE_FEATURE_VI_SEARCH
-	} else if (cmd[0] == 's') {	// substitute a pattern with a replacement pattern
-		char *F, *R, *flags;
-		size_t len_F, len_R;
-		int gflag;		// global replace flag
-#  if ENABLE_FEATURE_VI_UNDO
-		int dont_chain_first_item = ALLOW_UNDO;
-#  endif
+// Get input line (uses "status line" area)
+static char *get_input_line(const char *prompt)
+{
+	// char [MAX_INPUT_LEN]
+#define buf get_input_line__buf
 
-		// F points to the "find" pattern
-		// R points to the "replace" pattern
-		// replace the cmd line delimiters "/" with NULs
-		c = orig_buf[1];	// what is the delimiter
-		F = orig_buf + 2;	// start of "find"
-		R = strchr(F, c);	// middle delimiter
-		if (!R)
-			goto colon_s_fail;
-		len_F = R - F;
-		*R++ = '\0';	// terminate "find"
-		flags = strchr(R, c);
-		if (!flags)
-			goto colon_s_fail;
-		len_R = flags - R;
-		*flags++ = '\0';	// terminate "replace"
-		gflag = *flags;
-
-		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/
+	int c;
+	int i;
 
-		for (i = b; i <= e; i++) {	// so, :20,23 s \0 find \0 replace \0
-			char *ls = q;		// orig line start
-			char *found;
- vc4:
-			found = char_search(q, F, (FORWARD << 1) | LIMITED);	// search cur line only for "find"
-			if (found) {
-				uintptr_t bias;
-				// we found the "find" pattern - delete it
-				// For undo support, the first item should not be chained
-				text_hole_delete(found, found + len_F - 1, dont_chain_first_item);
-#  if ENABLE_FEATURE_VI_UNDO
-				dont_chain_first_item = ALLOW_UNDO_CHAIN;
-#  endif
-				// insert the "replace" patern
-				bias = string_insert(found, R, ALLOW_UNDO_CHAIN);
-				found += bias;
-				ls += bias;
-				/*q += bias; - recalculated anyway */
-				// check for "global"  :s/foo/bar/g
-				if (gflag == 'g') {
-					if ((found + len_R) < end_line(ls)) {
-						q = found + len_R;
-						goto vc4;	// don't let q move past cur line
-					}
-				}
-			}
-			q = next_line(ls);
-		}
-# endif /* FEATURE_VI_SEARCH */
-	} else if (strncmp(cmd, "version", i) == 0) {  // show software version
-		status_line(BB_VER);
-	} else if (strncmp(cmd, "write", i) == 0  // write text to file
-	        || strncmp(cmd, "wq", i) == 0
-	        || strncmp(cmd, "wn", i) == 0
-	        || (cmd[0] == 'x' && !cmd[1])
-	) {
-		int size;
-		//int forced = FALSE;
+	strcpy(buf, prompt);
+	last_status_cksum = 0;	// force status update
+	go_bottom_and_clear_to_eol();
+	write1(prompt);      // write out the :, /, or ? prompt
 
-		// is there a file name to write to?
-		if (args[0]) {
-			fn = args;
-		}
-# if ENABLE_FEATURE_VI_READONLY
-		if (readonly_mode && !useforce) {
-			status_line_bold("'%s' is read only", fn);
-			goto ret;
-		}
-# endif
-		//if (useforce) {
-			// if "fn" is not write-able, chmod u+w
-			// sprintf(syscmd, "chmod u+w %s", fn);
-			// system(syscmd);
-			// forced = TRUE;
-		//}
-		if (modified_count != 0 || cmd[0] != 'x') {
-			size = r - q + 1;
-			l = file_write(fn, q, r);
-		} else {
-			size = 0;
-			l = 0;
-		}
-		//if (useforce && forced) {
-			// chmod u-w
-			// sprintf(syscmd, "chmod u-w %s", fn);
-			// system(syscmd);
-			// forced = FALSE;
-		//}
-		if (l < 0) {
-			if (l == -1)
-				status_line_bold_errno(fn);
-		} else {
-			// how many lines written
-			li = count_lines(q, q + l - 1);
-			status_line("'%s' %dL, %dC", fn, li, l);
-			if (l == size) {
-				if (q == text && q + l == end) {
-					modified_count = 0;
-					last_modified_count = -1;
-				}
-				if (cmd[0] == 'x'
-				 || cmd[1] == 'q' || cmd[1] == 'n'
-				 || cmd[1] == 'Q' || cmd[1] == 'N'
-				) {
-					editing = 0;
-				}
-			}
-		}
-# if ENABLE_FEATURE_VI_YANKMARK
-	} else if (strncmp(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);
+	i = strlen(buf);
+	while (i < MAX_INPUT_LEN) {
+		c = get_one_char();
+		if (c == '\n' || c == '\r' || c == 27)
+			break;		// this is end of input
+		if (c == erase_char || c == 8 || c == 127) {
+			// user wants to erase prev char
+			buf[--i] = '\0';
+			write1("\b \b"); // erase char on screen
+			if (i <= 0) // user backs up before b-o-l, exit
+				break;
+		} else if (c > 0 && c < 256) { // exclude Unicode
+			// (TODO: need to handle Unicode)
+			buf[i] = c;
+			buf[++i] = '\0';
+			bb_putchar(c);
 		}
-		text_yank(q, r, YDreg);
-		li = count_lines(q, r);
-		status_line("Yank %d lines (%d chars) into [%c]",
-				li, strlen(reg[YDreg]), what_reg());
-# endif
-	} else {
-		// cmd unknown
-		not_implemented(cmd);
 	}
- ret:
-	dot = bound_dot(dot);	// make sure "dot" is valid
-	return;
-# if ENABLE_FEATURE_VI_SEARCH
- colon_s_fail:
-	status_line(":s expression missing delimiters");
-# endif
-#endif /* FEATURE_VI_COLON */
+	refresh(FALSE);
+	return buf;
+#undef buf
 }
 
 static void Hit_Return(void)
@@ -1631,436 +1119,366 @@ static void Hit_Return(void)
 	redraw(TRUE);		// force redraw all
 }
 
-//----- Dot Movement Routines ----------------------------------
-static void dot_left(void)
+//----- Draw the status line at bottom of the screen -------------
+// show file status on status line
+static int format_edit_status(void)
 {
-	undo_queue_commit();
-	if (dot > text && dot[-1] != '\n')
-		dot--;
-}
+	static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
 
-static void dot_right(void)
-{
-	undo_queue_commit();
-	if (dot < end - 1 && *dot != '\n')
-		dot++;
-}
+#define tot format_edit_status__tot
 
-static void dot_begin(void)
-{
-	undo_queue_commit();
-	dot = begin_line(dot);	// return pointer to first char cur line
-}
+	int cur, percent, ret, trunc_at;
 
-static void dot_end(void)
-{
-	undo_queue_commit();
-	dot = end_line(dot);	// return pointer to last char cur line
-}
+	// modified_count is now a counter rather than a flag.  this
+	// helps reduce the amount of line counting we need to do.
+	// (this will cause a mis-reporting of modified status
+	// once every MAXINT editing operations.)
 
-static char *move_to_col(char *p, int l)
-{
-	int co;
+	// it would be nice to do a similar optimization here -- if
+	// we haven't done a motion that could have changed which line
+	// we're on, then we shouldn't have to do this count_lines()
+	cur = count_lines(text, dot);
 
-	p = begin_line(p);
-	co = 0;
-	while (co < l && p < end) {
-		if (*p == '\n') //vda || *p == '\0')
-			break;
-		if (*p == '\t') {
-			co = next_tabstop(co);
-		} else if (*p < ' ' || *p == 127) {
-			co++; // display as ^X, use 2 columns
-		}
-		co++;
-		p++;
+	// count_lines() is expensive.
+	// Call it only if something was changed since last time
+	// we were here:
+	if (modified_count != last_modified_count) {
+		tot = cur + count_lines(dot, end - 1) - 1;
+		last_modified_count = modified_count;
 	}
-	return p;
-}
 
-static void dot_next(void)
-{
-	undo_queue_commit();
-	dot = next_line(dot);
+	//    current line         percent
+	//   -------------    ~~ ----------
+	//    total lines            100
+	if (tot > 0) {
+		percent = (100 * cur) / tot;
+	} else {
+		cur = tot = 0;
+		percent = 100;
+	}
+
+	trunc_at = columns < STATUS_BUFFER_LEN-1 ?
+		columns : STATUS_BUFFER_LEN-1;
+
+	ret = snprintf(status_buffer, trunc_at+1,
+#if ENABLE_FEATURE_VI_READONLY
+		"%c %s%s%s %d/%d %d%%",
+#else
+		"%c %s%s %d/%d %d%%",
+#endif
+		cmd_mode_indicator[cmd_mode & 3],
+		(current_filename != NULL ? current_filename : "No file"),
+#if ENABLE_FEATURE_VI_READONLY
+		(readonly_mode ? " [Readonly]" : ""),
+#endif
+		(modified_count ? " [Modified]" : ""),
+		cur, tot, percent);
+
+	if (ret >= 0 && ret < trunc_at)
+		return ret;  // it all fit
+
+	return trunc_at;  // had to truncate
+#undef tot
 }
 
-static void dot_prev(void)
+static int bufsum(char *buf, int count)
 {
-	undo_queue_commit();
-	dot = prev_line(dot);
+	int sum = 0;
+	char *e = buf + count;
+	while (buf < e)
+		sum += (unsigned char) *buf++;
+	return sum;
 }
 
-static void dot_scroll(int cnt, int dir)
+static void show_status_line(void)
 {
-	char *q;
+	int cnt = 0, cksum = 0;
 
-	undo_queue_commit();
-	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);
+	// either we already have an error or status message, or we
+	// create one.
+	if (!have_status_msg) {
+		cnt = format_edit_status();
+		cksum = bufsum(status_buffer, cnt);
+	}
+	if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
+		last_status_cksum = cksum;		// remember if we have seen this line
+		go_bottom_and_clear_to_eol();
+		write1(status_buffer);
+		if (have_status_msg) {
+			if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
+					(columns - 1) ) {
+				have_status_msg = 0;
+				Hit_Return();
+			}
+			have_status_msg = 0;
 		}
+		place_cursor(crow, ccol);  // put cursor back in correct place
 	}
-	// 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();
+	fflush_all();
 }
 
-static void dot_skip_over_ws(void)
+//----- format the status buffer, the bottom line of screen ------
+// format status buffer, with STANDOUT mode
+static void status_line_bold(const char *format, ...)
 {
-	// skip WS
-	while (isspace(*dot) && *dot != '\n' && dot < end - 1)
-		dot++;
+	va_list args;
+
+	va_start(args, format);
+	strcpy(status_buffer, ESC_BOLD_TEXT);
+	vsprintf(status_buffer + sizeof(ESC_BOLD_TEXT)-1, format, args);
+	strcat(status_buffer, ESC_NORM_TEXT);
+	va_end(args);
+
+	have_status_msg = 1 + sizeof(ESC_BOLD_TEXT) + sizeof(ESC_NORM_TEXT) - 2;
 }
 
-static char *bound_dot(char *p) // make sure  text[0] <= P < "end"
+static void status_line_bold_errno(const char *fn)
 {
-	if (p >= end && end > text) {
-		p = end - 1;
-		indicate_error();
-	}
-	if (p < text) {
-		p = text;
-		indicate_error();
-	}
-	return p;
+	status_line_bold("'%s' "STRERROR_FMT, fn STRERROR_ERRNO);
 }
 
-//----- 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 char *new_screen(int ro, int co)
+// format status buffer
+static void status_line(const char *format, ...)
 {
-	int li;
-
-	free(screen);
-	screensize = ro * co + 8;
-	screen = xmalloc(screensize);
-	// initialize the new screen. assume this will be a empty file.
-	screen_erase();
-	//   non-existent text[] lines start with a tilde (~).
-	for (li = 1; li < ro - 1; li++) {
-		screen[(li * co) + 0] = '~';
-	}
-	return screen;
-}
+	va_list args;
 
-#if ENABLE_FEATURE_VI_SEARCH
+	va_start(args, format);
+	vsprintf(status_buffer, format, args);
+	va_end(args);
 
-# if ENABLE_FEATURE_VI_REGEX_SEARCH
+	have_status_msg = 1;
+}
 
-// search for pattern starting at p
-static char *char_search(char *p, const char *pat, int dir_and_range)
+// copy s to buf, convert unprintable
+static void print_literal(char *buf, const char *s)
 {
-	struct re_pattern_buffer preg;
-	const char *err;
-	char *q;
-	int i;
-	int size;
-	int range;
-
-	re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
-	if (ignorecase)
-		re_syntax_options = RE_SYNTAX_POSIX_EXTENDED | RE_ICASE;
+	char *d;
+	unsigned char c;
 
-	memset(&preg, 0, sizeof(preg));
-	err = re_compile_pattern(pat, strlen(pat), &preg);
-	if (err != NULL) {
-		status_line_bold("bad search pattern '%s': %s", pat, err);
-		return p;
-	}
+	buf[0] = '\0';
+	if (!s[0])
+		s = "(NULL)";
 
-	range = (dir_and_range & 1);
-	q = end - 1; // if FULL
-	if (range == LIMITED)
-		q = next_line(p);
-	if (dir_and_range < 0) { // BACK?
-		q = text;
-		if (range == LIMITED)
-			q = prev_line(p);
-	}
+	d = buf;
+	for (; *s; s++) {
+		int c_is_no_print;
 
-	// RANGE could be negative if we are searching backwards
-	range = q - p;
-	q = p;
-	size = range;
-	if (range < 0) {
-		size = -size;
-		q = p - size;
-		if (q < text)
-			q = text;
+		c = *s;
+		c_is_no_print = (c & 0x80) && !Isprint(c);
+		if (c_is_no_print) {
+			strcpy(d, ESC_NORM_TEXT);
+			d += sizeof(ESC_NORM_TEXT)-1;
+			c = '.';
+		}
+		if (c < ' ' || c == 0x7f) {
+			*d++ = '^';
+			c |= '@'; // 0x40
+			if (c == 0x7f)
+				c = '?';
+		}
+		*d++ = c;
+		*d = '\0';
+		if (c_is_no_print) {
+			strcpy(d, ESC_BOLD_TEXT);
+			d += sizeof(ESC_BOLD_TEXT)-1;
+		}
+		if (*s == '\n') {
+			*d++ = '$';
+			*d = '\0';
+		}
+		if (d - buf > MAX_INPUT_LEN - 10) // paranoia
+			break;
 	}
-	// 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, /*start:*/ 0, range, /*struct re_registers*:*/ NULL);
-	regfree(&preg);
-	if (i < 0)
-		return NULL;
-	if (dir_and_range > 0) // FORWARD?
-		p = p + i;
-	else
-		p = p - i;
-	return p;
 }
 
-# else
-
-#  if ENABLE_FEATURE_VI_SETOPTS
-static int mycmp(const char *s1, const char *s2, int len)
+static void not_implemented(const char *s)
 {
-	if (ignorecase) {
-		return strncasecmp(s1, s2, len);
-	}
-	return strncmp(s1, s2, len);
+	char buf[MAX_INPUT_LEN];
+
+	print_literal(buf, s);
+	status_line_bold("\'%s\' is not implemented", buf);
 }
-#  else
-#   define mycmp strncmp
-#  endif
 
-static char *char_search(char *p, const char *pat, int dir_and_range)
+#if ENABLE_FEATURE_VI_YANKMARK
+static char *text_yank(char *p, char *q, int dest)	// copy text into a register
 {
-	char *start, *stop;
-	int len;
-	int range;
-
-	len = strlen(pat);
-	range = (dir_and_range & 1);
-	if (dir_and_range > 0) { //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 { //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;
-			}
-		}
+	int cnt = q - p;
+	if (cnt < 0) {		// they are backwards- reverse them
+		p = q;
+		cnt = -cnt;
 	}
-	// pattern not found
-	return NULL;
+	free(reg[dest]);	//  if already a yank register, free it
+	reg[dest] = xstrndup(p, cnt + 1);
+	return p;
 }
 
-# endif
+static char what_reg(void)
+{
+	char c;
 
-#endif /* FEATURE_VI_SEARCH */
+	c = 'D';			// default to D-reg
+	if (0 <= YDreg && YDreg <= 25)
+		c = 'a' + (char) YDreg;
+	if (YDreg == 26)
+		c = 'D';
+	if (YDreg == 27)
+		c = 'U';
+	return c;
+}
 
-static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
+static void check_context(char cmd)
 {
-	if (c == 22) {		// Is this an ctrl-V?
-		p += stupid_insert(p, '^');	// use ^ to indicate literal next
-		refresh(FALSE);	// show the ^
-		c = get_one_char();
-		*p = c;
-#if ENABLE_FEATURE_VI_UNDO
-		undo_push_insert(p, 1, undo);
-#else
-		modified_count++;
-#endif /* ENABLE_FEATURE_VI_UNDO */
-		p++;
-	} else if (c == 27) {	// Is this an ESC?
-		cmd_mode = 0;
-		undo_queue_commit();
-		cmdcnt = 0;
-		end_cmd_q();	// stop adding to q
-		last_status_cksum = 0;	// force status update
-		if ((p[-1] != '\n') && (dot > text)) {
-			p--;
-		}
-	} else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
-		if (p > text) {
-			p--;
-			p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED);	// shrink buffer 1 char
-		}
-	} else {
-		// insert a char into text[]
-		if (c == 13)
-			c = '\n';	// translate \r to \n
-#if ENABLE_FEATURE_VI_UNDO
-# if ENABLE_FEATURE_VI_UNDO_QUEUE
-		if (c == '\n')
-			undo_queue_commit();
-# endif
-		undo_push_insert(p, 1, undo);
-#else
-		modified_count++;
-#endif /* ENABLE_FEATURE_VI_UNDO */
-		p += 1 + stupid_insert(p, c);	// insert the char
-#if ENABLE_FEATURE_VI_SETOPTS
-		if (showmatch && strchr(")]}", c) != NULL) {
-			showmatching(p - 1);
-		}
-		if (autoindent && c == '\n') {	// auto indent the new line
-			char *q;
-			size_t len;
-			q = prev_line(p);	// use prev line as template
-			len = strspn(q, " \t"); // space or tab
-			if (len) {
-				uintptr_t bias;
-				bias = text_hole_make(p, len);
-				p += bias;
-				q += bias;
-#if ENABLE_FEATURE_VI_UNDO
-				undo_push_insert(p, len, undo);
-#endif
-				memcpy(p, q, len);
-				p += len;
-			}
+	// A context is defined to be "modifying text"
+	// Any modifying command establishes a new context.
+
+	if (dot < context_start || dot > context_end) {
+		if (strchr(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;
 		}
-#endif
 	}
-	return p;
 }
 
-// might reallocate text[]! use p += stupid_insert(p, ...),
-// and be careful to not use pointers into potentially freed text[]!
-static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at 'p'
+static char *swap_context(char *p) // goto new context for '' command make this the current context
 {
-	uintptr_t bias;
-	bias = text_hole_make(p, 1);
-	p += bias;
-	*p = c;
-	return bias;
+	char *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] = p;
+		mark[26] = p = tmp;
+		context_start = prev_line(prev_line(prev_line(p)));
+		context_end = next_line(next_line(next_line(p)));
+	}
+	return p;
 }
+#endif /* FEATURE_VI_YANKMARK */
 
-static int st_test(char *p, int type, int dir, char *tested)
-{
-	char c, c0, ci;
-	int test, inc;
+#if ENABLE_FEATURE_VI_UNDO
+static void undo_push(char *, unsigned, unsigned char);
+#endif
 
-	inc = dir;
-	c = c0 = p[0];
-	ci = p[inc];
-	test = 0;
+// open a hole in text[]
+// might reallocate text[]! use p += text_hole_make(p, ...),
+// and be careful to not use pointers into potentially freed text[]!
+static uintptr_t text_hole_make(char *p, int size)	// at "p", make a 'size' byte hole
+{
+	uintptr_t bias = 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 == '_');
+	if (size <= 0)
+		return bias;
+	end += size;		// adjust the new END
+	if (end >= (text + text_size)) {
+		char *new_text;
+		text_size += end - (text + text_size) + 10240;
+		new_text = xrealloc(text, text_size);
+		bias = (new_text - text);
+		screenbegin += bias;
+		dot         += bias;
+		end         += bias;
+		p           += bias;
+#if ENABLE_FEATURE_VI_YANKMARK
+		{
+			int i;
+			for (i = 0; i < ARRAY_SIZE(mark); i++)
+				if (mark[i])
+					mark[i] += bias;
+		}
+#endif
+		text = new_text;
 	}
-	*tested = c;
-	return test;
+	memmove(p + size, p, end - size - p);
+	memset(p, ' ', size);	// clear new hole
+	return bias;
 }
 
-static char *skip_thing(char *p, int linecnt, int dir, int type)
+// close a hole in text[] - delete "p" through "q", inclusive
+// "undo" value indicates if this operation should be undo-able
+#if !ENABLE_FEATURE_VI_UNDO
+#define text_hole_delete(a,b,c) text_hole_delete(a,b)
+#endif
+static char *text_hole_delete(char *p, char *q, int undo)
 {
-	char c;
+	char *src, *dest;
+	int cnt, hole_size;
 
-	while (st_test(p, type, dir, &c)) {
-		// make sure we limit search to correct number of lines
-		if (c == '\n' && --linecnt < 1)
+	// 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 ENABLE_FEATURE_VI_UNDO
+	switch (undo) {
+		case NO_UNDO:
 			break;
-		if (dir >= 0 && p >= end - 1)
+		case ALLOW_UNDO:
+			undo_push(p, hole_size, UNDO_DEL);
 			break;
-		if (dir < 0 && p <= text)
+		case ALLOW_UNDO_CHAIN:
+			undo_push(p, hole_size, UNDO_DEL_CHAIN);
 			break;
-		p += dir;		// move to next char
+# if ENABLE_FEATURE_VI_UNDO_QUEUE
+		case ALLOW_UNDO_QUEUED:
+			undo_push(p, hole_size, UNDO_DEL_QUEUED);
+			break;
+# endif
 	}
-	return p;
+	modified_count--;
+#endif
+	if (src < text || src > end)
+		goto thd0;
+	if (dest < text || dest >= end)
+		goto thd0;
+	modified_count++;
+	if (src >= end)
+		goto thd_atend;	// just delete the end of the buffer
+	memmove(dest, src, cnt);
+ 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
+ thd0:
+	return dest;
 }
 
-// find matching char of pair  ()  []  {}
-// will crash if c is not one of these
-static char *find_pair(char *p, const char c)
-{
-	const char *braces = "()[]{}";
-	char match;
-	int dir, level;
-
-	dir = strchr(braces, c) - braces;
-	dir ^= 1;
-	match = braces[dir];
-	dir = ((dir & 1) << 1) - 1; // 1 for ([{, -1 for )\}
+#if ENABLE_FEATURE_VI_UNDO
 
-	// look for match, count levels of pairs  (( ))
-	level = 1;
-	for (;;) {
-		p += dir;
-		if (p < text || p >= end)
-			return NULL;
-		if (*p == c)
-			level++;	// increase pair levels
-		if (*p == match) {
-			level--;	// reduce pair level
-			if (level == 0)
-				return p; // found matching pair
-		}
+# if ENABLE_FEATURE_VI_UNDO_QUEUE
+// Flush any queued objects to the undo stack
+static void undo_queue_commit(void)
+{
+	// Pushes the queue object onto the undo stack
+	if (undo_q > 0) {
+		// Deleted character undo events grow from the end
+		undo_push(undo_queue + CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q,
+			undo_q,
+			(undo_queue_state | UNDO_USE_SPOS)
+		);
+		undo_queue_state = UNDO_EMPTY;
+		undo_q = 0;
 	}
 }
+# else
+#  define undo_queue_commit() ((void)0)
+# endif
 
-#if ENABLE_FEATURE_VI_SETOPTS
-// show the matching char of a pair,  ()  []  {}
-static void showmatching(char *p)
+static void flush_undo_data(void)
 {
-	char *q, *save_dot;
-
-	// we found half of a pair
-	q = find_pair(p, *p);	// get loc of matching char
-	if (q == NULL) {
-		indicate_error();	// 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
-		mysleep(40);	// give user some time
-		dot = save_dot;	// go back to old loc
-		refresh(FALSE);
-	}
-}
-#endif /* FEATURE_VI_SETOPTS */
-
-#if ENABLE_FEATURE_VI_UNDO
-static void flush_undo_data(void)
-{
-	struct undo_object *undo_entry;
+	struct undo_object *undo_entry;
 
 	while (undo_stack_tail) {
 		undo_entry = undo_stack_tail;
@@ -2085,7 +1503,7 @@ static void undo_push(char *src, unsigned length, uint8_t u_type)
 	// for the INS/DEL operation. The raw values should be equal to the values of
 	// UNDO_{INS,DEL} ORed with UNDO_QUEUED_FLAG
 
-#if ENABLE_FEATURE_VI_UNDO_QUEUE
+# if ENABLE_FEATURE_VI_UNDO_QUEUE
 	// This undo queuing functionality groups multiple character typing or backspaces
 	// into a single large undo object. This greatly reduces calls to malloc() for
 	// single-character operations while typing and has the side benefit of letting
@@ -2136,10 +1554,10 @@ static void undo_push(char *src, unsigned length, uint8_t u_type)
 		}
 		break;
 	}
-#else
+# else
 	// If undo queuing is disabled, ignore the queuing flag entirely
 	u_type = u_type & ~UNDO_QUEUED_FLAG;
-#endif
+# endif
 
 	// Allocate a new undo object
 	if (u_type == UNDO_DEL || u_type == UNDO_DEL_CHAIN) {
@@ -2154,16 +1572,16 @@ static void undo_push(char *src, unsigned length, uint8_t u_type)
 		undo_entry = xzalloc(sizeof(*undo_entry));
 	}
 	undo_entry->length = length;
-#if ENABLE_FEATURE_VI_UNDO_QUEUE
+# if ENABLE_FEATURE_VI_UNDO_QUEUE
 	if ((u_type & UNDO_USE_SPOS) != 0) {
 		undo_entry->start = undo_queue_spos - text;	// use start position from queue
 	} else {
 		undo_entry->start = src - text;	// use offset from start of text buffer
 	}
 	u_type = (u_type & ~UNDO_USE_SPOS);
-#else
+# else
 	undo_entry->start = src - text;
-#endif
+# endif
 	undo_entry->u_type = u_type;
 
 	// Push it on undo stack
@@ -2253,115 +1671,145 @@ static void undo_pop(void)
 	}
 }
 
-#if ENABLE_FEATURE_VI_UNDO_QUEUE
-// Flush any queued objects to the undo stack
-static void undo_queue_commit(void)
+#else
+# define flush_undo_data()   ((void)0)
+# define undo_queue_commit() ((void)0)
+#endif /* ENABLE_FEATURE_VI_UNDO */
+
+//----- Dot Movement Routines ----------------------------------
+static void dot_left(void)
 {
-	// Pushes the queue object onto the undo stack
-	if (undo_q > 0) {
-		// Deleted character undo events grow from the end
-		undo_push(undo_queue + CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q,
-			undo_q,
-			(undo_queue_state | UNDO_USE_SPOS)
-		);
-		undo_queue_state = UNDO_EMPTY;
-		undo_q = 0;
+	undo_queue_commit();
+	if (dot > text && dot[-1] != '\n')
+		dot--;
+}
+
+static void dot_right(void)
+{
+	undo_queue_commit();
+	if (dot < end - 1 && *dot != '\n')
+		dot++;
+}
+
+static void dot_begin(void)
+{
+	undo_queue_commit();
+	dot = begin_line(dot);	// return pointer to first char cur line
+}
+
+static void dot_end(void)
+{
+	undo_queue_commit();
+	dot = end_line(dot);	// return pointer to last char cur line
+}
+
+static char *move_to_col(char *p, int l)
+{
+	int co;
+
+	p = begin_line(p);
+	co = 0;
+	while (co < l && p < end) {
+		if (*p == '\n') //vda || *p == '\0')
+			break;
+		if (*p == '\t') {
+			co = next_tabstop(co);
+		} else if (*p < ' ' || *p == 127) {
+			co++; // display as ^X, use 2 columns
+		}
+		co++;
+		p++;
 	}
+	return p;
 }
-#endif
 
-#endif /* ENABLE_FEATURE_VI_UNDO */
+static void dot_next(void)
+{
+	undo_queue_commit();
+	dot = next_line(dot);
+}
 
-// open a hole in text[]
-// might reallocate text[]! use p += text_hole_make(p, ...),
-// and be careful to not use pointers into potentially freed text[]!
-static uintptr_t text_hole_make(char *p, int size)	// at "p", make a 'size' byte hole
+static void dot_prev(void)
 {
-	uintptr_t bias = 0;
+	undo_queue_commit();
+	dot = prev_line(dot);
+}
 
-	if (size <= 0)
-		return bias;
-	end += size;		// adjust the new END
-	if (end >= (text + text_size)) {
-		char *new_text;
-		text_size += end - (text + text_size) + 10240;
-		new_text = xrealloc(text, text_size);
-		bias = (new_text - text);
-		screenbegin += bias;
-		dot         += bias;
-		end         += bias;
-		p           += bias;
-#if ENABLE_FEATURE_VI_YANKMARK
-		{
-			int i;
-			for (i = 0; i < ARRAY_SIZE(mark); i++)
-				if (mark[i])
-					mark[i] += bias;
+static void dot_skip_over_ws(void)
+{
+	// skip WS
+	while (isspace(*dot) && *dot != '\n' && dot < end - 1)
+		dot++;
+}
+
+static void dot_scroll(int cnt, int dir)
+{
+	char *q;
+
+	undo_queue_commit();
+	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);
 		}
-#endif
-		text = new_text;
 	}
-	memmove(p + size, p, end - size - p);
-	memset(p, ' ', size);	// clear new hole
-	return bias;
+	// 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();
 }
 
-//  close a hole in text[]
-//  "undo" value indicates if this operation should be undo-able
-static char *text_hole_delete(char *p, char *q, int undo) // delete "p" through "q", inclusive
+static char *bound_dot(char *p) // make sure  text[0] <= P < "end"
 {
-	char *src, *dest;
-	int cnt, hole_size;
+	if (p >= end && end > text) {
+		p = end - 1;
+		indicate_error();
+	}
+	if (p < text) {
+		p = text;
+		indicate_error();
+	}
+	return p;
+}
 
-	// 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;
+#if ENABLE_FEATURE_VI_DOT_CMD
+static void start_new_cmd_q(char c)
+{
+	// get buffer for new cmd
+	// if there is a current cmd count put it in the buffer first
+	if (cmdcnt > 0) {
+		lmc_len = sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
+	} else { // just save char c onto queue
+		last_modifying_cmd[0] = c;
+		lmc_len = 1;
 	}
-	hole_size = q - p + 1;
-	cnt = end - src;
-#if ENABLE_FEATURE_VI_UNDO
-	switch (undo) {
-		case NO_UNDO:
-			break;
-		case ALLOW_UNDO:
-			undo_push(p, hole_size, UNDO_DEL);
-			break;
-		case ALLOW_UNDO_CHAIN:
-			undo_push(p, hole_size, UNDO_DEL_CHAIN);
-			break;
-# if ENABLE_FEATURE_VI_UNDO_QUEUE
-		case ALLOW_UNDO_QUEUED:
-			undo_push(p, hole_size, UNDO_DEL_QUEUED);
-			break;
+	adding2q = 1;
+}
+static void end_cmd_q(void)
+{
+# if ENABLE_FEATURE_VI_YANKMARK
+	YDreg = 26;			// go back to default Yank/Delete reg
 # endif
-	}
-	modified_count--;
-#endif
-	if (src < text || src > end)
-		goto thd0;
-	if (dest < text || dest >= end)
-		goto thd0;
-	modified_count++;
-	if (src >= end)
-		goto thd_atend;	// just delete the end of the buffer
-	memmove(dest, src, cnt);
- 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
- thd0:
-	return dest;
+	adding2q = 0;
 }
+#else
+# define end_cmd_q() ((void)0)
+#endif /* FEATURE_VI_DOT_CMD */
 
 // copy text into register, then delete text.
 // if dist <= 0, do not include, or go past, a NewLine
 //
+#if !ENABLE_FEATURE_VI_UNDO
+#define yank_delete(a,b,c,d,e) yank_delete(a,b,c,d)
+#endif
 static char *yank_delete(char *start, char *stop, int dist, int yf, int undo)
 {
 	char *p;
@@ -2396,226 +1844,12 @@ static char *yank_delete(char *start, char *stop, int dist, int yf, int undo)
 	return p;
 }
 
-#if ENABLE_FEATURE_VI_DOT_CMD
-static void start_new_cmd_q(char c)
+// might reallocate text[]!
+static int file_insert(const char *fn, char *p, int initial)
 {
-	// get buffer for new cmd
-	// if there is a current cmd count put it in the buffer first
-	if (cmdcnt > 0) {
-		lmc_len = sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
-	} else { // just save char c onto queue
-		last_modifying_cmd[0] = c;
-		lmc_len = 1;
-	}
-	adding2q = 1;
-}
-
-static void end_cmd_q(void)
-{
-#if ENABLE_FEATURE_VI_YANKMARK
-	YDreg = 26;			// go back to default Yank/Delete reg
-#endif
-	adding2q = 0;
-}
-#endif /* FEATURE_VI_DOT_CMD */
-
-#if ENABLE_FEATURE_VI_YANKMARK \
- || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
- || ENABLE_FEATURE_VI_CRASHME
-// might reallocate text[]! use p += string_insert(p, ...),
-// and be careful to not use pointers into potentially freed text[]!
-static uintptr_t string_insert(char *p, const char *s, int undo) // insert the string at 'p'
-{
-	uintptr_t bias;
-	int i;
-
-	i = strlen(s);
-#if ENABLE_FEATURE_VI_UNDO
-	undo_push_insert(p, i, undo);
-#endif
-	bias = text_hole_make(p, i);
-	p += bias;
-	memcpy(p, s, i);
-#if ENABLE_FEATURE_VI_YANKMARK
-	{
-		int cnt;
-		for (cnt = 0; *s != '\0'; s++) {
-			if (*s == '\n')
-				cnt++;
-		}
-		status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
-	}
-#endif
-	return bias;
-}
-#endif
-
-#if ENABLE_FEATURE_VI_YANKMARK
-static char *text_yank(char *p, char *q, int dest)	// copy text into a register
-{
-	int cnt = q - p;
-	if (cnt < 0) {		// they are backwards- reverse them
-		p = q;
-		cnt = -cnt;
-	}
-	free(reg[dest]);	//  if already a yank register, free it
-	reg[dest] = xstrndup(p, cnt + 1);
-	return p;
-}
-
-static char what_reg(void)
-{
-	char c;
-
-	c = 'D';			// default to D-reg
-	if (0 <= YDreg && YDreg <= 25)
-		c = 'a' + (char) YDreg;
-	if (YDreg == 26)
-		c = 'D';
-	if (YDreg == 27)
-		c = 'U';
-	return c;
-}
-
-static void check_context(char 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(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;
-		}
-	}
-}
-
-static char *swap_context(char *p) // goto new context for '' command make this the current context
-{
-	char *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] = p;
-		mark[26] = p = tmp;
-		context_start = prev_line(prev_line(prev_line(p)));
-		context_end = next_line(next_line(next_line(p)));
-	}
-	return p;
-}
-#endif /* FEATURE_VI_YANKMARK */
-
-//----- IO Routines --------------------------------------------
-static int readit(void) // read (maybe cursor) key from stdin
-{
-	int c;
-
-	fflush_all();
-
-	// Wait for input. TIMEOUT = -1 makes read_key wait even
-	// on nonblocking stdin.
-	// Note: read_key sets errno to 0 on success.
- again:
-	c = read_key(STDIN_FILENO, readbuffer, /*timeout:*/ -1);
-	if (c == -1) { // EOF/error
-		if (errno == EAGAIN) // paranoia
-			goto again;
-		go_bottom_and_clear_to_eol();
-		cookmode(); // terminal to "cooked"
-		bb_error_msg_and_die("can't read user input");
-	}
-	return c;
-}
-
-//----- IO Routines --------------------------------------------
-static int get_one_char(void)
-{
-	int c;
-
-#if ENABLE_FEATURE_VI_DOT_CMD
-	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
-			// careful with correct sign expansion!
-			c = (unsigned char)*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 (lmc_len >= MAX_INPUT_LEN - 1) {
-			status_line_bold("last_modifying_cmd overrun");
-		} else {
-			// add new char to q
-			last_modifying_cmd[lmc_len++] = c;
-		}
-	}
-#else
-	c = readit();		// get the users input
-#endif /* FEATURE_VI_DOT_CMD */
-	return c;
-}
-
-// Get input line (uses "status line" area)
-static char *get_input_line(const char *prompt)
-{
-	// char [MAX_INPUT_LEN]
-#define buf get_input_line__buf
-
-	int c;
-	int i;
-
-	strcpy(buf, prompt);
-	last_status_cksum = 0;	// force status update
-	go_bottom_and_clear_to_eol();
-	write1(prompt);      // write out the :, /, or ? prompt
-
-	i = strlen(buf);
-	while (i < MAX_INPUT_LEN) {
-		c = get_one_char();
-		if (c == '\n' || c == '\r' || c == 27)
-			break;		// this is end of input
-		if (c == erase_char || c == 8 || c == 127) {
-			// user wants to erase prev char
-			buf[--i] = '\0';
-			write1("\b \b"); // erase char on screen
-			if (i <= 0) // user backs up before b-o-l, exit
-				break;
-		} else if (c > 0 && c < 256) { // exclude Unicode
-			// (TODO: need to handle Unicode)
-			buf[i] = c;
-			buf[++i] = '\0';
-			bb_putchar(c);
-		}
-	}
-	refresh(FALSE);
-	return buf;
-#undef buf
-}
-
-// might reallocate text[]!
-static int file_insert(const char *fn, char *p, int initial)
-{
-	int cnt = -1;
-	int fd, size;
-	struct stat statbuf;
+	int cnt = -1;
+	int fd, size;
+	struct stat statbuf;
 
 	if (p < text)
 		p = text;
@@ -2666,399 +1900,1064 @@ static int file_insert(const char *fn, char *p, int initial)
 	return cnt;
 }
 
-static int file_write(char *fn, char *first, char *last)
+// find matching char of pair  ()  []  {}
+// will crash if c is not one of these
+static char *find_pair(char *p, const char c)
 {
-	int fd, cnt, charcnt;
+	const char *braces = "()[]{}";
+	char match;
+	int dir, level;
 
-	if (fn == 0) {
-		status_line_bold("No current filename");
-		return -2;
-	}
-	// By popular request we do not open file with O_TRUNC,
-	// but instead ftruncate() it _after_ successful write.
-	// Might reduce amount of data lost on power fail etc.
-	fd = open(fn, (O_WRONLY | O_CREAT), 0666);
-	if (fd < 0)
-		return -1;
-	cnt = last - first + 1;
-	charcnt = full_write(fd, first, cnt);
-	ftruncate(fd, charcnt);
-	if (charcnt == cnt) {
-		// good write
-		//modified_count = FALSE;
-	} else {
-		charcnt = 0;
+	dir = strchr(braces, c) - braces;
+	dir ^= 1;
+	match = braces[dir];
+	dir = ((dir & 1) << 1) - 1; // 1 for ([{, -1 for )\}
+
+	// look for match, count levels of pairs  (( ))
+	level = 1;
+	for (;;) {
+		p += dir;
+		if (p < text || p >= end)
+			return NULL;
+		if (*p == c)
+			level++;	// increase pair levels
+		if (*p == match) {
+			level--;	// reduce pair level
+			if (level == 0)
+				return p; // found matching pair
+		}
 	}
-	close(fd);
-	return charcnt;
 }
 
-//----- Flash the screen  --------------------------------------
-static void flash(int h)
+#if ENABLE_FEATURE_VI_SETOPTS
+// show the matching char of a pair,  ()  []  {}
+static void showmatching(char *p)
 {
-	standout_start();
-	redraw(TRUE);
-	mysleep(h);
-	standout_end();
-	redraw(TRUE);
+	char *q, *save_dot;
+
+	// we found half of a pair
+	q = find_pair(p, *p);	// get loc of matching char
+	if (q == NULL) {
+		indicate_error();	// 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
+		mysleep(40);	// give user some time
+		dot = save_dot;	// go back to old loc
+		refresh(FALSE);
+	}
 }
+#endif /* FEATURE_VI_SETOPTS */
 
-static void indicate_error(void)
+// might reallocate text[]! use p += stupid_insert(p, ...),
+// and be careful to not use pointers into potentially freed text[]!
+static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at 'p'
 {
-#if ENABLE_FEATURE_VI_CRASHME
-	if (crashme > 0)
-		return;
+	uintptr_t bias;
+	bias = text_hole_make(p, 1);
+	p += bias;
+	*p = c;
+	return bias;
+}
+
+#if !ENABLE_FEATURE_VI_UNDO
+#define char_insert(a,b,c) char_insert(a,b)
 #endif
-	if (!err_method) {
-		write1(ESC_BELL);
+static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
+{
+	if (c == 22) {		// Is this an ctrl-V?
+		p += stupid_insert(p, '^');	// use ^ to indicate literal next
+		refresh(FALSE);	// show the ^
+		c = get_one_char();
+		*p = c;
+#if ENABLE_FEATURE_VI_UNDO
+		undo_push_insert(p, 1, undo);
+#else
+		modified_count++;
+#endif /* ENABLE_FEATURE_VI_UNDO */
+		p++;
+	} else if (c == 27) {	// Is this an ESC?
+		cmd_mode = 0;
+		undo_queue_commit();
+		cmdcnt = 0;
+		end_cmd_q();	// stop adding to q
+		last_status_cksum = 0;	// force status update
+		if ((p[-1] != '\n') && (dot > text)) {
+			p--;
+		}
+	} else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
+		if (p > text) {
+			p--;
+			p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED);	// shrink buffer 1 char
+		}
 	} else {
-		flash(10);
+		// insert a char into text[]
+		if (c == 13)
+			c = '\n';	// translate \r to \n
+#if ENABLE_FEATURE_VI_UNDO
+# if ENABLE_FEATURE_VI_UNDO_QUEUE
+		if (c == '\n')
+			undo_queue_commit();
+# endif
+		undo_push_insert(p, 1, undo);
+#else
+		modified_count++;
+#endif /* ENABLE_FEATURE_VI_UNDO */
+		p += 1 + stupid_insert(p, c);	// insert the char
+#if ENABLE_FEATURE_VI_SETOPTS
+		if (showmatch && strchr(")]}", c) != NULL) {
+			showmatching(p - 1);
+		}
+		if (autoindent && c == '\n') {	// auto indent the new line
+			char *q;
+			size_t len;
+			q = prev_line(p);	// use prev line as template
+			len = strspn(q, " \t"); // space or tab
+			if (len) {
+				uintptr_t bias;
+				bias = text_hole_make(p, len);
+				p += bias;
+				q += bias;
+#if ENABLE_FEATURE_VI_UNDO
+				undo_push_insert(p, len, undo);
+#endif
+				memcpy(p, q, len);
+				p += len;
+			}
+		}
+#endif
 	}
+	return p;
 }
 
-static int bufsum(char *buf, int count)
+// read text from file or create an empty buf
+// will also update current_filename
+static int init_text_buffer(char *fn)
 {
-	int sum = 0;
-	char *e = buf + count;
+	int rc;
 
-	while (buf < e)
-		sum += (unsigned char) *buf++;
-	return sum;
+	// allocate/reallocate text buffer
+	free(text);
+	text_size = 10240;
+	screenbegin = dot = end = text = xzalloc(text_size);
+
+	if (fn != current_filename) {
+		free(current_filename);
+		current_filename = xstrdup(fn);
+	}
+	rc = file_insert(fn, text, 1);
+	if (rc < 0) {
+		// file doesnt exist. Start empty buf with dummy line
+		char_insert(text, '\n', NO_UNDO);
+	}
+
+	flush_undo_data();
+	modified_count = 0;
+	last_modified_count = -1;
+#if ENABLE_FEATURE_VI_YANKMARK
+	// init the marks
+	memset(mark, 0, sizeof(mark));
+#endif
+	return rc;
 }
 
-//----- Draw the status line at bottom of the screen -------------
-static void show_status_line(void)
+#if ENABLE_FEATURE_VI_YANKMARK \
+ || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
+ || ENABLE_FEATURE_VI_CRASHME
+// might reallocate text[]! use p += string_insert(p, ...),
+// and be careful to not use pointers into potentially freed text[]!
+# if !ENABLE_FEATURE_VI_UNDO
+#  define string_insert(a,b,c) string_insert(a,b)
+# endif
+static uintptr_t string_insert(char *p, const char *s, int undo) // insert the string at 'p'
 {
-	int cnt = 0, cksum = 0;
+	uintptr_t bias;
+	int i;
 
-	// either we already have an error or status message, or we
-	// create one.
-	if (!have_status_msg) {
-		cnt = format_edit_status();
-		cksum = bufsum(status_buffer, cnt);
+	i = strlen(s);
+#if ENABLE_FEATURE_VI_UNDO
+	undo_push_insert(p, i, undo);
+#endif
+	bias = text_hole_make(p, i);
+	p += bias;
+	memcpy(p, s, i);
+#if ENABLE_FEATURE_VI_YANKMARK
+	{
+		int cnt;
+		for (cnt = 0; *s != '\0'; s++) {
+			if (*s == '\n')
+				cnt++;
+		}
+		status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
 	}
-	if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
-		last_status_cksum = cksum;		// remember if we have seen this line
-		go_bottom_and_clear_to_eol();
-		write1(status_buffer);
-		if (have_status_msg) {
-			if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
-					(columns - 1) ) {
-				have_status_msg = 0;
-				Hit_Return();
+#endif
+	return bias;
+}
+#endif
+
+static int file_write(char *fn, char *first, char *last)
+{
+	int fd, cnt, charcnt;
+
+	if (fn == 0) {
+		status_line_bold("No current filename");
+		return -2;
+	}
+	// By popular request we do not open file with O_TRUNC,
+	// but instead ftruncate() it _after_ successful write.
+	// Might reduce amount of data lost on power fail etc.
+	fd = open(fn, (O_WRONLY | O_CREAT), 0666);
+	if (fd < 0)
+		return -1;
+	cnt = last - first + 1;
+	charcnt = full_write(fd, first, cnt);
+	ftruncate(fd, charcnt);
+	if (charcnt == cnt) {
+		// good write
+		//modified_count = FALSE;
+	} else {
+		charcnt = 0;
+	}
+	close(fd);
+	return charcnt;
+}
+
+#if ENABLE_FEATURE_VI_SEARCH
+# if ENABLE_FEATURE_VI_REGEX_SEARCH
+// search for pattern starting at p
+static char *char_search(char *p, const char *pat, int dir_and_range)
+{
+	struct re_pattern_buffer preg;
+	const char *err;
+	char *q;
+	int i;
+	int size;
+	int range;
+
+	re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
+	if (ignorecase)
+		re_syntax_options = RE_SYNTAX_POSIX_EXTENDED | RE_ICASE;
+
+	memset(&preg, 0, sizeof(preg));
+	err = re_compile_pattern(pat, strlen(pat), &preg);
+	if (err != NULL) {
+		status_line_bold("bad search pattern '%s': %s", pat, err);
+		return p;
+	}
+
+	range = (dir_and_range & 1);
+	q = end - 1; // if FULL
+	if (range == LIMITED)
+		q = next_line(p);
+	if (dir_and_range < 0) { // BACK?
+		q = text;
+		if (range == LIMITED)
+			q = prev_line(p);
+	}
+
+	// RANGE could be negative if we are searching backwards
+	range = q - p;
+	q = p;
+	size = range;
+	if (range < 0) {
+		size = -size;
+		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, /*start:*/ 0, range, /*struct re_registers*:*/ NULL);
+	regfree(&preg);
+	if (i < 0)
+		return NULL;
+	if (dir_and_range > 0) // FORWARD?
+		p = p + i;
+	else
+		p = p - i;
+	return p;
+}
+# else
+#  if ENABLE_FEATURE_VI_SETOPTS
+static int mycmp(const char *s1, const char *s2, int len)
+{
+	if (ignorecase) {
+		return strncasecmp(s1, s2, len);
+	}
+	return strncmp(s1, s2, len);
+}
+#  else
+#   define mycmp strncmp
+#  endif
+static char *char_search(char *p, const char *pat, int dir_and_range)
+{
+	char *start, *stop;
+	int len;
+	int range;
+
+	len = strlen(pat);
+	range = (dir_and_range & 1);
+	if (dir_and_range > 0) { //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 { //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;
+}
+# endif
+#endif /* FEATURE_VI_SEARCH */
+
+//----- The Colon commands -------------------------------------
+#if ENABLE_FEATURE_VI_COLON
+static char *get_one_address(char *p, int *addr)	// get colon addr, if present
+{
+	int st;
+	char *q;
+	IF_FEATURE_VI_YANKMARK(char c;)
+	IF_FEATURE_VI_SEARCH(char *pat;)
+
+	*addr = -1;			// assume no addr
+	if (*p == '.') {	// the current line
+		p++;
+		q = begin_line(dot);
+		*addr = count_lines(text, q);
+	}
+#if ENABLE_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[(unsigned char) c];
+			if (q != NULL) {	// is mark valid
+				*addr = count_lines(text, q);
+			}
+		}
+	}
+#endif
+#if ENABLE_FEATURE_VI_SEARCH
+	else if (*p == '/') {	// a search pattern
+		q = strchrnul(++p, '/');
+		pat = xstrndup(p, q - p); // save copy of pattern
+		p = q;
+		if (*p == '/')
+			p++;
+		q = char_search(dot, pat, (FORWARD << 1) | FULL);
+		if (q != NULL) {
+			*addr = count_lines(text, q);
+		}
+		free(pat);
+	}
+#endif
+	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(p, "%d%n", addr, &st);
+		p += st;
+	} else {
+		// unrecognized address - assume -1
+		*addr = -1;
+	}
+	return p;
+}
+
+static char *get_address(char *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 (isblank(*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 (isblank(*p))
+		p++;
+	if (*p == ',') {			// is there a address separator
+		p++;
+		while (isblank(*p))
+			p++;
+		// get SECOND addr, if present
+		p = get_one_address(p, e);
+	}
+ ga0:
+	while (isblank(*p))
+		p++;				// skip over trailing spaces
+	return p;
+}
+
+#if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
+static void setops(const char *args, const char *opname, int flg_no,
+			const char *short_opname, int opt)
+{
+	const char *a = args + flg_no;
+	int l = strlen(opname) - 1; // opname have + ' '
+
+	// maybe strncmp? we had tons of erroneous strncasecmp's...
+	if (strncasecmp(a, opname, l) == 0
+	 || strncasecmp(a, short_opname, 2) == 0
+	) {
+		if (flg_no)
+			vi_setops &= ~opt;
+		else
+			vi_setops |= opt;
+	}
+}
+#endif
+
+#endif /* FEATURE_VI_COLON */
+
+// buf must be no longer than MAX_INPUT_LEN!
+static void colon(char *buf)
+{
+#if !ENABLE_FEATURE_VI_COLON
+	// Simple ":cmd" handler with minimal set of commands
+	char *p = buf;
+	int cnt;
+
+	if (*p == ':')
+		p++;
+	cnt = strlen(p);
+	if (cnt == 0)
+		return;
+	if (strncmp(p, "quit", cnt) == 0
+	 || strncmp(p, "q!", cnt) == 0
+	) {
+		if (modified_count && p[1] != '!') {
+			status_line_bold("No write since last change (:%s! overrides)", p);
+		} else {
+			editing = 0;
+		}
+		return;
+	}
+	if (strncmp(p, "write", cnt) == 0
+	 || strncmp(p, "wq", cnt) == 0
+	 || strncmp(p, "wn", cnt) == 0
+	 || (p[0] == 'x' && !p[1])
+	) {
+		if (modified_count != 0 || p[0] != 'x') {
+			cnt = file_write(current_filename, text, end - 1);
+		}
+		if (cnt < 0) {
+			if (cnt == -1)
+				status_line_bold("Write error: "STRERROR_FMT STRERROR_ERRNO);
+		} else {
+			modified_count = 0;
+			last_modified_count = -1;
+			status_line("'%s' %dL, %dC",
+				current_filename,
+				count_lines(text, end - 1), cnt
+			);
+			if (p[0] == 'x'
+			 || p[1] == 'q' || p[1] == 'n'
+			 || p[1] == 'Q' || p[1] == 'N'
+			) {
+				editing = 0;
+			}
+		}
+		return;
+	}
+	if (strncmp(p, "file", cnt) == 0) {
+		last_status_cksum = 0;	// force status update
+		return;
+	}
+	if (sscanf(p, "%d", &cnt) > 0) {
+		dot = find_line(cnt);
+		dot_skip_over_ws();
+		return;
+	}
+	not_implemented(p);
+#else
+
+	char c, *buf1, *q, *r;
+	char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN];
+	int i, l, li, b, e;
+	int useforce;
+# if ENABLE_FEATURE_VI_SEARCH || ENABLE_FEATURE_ALLOW_EXEC
+	char *orig_buf;
+# endif
+
+	// :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 (!buf[0])
+		goto ret;
+	if (*buf == ':')
+		buf++;			// move past the ':'
+
+	li = i = 0;
+	b = e = -1;
+	q = text;			// assume 1,$ for the range
+	r = end - 1;
+	li = count_lines(text, end - 1);
+	fn = current_filename;
+
+	// look for optional address(es)  :.  :1  :1,9   :'q,'a   :%
+	buf = get_address(buf, &b, &e);
+
+# if ENABLE_FEATURE_VI_SEARCH || ENABLE_FEATURE_ALLOW_EXEC
+	// remember orig command line
+	orig_buf = buf;
+# endif
+
+	// get the COMMAND into cmd[]
+	buf1 = cmd;
+	while (*buf != '\0') {
+		if (isspace(*buf))
+			break;
+		*buf1++ = *buf++;
+	}
+	*buf1 = '\0';
+	// get any ARGuments
+	while (isblank(*buf))
+		buf++;
+	strcpy(args, buf);
+	useforce = FALSE;
+	buf1 = last_char_is(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(cmd);
+	if (i == 0) {		// :123CR goto line #123
+		if (b >= 0) {
+			dot = find_line(b);	// what line is #b
+			dot_skip_over_ws();
+		}
+	}
+# if ENABLE_FEATURE_ALLOW_EXEC
+	else if (cmd[0] == '!') {	// run a cmd
+		int retcode;
+		// :!ls   run the <cmd>
+		go_bottom_and_clear_to_eol();
+		cookmode();
+		retcode = system(orig_buf + 1);	// run the cmd
+		if (retcode)
+			printf("\nshell returned %i\n\n", retcode);
+		rawmode();
+		Hit_Return();			// let user see results
+	}
+# endif
+	else if (cmd[0] == '=' && !cmd[1]) {	// where is the address
+		if (b < 0) {	// no addr given- use defaults
+			b = e = count_lines(text, dot);
+		}
+		status_line("%d", b);
+	} else if (strncmp(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, ALLOW_UNDO);	// save, then delete lines
+		dot_skip_over_ws();
+	} else if (strncmp(cmd, "edit", i) == 0) {	// Edit a file
+		int size;
+
+		// don't edit, if the current file has been modified
+		if (modified_count && !useforce) {
+			status_line_bold("No write since last change (:%s! overrides)", cmd);
+			goto ret;
+		}
+		if (args[0]) {
+			// the user supplied a file name
+			fn = args;
+		} else if (current_filename && current_filename[0]) {
+			// no user supplied name- use the current filename
+			// fn = current_filename;  was set by default
+		} else {
+			// no user file name, no current name- punt
+			status_line_bold("No current filename");
+			goto ret;
+		}
+
+		size = init_text_buffer(fn);
+
+# if ENABLE_FEATURE_VI_YANKMARK
+		if (Ureg >= 0 && Ureg < 28) {
+			free(reg[Ureg]);	//   free orig line reg- for 'U'
+			reg[Ureg] = NULL;
+		}
+		if (YDreg >= 0 && YDreg < 28) {
+			free(reg[YDreg]);	//   free default yank/delete register
+			reg[YDreg] = NULL;
+		}
+# endif
+		// how many lines in text[]?
+		li = count_lines(text, end - 1);
+		status_line("'%s'%s"
+			IF_FEATURE_VI_READONLY("%s")
+			" %dL, %dC",
+			current_filename,
+			(size < 0 ? " [New file]" : ""),
+			IF_FEATURE_VI_READONLY(
+				((readonly_mode) ? " [Readonly]" : ""),
+			)
+			li, (int)(end - text)
+		);
+	} else if (strncmp(cmd, "file", i) == 0) {	// what File is this
+		if (b != -1 || e != -1) {
+			status_line_bold("No address allowed on this command");
+			goto ret;
+		}
+		if (args[0]) {
+			// user wants a new filename
+			free(current_filename);
+			current_filename = xstrdup(args);
+		} else {
+			// user wants file status info
+			last_status_cksum = 0;	// force status update
+		}
+	} else if (strncmp(cmd, "features", i) == 0) {	// what features are available
+		// print out values of all features
+		go_bottom_and_clear_to_eol();
+		cookmode();
+		show_help();
+		rawmode();
+		Hit_Return();
+	} else if (strncmp(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);
+		}
+		go_bottom_and_clear_to_eol();
+		puts("\r");
+		for (; q <= r; q++) {
+			int c_is_no_print;
+
+			c = *q;
+			c_is_no_print = (c & 0x80) && !Isprint(c);
+			if (c_is_no_print) {
+				c = '.';
+				standout_start();
+			}
+			if (c == '\n') {
+				write1("$\r");
+			} else if (c < ' ' || c == 127) {
+				bb_putchar('^');
+				if (c == 127)
+					c = '?';
+				else
+					c += '@';
+			}
+			bb_putchar(c);
+			if (c_is_no_print)
+				standout_end();
+		}
+		Hit_Return();
+	} else if (strncmp(cmd, "quit", i) == 0 // quit
+	        || strncmp(cmd, "next", i) == 0 // edit next file
+	        || strncmp(cmd, "prev", i) == 0 // edit previous file
+	) {
+		int n;
+		if (useforce) {
+			if (*cmd == 'q') {
+				// force end of argv list
+				optind = save_argc;
+			}
+			editing = 0;
+			goto ret;
+		}
+		// don't exit if the file been modified
+		if (modified_count) {
+			status_line_bold("No write since last change (:%s! overrides)", cmd);
+			goto ret;
+		}
+		// are there other file to edit
+		n = save_argc - optind - 1;
+		if (*cmd == 'q' && n > 0) {
+			status_line_bold("%d more file(s) to edit", n);
+			goto ret;
+		}
+		if (*cmd == 'n' && n <= 0) {
+			status_line_bold("No more files to edit");
+			goto ret;
+		}
+		if (*cmd == 'p') {
+			// are there previous files to edit
+			if (optind < 1) {
+				status_line_bold("No previous files to edit");
+				goto ret;
+			}
+			optind -= 2;
+		}
+		editing = 0;
+	} else if (strncmp(cmd, "read", i) == 0) {	// read file into text[]
+		int size;
+
+		fn = args;
+		if (!fn[0]) {
+			status_line_bold("No filename given");
+			goto ret;
+		}
+		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);
+			// read after last line
+			if (q == end-1)
+				++q;
+		}
+		{ // dance around potentially-reallocated text[]
+			uintptr_t ofs = q - text;
+			size = file_insert(fn, q, 0);
+			q = text + ofs;
+		}
+		if (size < 0)
+			goto ret;	// nothing was inserted
+		// how many lines in text[]?
+		li = count_lines(q, q + size - 1);
+		status_line("'%s'"
+			IF_FEATURE_VI_READONLY("%s")
+			" %dL, %dC",
+			fn,
+			IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
+			li, size
+		);
+		if (size > 0) {
+			// if the insert is before "dot" then we need to update
+			if (q <= dot)
+				dot += size;
+		}
+	} else if (strncmp(cmd, "rewind", i) == 0) {	// rewind cmd line args
+		if (modified_count && !useforce) {
+			status_line_bold("No write since last change (:%s! overrides)", cmd);
+		} else {
+			// reset the filenames to edit
+			optind = -1; // start from 0th file
+			editing = 0;
+		}
+# if ENABLE_FEATURE_VI_SET
+	} else if (strncmp(cmd, "set", i) == 0) {	// set or clear features
+#  if ENABLE_FEATURE_VI_SETOPTS
+		char *argp;
+#  endif
+		i = 0;			// offset into args
+		// only blank is regarded as args delimiter. What about tab '\t'?
+		if (!args[0] || strcasecmp(args, "all") == 0) {
+			// print out values of all options
+#  if ENABLE_FEATURE_VI_SETOPTS
+			status_line_bold(
+				"%sautoindent "
+				"%sflash "
+				"%signorecase "
+				"%sshowmatch "
+				"tabstop=%u",
+				autoindent ? "" : "no",
+				err_method ? "" : "no",
+				ignorecase ? "" : "no",
+				showmatch ? "" : "no",
+				tabstop
+			);
+#  endif
+			goto ret;
+		}
+#  if ENABLE_FEATURE_VI_SETOPTS
+		argp = args;
+		while (*argp) {
+			if (strncmp(argp, "no", 2) == 0)
+				i = 2;		// ":set noautoindent"
+			setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT);
+			setops(argp, "flash "     , i, "fl", VI_ERR_METHOD);
+			setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE);
+			setops(argp, "showmatch " , i, "sm", VI_SHOWMATCH );
+			if (strncmp(argp + i, "tabstop=", 8) == 0) {
+				int t = 0;
+				sscanf(argp + i+8, "%u", &t);
+				if (t > 0 && t <= MAX_TABSTOP)
+					tabstop = t;
 			}
-			have_status_msg = 0;
+			argp = skip_non_whitespace(argp);
+			argp = skip_whitespace(argp);
 		}
-		place_cursor(crow, ccol);  // put cursor back in correct place
-	}
-	fflush_all();
-}
-
-//----- format the status buffer, the bottom line of screen ------
-// format status buffer, with STANDOUT mode
-static void status_line_bold(const char *format, ...)
-{
-	va_list args;
-
-	va_start(args, format);
-	strcpy(status_buffer, ESC_BOLD_TEXT);
-	vsprintf(status_buffer + sizeof(ESC_BOLD_TEXT)-1, format, args);
-	strcat(status_buffer, ESC_NORM_TEXT);
-	va_end(args);
-
-	have_status_msg = 1 + sizeof(ESC_BOLD_TEXT) + sizeof(ESC_NORM_TEXT) - 2;
-}
-
-static void status_line_bold_errno(const char *fn)
-{
-	status_line_bold("'%s' "STRERROR_FMT, fn STRERROR_ERRNO);
-}
-
-// format status buffer
-static void status_line(const char *format, ...)
-{
-	va_list args;
-
-	va_start(args, format);
-	vsprintf(status_buffer, format, args);
-	va_end(args);
+#  endif /* FEATURE_VI_SETOPTS */
+# endif /* FEATURE_VI_SET */
 
-	have_status_msg = 1;
-}
+# if ENABLE_FEATURE_VI_SEARCH
+	} else if (cmd[0] == 's') {	// substitute a pattern with a replacement pattern
+		char *F, *R, *flags;
+		size_t len_F, len_R;
+		int gflag;		// global replace flag
+#  if ENABLE_FEATURE_VI_UNDO
+		int dont_chain_first_item = ALLOW_UNDO;
+#  endif
 
-// copy s to buf, convert unprintable
-static void print_literal(char *buf, const char *s)
-{
-	char *d;
-	unsigned char c;
+		// F points to the "find" pattern
+		// R points to the "replace" pattern
+		// replace the cmd line delimiters "/" with NULs
+		c = orig_buf[1];	// what is the delimiter
+		F = orig_buf + 2;	// start of "find"
+		R = strchr(F, c);	// middle delimiter
+		if (!R)
+			goto colon_s_fail;
+		len_F = R - F;
+		*R++ = '\0';	// terminate "find"
+		flags = strchr(R, c);
+		if (!flags)
+			goto colon_s_fail;
+		len_R = flags - R;
+		*flags++ = '\0';	// terminate "replace"
+		gflag = *flags;
 
-	buf[0] = '\0';
-	if (!s[0])
-		s = "(NULL)";
+		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/
 
-	d = buf;
-	for (; *s; s++) {
-		int c_is_no_print;
+		for (i = b; i <= e; i++) {	// so, :20,23 s \0 find \0 replace \0
+			char *ls = q;		// orig line start
+			char *found;
+ vc4:
+			found = char_search(q, F, (FORWARD << 1) | LIMITED);	// search cur line only for "find"
+			if (found) {
+				uintptr_t bias;
+				// we found the "find" pattern - delete it
+				// For undo support, the first item should not be chained
+				text_hole_delete(found, found + len_F - 1, dont_chain_first_item);
+#  if ENABLE_FEATURE_VI_UNDO
+				dont_chain_first_item = ALLOW_UNDO_CHAIN;
+#  endif
+				// insert the "replace" patern
+				bias = string_insert(found, R, ALLOW_UNDO_CHAIN);
+				found += bias;
+				ls += bias;
+				/*q += bias; - recalculated anyway */
+				// check for "global"  :s/foo/bar/g
+				if (gflag == 'g') {
+					if ((found + len_R) < end_line(ls)) {
+						q = found + len_R;
+						goto vc4;	// don't let q move past cur line
+					}
+				}
+			}
+			q = next_line(ls);
+		}
+# endif /* FEATURE_VI_SEARCH */
+	} else if (strncmp(cmd, "version", i) == 0) {  // show software version
+		status_line(BB_VER);
+	} else if (strncmp(cmd, "write", i) == 0  // write text to file
+	        || strncmp(cmd, "wq", i) == 0
+	        || strncmp(cmd, "wn", i) == 0
+	        || (cmd[0] == 'x' && !cmd[1])
+	) {
+		int size;
+		//int forced = FALSE;
 
-		c = *s;
-		c_is_no_print = (c & 0x80) && !Isprint(c);
-		if (c_is_no_print) {
-			strcpy(d, ESC_NORM_TEXT);
-			d += sizeof(ESC_NORM_TEXT)-1;
-			c = '.';
+		// is there a file name to write to?
+		if (args[0]) {
+			fn = args;
 		}
-		if (c < ' ' || c == 0x7f) {
-			*d++ = '^';
-			c |= '@'; // 0x40
-			if (c == 0x7f)
-				c = '?';
+# if ENABLE_FEATURE_VI_READONLY
+		if (readonly_mode && !useforce) {
+			status_line_bold("'%s' is read only", fn);
+			goto ret;
 		}
-		*d++ = c;
-		*d = '\0';
-		if (c_is_no_print) {
-			strcpy(d, ESC_BOLD_TEXT);
-			d += sizeof(ESC_BOLD_TEXT)-1;
+# endif
+		//if (useforce) {
+			// if "fn" is not write-able, chmod u+w
+			// sprintf(syscmd, "chmod u+w %s", fn);
+			// system(syscmd);
+			// forced = TRUE;
+		//}
+		if (modified_count != 0 || cmd[0] != 'x') {
+			size = r - q + 1;
+			l = file_write(fn, q, r);
+		} else {
+			size = 0;
+			l = 0;
 		}
-		if (*s == '\n') {
-			*d++ = '$';
-			*d = '\0';
+		//if (useforce && forced) {
+			// chmod u-w
+			// sprintf(syscmd, "chmod u-w %s", fn);
+			// system(syscmd);
+			// forced = FALSE;
+		//}
+		if (l < 0) {
+			if (l == -1)
+				status_line_bold_errno(fn);
+		} else {
+			// how many lines written
+			li = count_lines(q, q + l - 1);
+			status_line("'%s' %dL, %dC", fn, li, l);
+			if (l == size) {
+				if (q == text && q + l == end) {
+					modified_count = 0;
+					last_modified_count = -1;
+				}
+				if (cmd[0] == 'x'
+				 || cmd[1] == 'q' || cmd[1] == 'n'
+				 || cmd[1] == 'Q' || cmd[1] == 'N'
+				) {
+					editing = 0;
+				}
+			}
 		}
-		if (d - buf > MAX_INPUT_LEN - 10) // paranoia
-			break;
-	}
-}
-
-static void not_implemented(const char *s)
-{
-	char buf[MAX_INPUT_LEN];
-
-	print_literal(buf, s);
-	status_line_bold("\'%s\' is not implemented", buf);
-}
-
-// show file status on status line
-static int format_edit_status(void)
-{
-	static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
-
-#define tot format_edit_status__tot
-
-	int cur, percent, ret, trunc_at;
-
-	// modified_count is now a counter rather than a flag.  this
-	// helps reduce the amount of line counting we need to do.
-	// (this will cause a mis-reporting of modified status
-	// once every MAXINT editing operations.)
-
-	// it would be nice to do a similar optimization here -- if
-	// we haven't done a motion that could have changed which line
-	// we're on, then we shouldn't have to do this count_lines()
-	cur = count_lines(text, dot);
-
-	// count_lines() is expensive.
-	// Call it only if something was changed since last time
-	// we were here:
-	if (modified_count != last_modified_count) {
-		tot = cur + count_lines(dot, end - 1) - 1;
-		last_modified_count = modified_count;
-	}
-
-	//    current line         percent
-	//   -------------    ~~ ----------
-	//    total lines            100
-	if (tot > 0) {
-		percent = (100 * cur) / tot;
+# if ENABLE_FEATURE_VI_YANKMARK
+	} else if (strncmp(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);
+		status_line("Yank %d lines (%d chars) into [%c]",
+				li, strlen(reg[YDreg]), what_reg());
+# endif
 	} else {
-		cur = tot = 0;
-		percent = 100;
+		// cmd unknown
+		not_implemented(cmd);
 	}
-
-	trunc_at = columns < STATUS_BUFFER_LEN-1 ?
-		columns : STATUS_BUFFER_LEN-1;
-
-	ret = snprintf(status_buffer, trunc_at+1,
-#if ENABLE_FEATURE_VI_READONLY
-		"%c %s%s%s %d/%d %d%%",
-#else
-		"%c %s%s %d/%d %d%%",
-#endif
-		cmd_mode_indicator[cmd_mode & 3],
-		(current_filename != NULL ? current_filename : "No file"),
-#if ENABLE_FEATURE_VI_READONLY
-		(readonly_mode ? " [Readonly]" : ""),
-#endif
-		(modified_count ? " [Modified]" : ""),
-		cur, tot, percent);
-
-	if (ret >= 0 && ret < trunc_at)
-		return ret;  // it all fit
-
-	return trunc_at;  // had to truncate
-#undef tot
+ ret:
+	dot = bound_dot(dot);	// make sure "dot" is valid
+	return;
+# if ENABLE_FEATURE_VI_SEARCH
+ colon_s_fail:
+	status_line(":s expression missing delimiters");
+# endif
+#endif /* FEATURE_VI_COLON */
 }
 
-//----- Force refresh of all Lines -----------------------------
-static void redraw(int full_screen)
-{
-	// cursor to top,left; clear to the end of screen
-	write1(ESC_SET_CURSOR_TOPLEFT ESC_CLEAR2EOS);
-	screen_erase();		// erase the internal screen buffer
-	last_status_cksum = 0;	// force status update
-	refresh(full_screen);	// this will redraw the entire display
-	show_status_line();
-}
+//----- Helper Utility Routines --------------------------------
 
-//----- Format a text[] line into a buffer ---------------------
-static char* format_line(char *src /*, int li*/)
+//----------------------------------------------------------------
+//----- 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 char *new_screen(int ro, int co)
 {
-	unsigned char c;
-	int co;
-	int ofs = offset;
-	char *dest = scr_out_buf; // [MAX_SCR_COLS + MAX_TABSTOP * 2]
+	int li;
 
-	c = '~'; // char in col 0 in non-existent lines is '~'
-	co = 0;
-	while (co < columns + tabstop) {
-		// have we gone past the end?
-		if (src < end) {
-			c = *src++;
-			if (c == '\n')
-				break;
-			if ((c & 0x80) && !Isprint(c)) {
-				c = '.';
-			}
-			if (c < ' ' || c == 0x7f) {
-				if (c == '\t') {
-					c = ' ';
-					//      co %    8     !=     7
-					while ((co % tabstop) != (tabstop - 1)) {
-						dest[co++] = c;
-					}
-				} else {
-					dest[co++] = '^';
-					if (c == 0x7f)
-						c = '?';
-					else
-						c += '@'; // Ctrl-X -> 'X'
-				}
-			}
-		}
-		dest[co++] = c;
-		// discard scrolled-off-to-the-left portion,
-		// in tabstop-sized pieces
-		if (ofs >= tabstop && co >= tabstop) {
-			memmove(dest, dest + tabstop, co);
-			co -= tabstop;
-			ofs -= tabstop;
-		}
-		if (src >= end)
-			break;
+	free(screen);
+	screensize = ro * co + 8;
+	screen = xmalloc(screensize);
+	// initialize the new screen. assume this will be a empty file.
+	screen_erase();
+	//   non-existent text[] lines start with a tilde (~).
+	for (li = 1; li < ro - 1; li++) {
+		screen[(li * co) + 0] = '~';
 	}
-	// check "short line, gigantic offset" case
-	if (co < ofs)
-		ofs = co;
-	// discard last scrolled off part
-	co -= ofs;
-	dest += ofs;
-	// fill the rest with spaces
-	if (co < columns)
-		memset(&dest[co], ' ', columns - co);
-	return dest;
+	return screen;
 }
 
-//----- 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 st_test(char *p, int type, int dir, char *tested)
 {
-#define old_offset refresh__old_offset
+	char c, c0, ci;
+	int test, inc;
 
-	int li, changed;
-	char *tp, *sp;		// pointer into text[] and screen[]
+	inc = dir;
+	c = c0 = p[0];
+	ci = p[inc];
+	test = 0;
 
-	if (ENABLE_FEATURE_VI_WIN_RESIZE IF_FEATURE_VI_ASK_TERMINAL(&& !G.get_rowcol_error) ) {
-		unsigned c = columns, r = rows;
-		query_screen_dimensions();
-#if ENABLE_FEATURE_VI_USE_SIGNALS
-		full_screen |= (c - columns) | (r - rows);
-#else
-		if (c != columns || r != rows) {
-			full_screen = TRUE;
-			// update screen memory since SIGWINCH won't have done it
-			new_screen(rows, columns);
-		}
-#endif
+	if (type == S_BEFORE_WS) {
+		c = ci;
+		test = (!isspace(c) || c == '\n');
 	}
-	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
-		char *out_buf;
-		// format current text line
-		out_buf = format_line(tp /*, li*/);
-
-		// skip to the end of the current text[] line
-		if (tp < end) {
-			char *t = memchr(tp, '\n', end - tp);
-			if (!t) t = end - 1;
-			tp = t + 1;
-		}
-
-		// see if there are any changes between virtual screen and out_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 (out_buf[cs] != sp[cs]) {
-				changed = TRUE;	// mark for redraw
-				break;
-			}
-		}
-
-		// look backward for last difference between out_buf and screen
-		for (; ce >= cs; ce--) {
-			if (out_buf[ce] != 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 virtual screen and out_buf
-		if (changed) {
-			// copy changed part of buffer to virtual screen
-			memcpy(sp+cs, out_buf+cs, ce-cs+1);
-			place_cursor(li, cs);
-			// write line out to terminal
-			fwrite(&sp[cs], ce - cs + 1, 1, stdout);
-		}
+	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;
+}
 
-	place_cursor(crow, ccol);
+static char *skip_thing(char *p, int linecnt, int dir, int type)
+{
+	char c;
 
-	old_offset = offset;
-#undef old_offset
+	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;
 }
 
 #if ENABLE_FEATURE_VI_USE_SIGNALS
-- 
cgit v1.2.3