vi: rearrange functions, no logic changes
authorDenys Vlasenko <vda.linux@googlemail.com>
Mon, 1 Apr 2019 12:08:00 +0000 (14:08 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Mon, 1 Apr 2019 12:16:53 +0000 (14:16 +0200)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
editors/vi.c

index e960afc3768e38be9ea46e65a063159414622021..9db763ccdb1c0c8261fe950ad54ff96548a81f28 100644 (file)
@@ -693,424 +693,700 @@ static ALWAYS_INLINE int query_screen_dimensions(void)
 }
 #endif
 
-//----- The Colon commands -------------------------------------
-#if ENABLE_FEATURE_VI_COLON
-static char *get_one_address(char *p, int *addr)       // get colon addr, if present
+// sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready)
+static int mysleep(int hund)
 {
-       int st;
-       char *q;
-       IF_FEATURE_VI_YANKMARK(char c;)
-       IF_FEATURE_VI_SEARCH(char *pat;)
+       struct pollfd pfd[1];
 
-       *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;
+       if (hund != 0)
+               fflush_all();
+
+       pfd[0].fd = STDIN_FILENO;
+       pfd[0].events = POLLIN;
+       return safe_poll(pfd, 1, hund*10) > 0;
+}
+
+//----- Set terminal attributes --------------------------------
+static void rawmode(void)
+{
+       // no TERMIOS_CLEAR_ISIG: leave ISIG on - allow signals
+       set_termios_to_raw(STDIN_FILENO, &term_orig, TERMIOS_RAW_CRNL);
+       erase_char = term_orig.c_cc[VERASE];
+}
+
+static void cookmode(void)
+{
+       fflush_all();
+       tcsetattr_stdin_TCSANOW(&term_orig);
+}
+
+//----- Terminal Drawing ---------------------------------------
+// The terminal is made up of 'rows' line of 'columns' columns.
+// classically this would be 24 x 80.
+//  screen coordinates
+//  0,0     ...     0,79
+//  1,0     ...     1,79
+//  .       ...     .
+//  .       ...     .
+//  22,0    ...     22,79
+//  23,0    ...     23,79   <- status line
+
+//----- Move the cursor to row x col (count from 0, not 1) -------
+static void place_cursor(int row, int col)
+{
+       char cm1[sizeof(ESC_SET_CURSOR_POS) + sizeof(int)*3 * 2];
+
+       if (row < 0) row = 0;
+       if (row >= rows) row = rows - 1;
+       if (col < 0) col = 0;
+       if (col >= columns) col = columns - 1;
+
+       sprintf(cm1, ESC_SET_CURSOR_POS, row + 1, col + 1);
+       write1(cm1);
+}
+
+//----- Erase from cursor to end of line -----------------------
+static void clear_to_eol(void)
+{
+       write1(ESC_CLEAR2EOL);
+}
+
+static void go_bottom_and_clear_to_eol(void)
+{
+       place_cursor(rows - 1, 0);
+       clear_to_eol();
+}
+
+//----- Start standout mode ------------------------------------
+static void standout_start(void)
+{
+       write1(ESC_BOLD_TEXT);
+}
+
+//----- End standout mode --------------------------------------
+static void standout_end(void)
+{
+       write1(ESC_NORM_TEXT);
+}
+
+//----- Text Movement Routines ---------------------------------
+static char *begin_line(char *p) // return pointer to first char cur line
+{
+       if (p > text) {
+               p = memrchr(text, '\n', p - text);
+               if (!p)
+                       return text;
+               return p + 1;
        }
        return p;
 }
 
-static char *get_address(char *p, int *b, int *e)      // get two colon addrs, if present
+static char *end_line(char *p) // return pointer to NL of cur line
 {
-       //----- 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);
+       if (p < end - 1) {
+               p = memchr(p, '\n', end - p - 1);
+               if (!p)
+                       return end - 1;
        }
- 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)
+static char *dollar_line(char *p) // return pointer to just before NL line
 {
-       const char *a = args + flg_no;
-       int l = strlen(opname) - 1; // opname have + ' '
+       p = end_line(p);
+       // Try to stay off of the Newline
+       if (*p == '\n' && (p - begin_line(p)) > 0)
+               p--;
+       return p;
+}
 
-       // 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;
-       }
+static char *prev_line(char *p) // return pointer first char prev line
+{
+       p = begin_line(p);      // goto beginning of cur line
+       if (p > text && p[-1] == '\n')
+               p--;                    // step to prev line
+       p = begin_line(p);      // goto beginning of prev line
+       return p;
 }
-#endif
 
-#endif /* FEATURE_VI_COLON */
+static char *next_line(char *p) // return pointer first char next line
+{
+       p = end_line(p);
+       if (p < end - 1 && *p == '\n')
+               p++;                    // step to next line
+       return p;
+}
 
-// buf must be no longer than MAX_INPUT_LEN!
-static void colon(char *buf)
+//----- Text Information Routines ------------------------------
+static char *end_screen(void)
 {
-#if !ENABLE_FEATURE_VI_COLON
-       // Simple ":cmd" handler with minimal set of commands
-       char *p = buf;
+       char *q;
        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;
+       // find new bottom line
+       q = screenbegin;
+       for (cnt = 0; cnt < rows - 2; cnt++)
+               q = next_line(q);
+       q = end_line(q);
+       return q;
+}
+
+// count line from start to stop
+static int count_lines(char *start, char *stop)
+{
+       char *q;
+       int cnt;
+
+       if (stop < start) { // start and stop are backwards- reverse them
+               q = start;
+               start = stop;
+               stop = q;
        }
-       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;
+       cnt = 0;
+       stop = end_line(stop);
+       while (start <= stop && start <= end - 1) {
+               start = end_line(start);
+               if (*start == '\n')
+                       cnt++;
+               start++;
        }
-       not_implemented(p);
-#else
+       return cnt;
+}
 
-       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
+static char *find_line(int li) // find beginning of line #li
+{
+       char *q;
 
-       // :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
-       //
+       for (q = text; li > 1; li--) {
+               q = next_line(q);
+       }
+       return q;
+}
 
-       if (!buf[0])
-               goto ret;
-       if (*buf == ':')
-               buf++;                  // move past the ':'
+static int next_tabstop(int col)
+{
+       return col + ((tabstop - 1) - (col % tabstop));
+}
 
-       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;
+//----- Erase the Screen[] memory ------------------------------
+static void screen_erase(void)
+{
+       memset(screen, ' ', screensize);        // clear new screen
+}
 
-       // look for optional address(es)  :.  :1  :1,9   :'q,'a   :%
-       buf = get_address(buf, &b, &e);
+//----- Synchronize the cursor to Dot --------------------------
+static NOINLINE void sync_cursor(char *d, int *row, int *col)
+{
+       char *beg_cur;  // begin and end of "d" line
+       char *tp;
+       int cnt, ro, co;
 
-# if ENABLE_FEATURE_VI_SEARCH || ENABLE_FEATURE_ALLOW_EXEC
-       // remember orig command line
-       orig_buf = buf;
-# endif
+       beg_cur = begin_line(d);        // first char of cur line
 
-       // get the COMMAND into cmd[]
-       buf1 = cmd;
-       while (*buf != '\0') {
-               if (isspace(*buf))
-                       break;
-               *buf1++ = *buf++;
+       if (beg_cur < screenbegin) {
+               // "d" is before top line on screen
+               // how many lines do we have to move
+               cnt = count_lines(beg_cur, screenbegin);
+ sc1:
+               screenbegin = beg_cur;
+               if (cnt > (rows - 1) / 2) {
+                       // we moved too many lines. put "dot" in middle of screen
+                       for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
+                               screenbegin = prev_line(screenbegin);
+                       }
+               }
+       } else {
+               char *end_scr;  // begin and end of screen
+               end_scr = end_screen(); // last char of screen
+               if (beg_cur > end_scr) {
+                       // "d" is after bottom line on screen
+                       // how many lines do we have to move
+                       cnt = count_lines(end_scr, beg_cur);
+                       if (cnt > (rows - 1) / 2)
+                               goto sc1;       // too many lines
+                       for (ro = 0; ro < cnt - 1; ro++) {
+                               // move screen begin the same amount
+                               screenbegin = next_line(screenbegin);
+                               // now, move the end of screen
+                               end_scr = next_line(end_scr);
+                               end_scr = end_line(end_scr);
+                       }
+               }
        }
-       *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 !
+       // "d" is on screen- find out which row
+       tp = screenbegin;
+       for (ro = 0; ro < rows - 1; ro++) {     // drive "ro" to correct row
+               if (tp == beg_cur)
+                       break;
+               tp = next_line(tp);
        }
-       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;
+
+       // find out what col "d" is on
+       co = 0;
+       while (tp < d) { // drive "co" to correct column
+               if (*tp == '\n') //vda || *tp == '\0')
+                       break;
+               if (*tp == '\t') {
+                       // handle tabs like real vi
+                       if (d == tp && cmd_mode) {
+                               break;
+                       }
+                       co = next_tabstop(co);
+               } else if ((unsigned char)*tp < ' ' || *tp == 0x7f) {
+                       co++; // display as ^X, use 2 columns
+               }
+               co++;
+               tp++;
        }
-       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;
+
+       // "co" is the column where "dot" is.
+       // The screen has "columns" columns.
+       // The currently displayed columns are  0+offset -- columns+ofset
+       // |-------------------------------------------------------------|
+       //               ^ ^                                ^
+       //        offset | |------- columns ----------------|
+       //
+       // If "co" is already in this range then we do not have to adjust offset
+       //      but, we do have to subtract the "offset" bias from "co".
+       // If "co" is outside this range then we have to change "offset".
+       // If the first char of a line is a tab the cursor will try to stay
+       //  in column 7, but we have to set offset to 0.
+
+       if (co < 0 + offset) {
+               offset = co;
        }
-       // ------------ 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 (co >= columns + offset) {
+               offset = co - columns + 1;
        }
-# 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
+       // if the first char of the line is a tab, and "dot" is sitting on it
+       //  force offset to 0.
+       if (d == beg_cur && *d == '\t') {
+               offset = 0;
        }
-# 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;
+       co -= offset;
 
-               // 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;
-               }
+       *row = ro;
+       *col = co;
+}
 
-               size = init_text_buffer(fn);
+//----- 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;)
 
-# 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
+       *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);
+                       }
                }
-       } 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);
+       }
+#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);
                }
-               go_bottom_and_clear_to_eol();
-               puts("\r");
-               for (; q <= r; q++) {
-                       int c_is_no_print;
+               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;
+}
 
-                       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
+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
        ) {
-               int n;
-               if (useforce) {
-                       if (*cmd == 'q') {
-                               // force end of argv list
-                               optind = save_argc;
-                       }
+               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;
-                       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;
+               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 (*cmd == 'p') {
-                       // are there previous files to edit
-                       if (optind < 1) {
-                               status_line_bold("No previous files to edit");
-                               goto ret;
+               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;
                        }
-                       optind -= 2;
                }
-               editing = 0;
-       } else if (strncmp(cmd, "read", i) == 0) {      // read file into text[]
-               int size;
+               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
 
-               fn = args;
-               if (!fn[0]) {
-                       status_line_bold("No filename given");
-                       goto ret;
-               }
+       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"
                }
@@ -1205,348 +1481,154 @@ static void colon(char *buf)
                int dont_chain_first_item = ALLOW_UNDO;
 #  endif
 
-               // 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/
-
-               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;
-
-               // 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);
-               }
-               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 */
-}
-
-static void Hit_Return(void)
-{
-       int c;
-
-       standout_start();
-       write1("[Hit return to continue]");
-       standout_end();
-       while ((c = get_one_char()) != '\n' && c != '\r')
-               continue;
-       redraw(TRUE);           // force redraw all
-}
-
-static int next_tabstop(int col)
-{
-       return col + ((tabstop - 1) - (col % tabstop));
-}
-
-//----- Synchronize the cursor to Dot --------------------------
-static NOINLINE void sync_cursor(char *d, int *row, int *col)
-{
-       char *beg_cur;  // begin and end of "d" line
-       char *tp;
-       int cnt, ro, co;
-
-       beg_cur = begin_line(d);        // first char of cur line
-
-       if (beg_cur < screenbegin) {
-               // "d" is before top line on screen
-               // how many lines do we have to move
-               cnt = count_lines(beg_cur, screenbegin);
- sc1:
-               screenbegin = beg_cur;
-               if (cnt > (rows - 1) / 2) {
-                       // we moved too many lines. put "dot" in middle of screen
-                       for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
-                               screenbegin = prev_line(screenbegin);
-                       }
-               }
-       } else {
-               char *end_scr;  // begin and end of screen
-               end_scr = end_screen(); // last char of screen
-               if (beg_cur > end_scr) {
-                       // "d" is after bottom line on screen
-                       // how many lines do we have to move
-                       cnt = count_lines(end_scr, beg_cur);
-                       if (cnt > (rows - 1) / 2)
-                               goto sc1;       // too many lines
-                       for (ro = 0; ro < cnt - 1; ro++) {
-                               // move screen begin the same amount
-                               screenbegin = next_line(screenbegin);
-                               // now, move the end of screen
-                               end_scr = next_line(end_scr);
-                               end_scr = end_line(end_scr);
-                       }
-               }
-       }
-       // "d" is on screen- find out which row
-       tp = screenbegin;
-       for (ro = 0; ro < rows - 1; ro++) {     // drive "ro" to correct row
-               if (tp == beg_cur)
-                       break;
-               tp = next_line(tp);
-       }
-
-       // find out what col "d" is on
-       co = 0;
-       while (tp < d) { // drive "co" to correct column
-               if (*tp == '\n') //vda || *tp == '\0')
-                       break;
-               if (*tp == '\t') {
-                       // handle tabs like real vi
-                       if (d == tp && cmd_mode) {
-                               break;
-                       }
-                       co = next_tabstop(co);
-               } else if ((unsigned char)*tp < ' ' || *tp == 0x7f) {
-                       co++; // display as ^X, use 2 columns
-               }
-               co++;
-               tp++;
-       }
-
-       // "co" is the column where "dot" is.
-       // The screen has "columns" columns.
-       // The currently displayed columns are  0+offset -- columns+ofset
-       // |-------------------------------------------------------------|
-       //               ^ ^                                ^
-       //        offset | |------- columns ----------------|
-       //
-       // If "co" is already in this range then we do not have to adjust offset
-       //      but, we do have to subtract the "offset" bias from "co".
-       // If "co" is outside this range then we have to change "offset".
-       // If the first char of a line is a tab the cursor will try to stay
-       //  in column 7, but we have to set offset to 0.
-
-       if (co < 0 + offset) {
-               offset = co;
-       }
-       if (co >= columns + offset) {
-               offset = co - columns + 1;
-       }
-       // if the first char of the line is a tab, and "dot" is sitting on it
-       //  force offset to 0.
-       if (d == beg_cur && *d == '\t') {
-               offset = 0;
-       }
-       co -= offset;
-
-       *row = ro;
-       *col = co;
-}
-
-//----- Text Movement Routines ---------------------------------
-static char *begin_line(char *p) // return pointer to first char cur line
-{
-       if (p > text) {
-               p = memrchr(text, '\n', p - text);
-               if (!p)
-                       return text;
-               return p + 1;
-       }
-       return p;
-}
-
-static char *end_line(char *p) // return pointer to NL of cur line
-{
-       if (p < end - 1) {
-               p = memchr(p, '\n', end - p - 1);
-               if (!p)
-                       return end - 1;
-       }
-       return p;
-}
-
-static char *dollar_line(char *p) // return pointer to just before NL line
-{
-       p = end_line(p);
-       // Try to stay off of the Newline
-       if (*p == '\n' && (p - begin_line(p)) > 0)
-               p--;
-       return p;
-}
-
-static char *prev_line(char *p) // return pointer first char prev line
-{
-       p = begin_line(p);      // goto beginning of cur line
-       if (p > text && p[-1] == '\n')
-               p--;                    // step to prev line
-       p = begin_line(p);      // goto beginning of prev line
-       return p;
-}
-
-static char *next_line(char *p) // return pointer first char next line
-{
-       p = end_line(p);
-       if (p < end - 1 && *p == '\n')
-               p++;                    // step to next line
-       return p;
-}
-
-//----- Text Information Routines ------------------------------
-static char *end_screen(void)
-{
-       char *q;
-       int cnt;
+               // 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;
 
-       // find new bottom line
-       q = screenbegin;
-       for (cnt = 0; cnt < rows - 2; cnt++)
-               q = next_line(q);
-       q = end_line(q);
-       return q;
-}
+               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/
 
-// count line from start to stop
-static int count_lines(char *start, char *stop)
-{
-       char *q;
-       int cnt;
+               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;
 
-       if (stop < start) { // start and stop are backwards- reverse them
-               q = start;
-               start = stop;
-               stop = q;
-       }
-       cnt = 0;
-       stop = end_line(stop);
-       while (start <= stop && start <= end - 1) {
-               start = end_line(start);
-               if (*start == '\n')
-                       cnt++;
-               start++;
+               // 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);
+               }
+               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);
        }
-       return cnt;
+ 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 */
 }
 
-static char *find_line(int li) // find beginning of line #li
+static void Hit_Return(void)
 {
-       char *q;
+       int c;
 
-       for (q = text; li > 1; li--) {
-               q = next_line(q);
-       }
-       return q;
+       standout_start();
+       write1("[Hit return to continue]");
+       standout_end();
+       while ((c = get_one_char()) != '\n' && c != '\r')
+               continue;
+       redraw(TRUE);           // force redraw all
 }
 
 //----- Dot Movement Routines ----------------------------------
@@ -2430,32 +2512,6 @@ static char *swap_context(char *p) // goto new context for '' command make this
 }
 #endif /* FEATURE_VI_YANKMARK */
 
-//----- Set terminal attributes --------------------------------
-static void rawmode(void)
-{
-       // no TERMIOS_CLEAR_ISIG: leave ISIG on - allow signals
-       set_termios_to_raw(STDIN_FILENO, &term_orig, TERMIOS_RAW_CRNL);
-       erase_char = term_orig.c_cc[VERASE];
-}
-
-static void cookmode(void)
-{
-       fflush_all();
-       tcsetattr_stdin_TCSANOW(&term_orig);
-}
-
-static int mysleep(int hund)   // sleep for 'hund' 1/100 seconds or stdin ready
-{
-       struct pollfd pfd[1];
-
-       if (hund != 0)
-               fflush_all();
-
-       pfd[0].fd = STDIN_FILENO;
-       pfd[0].events = POLLIN;
-       return safe_poll(pfd, 1, hund*10) > 0;
-}
-
 //----- IO Routines --------------------------------------------
 static int readit(void) // read (maybe cursor) key from stdin
 {
@@ -2637,55 +2693,6 @@ static int file_write(char *fn, char *first, char *last)
        return charcnt;
 }
 
-//----- Terminal Drawing ---------------------------------------
-// The terminal is made up of 'rows' line of 'columns' columns.
-// classically this would be 24 x 80.
-//  screen coordinates
-//  0,0     ...     0,79
-//  1,0     ...     1,79
-//  .       ...     .
-//  .       ...     .
-//  22,0    ...     22,79
-//  23,0    ...     23,79   <- status line
-
-//----- Move the cursor to row x col (count from 0, not 1) -------
-static void place_cursor(int row, int col)
-{
-       char cm1[sizeof(ESC_SET_CURSOR_POS) + sizeof(int)*3 * 2];
-
-       if (row < 0) row = 0;
-       if (row >= rows) row = rows - 1;
-       if (col < 0) col = 0;
-       if (col >= columns) col = columns - 1;
-
-       sprintf(cm1, ESC_SET_CURSOR_POS, row + 1, col + 1);
-       write1(cm1);
-}
-
-//----- Erase from cursor to end of line -----------------------
-static void clear_to_eol(void)
-{
-       write1(ESC_CLEAR2EOL);
-}
-
-static void go_bottom_and_clear_to_eol(void)
-{
-       place_cursor(rows - 1, 0);
-       clear_to_eol();
-}
-
-//----- Start standout mode ------------------------------------
-static void standout_start(void)
-{
-       write1(ESC_BOLD_TEXT);
-}
-
-//----- End standout mode --------------------------------------
-static void standout_end(void)
-{
-       write1(ESC_NORM_TEXT);
-}
-
 //----- Flash the screen  --------------------------------------
 static void flash(int h)
 {
@@ -2709,13 +2716,6 @@ static void indicate_error(void)
        }
 }
 
-//----- Screen[] Routines --------------------------------------
-//----- Erase the Screen[] memory ------------------------------
-static void screen_erase(void)
-{
-       memset(screen, ' ', screensize);        // clear new screen
-}
-
 static int bufsum(char *buf, int count)
 {
        int sum = 0;