vi: trivial size optimization -65 bytes
[oweals/busybox.git] / editors / vi.c
index 249bf29bf75f281157511570414ad5d7073f34b6..a01fa7c46328b1eb08728edc701445b242fabaea 100644 (file)
 
 #include "libbb.h"
 
+/* the CRASHME code is unmaintained, and doesn't currently build */
 #define ENABLE_FEATURE_VI_CRASHME 0
 
+
 #if ENABLE_LOCALE_SUPPORT
-#define Isprint(c) isprint((c))
+
+#if ENABLE_FEATURE_VI_8BIT
+#define Isprint(c) isprint(c)
+#else
+#define Isprint(c) (isprint(c) && (unsigned char)(c) < 0x7f)
+#endif
+
 #else
+
 /* 0x9b is Meta-ESC */
+#if ENABLE_FEATURE_VI_8BIT
 #define Isprint(c) ((unsigned char)(c) >= ' ' && (c) != 0x7f && (unsigned char)(c) != 0x9b)
+#else
+#define Isprint(c) ((unsigned char)(c) >= ' ' && (unsigned char)(c) < 0x7f)
 #endif
 
+#endif
+
+
 enum {
-       MAX_LINELEN = CONFIG_FEATURE_VI_MAX_LEN,
+       MAX_TABSTOP = 32, // sanity limit
+       // User input len. Need not be extra big.
+       // Lines in file being edited *can* be bigger than this.
+       MAX_INPUT_LEN = 128,
+       // Sanity limits. We have only one buffer of this size.
        MAX_SCR_COLS = CONFIG_FEATURE_VI_MAX_LEN,
+       MAX_SCR_ROWS = CONFIG_FEATURE_VI_MAX_LEN,
 };
 
 // Misc. non-Ascii keys that report an escape sequence
@@ -45,20 +65,21 @@ enum {
 #define VI_K_HOME              (char)132       // Cursor Key Home
 #define VI_K_END               (char)133       // Cursor Key End
 #define VI_K_INSERT            (char)134       // Cursor Key Insert
-#define VI_K_PAGEUP            (char)135       // Cursor Key Page Up
-#define VI_K_PAGEDOWN          (char)136       // Cursor Key Page Down
-#define VI_K_FUN1              (char)137       // Function Key F1
-#define VI_K_FUN2              (char)138       // Function Key F2
-#define VI_K_FUN3              (char)139       // Function Key F3
-#define VI_K_FUN4              (char)140       // Function Key F4
-#define VI_K_FUN5              (char)141       // Function Key F5
-#define VI_K_FUN6              (char)142       // Function Key F6
-#define VI_K_FUN7              (char)143       // Function Key F7
-#define VI_K_FUN8              (char)144       // Function Key F8
-#define VI_K_FUN9              (char)145       // Function Key F9
-#define VI_K_FUN10             (char)146       // Function Key F10
-#define VI_K_FUN11             (char)147       // Function Key F11
-#define VI_K_FUN12             (char)148       // Function Key F12
+#define VI_K_DELETE            (char)135       // Cursor Key Insert
+#define VI_K_PAGEUP            (char)136       // Cursor Key Page Up
+#define VI_K_PAGEDOWN          (char)137       // Cursor Key Page Down
+#define VI_K_FUN1              (char)138       // Function Key F1
+#define VI_K_FUN2              (char)139       // Function Key F2
+#define VI_K_FUN3              (char)140       // Function Key F3
+#define VI_K_FUN4              (char)141       // Function Key F4
+#define VI_K_FUN5              (char)142       // Function Key F5
+#define VI_K_FUN6              (char)143       // Function Key F6
+#define VI_K_FUN7              (char)144       // Function Key F7
+#define VI_K_FUN8              (char)145       // Function Key F8
+#define VI_K_FUN9              (char)146       // Function Key F9
+#define VI_K_FUN10             (char)147       // Function Key F10
+#define VI_K_FUN11             (char)148       // Function Key F11
+#define VI_K_FUN12             (char)149       // Function Key F12
 
 /* vt102 typical ESC sequence */
 /* terminal standout start/normal ESC sequence */
@@ -91,11 +112,19 @@ enum {
        S_END_ALNUM = 5,        // used in skip_thing() for moving "dot"
 };
 
+
 /* vi.c expects chars to be unsigned. */
 /* busybox build system provides that, but it's better */
 /* to audit and fix the source */
 
-static smallint vi_setops;
+struct globals {
+       /* many references - keep near the top of globals */
+       char *text, *end;       // pointers to the user data in memory
+       char *dot;              // where all the action takes place
+       int text_size;          // size of the allocated buffer
+
+       /* the rest */
+       smallint vi_setops;
 #define VI_AUTOINDENT 1
 #define VI_SHOWMATCH  2
 #define VI_IGNORECASE 4
@@ -106,84 +135,94 @@ static smallint vi_setops;
 /* indicate error with beep or flash */
 #define err_method (vi_setops & VI_ERR_METHOD)
 
-
-static smallint editing;        // >0 while we are editing a file
-                                // [code audit says "can be 0 or 1 only"]
-static smallint cmd_mode;       // 0=command  1=insert 2=replace
-static smallint file_modified;  // buffer contents changed
-static smallint last_file_modified = -1;
-static int fn_start;            // index of first cmd line file name
-static int save_argc;           // how many file names on cmd line
-static int cmdcnt;              // repetition count
-static int rows, columns;       // the terminal screen is this size
-static int crow, ccol, offset;  // cursor is on Crow x Ccol with Horz Ofset
-static char *status_buffer;     // mesages to the user
-#define STATUS_BUFFER_LEN  200
-static int have_status_msg;     // is default edit status needed?
-                                // [don't make smallint!]
-static int last_status_cksum;   // hash of current status line
-static char *current_filename;               // current file name
-//static char *text, *end;        // pointers to the user data in memory
-static char *screen;            // pointer to the virtual screen buffer
-static int screensize;          //            and its size
-static char *screenbegin;       // index into text[], of top line on the screen
-//static char *dot;               // where all the action takes place
-static int tabstop;
-static char erase_char;         // the users erase character
-static char last_input_char;    // last char read from user
-static char last_forward_char;  // last char searched for with 'f'
-
 #if ENABLE_FEATURE_VI_READONLY
-//static smallint vi_readonly, readonly;
-static smallint readonly_mode = 0;
+       smallint readonly_mode;
 #define SET_READONLY_FILE(flags)        ((flags) |= 0x01)
 #define SET_READONLY_MODE(flags)        ((flags) |= 0x02)
 #define UNSET_READONLY_FILE(flags)      ((flags) &= 0xfe)
 #else
-#define readonly_mode 0
-#define SET_READONLY_FILE(flags)
-#define SET_READONLY_MODE(flags)
-#define UNSET_READONLY_FILE(flags)
-#endif
+#define SET_READONLY_FILE(flags)        ((void)0)
+#define SET_READONLY_MODE(flags)        ((void)0)
+#define UNSET_READONLY_FILE(flags)      ((void)0)
+#endif
+
+       smallint editing;        // >0 while we are editing a file
+                                // [code audit says "can be 0, 1 or 2 only"]
+       smallint cmd_mode;       // 0=command  1=insert 2=replace
+       int file_modified;       // buffer contents changed (counter, not flag!)
+       int last_file_modified;  // = -1;
+       int fn_start;            // index of first cmd line file name
+       int save_argc;           // how many file names on cmd line
+       int cmdcnt;              // repetition count
+       unsigned rows, columns;  // the terminal screen is this size
+       int crow, ccol;          // cursor is on Crow x Ccol
+       int offset;              // chars scrolled off the screen to the left
+       int have_status_msg;     // is default edit status needed?
+                                // [don't make smallint!]
+       int last_status_cksum;   // hash of current status line
+       char *current_filename;
+       char *screenbegin;       // index into text[], of top line on the screen
+       char *screen;            // pointer to the virtual screen buffer
+       int screensize;          //            and its size
+       int tabstop;
+       char erase_char;         // the users erase character
+       char last_input_char;    // last char read from user
+       char last_forward_char;  // last char searched for with 'f'
 
 #if ENABLE_FEATURE_VI_DOT_CMD
-static smallint adding2q;              // are we currently adding user input to q
-static char *last_modifying_cmd;       // last modifying cmd for "."
-static char *ioq, *ioq_start;           // pointer to string for get_one_char to "read"
+       smallint adding2q;       // are we currently adding user input to q
+       int lmc_len;             // length of last_modifying_cmd
+       char *ioq, *ioq_start;   // pointer to string for get_one_char to "read"
 #endif
 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
-static int last_row;           // where the cursor was last moved to
+       int last_row;            // where the cursor was last moved to
 #endif
 #if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
-static int my_pid;
+       int my_pid;
 #endif
 #if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
-static char *modifying_cmds;            // cmds that modify text[]
+       char *modifying_cmds;    // cmds that modify text[]
 #endif
 #if ENABLE_FEATURE_VI_SEARCH
-static char *last_search_pattern;      // last pattern from a '/' or '?' search
+       char *last_search_pattern; // last pattern from a '/' or '?' search
+#endif
+       int chars_to_parse;
+       /* former statics */
+#if ENABLE_FEATURE_VI_YANKMARK
+       char *edit_file__cur_line;
 #endif
+       int refresh__old_offset;
+       int format_edit_status__tot;
 
-/* Moving biggest data to malloced space... */
-struct globals {
-       /* many references - keep near the top of globals */
-       char *text, *end;       // pointers to the user data in memory
-       int text_size;          // size of the allocated buffer
-       char *dot;              // where all the action takes place
+       /* a few references only */
 #if ENABLE_FEATURE_VI_YANKMARK
-       char *reg[28];          // named register a-z, "D", and "U" 0-25,26,27
        int YDreg, Ureg;        // default delete register and orig line for "U"
+       char *reg[28];          // named register a-z, "D", and "U" 0-25,26,27
        char *mark[28];         // user marks points somewhere in text[]-  a-z and previous context ''
        char *context_start, *context_end;
 #endif
-       /* a few references only */
 #if ENABLE_FEATURE_VI_USE_SIGNALS
-       jmp_buf restart;                // catch_sig()
+       sigjmp_buf restart;     // catch_sig()
 #endif
-       struct termios term_orig, term_vi;      // remember what the cooked mode was
+       struct termios term_orig, term_vi; // remember what the cooked mode was
 #if ENABLE_FEATURE_VI_COLON
        char *initial_cmds[3];  // currently 2 entries, NULL terminated
 #endif
+       // Should be just enough to hold a key sequence,
+       // but CRASHME mode uses it as generated command buffer too
+#if ENABLE_FEATURE_VI_CRASHME
+        char readbuffer[128];
+#else
+        char readbuffer[8];
+#endif
+#define STATUS_BUFFER_LEN  200
+       char status_buffer[STATUS_BUFFER_LEN]; // messages to the user
+#if ENABLE_FEATURE_VI_DOT_CMD
+       char last_modifying_cmd[MAX_INPUT_LEN]; // last modifying cmd for "."
+#endif
+       char get_input_line__buf[MAX_INPUT_LEN]; /* former static */
+
+       char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2];
 };
 #define G (*ptr_to_globals)
 #define text           (G.text          )
@@ -191,6 +230,50 @@ struct globals {
 #define end            (G.end           )
 #define dot            (G.dot           )
 #define reg            (G.reg           )
+
+#define vi_setops               (G.vi_setops          )
+#define editing                 (G.editing            )
+#define cmd_mode                (G.cmd_mode           )
+#define file_modified           (G.file_modified      )
+#define last_file_modified      (G.last_file_modified )
+#define fn_start                (G.fn_start           )
+#define save_argc               (G.save_argc          )
+#define cmdcnt                  (G.cmdcnt             )
+#define rows                    (G.rows               )
+#define columns                 (G.columns            )
+#define crow                    (G.crow               )
+#define ccol                    (G.ccol               )
+#define offset                  (G.offset             )
+#define status_buffer           (G.status_buffer      )
+#define have_status_msg         (G.have_status_msg    )
+#define last_status_cksum       (G.last_status_cksum  )
+#define current_filename        (G.current_filename   )
+#define screen                  (G.screen             )
+#define screensize              (G.screensize         )
+#define screenbegin             (G.screenbegin        )
+#define tabstop                 (G.tabstop            )
+#define erase_char              (G.erase_char         )
+#define last_input_char         (G.last_input_char    )
+#define last_forward_char       (G.last_forward_char  )
+#if ENABLE_FEATURE_VI_READONLY
+#define readonly_mode           (G.readonly_mode      )
+#else
+#define readonly_mode           0
+#endif
+#define adding2q                (G.adding2q           )
+#define lmc_len                 (G.lmc_len            )
+#define ioq                     (G.ioq                )
+#define ioq_start               (G.ioq_start          )
+#define last_row                (G.last_row           )
+#define my_pid                  (G.my_pid             )
+#define modifying_cmds          (G.modifying_cmds     )
+#define last_search_pattern     (G.last_search_pattern)
+#define chars_to_parse          (G.chars_to_parse     )
+
+#define edit_file__cur_line     (G.edit_file__cur_line)
+#define refresh__old_offset     (G.refresh__old_offset)
+#define format_edit_status__tot (G.format_edit_status__tot)
+
 #define YDreg          (G.YDreg         )
 #define Ureg           (G.Ureg          )
 #define mark           (G.mark          )
@@ -200,6 +283,16 @@ struct globals {
 #define term_orig      (G.term_orig     )
 #define term_vi        (G.term_vi       )
 #define initial_cmds   (G.initial_cmds  )
+#define readbuffer     (G.readbuffer    )
+#define scr_out_buf    (G.scr_out_buf   )
+#define last_modifying_cmd  (G.last_modifying_cmd )
+#define get_input_line__buf (G.get_input_line__buf)
+
+#define INIT_G() do { \
+       SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+       last_file_modified = -1; \
+} while (0)
+
 
 static int init_text_buffer(char *); // init from file or create new
 static void edit_file(char *); // edit one file
@@ -227,7 +320,7 @@ static char *bound_dot(char *);     // make sure  text[0] <= P < "end"
 static char *new_screen(int, int);     // malloc virtual screen memory
 static char *char_insert(char *, char);        // insert the char c at 'p'
 static char *stupid_insert(char *, char);      // stupidly insert the char c at 'p'
-static char find_range(char **, char **, char);        // return pointers for an object
+static int find_range(char **, char **, char); // return pointers for an object
 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 ()  []  {}
@@ -248,20 +341,24 @@ static int file_insert(const char *, char *, int);
 static int file_insert(const char *, char *);
 #endif
 static int file_write(char *, char *, char *);
+#if !ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
+#define place_cursor(a, b, optimize) place_cursor(a, b)
+#endif
 static void place_cursor(int, int, int);
 static void screen_erase(void);
 static void clear_to_eol(void);
 static void clear_to_eos(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 psb(const char *, ...);     // Print Status Buf
-static void psbs(const char *, ...);    // Print Status Buf in standout mode
-static void ni(const char *);          // display messages
+static void status_line(const char *, ...);     // print to status buf
+static void status_line_bold(const char *, ...);
+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 void format_line(char*, char*, int);
+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
@@ -315,22 +412,15 @@ int vi_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int vi_main(int argc, char **argv)
 {
        int c;
-       RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN);
+
+       INIT_G();
 
 #if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
        my_pid = getpid();
 #endif
-
-       PTR_TO_GLOBALS = xzalloc(sizeof(G));
-
 #if ENABLE_FEATURE_VI_CRASHME
        srand((long) my_pid);
 #endif
-
-       status_buffer = STATUS_BUFFER;
-       last_status_cksum = 0;
-       text = NULL;
-
 #ifdef NO_SUCH_APPLET_YET
        /* If we aren't "vi", we are "view" */
        if (ENABLE_FEATURE_VI_READONLY && applet_name[2]) {
@@ -339,9 +429,6 @@ int vi_main(int argc, char **argv)
 #endif
 
        vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE;
-#if ENABLE_FEATURE_VI_YANKMARK
-       memset(reg, 0, sizeof(reg)); // init the yank regs
-#endif
 #if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
        modifying_cmds = (char *) "aAcCdDiIJoOpPrRsxX<>~";      // cmds modifying text[]
 #endif
@@ -353,10 +440,10 @@ int vi_main(int argc, char **argv)
        {
                char *p = getenv("EXINIT");
                if (p && *p)
-                       initial_cmds[0] = xstrdup(p);
+                       initial_cmds[0] = xstrndup(p, MAX_INPUT_LEN);
        }
 #endif
-       while ((c = getopt(argc, argv, "hCR" USE_FEATURE_VI_COLON("c:"))) != -1) {
+       while ((c = getopt(argc, argv, "hCRH" USE_FEATURE_VI_COLON("c:"))) != -1) {
                switch (c) {
 #if ENABLE_FEATURE_VI_CRASHME
                case 'C':
@@ -368,18 +455,17 @@ int vi_main(int argc, char **argv)
                        SET_READONLY_MODE(readonly_mode);
                        break;
 #endif
-                       //case 'r':     // recover flag-  ignore- we don't use tmp file
-                       //case 'x':     // encryption flag- ignore
-                       //case 'c':     // execute command first
 #if ENABLE_FEATURE_VI_COLON
                case 'c':               // cmd line vi command
                        if (*optarg)
-                               initial_cmds[initial_cmds[0] != 0] = xstrdup(optarg);
+                               initial_cmds[initial_cmds[0] != 0] = xstrndup(optarg, MAX_INPUT_LEN);
                        break;
-                       //case 'h':     // help -- just use default
 #endif
-               default:
+               case 'H':
                        show_help();
+                       /* fall through */
+               default:
+                       bb_show_usage();
                        return 1;
                }
        }
@@ -411,9 +497,7 @@ static int init_text_buffer(char *fn)
 
        /* allocate/reallocate text buffer */
        free(text);
-       text_size = size * 2;
-       if (text_size < 10240)
-               text_size = 10240;      // have a minimum size for new files
+       text_size = size + 10240;
        screenbegin = dot = end = text = xzalloc(text_size);
 
        if (fn != current_filename) {
@@ -437,25 +521,27 @@ static int init_text_buffer(char *fn)
        return rc;
 }
 
-static void edit_file(char * fn)
+static void edit_file(char *fn)
 {
+#if ENABLE_FEATURE_VI_YANKMARK
+#define cur_line edit_file__cur_line
+#endif
        char c;
        int size;
-
 #if ENABLE_FEATURE_VI_USE_SIGNALS
        int sig;
 #endif
-#if ENABLE_FEATURE_VI_YANKMARK
-       static char *cur_line;
-#endif
 
-       editing = 1;    // 0= exit,  1= one file, 2= multiple files
+       editing = 1;    // 0 = exit, 1 = one file, 2 = multiple files
        rawmode();
        rows = 24;
        columns = 80;
        size = 0;
-       if (ENABLE_FEATURE_VI_WIN_RESIZE)
+       if (ENABLE_FEATURE_VI_WIN_RESIZE) {
                get_terminal_width_height(0, &columns, &rows);
+               if (rows > MAX_SCR_ROWS) rows = MAX_SCR_ROWS;
+               if (columns > MAX_SCR_COLS) columns = MAX_SCR_COLS;
+       }
        new_screen(rows, columns);      // get memory for virtual screen
        init_text_buffer(fn);
 
@@ -473,7 +559,7 @@ static void edit_file(char * fn)
        catch_sig(0);
        signal(SIGWINCH, winch_sig);
        signal(SIGTSTP, suspend_sig);
-       sig = setjmp(restart);
+       sig = sigsetjmp(restart, 1);
        if (sig != 0) {
                screenbegin = dot = text;
        }
@@ -485,12 +571,11 @@ static void edit_file(char * fn)
        offset = 0;                     // no horizontal offset
        c = '\0';
 #if ENABLE_FEATURE_VI_DOT_CMD
-       free(last_modifying_cmd);
        free(ioq_start);
-       ioq = ioq_start = last_modifying_cmd = NULL;
+       ioq = ioq_start = NULL;
+       lmc_len = 0;
        adding2q = 0;
 #endif
-       redraw(FALSE);                  // dont force every col re-draw
 
 #if ENABLE_FEATURE_VI_COLON
        {
@@ -500,7 +585,7 @@ static void edit_file(char * fn)
                while ((p = initial_cmds[n])) {
                        do {
                                q = p;
-                               p = strchr(q,'\n');
+                               p = strchr(q, '\n');
                                if (p)
                                        while (*p == '\n')
                                                *p++ = '\0';
@@ -513,6 +598,7 @@ static void edit_file(char * fn)
                }
        }
 #endif
+       redraw(FALSE);                  // dont force every col re-draw
        //------This is the main Vi cmd handling loop -----------------------
        while (editing > 0) {
 #if ENABLE_FEATURE_VI_CRASHME
@@ -537,8 +623,8 @@ static void edit_file(char * fn)
 #if ENABLE_FEATURE_VI_DOT_CMD
                // These are commands that change text[].
                // Remember the input for the "." command
-               if (!adding2q && ioq_start == 0
-                && strchr(modifying_cmds, c)
+               if (!adding2q && ioq_start == NULL
+                && c != '\0' && strchr(modifying_cmds, c)
                ) {
                        start_new_cmd_q(c);
                }
@@ -560,32 +646,28 @@ static void edit_file(char * fn)
        }
        //-------------------------------------------------------------------
 
-       place_cursor(rows, 0, FALSE);   // go to bottom of screen
-       clear_to_eol();         // Erase to end of line
+       go_bottom_and_clear_to_eol();
        cookmode();
+#undef cur_line
 }
 
 //----- The Colon commands -------------------------------------
 #if ENABLE_FEATURE_VI_COLON
-static char *get_one_address(char * p, int *addr)      // get colon addr, if present
+static char *get_one_address(char *p, int *addr)       // get colon addr, if present
 {
        int st;
        char *q;
-
-#if ENABLE_FEATURE_VI_YANKMARK
-       char c;
-#endif
-#if ENABLE_FEATURE_VI_SEARCH
-       char *pat, buf[MAX_LINELEN];
-#endif
+       USE_FEATURE_VI_YANKMARK(char c;)
+       USE_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
+       else if (*p == '\'') {  // is this a mark addr
                p++;
                c = tolower(*p);
                p++;
@@ -597,17 +679,13 @@ static char *get_one_address(char * p, int *addr) // get colon addr, if present
                                *addr = count_lines(text, q);   // count lines
                        }
                }
+       }
 #endif
 #if ENABLE_FEATURE_VI_SEARCH
-       } else if (*p == '/') { // a search pattern
-               q = buf;
-               for (p++; *p; p++) {
-                       if (*p == '/')
-                               break;
-                       *q++ = *p;
-                       *q = '\0';
-               }
-               pat = xstrdup(buf);     // save copy of pattern
+       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, FULL);
@@ -615,16 +693,17 @@ static char *get_one_address(char * p, int *addr) // get colon addr, if present
                        *addr = count_lines(text, q);
                }
                free(pat);
+       }
 #endif
-       } else if (*p == '$') { // the last line in file
+       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 {                        // I don't reconise this
-               // unrecognised address- assume -1
+       } else {
+               // unrecognised address - assume -1
                *addr = -1;
        }
        return p;
@@ -676,12 +755,13 @@ static void setops(const char *args, const char *opname, int flg_no,
 }
 #endif
 
-static void colon(char * buf)
+// buf must be no longer than MAX_INPUT_LEN!
+static void colon(char *buf)
 {
        char c, *orig_buf, *buf1, *q, *r;
-       char *fn, cmd[MAX_LINELEN], args[MAX_LINELEN];
+       char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN];
        int i, l, li, ch, b, e;
-       int useforce = FALSE, forced = FALSE;
+       int useforce, forced = FALSE;
 
        // :3154        // if (-e line 3154) goto it  else stay put
        // :4,33w! foo  // write a portion of buffer to file "foo"
@@ -708,9 +788,7 @@ static void colon(char * buf)
        q = text;                       // assume 1,$ for the range
        r = end - 1;
        li = count_lines(text, end - 1);
-       fn = current_filename;                  // default to current file
-       memset(cmd, '\0', MAX_LINELEN); // clear cmd[]
-       memset(args, '\0', MAX_LINELEN);        // clear args[]
+       fn = current_filename;
 
        // look for optional address(es)  :.  :1  :1,9   :'q,'a   :%
        buf = get_address(buf, &b, &e);
@@ -725,10 +803,12 @@ static void colon(char * 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;
@@ -762,23 +842,20 @@ static void colon(char * buf)
        else if (strncmp(cmd, "!", 1) == 0) {   // run a cmd
                int retcode;
                // :!ls   run the <cmd>
-               alarm(0);               // wait for input- no alarms
-               place_cursor(rows - 1, 0, FALSE);       // go to Status line
-               clear_to_eol();                 // clear the line
+               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
-               alarm(3);               // done waiting for input
        }
 #endif
        else if (strncmp(cmd, "=", i) == 0) {   // where is the address
                if (b < 0) {    // no addr given- use defaults
                        b = e = count_lines(text, dot);
                }
-               psb("%d", b);
+               status_line("%d", b);
        } else if (strncasecmp(cmd, "delete", i) == 0) {        // delete lines
                if (b < 0) {    // no addr given- use defaults
                        q = begin_line(dot);    // assume .,. for the range
@@ -788,8 +865,8 @@ static void colon(char * buf)
                dot_skip_over_ws();
        } else if (strncasecmp(cmd, "edit", i) == 0) {  // Edit a file
                // don't edit, if the current file has been modified
-               if (file_modified && ! useforce) {
-                       psbs("No write since last change (:edit! overrides)");
+               if (file_modified && !useforce) {
+                       status_line_bold("No write since last change (:edit! overrides)");
                        goto vc1;
                }
                if (args[0]) {
@@ -800,7 +877,7 @@ static void colon(char * buf)
                        // fn = current_filename;  was set by default
                } else {
                        // no user file name, no current name- punt
-                       psbs("No current filename");
+                       status_line_bold("No current filename");
                        goto vc1;
                }
 
@@ -819,7 +896,7 @@ static void colon(char * buf)
 #endif
                // how many lines in text[]?
                li = count_lines(text, end - 1);
-               psb("\"%s\"%s"
+               status_line("\"%s\"%s"
                        USE_FEATURE_VI_READONLY("%s")
                        " %dL, %dC", current_filename,
                        (file_size(fn) < 0 ? " [New file]" : ""),
@@ -829,7 +906,7 @@ static void colon(char * buf)
                        li, ch);
        } else if (strncasecmp(cmd, "file", i) == 0) {  // what File is this
                if (b != -1 || e != -1) {
-                       ni("No address allowed on this command");
+                       not_implemented("No address allowed on this command");
                        goto vc1;
                }
                if (args[0]) {
@@ -842,8 +919,7 @@ static void colon(char * buf)
                }
        } else if (strncasecmp(cmd, "features", i) == 0) {      // what features are available
                // print out values of all features
-               place_cursor(rows - 1, 0, FALSE);       // go to Status line, bottom of screen
-               clear_to_eol(); // clear the line
+               go_bottom_and_clear_to_eol();
                cookmode();
                show_help();
                rawmode();
@@ -853,8 +929,7 @@ static void colon(char * buf)
                        q = begin_line(dot);    // assume .,. for the range
                        r = end_line(dot);
                }
-               place_cursor(rows - 1, 0, FALSE);       // go to Status line, bottom of screen
-               clear_to_eol(); // clear the line
+               go_bottom_and_clear_to_eol();
                puts("\r");
                for (; q <= r; q++) {
                        int c_is_no_print;
@@ -864,7 +939,7 @@ static void colon(char * buf)
                        if (c_is_no_print) {
                                c = '.';
                                standout_start();
-                               }
+                       }
                        if (c == '\n') {
                                write1("$\r");
                        } else if (c < ' ' || c == 127) {
@@ -895,24 +970,24 @@ static void colon(char * buf)
                }
                // don't exit if the file been modified
                if (file_modified) {
-                       psbs("No write since last change (:%s! overrides)",
+                       status_line_bold("No write since last change (:%s! overrides)",
                                 (*cmd == 'q' ? "quit" : "next"));
                        goto vc1;
                }
                // are there other file to edit
                if (*cmd == 'q' && optind < save_argc - 1) {
-                       psbs("%d more file to edit", (save_argc - optind - 1));
+                       status_line_bold("%d more file to edit", (save_argc - optind - 1));
                        goto vc1;
                }
                if (*cmd == 'n' && optind >= save_argc - 1) {
-                       psbs("No more files to edit");
+                       status_line_bold("No more files to edit");
                        goto vc1;
                }
                editing = 0;
        } else if (strncasecmp(cmd, "read", i) == 0) {  // read file into text[]
                fn = args;
                if (!fn[0]) {
-                       psbs("No filename given");
+                       status_line_bold("No filename given");
                        goto vc1;
                }
                if (b < 0) {    // no addr given- use defaults
@@ -926,7 +1001,7 @@ static void colon(char * buf)
                        goto vc1;       // nothing was inserted
                // how many lines in text[]?
                li = count_lines(q, q + ch - 1);
-               psb("\"%s\""
+               status_line("\"%s\""
                        USE_FEATURE_VI_READONLY("%s")
                        " %dL, %dC", fn,
                        USE_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
@@ -938,8 +1013,8 @@ static void colon(char * buf)
                        file_modified++;
                }
        } else if (strncasecmp(cmd, "rewind", i) == 0) {        // rewind cmd line args
-               if (file_modified && ! useforce) {
-                       psbs("No write since last change (:rewind! overrides)");
+               if (file_modified && !useforce) {
+                       status_line_bold("No write since last change (:rewind! overrides)");
                } else {
                        // reset the filenames to edit
                        optind = fn_start - 1;
@@ -954,8 +1029,7 @@ static void colon(char * buf)
                // only blank is regarded as args delmiter. What about tab '\t' ?
                if (!args[0] || strcasecmp(args, "all") == 0) {
                        // print out values of all options
-                       place_cursor(rows - 1, 0, FALSE);       // go to Status line, bottom of screen
-                       clear_to_eol(); // clear the line
+                       go_bottom_and_clear_to_eol();
                        printf("----------------------------------------\r\n");
 #if ENABLE_FEATURE_VI_SETOPTS
                        if (!autoindent)
@@ -987,7 +1061,7 @@ static void colon(char * buf)
                        /* tabstopXXXX */
                        if (strncasecmp(argp + i, "tabstop=%d ", 7) == 0) {
                                sscanf(strchr(argp + i, '='), "tabstop=%d" + 7, &ch);
-                               if (ch > 0 && ch < columns - 1)
+                               if (ch > 0 && ch <= MAX_TABSTOP)
                                        tabstop = ch;
                        }
                        while (*argp && *argp != ' ')
@@ -1046,7 +1120,7 @@ static void colon(char * buf)
                }
 #endif /* FEATURE_VI_SEARCH */
        } else if (strncasecmp(cmd, "version", i) == 0) {  // show software version
-               psb("%s", BB_VER " " BB_BT);
+               status_line(BB_VER " " BB_BT);
        } else if (strncasecmp(cmd, "write", i) == 0  // write text to file
                || strncasecmp(cmd, "wq", i) == 0
                || strncasecmp(cmd, "wn", i) == 0
@@ -1058,7 +1132,7 @@ static void colon(char * buf)
                }
 #if ENABLE_FEATURE_VI_READONLY
                if (readonly_mode && !useforce) {
-                       psbs("\"%s\" File is read only", fn);
+                       status_line_bold("\"%s\" File is read only", fn);
                        goto vc3;
                }
 #endif
@@ -1081,9 +1155,9 @@ static void colon(char * buf)
                }
                if (l < 0) {
                        if (l == -1)
-                               psbs("\"%s\" %s", fn, strerror(errno));
+                               status_line_bold("\"%s\" %s", fn, strerror(errno));
                } else {
-                       psb("\"%s\" %dL, %dC", fn, li, l);
+                       status_line("\"%s\" %dL, %dC", fn, li, l);
                        if (q == text && r == end - 1 && l == ch) {
                                file_modified = 0;
                                last_file_modified = -1;
@@ -1105,19 +1179,19 @@ static void colon(char * buf)
                }
                text_yank(q, r, YDreg);
                li = count_lines(q, r);
-               psb("Yank %d lines (%d chars) into [%c]",
+               status_line("Yank %d lines (%d chars) into [%c]",
                                li, strlen(reg[YDreg]), what_reg());
 #endif
        } else {
                // cmd unknown
-               ni(cmd);
+               not_implemented(cmd);
        }
  vc1:
        dot = bound_dot(dot);   // make sure "dot" is valid
        return;
 #if ENABLE_FEATURE_VI_SEARCH
  colon_s_fail:
-       psb(":s expression missing delimiters");
+       status_line(":s expression missing delimiters");
 #endif
 }
 
@@ -1127,11 +1201,11 @@ static void Hit_Return(void)
 {
        char c;
 
-       standout_start();       // start reverse video
+       standout_start();
        write1("[Hit return to continue]");
-       standout_end();         // end reverse video
-       while ((c = get_one_char()) != '\n' && c != '\r')       /*do nothing */
-               ;
+       standout_end();
+       while ((c = get_one_char()) != '\n' && c != '\r')
+               continue;
        redraw(TRUE);           // force redraw all
 }
 
@@ -1141,19 +1215,16 @@ static int next_tabstop(int col)
 }
 
 //----- Synchronize the cursor to Dot --------------------------
-static void sync_cursor(char * d, int *row, int *col)
+static void sync_cursor(char *d, int *row, int *col)
 {
        char *beg_cur;  // begin and end of "d" line
-       char *end_scr;  // begin and end of screen
        char *tp;
        int cnt, ro, co;
 
        beg_cur = begin_line(d);        // first char of cur line
 
-       end_scr = end_screen(); // last char of screen
-
        if (beg_cur < screenbegin) {
-               // "d" is before  top line on screen
+               // "d" is before top line on screen
                // how many lines do we have to move
                cnt = count_lines(beg_cur, screenbegin);
  sc1:
@@ -1164,18 +1235,22 @@ static void sync_cursor(char * d, int *row, int *col)
                                screenbegin = prev_line(screenbegin);
                        }
                }
-       } else if (beg_cur > end_scr) {
-               // "d" is after bottom line on screen
-               // how many lines do we have to move
-               cnt = count_lines(end_scr, beg_cur);
-               if (cnt > (rows - 1) / 2)
-                       goto sc1;       // too many lines
-               for (ro = 0; ro < cnt - 1; ro++) {
-                       // move screen begin the same amount
-                       screenbegin = next_line(screenbegin);
-                       // now, move the end of screen
-                       end_scr = next_line(end_scr);
-                       end_scr = end_line(end_scr);
+       } 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
@@ -1188,19 +1263,21 @@ static void sync_cursor(char * d, int *row, int *col)
 
        // find out what col "d" is on
        co = 0;
-       do {                            // drive "co" to correct column
-               if (*tp == '\n' || *tp == '\0')
+       while (tp < d) { // drive "co" to correct column
+               if (*tp == '\n') //vda || *tp == '\0')
                        break;
                if (*tp == '\t') {
-                       if (d == tp && cmd_mode) { /* handle tabs like real vi */
+                       // handle tabs like real vi
+                       if (d == tp && cmd_mode) {
                                break;
-                       } else {
-                               co = next_tabstop(co);
                        }
-               } else if (*tp < ' ' || *tp == 127) {
-                       co++;           // display as ^X, use 2 columns
+                       co = next_tabstop(co);
+               } else if ((unsigned char)*tp < ' ' || *tp == 0x7f) {
+                       co++; // display as ^X, use 2 columns
                }
-       } while (tp++ < d && ++co);
+               co++;
+               tp++;
+       }
 
        // "co" is the column where "dot" is.
        // The screen has "columns" columns.
@@ -1233,43 +1310,49 @@ static void sync_cursor(char * d, int *row, int *col)
 }
 
 //----- Text Movement Routines ---------------------------------
-static char *begin_line(char * p) // return pointer to first char cur line
+static char *begin_line(char *p) // return pointer to first char cur line
 {
-       while (p > text && p[-1] != '\n')
-               p--;                    // go to cur line B-o-l
+       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 line
+static char *end_line(char *p) // return pointer to NL of cur line
 {
-       while (p < end - 1 && *p != '\n')
-               p++;                    // go to cur line E-o-l
+       if (p < end - 1) {
+               p = memchr(p, '\n', end - p - 1);
+               if (!p)
+                       return end - 1;
+       }
        return p;
 }
 
-static inline char *dollar_line(char * p) // return pointer to just before NL line
+static char *dollar_line(char *p) // return pointer to just before NL line
 {
-       while (p < end - 1 && *p != '\n')
-               p++;                    // go to cur line E-o-l
+       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
+static char *prev_line(char *p) // return pointer first char prev line
 {
        p = begin_line(p);      // goto begining of cur line
-       if (p[-1] == '\n' && p > text)
+       if (p > text && p[-1] == '\n')
                p--;                    // step to prev line
        p = begin_line(p);      // goto begining of prev line
        return p;
 }
 
-static char *next_line(char * p) // return pointer first char next line
+static char *next_line(char *p) // return pointer first char next line
 {
        p = end_line(p);
-       if (*p == '\n' && p < end - 1)
+       if (p < end - 1 && *p == '\n')
                p++;                    // step to next line
        return p;
 }
@@ -1288,21 +1371,24 @@ static char *end_screen(void)
        return q;
 }
 
-static int count_lines(char * start, char * stop) // count line from start to stop
+// 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
+       if (stop < start) { // start and stop are backwards- reverse them
                q = start;
                start = stop;
                stop = q;
        }
        cnt = 0;
-       stop = end_line(stop);  // get to end of this line
-       for (q = start; q <= stop && q <= end - 1; q++) {
-               if (*q == '\n')
+       stop = end_line(stop);
+       while (start <= stop && start <= end - 1) {
+               start = end_line(start);
+               if (*start == '\n')
                        cnt++;
+               start++;
        }
        return cnt;
 }
@@ -1340,21 +1426,23 @@ static void dot_end(void)
        dot = end_line(dot);    // return pointer to last char cur line
 }
 
-static char *move_to_col(char * p, int l)
+static char *move_to_col(char *p, int l)
 {
        int co;
 
        p = begin_line(p);
        co = 0;
-       do {
-               if (*p == '\n' || *p == '\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++; // display as ^X, use 2 columns
                }
-       } while (++co <= l && p++ < end);
+               co++;
+               p++;
+       }
        return p;
 }
 
@@ -1375,11 +1463,11 @@ static void dot_scroll(int cnt, int dir)
        for (; cnt > 0; cnt--) {
                if (dir < 0) {
                        // scroll Backwards
-                       // ctrl-Y  scroll up one line
+                       // ctrl-Y scroll up one line
                        screenbegin = prev_line(screenbegin);
                } else {
                        // scroll Forwards
-                       // ctrl-E  scroll down one line
+                       // ctrl-E scroll down one line
                        screenbegin = next_line(screenbegin);
                }
        }
@@ -1404,7 +1492,7 @@ static void dot_delete(void)      // delete the char at 'dot'
        text_hole_delete(dot, dot);
 }
 
-static char *bound_dot(char * p) // make sure  text[0] <= P < "end"
+static char *bound_dot(char *p) // make sure  text[0] <= P < "end"
 {
        if (p >= end && end > text) {
                p = end - 1;
@@ -1447,7 +1535,7 @@ static char *new_screen(int ro, int co)
 }
 
 #if ENABLE_FEATURE_VI_SEARCH
-static int mycmp(const char * s1, const char * s2, int len)
+static int mycmp(const char *s1, const char *s2, int len)
 {
        int i;
 
@@ -1459,7 +1547,7 @@ static int mycmp(const char * s1, const char * s2, int len)
 }
 
 // search for pattern starting at p
-static char *char_search(char * p, const char * pat, int dir, int range)
+static char *char_search(char *p, const char *pat, int dir, int range)
 {
 #ifndef REGEX_SEARCH
        char *start, *stop;
@@ -1517,7 +1605,7 @@ static char *char_search(char * p, const char * pat, int dir, int range)
        q = re_compile_pattern(pat, strlen(pat), &preg);
        if (q != 0) {
                // The pattern was not compiled
-               psbs("bad search pattern: \"%s\": %s", pat, q);
+               status_line_bold("bad search pattern: \"%s\": %s", pat, q);
                i = 0;                  // return p if pattern not compiled
                goto cs1;
        }
@@ -1552,7 +1640,7 @@ static char *char_search(char * p, const char * pat, int dir, int range)
 }
 #endif /* FEATURE_VI_SEARCH */
 
-static char *char_insert(char * p, char c) // insert the char c at 'p'
+static char *char_insert(char *p, char c) // insert the char c at 'p'
 {
        if (c == 22) {          // Is this an ctrl-V?
                p = stupid_insert(p, '^');      // use ^ to indicate literal next
@@ -1561,7 +1649,7 @@ static char *char_insert(char * p, char c) // insert the char c at 'p'
                c = get_one_char();
                *p = c;
                p++;
-               file_modified++;        // has the file been modified
+               file_modified++;
        } else if (c == 27) {   // Is this an ESC?
                cmd_mode = 0;
                cmdcnt = 0;
@@ -1601,21 +1689,18 @@ static char *char_insert(char * p, char c) // insert the char c at 'p'
        return p;
 }
 
-static char *stupid_insert(char * p, char c) // stupidly insert the char c at 'p'
+static char *stupid_insert(char *p, char c) // stupidly insert the char c at 'p'
 {
        p = text_hole_make(p, 1);
-       if (p != 0) {
-               *p = c;
-               file_modified++;        // has the file been modified
-               p++;
-       }
-       return p;
+       *p = c;
+       //file_modified++; - done by text_hole_make()
+       return p + 1;
 }
 
-static char find_range(char ** start, char ** stop, char c)
+static int find_range(char **start, char **stop, char c)
 {
-       char *save_dot, *p, *q;
-       int cnt;
+       char *save_dot, *p, *q, *t;
+       int cnt, multiline = 0;
 
        save_dot = dot;
        p = q = dot;
@@ -1627,7 +1712,7 @@ static char find_range(char ** start, char ** stop, char c)
                        q = next_line(q);
                }
                q = end_line(q);
-       } else if (strchr("^%$0bBeEft", c)) {
+       } else if (strchr("^%$0bBeEfth\b\177", c)) {
                // These cmds operate on char positions
                do_cmd(c);              // execute movement cmd
                q = dot;
@@ -1656,20 +1741,36 @@ static char find_range(char ** start, char ** stop, char c)
                dot_end();              // find NL
                q = dot;
        } else {
-               c = 27;                 // error- return an ESC char
-               //break;
+           // nothing -- this causes any other values of c to
+           // represent the one-character range under the
+           // cursor.  this is correct for ' ' and 'l', but
+           // perhaps no others.
+           //
        }
-       *start = p;
-       *stop = q;
        if (q < p) {
-               *start = q;
-               *stop = p;
+               t = q;
+               q = p;
+               p = t;
+       }
+
+       // backward char movements don't include start position
+       if (q > p && strchr("^0bBh\b\177", c)) q--;
+
+       multiline = 0;
+       for (t = p; t <= q; t++) {
+               if (*t == '\n') {
+                       multiline = 1;
+                       break;
+               }
        }
+
+       *start = p;
+       *stop = q;
        dot = save_dot;
-       return c;
+       return multiline;
 }
 
-static int st_test(char * p, int type, int dir, char * tested)
+static int st_test(char *p, int type, int dir, char *tested)
 {
        char c, c0, ci;
        int test, inc;
@@ -1703,7 +1804,7 @@ static int st_test(char * p, int type, int dir, char * tested)
        return test;
 }
 
-static char *skip_thing(char * p, int linecnt, int dir, int type)
+static char *skip_thing(char *p, int linecnt, int dir, int type)
 {
        char c;
 
@@ -1721,7 +1822,7 @@ static char *skip_thing(char * p, int linecnt, int dir, int type)
 }
 
 // find matching char of pair  ()  []  {}
-static char *find_pair(char * p, const char c)
+static char *find_pair(char *p, const char c)
 {
        char match, *q;
        int dir, level;
@@ -1730,27 +1831,12 @@ static char *find_pair(char * p, const char c)
        level = 1;
        dir = 1;                        // assume forward
        switch (c) {
-       case '(':
-               match = ')';
-               break;
-       case '[':
-               match = ']';
-               break;
-       case '{':
-               match = '}';
-               break;
-       case ')':
-               match = '(';
-               dir = -1;
-               break;
-       case ']':
-               match = '[';
-               dir = -1;
-               break;
-       case '}':
-               match = '{';
-               dir = -1;
-               break;
+       case '(': match = ')'; break;
+       case '[': match = ']'; break;
+       case '{': match = '}'; break;
+       case ')': match = '('; dir = -1; break;
+       case ']': match = '['; dir = -1; break;
+       case '}': match = '{'; dir = -1; break;
        }
        for (q = p + dir; text <= q && q < end; q += dir) {
                // look for match, count levels of pairs  (( ))
@@ -1768,7 +1854,7 @@ static char *find_pair(char * p, const char c)
 
 #if ENABLE_FEATURE_VI_SETOPTS
 // show the matching char of a pair,  ()  []  {}
-static void showmatching(char * p)
+static void showmatching(char *p)
 {
        char *q, *save_dot;
 
@@ -1789,31 +1875,29 @@ static void showmatching(char * p)
 #endif /* FEATURE_VI_SETOPTS */
 
 //  open a hole in text[]
-static char *text_hole_make(char * p, int size)        // at "p", make a 'size' byte hole
+static char *text_hole_make(char *p, int size) // at "p", make a 'size' byte hole
 {
-       char *src, *dest;
-       int cnt;
-
        if (size <= 0)
-               goto thm0;
-       src = p;
-       dest = p + size;
-       cnt = end - src;        // the rest of buffer
-       if ( ((end + size) >= (text + text_size)) // TODO: realloc here
-                       || memmove(dest, src, cnt) != dest) {
-               psbs("can't create room for new characters");
-               p = NULL;
-               goto thm0;
-       }
-       memset(p, ' ', size);   // clear new hole
+               return p;
        end += size;            // adjust the new END
-       file_modified++;        // has the file been modified
- thm0:
+       if (end >= (text + text_size)) {
+               char *new_text;
+               text_size += end - (text + text_size) + 10240;
+               new_text = xrealloc(text, text_size);
+               screenbegin = new_text + (screenbegin - text);
+               dot         = new_text + (dot         - text);
+               end         = new_text + (end         - text);
+               p           = new_text + (p           - text);
+               text = new_text;
+       }
+       memmove(p + size, p, end - size - p);
+       memset(p, ' ', size);   // clear new hole
+       file_modified++;
        return p;
 }
 
 //  close a hole in text[]
-static char *text_hole_delete(char * p, char * q) // delete "p" thru "q", inclusive
+static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclusive
 {
        char *src, *dest;
        int cnt, hole_size;
@@ -1834,16 +1918,14 @@ static char *text_hole_delete(char * p, char * q) // delete "p" thru "q", inclus
                goto thd0;
        if (src >= end)
                goto thd_atend; // just delete the end of the buffer
-       if (memmove(dest, src, cnt) != dest) {
-               psbs("can't delete the character");
-       }
+       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
-       file_modified++;        // has the file been modified
+       file_modified++;
  thd0:
        return dest;
 }
@@ -1851,7 +1933,7 @@ static char *text_hole_delete(char * p, char * q) // delete "p" thru "q", inclus
 // copy text into register, then delete text.
 // if dist <= 0, do not include, or go past, a NewLine
 //
-static char *yank_delete(char * start, char * stop, int dist, int yf)
+static char *yank_delete(char *start, char *stop, int dist, int yf)
 {
        char *p;
 
@@ -1918,52 +2000,17 @@ static void show_help(void)
        );
 }
 
-static inline void print_literal(char * buf, const char * s) // copy s to buf, convert unprintable
-{
-       unsigned char c;
-       char b[2];
-
-       b[1] = '\0';
-       buf[0] = '\0';
-       if (!s[0])
-               s = "(NULL)";
-       for (; *s; s++) {
-               int c_is_no_print;
-
-               c = *s;
-               c_is_no_print = (c & 0x80) && !Isprint(c);
-               if (c_is_no_print) {
-                       strcat(buf, SOn);
-                       c = '.';
-               }
-               if (c < ' ' || c == 127) {
-                       strcat(buf, "^");
-                       if (c == 127)
-                               c = '?';
-                       else
-                               c += '@';
-               }
-               b[0] = c;
-               strcat(buf, b);
-               if (c_is_no_print)
-                       strcat(buf, SOs);
-               if (*s == '\n')
-                       strcat(buf, "$");
-       }
-}
-
 #if ENABLE_FEATURE_VI_DOT_CMD
 static void start_new_cmd_q(char c)
 {
-       // release old cmd
-       free(last_modifying_cmd);
        // get buffer for new cmd
-       last_modifying_cmd = xzalloc(MAX_LINELEN);
        // if there is a current cmd count put it in the buffer first
-       if (cmdcnt > 0)
-               sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
-       else // just save char c onto queue
+       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;
 }
 
@@ -1979,27 +2026,26 @@ static void end_cmd_q(void)
 #if ENABLE_FEATURE_VI_YANKMARK \
  || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
  || ENABLE_FEATURE_VI_CRASHME
-static char *string_insert(char * p, char * s) // insert the string at 'p'
+static char *string_insert(char *p, char *s) // insert the string at 'p'
 {
        int cnt, i;
 
        i = strlen(s);
-       if (text_hole_make(p, i)) {
-               strncpy(p, s, i);
-               for (cnt = 0; *s != '\0'; s++) {
-                       if (*s == '\n')
-                               cnt++;
-               }
+       text_hole_make(p, i);
+       strncpy(p, s, i);
+       for (cnt = 0; *s != '\0'; s++) {
+               if (*s == '\n')
+                       cnt++;
+       }
 #if ENABLE_FEATURE_VI_YANKMARK
-               psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
+       status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
 #endif
-       }
        return p;
 }
 #endif
 
 #if ENABLE_FEATURE_VI_YANKMARK
-static char *text_yank(char * p, char * q, int dest)   // copy text into a register
+static char *text_yank(char *p, char *q, int dest)     // copy text into a register
 {
        char *t;
        int cnt;
@@ -2050,7 +2096,7 @@ static void check_context(char cmd)
        }
 }
 
-static inline char *swap_context(char * p) // goto new context for '' command make this the current context
+static char *swap_context(char *p) // goto new context for '' command make this the current context
 {
        char *tmp;
 
@@ -2091,33 +2137,36 @@ static void cookmode(void)
 
 //----- Come here when we get a window resize signal ---------
 #if ENABLE_FEATURE_VI_USE_SIGNALS
-static void winch_sig(int sig ATTRIBUTE_UNUSED)
+static void winch_sig(int sig UNUSED_PARAM)
 {
+       // FIXME: do it in main loop!!!
        signal(SIGWINCH, winch_sig);
-       if (ENABLE_FEATURE_VI_WIN_RESIZE)
+       if (ENABLE_FEATURE_VI_WIN_RESIZE) {
                get_terminal_width_height(0, &columns, &rows);
+               if (rows > MAX_SCR_ROWS) rows = MAX_SCR_ROWS;
+               if (columns > MAX_SCR_COLS) columns = MAX_SCR_COLS;
+       }
        new_screen(rows, columns);      // get memory for virtual screen
        redraw(TRUE);           // re-draw the screen
 }
 
 //----- Come here when we get a continue signal -------------------
-static void cont_sig(int sig ATTRIBUTE_UNUSED)
+static void cont_sig(int sig UNUSED_PARAM)
 {
-       rawmode();                      // terminal to "raw"
-       last_status_cksum = 0;  // force status update
-       redraw(TRUE);           // re-draw the screen
+       rawmode(); // terminal to "raw"
+       last_status_cksum = 0; // force status update
+       redraw(TRUE); // re-draw the screen
 
        signal(SIGTSTP, suspend_sig);
        signal(SIGCONT, SIG_DFL);
-       kill(my_pid, SIGCONT);
+       kill(my_pid, SIGCONT); // huh? why? we are already "continued"...
 }
 
 //----- Come here when we get a Suspend signal -------------------
-static void suspend_sig(int sig ATTRIBUTE_UNUSED)
+static void suspend_sig(int sig UNUSED_PARAM)
 {
-       place_cursor(rows - 1, 0, FALSE);       // go to bottom of screen
-       clear_to_eol();         // Erase to end of line
-       cookmode();                     // terminal to "cooked"
+       go_bottom_and_clear_to_eol();
+       cookmode(); // terminal to "cooked"
 
        signal(SIGCONT, cont_sig);
        signal(SIGTSTP, SIG_DFL);
@@ -2129,7 +2178,7 @@ static void catch_sig(int sig)
 {
        signal(SIGINT, catch_sig);
        if (sig)
-               longjmp(restart, sig);
+               siglongjmp(restart, sig);
 }
 #endif /* FEATURE_VI_USE_SIGNALS */
 
@@ -2142,135 +2191,133 @@ static int mysleep(int hund)  // sleep for 'h' 1/100 seconds
        return safe_poll(pfd, 1, hund*10) > 0;
 }
 
-#define readbuffer bb_common_bufsiz1
-
-static int readed_for_parse;
-
 //----- IO Routines --------------------------------------------
 static char readit(void)       // read (maybe cursor) key from stdin
 {
        char c;
        int n;
-       struct esc_cmds {
-               const char *seq;
-               char val;
-       };
 
-       static const struct esc_cmds esccmds[] = {
-               {"OA", VI_K_UP},       // cursor key Up
-               {"OB", VI_K_DOWN},     // cursor key Down
-               {"OC", VI_K_RIGHT},    // Cursor Key Right
-               {"OD", VI_K_LEFT},     // cursor key Left
-               {"OH", VI_K_HOME},     // Cursor Key Home
-               {"OF", VI_K_END},      // Cursor Key End
-               {"[A", VI_K_UP},       // cursor key Up
-               {"[B", VI_K_DOWN},     // cursor key Down
-               {"[C", VI_K_RIGHT},    // Cursor Key Right
-               {"[D", VI_K_LEFT},     // cursor key Left
-               {"[H", VI_K_HOME},     // Cursor Key Home
-               {"[F", VI_K_END},      // Cursor Key End
-               {"[1~", VI_K_HOME},    // Cursor Key Home
-               {"[2~", VI_K_INSERT},  // Cursor Key Insert
-               {"[4~", VI_K_END},     // Cursor Key End
-               {"[5~", VI_K_PAGEUP},  // Cursor Key Page Up
-               {"[6~", VI_K_PAGEDOWN},// Cursor Key Page Down
-               {"OP", VI_K_FUN1},     // Function Key F1
-               {"OQ", VI_K_FUN2},     // Function Key F2
-               {"OR", VI_K_FUN3},     // Function Key F3
-               {"OS", VI_K_FUN4},     // Function Key F4
-               {"[15~", VI_K_FUN5},   // Function Key F5
-               {"[17~", VI_K_FUN6},   // Function Key F6
-               {"[18~", VI_K_FUN7},   // Function Key F7
-               {"[19~", VI_K_FUN8},   // Function Key F8
-               {"[20~", VI_K_FUN9},   // Function Key F9
-               {"[21~", VI_K_FUN10},  // Function Key F10
-               {"[23~", VI_K_FUN11},  // Function Key F11
-               {"[24~", VI_K_FUN12},  // Function Key F12
-               {"[11~", VI_K_FUN1},   // Function Key F1
-               {"[12~", VI_K_FUN2},   // Function Key F2
-               {"[13~", VI_K_FUN3},   // Function Key F3
-               {"[14~", VI_K_FUN4},   // Function Key F4
+       // Known escape sequences for cursor and function keys.
+       static const struct esc_cmds {
+               const char seq[4];
+               char val;
+       } esccmds[] = {
+               {"OA"  , VI_K_UP      },   // cursor key Up
+               {"OB"  , VI_K_DOWN    },   // cursor key Down
+               {"OC"  , VI_K_RIGHT   },   // Cursor Key Right
+               {"OD"  , VI_K_LEFT    },   // cursor key Left
+               {"OH"  , VI_K_HOME    },   // Cursor Key Home
+               {"OF"  , VI_K_END     },   // Cursor Key End
+               {"OP"  , VI_K_FUN1    },   // Function Key F1
+               {"OQ"  , VI_K_FUN2    },   // Function Key F2
+               {"OR"  , VI_K_FUN3    },   // Function Key F3
+               {"OS"  , VI_K_FUN4    },   // Function Key F4
+
+               {"[A"  , VI_K_UP      },   // cursor key Up
+               {"[B"  , VI_K_DOWN    },   // cursor key Down
+               {"[C"  , VI_K_RIGHT   },   // Cursor Key Right
+               {"[D"  , VI_K_LEFT    },   // cursor key Left
+               {"[H"  , VI_K_HOME    },   // Cursor Key Home
+               {"[F"  , VI_K_END     },   // Cursor Key End
+               {"[1~" , VI_K_HOME    },   // Cursor Key Home
+               {"[2~" , VI_K_INSERT  },   // Cursor Key Insert
+               {"[3~" , VI_K_DELETE  },   // Cursor Key Delete
+               {"[4~" , VI_K_END     },   // Cursor Key End
+               {"[5~" , VI_K_PAGEUP  },   // Cursor Key Page Up
+               {"[6~" , VI_K_PAGEDOWN},   // Cursor Key Page Down
+               // careful: these have no terminating NUL!
+               {"[11~", VI_K_FUN1    },   // Function Key F1
+               {"[12~", VI_K_FUN2    },   // Function Key F2
+               {"[13~", VI_K_FUN3    },   // Function Key F3
+               {"[14~", VI_K_FUN4    },   // Function Key F4
+               {"[15~", VI_K_FUN5    },   // Function Key F5
+               {"[17~", VI_K_FUN6    },   // Function Key F6
+               {"[18~", VI_K_FUN7    },   // Function Key F7
+               {"[19~", VI_K_FUN8    },   // Function Key F8
+               {"[20~", VI_K_FUN9    },   // Function Key F9
+               {"[21~", VI_K_FUN10   },   // Function Key F10
+               {"[23~", VI_K_FUN11   },   // Function Key F11
+               {"[24~", VI_K_FUN12   },   // Function Key F12
        };
-       enum { ESCCMDS_COUNT = ARRAY_SIZE(esccmds) };
 
-       alarm(0);       // turn alarm OFF while we wait for input
        fflush(stdout);
-       n = readed_for_parse;
-       // get input from User- are there already input chars in Q?
-       if (n <= 0) {
- ri0:
-               // the Q is empty, wait for a typed char
-               n = read(0, readbuffer, MAX_LINELEN - 1);
-               if (n < 0) {
-                       if (errno == EINTR)
-                               goto ri0;       // interrupted sys call
-                       if (errno == EBADF || errno == EFAULT || errno == EINVAL
-                                       || errno == EIO)
-                               editing = 0;
-                       errno = 0;
-               }
-               if (n <= 0)
-                       return 0;       // error
-               if (readbuffer[0] == 27) {
-                       // This is an ESC char. Is this Esc sequence?
-                       // Could be bare Esc key. See if there are any
-                       // more chars to read after the ESC. This would
-                       // be a Function or Cursor Key sequence.
-                       struct pollfd pfd[1];
-                       pfd[0].fd = 0;
-                       pfd[0].events = POLLIN;
-                       // Wait 50 ms
-                       // keep reading while there are input chars and room in buffer
-                       while (safe_poll(pfd, 1, 50) > 0 && n <= (MAX_LINELEN - 5)) {
-                               // read the rest of the ESC string
-                               int r = read(0, readbuffer + n, MAX_LINELEN - n);
-                               if (r > 0)
-                                       n += r;
-                       }
+
+       n = chars_to_parse;
+       if (n == 0) {
+               // If no data, block waiting for input.
+               n = safe_read(0, readbuffer, 1);
+               if (n <= 0) {
+ error:
+                       go_bottom_and_clear_to_eol();
+                       cookmode(); // terminal to "cooked"
+                       bb_error_msg_and_die("can't read user input");
                }
-               readed_for_parse = n;
        }
+
+       // Grab character to return from buffer
        c = readbuffer[0];
-       if (c == 27 && n > 1) {
-               // Maybe cursor or function key?
+       // Returning NUL from this routine would be bad.
+       if (c == '\0')
+               c = ' ';
+       n--;
+       if (n) memmove(readbuffer, readbuffer + 1, n);
+
+       // If it's an escape sequence, loop through known matches.
+       if (c == 27) {
                const struct esc_cmds *eindex;
 
-               for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
-                       int cnt = strlen(eindex->seq);
-
-                       if (n <= cnt)
-                               continue;
-                       if (strncmp(eindex->seq, readbuffer + 1, cnt))
-                               continue;
-                       // is a Cursor key- put derived value back into Q
-                       c = eindex->val;
-                       // for squeeze out the ESC sequence
-                       n = cnt + 1;
-                       break;
+               for (eindex = esccmds; eindex < esccmds + ARRAY_SIZE(esccmds); eindex++) {
+                       // n - position in seq to read
+                       int i = 0; // position in seq to compare
+                       int cnt = strnlen(eindex->seq, 4);
+
+                       // Loop through chars in this sequence.
+                       for (;;) {
+                               // We've matched this escape sequence up to [i-1]
+                               if (n <= i) {
+                                       // Need more chars, read another one if it wouldn't block.
+                                       // (Note that escape sequences come in as a unit,
+                                       // so if we would block it's not really an escape sequence.)
+                                       struct pollfd pfd;
+                                       pfd.fd = 0;
+                                       pfd.events = POLLIN;
+                                       // Rob needed 300ms timeout on qemu
+                                       if (safe_poll(&pfd, 1, /*timeout:*/ 300)) {
+                                               if (safe_read(0, readbuffer + n, 1) <= 0)
+                                                       goto error;
+                                               n++;
+                                       } else {
+                                               // No more data!
+                                               // Array is sorted from shortest to longest,
+                                               // we can't match anything later in array,
+                                               // break out of both loops.
+                                               goto loop_out;
+                                       }
+                               }
+                               if (readbuffer[i] != eindex->seq[i])
+                                       break; // try next seq
+                               if (++i == cnt) { // entire seq matched
+                                       c = eindex->val;
+                                       n = 0;
+                                       goto loop_out;
+                               }
+                       }
                }
-               if (eindex == &esccmds[ESCCMDS_COUNT]) {
-                       /* defined ESC sequence not found, set only one ESC */
-                       n = 1;
-       }
-       } else {
-               n = 1;
+               // We did not find matching sequence, it was a bare ESC.
+               // We possibly read and stored more input in readbuffer by now.
        }
-       // remove key sequence from Q
-       readed_for_parse -= n;
-       memmove(readbuffer, readbuffer + n, MAX_LINELEN - n);
-       alarm(3);       // we are done waiting for input, turn alarm ON
+loop_out:
+
+       chars_to_parse = n;
        return c;
 }
 
 //----- IO Routines --------------------------------------------
 static char get_one_char(void)
 {
-       static char c;
+       char c;
 
 #if ENABLE_FEATURE_VI_DOT_CMD
-       // ! adding2q  && ioq == 0  read()
-       // ! adding2q  && ioq != 0  *ioq
-       // adding2q         *last_modifying_cmd= read()
        if (!adding2q) {
                // we are not adding to the q.
                // but, we may be reading from a q
@@ -2290,61 +2337,53 @@ static char get_one_char(void)
        } else {
                // adding STDIN chars to q
                c = readit();   // get the users input
-               if (last_modifying_cmd != 0) {
-                       int len = strlen(last_modifying_cmd);
-                       if (len >= MAX_LINELEN - 1) {
-                               psbs("last_modifying_cmd overrun");
-                       } else {
-                               // add new char to q
-                               last_modifying_cmd[len] = c;
-                       }
+               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;                       // return the char, where ever it came from
+       return c;
 }
 
-static char *get_input_line(const char * prompt) // get input line- use "status line"
+// Get input line (uses "status line" area)
+static char *get_input_line(const char *prompt)
 {
-       static char *obufp;
+       // char [MAX_INPUT_LEN]
+#define buf get_input_line__buf
 
-       char buf[MAX_LINELEN];
        char c;
        int i;
 
        strcpy(buf, prompt);
        last_status_cksum = 0;  // force status update
-       place_cursor(rows - 1, 0, FALSE);       // go to Status line, bottom of screen
-       clear_to_eol();         // clear the line
+       go_bottom_and_clear_to_eol();
        write1(prompt);      // write out the :, /, or ? prompt
 
        i = strlen(buf);
-       while (i < MAX_LINELEN) {
-               c = get_one_char();     // read user input
+       while (i < MAX_INPUT_LEN) {
+               c = get_one_char();
                if (c == '\n' || c == '\r' || c == 27)
-                       break;          // is this end of input
+                       break;          // this is end of input
                if (c == erase_char || c == 8 || c == 127) {
                        // user wants to erase prev char
-                       i--;            // backup to prev char
-                       buf[i] = '\0';  // erase the char
-                       buf[i + 1] = '\0';      // null terminate buffer
-                       write1("\b \b");     // erase char on screen
-                       if (i <= 0) {   // user backs up before b-o-l, exit
+                       buf[--i] = '\0';
+                       write1("\b \b"); // erase char on screen
+                       if (i <= 0) // user backs up before b-o-l, exit
                                break;
-                       }
                } else {
-                       buf[i] = c;     // save char in buffer
-                       buf[i + 1] = '\0';      // make sure buffer is null terminated
-                       bb_putchar(c);   // echo the char back to user
-                       i++;
+                       buf[i] = c;
+                       buf[++i] = '\0';
+                       bb_putchar(c);
                }
        }
        refresh(FALSE);
-       free(obufp);
-       obufp = xstrdup(buf);
-       return obufp;
+       return buf;
+#undef buf
 }
 
 static int file_size(const char *fn) // what is the byte size of "fn"
@@ -2358,7 +2397,7 @@ static int file_size(const char *fn) // what is the byte size of "fn"
        return cnt;
 }
 
-static int file_insert(const char * fn, char *p
+static int file_insert(const char *fn, char *p
                USE_FEATURE_VI_READONLY(, int update_ro_status))
 {
        int cnt = -1;
@@ -2367,44 +2406,35 @@ static int file_insert(const char * fn, char *p
 
        /* Validate file */
        if (stat(fn, &statbuf) < 0) {
-               psbs("\"%s\" %s", fn, strerror(errno));
+               status_line_bold("\"%s\" %s", fn, strerror(errno));
                goto fi0;
        }
-       if ((statbuf.st_mode & S_IFREG) == 0) {
+       if (!S_ISREG(statbuf.st_mode)) {
                // This is not a regular file
-               psbs("\"%s\" Not a regular file", fn);
+               status_line_bold("\"%s\" Not a regular file", fn);
                goto fi0;
        }
-       /* // this check is done by open()
-       if ((statbuf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
-               // dont have any read permissions
-               psbs("\"%s\" Not readable", fn);
-               goto fi0;
-       }
-       */
        if (p < text || p > end) {
-               psbs("Trying to insert file outside of memory");
+               status_line_bold("Trying to insert file outside of memory");
                goto fi0;
        }
 
        // read file to buffer
        fd = open(fn, O_RDONLY);
        if (fd < 0) {
-               psbs("\"%s\" %s", fn, strerror(errno));
+               status_line_bold("\"%s\" %s", fn, strerror(errno));
                goto fi0;
        }
        size = statbuf.st_size;
        p = text_hole_make(p, size);
-       if (p == NULL)
-               goto fi0;
-       cnt = read(fd, p, size);
+       cnt = safe_read(fd, p, size);
        if (cnt < 0) {
-               psbs("\"%s\" %s", fn, strerror(errno));
+               status_line_bold("\"%s\" %s", fn, strerror(errno));
                p = text_hole_delete(p, p + size - 1);  // un-do buffer insert
        } else if (cnt < size) {
                // There was a partial read, shrink unused space text[]
                p = text_hole_delete(p + cnt, p + (size - cnt) - 1);    // un-do buffer insert
-               psbs("cannot read all of file \"%s\"", fn);
+               status_line_bold("cannot read all of file \"%s\"", fn);
        }
        if (cnt >= size)
                file_modified++;
@@ -2424,25 +2454,28 @@ static int file_insert(const char * fn, char *p
        return cnt;
 }
 
-
-static int file_write(char * fn, char * first, char * last)
+static int file_write(char *fn, char *first, char *last)
 {
        int fd, cnt, charcnt;
 
        if (fn == 0) {
-               psbs("No current filename");
+               status_line_bold("No current filename");
                return -2;
        }
        charcnt = 0;
-       // FIXIT- use the correct umask()
-       fd = open(fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
+       /* 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 = write(fd, first, cnt);
+       charcnt = full_write(fd, first, cnt);
+       ftruncate(fd, charcnt);
        if (charcnt == cnt) {
                // good write
-               //file_modified = FALSE; // the file has not been modified
+               //file_modified = FALSE;
        } else {
                charcnt = 0;
        }
@@ -2459,22 +2492,21 @@ static int file_write(char * fn, char * first, char * last)
 //  .       ...     .
 //  .       ...     .
 //  22,0    ...     22,79
-//  23,0    ...     23,79   status line
-//
+//  23,0    ...     23,79   <- status line
 
 //----- Move the cursor to row x col (count from 0, not 1) -------
-static void place_cursor(int row, int col, int opti)
+static void place_cursor(int row, int col, int optimize)
 {
-       char cm1[MAX_LINELEN];
-       char *cm;
+       char cm1[sizeof(CMrc) + sizeof(int)*3 * 2];
 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
-       char cm2[MAX_LINELEN];
-       char *screenp;
-       // char cm3[MAX_LINELEN];
-       int Rrow = last_row;
+       enum {
+               SZ_UP = sizeof(CMup),
+               SZ_DN = sizeof(CMdown),
+               SEQ_SIZE = SZ_UP > SZ_DN ? SZ_UP : SZ_DN,
+       };
+       char cm2[SEQ_SIZE * 5 + 32]; // bigger than worst case size
 #endif
-
-       memset(cm1, '\0', MAX_LINELEN);  // clear the buffer
+       char *cm;
 
        if (row < 0) row = 0;
        if (row >= rows) row = rows - 1;
@@ -2484,45 +2516,47 @@ static void place_cursor(int row, int col, int opti)
        //----- 1.  Try the standard terminal ESC sequence
        sprintf(cm1, CMrc, row + 1, col + 1);
        cm = cm1;
-       if (!opti)
-               goto pc0;
 
 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
-       //----- find the minimum # of chars to move cursor -------------
-       //----- 2.  Try moving with discreet chars (Newline, [back]space, ...)
-       memset(cm2, '\0', MAX_LINELEN);  // clear the buffer
+       if (optimize && col < 16) {
+               char *screenp;
+               int Rrow = last_row;
+               int diff = Rrow - row;
 
-       // move to the correct row
-       while (row < Rrow) {
-               // the cursor has to move up
-               strcat(cm2, CMup);
-               Rrow--;
-       }
-       while (row > Rrow) {
-               // the cursor has to move down
-               strcat(cm2, CMdown);
-               Rrow++;
-       }
+               if (diff < -5 || diff > 5)
+                       goto skip;
 
-       // now move to the correct column
-       strcat(cm2, "\r");                      // start at col 0
-       // just send out orignal source char to get to correct place
-       screenp = &screen[row * columns];       // start of screen line
-       strncat(cm2, screenp, col);
+               //----- find the minimum # of chars to move cursor -------------
+               //----- 2.  Try moving with discreet chars (Newline, [back]space, ...)
+               cm2[0] = '\0';
 
-       //----- 3.  Try some other way of moving cursor
-       //---------------------------------------------
+               // move to the correct row
+               while (row < Rrow) {
+                       // the cursor has to move up
+                       strcat(cm2, CMup);
+                       Rrow--;
+               }
+               while (row > Rrow) {
+                       // the cursor has to move down
+                       strcat(cm2, CMdown);
+                       Rrow++;
+               }
 
-       // pick the shortest cursor motion to send out
-       cm = cm1;
-       if (strlen(cm2) < strlen(cm)) {
-               cm = cm2;
-       }  /* else if (strlen(cm3) < strlen(cm)) {
-               cm= cm3;
-       } */
+               // now move to the correct column
+               strcat(cm2, "\r");                      // start at col 0
+               // just send out orignal source char to get to correct place
+               screenp = &screen[row * columns];       // start of screen line
+               strncat(cm2, screenp, col);
+
+               // pick the shortest cursor motion to send out
+               if (strlen(cm2) < strlen(cm)) {
+                       cm = cm2;
+               }
+ skip: ;
+       }
+       last_row = row;
 #endif /* FEATURE_VI_OPTIMIZE_CURSOR */
- pc0:
-       write1(cm);                 // move the cursor
+       write1(cm);
 }
 
 //----- Erase from cursor to end of line -----------------------
@@ -2531,6 +2565,12 @@ static void clear_to_eol(void)
        write1(Ceol);   // Erase from cursor to end of line
 }
 
+static void go_bottom_and_clear_to_eol(void)
+{
+       place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
+       clear_to_eol(); // erase to end of line
+}
+
 //----- Erase from cursor to end of screen -----------------------
 static void clear_to_eos(void)
 {
@@ -2601,10 +2641,9 @@ static void show_status_line(void)
                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
-               place_cursor(rows - 1, 0, FALSE);       // put cursor on status line
+               last_status_cksum = cksum;              // remember if we have seen this line
+               go_bottom_and_clear_to_eol();
                write1(status_buffer);
-               clear_to_eol();
                if (have_status_msg) {
                        if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
                                        (columns - 1) ) {
@@ -2620,13 +2659,13 @@ static void show_status_line(void)
 
 //----- format the status buffer, the bottom line of screen ------
 // format status buffer, with STANDOUT mode
-static void psbs(const char *format, ...)
+static void status_line_bold(const char *format, ...)
 {
        va_list args;
 
        va_start(args, format);
        strcpy(status_buffer, SOs);     // Terminal standout mode on
-       vsprintf(status_buffer + strlen(status_buffer), format, args);
+       vsprintf(status_buffer + sizeof(SOs)-1, format, args);
        strcat(status_buffer, SOn);     // Terminal standout mode off
        va_end(args);
 
@@ -2634,7 +2673,7 @@ static void psbs(const char *format, ...)
 }
 
 // format status buffer
-static void psb(const char *format, ...)
+static void status_line(const char *format, ...)
 {
        va_list args;
 
@@ -2645,18 +2684,58 @@ static void psb(const char *format, ...)
        have_status_msg = 1;
 }
 
-static void ni(const char * s) // display messages
+// copy s to buf, convert unprintable
+static void print_literal(char *buf, const char *s)
+{
+       unsigned char c;
+       char b[2];
+
+       b[1] = '\0';
+       buf[0] = '\0';
+       if (!s[0])
+               s = "(NULL)";
+       for (; *s; s++) {
+               int c_is_no_print;
+
+               c = *s;
+               c_is_no_print = (c & 0x80) && !Isprint(c);
+               if (c_is_no_print) {
+                       strcat(buf, SOn);
+                       c = '.';
+               }
+               if (c < ' ' || c == 127) {
+                       strcat(buf, "^");
+                       if (c == 127)
+                               c = '?';
+                       else
+                               c += '@';
+               }
+               b[0] = c;
+               strcat(buf, b);
+               if (c_is_no_print)
+                       strcat(buf, SOs);
+               if (*s == '\n')
+                       strcat(buf, "$");
+               if (strlen(buf) > MAX_INPUT_LEN - 10) // paranoia
+                       break;
+       }
+}
+
+static void not_implemented(const char *s)
 {
-       char buf[MAX_LINELEN];
+       char buf[MAX_INPUT_LEN];
 
        print_literal(buf, s);
-       psbs("\'%s\' is not implemented", buf);
+       status_line_bold("\'%s\' is not implemented", buf);
 }
 
-static int format_edit_status(void)    // show file status on status line
+// show file status on status line
+static int format_edit_status(void)
 {
-       static int tot;
        static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
+
+#define tot format_edit_status__tot
+
        int cur, percent, ret, trunc_at;
 
        // file_modified is now a counter rather than a flag.  this
@@ -2707,13 +2786,14 @@ static int format_edit_status(void)     // show file status on status line
                return ret;  /* it all fit */
 
        return trunc_at;  /* had to truncate */
+#undef tot
 }
 
 //----- Force refresh of all Lines -----------------------------
 static void redraw(int full_screen)
 {
        place_cursor(0, 0, FALSE);      // put cursor in correct place
-       clear_to_eos();         // tel terminal to erase display
+       clear_to_eos();         // tell terminal to erase display
        screen_erase();         // erase the internal screen buffer
        last_status_cksum = 0;  // force status update
        refresh(full_screen);   // this will redraw the entire display
@@ -2721,46 +2801,61 @@ static void redraw(int full_screen)
 }
 
 //----- Format a text[] line into a buffer ---------------------
-static void format_line(char *dest, char *src, int li)
+static char* format_line(char *src /*, int li*/)
 {
+       unsigned char c;
        int co;
-       char c;
+       int ofs = offset;
+       char *dest = scr_out_buf; // [MAX_SCR_COLS + MAX_TABSTOP * 2]
 
-       for (co = 0; co < MAX_SCR_COLS; co++) {
-               c = ' ';                // assume blank
-               if (li > 0 && co == 0) {
-                       c = '~';        // not first line, assume Tilde
-               }
-               // are there chars in text[] and have we gone past the end
-               if (text < end && src < end) {
+       c = '~'; // 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 ((unsigned char)(c) < ' ' || c == 0x7f) {
-                       if (c == '\t') {
-                               c = ' ';
-                               //       co %    8     !=     7
-                               for (; (co % tabstop) != (tabstop - 1); co++) {
-                                       dest[co] = c;
+                       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'
                                }
-                       } else {
-                               dest[co++] = '^';
-                               if (c == 0x7f)
-                                       c = '?';
-                               else
-                                       c += '@';       // make it visible
                        }
                }
-               // the co++ is done here so that the column will
-               // not be overwritten when we blank-out the rest of line
-               dest[co] = c;
+               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;
        }
+       // 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;
 }
 
 //----- Refresh the changed screen lines -----------------------
@@ -2770,35 +2865,39 @@ static void format_line(char *dest, char *src, int li)
 //
 static void refresh(int full_screen)
 {
-       static int old_offset;
+#define old_offset refresh__old_offset
 
        int li, changed;
-       char buf[MAX_SCR_COLS];
        char *tp, *sp;          // pointer into text[] and screen[]
-#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
-       int last_li = -2; // last line that changed- for optimizing cursor movement
-#endif
 
-       if (ENABLE_FEATURE_VI_WIN_RESIZE)
+       if (ENABLE_FEATURE_VI_WIN_RESIZE) {
+               unsigned c = columns, r = rows;
                get_terminal_width_height(0, &columns, &rows);
+               if (rows > MAX_SCR_ROWS) rows = MAX_SCR_ROWS;
+               if (columns > MAX_SCR_COLS) columns = MAX_SCR_COLS;
+               full_screen |= (c - columns) | (r - rows);
+       }
        sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
        tp = screenbegin;       // index into text[] of top line
 
        // compare text[] to screen[] and mark screen[] lines that need updating
        for (li = 0; li < rows - 1; li++) {
                int cs, ce;                             // column start & end
-               memset(buf, ' ', MAX_SCR_COLS);         // blank-out the buffer
-               buf[MAX_SCR_COLS-1] = 0;                // NULL terminate the buffer
-               // format current text line into buf
-               format_line(buf, tp, li);
+               char *out_buf;
+               // format current text line
+               out_buf = format_line(tp /*, li*/);
 
                // skip to the end of the current text[] line
-               while (tp < end && *tp++ != '\n') /*no-op*/;
+               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 vitual screen and buf
+               // see if there are any changes between vitual screen and out_buf
                changed = FALSE;        // assume no change
-               cs= 0;
-               ce= columns-1;
+               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
@@ -2807,15 +2906,15 @@ static void refresh(int full_screen)
                // compare newly formatted buffer with virtual screen
                // look forward for first difference between buf and screen
                for (; cs <= ce; cs++) {
-                       if (buf[cs + offset] != sp[cs]) {
+                       if (out_buf[cs] != sp[cs]) {
                                changed = TRUE; // mark for redraw
                                break;
                        }
                }
 
-               // look backward for last difference between buf and screen
-               for ( ; ce >= cs; ce--) {
-                       if (buf[ce + offset] != sp[ce]) {
+               // 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;
                        }
@@ -2829,56 +2928,32 @@ static void refresh(int full_screen)
                }
 
                // make a sanity check of columns indexes
-               if (cs < 0) cs= 0;
-               if (ce > columns-1) ce= columns-1;
-               if (cs > ce) {  cs= 0;  ce= columns-1;  }
-               // is there a change between vitual screen and buf
+               if (cs < 0) cs = 0;
+               if (ce > columns - 1) ce = columns - 1;
+               if (cs > ce) { cs = 0; ce = columns - 1; }
+               // is there a change between vitual screen and out_buf
                if (changed) {
-                       //  copy changed part of buffer to virtual screen
-                       memmove(sp+cs, buf+(cs+offset), ce-cs+1);
+                       // copy changed part of buffer to virtual screen
+                       memcpy(sp+cs, out_buf+cs, ce-cs+1);
 
                        // move cursor to column of first change
-                       if (offset != old_offset) {
-                               // opti_cur_move is still too stupid
-                               // to handle offsets correctly
-                               place_cursor(li, cs, FALSE);
-                       } else {
-#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
-                               // if this just the next line
-                               //  try to optimize cursor movement
-                               //  otherwise, use standard ESC sequence
-                               place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
-                               last_li= li;
-#else
-                               place_cursor(li, cs, FALSE);    // use standard ESC sequence
-#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
-                       }
+                       //if (offset != old_offset) {
+                       //      // place_cursor is still too stupid
+                       //      // to handle offsets correctly
+                       //      place_cursor(li, cs, FALSE);
+                       //} else {
+                               place_cursor(li, cs, TRUE);
+                       //}
 
                        // write line out to terminal
-                       {
-                               int nic = ce - cs + 1;
-                               char *out = sp + cs;
-
-                               while (nic-- > 0) {
-                                       bb_putchar(*out);
-                                       out++;
-                               }
-                       }
-#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
-                       last_row = li;
-#endif
+                       fwrite(&sp[cs], ce - cs + 1, 1, stdout);
                }
        }
 
-#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
-       place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
-       last_row = crow;
-#else
-       place_cursor(crow, ccol, FALSE);
-#endif
+       place_cursor(crow, ccol, TRUE);
 
-       if (offset != old_offset)
-               old_offset = offset;
+       old_offset = offset;
+#undef old_offset
 }
 
 //---------------------------------------------------------------------
@@ -2905,14 +2980,16 @@ static void refresh(int full_screen)
 //----- Execute a Vi Command -----------------------------------
 static void do_cmd(char c)
 {
-       const char *msg;
-       char c1, *p, *q, buf[9], *save_dot;
-       int cnt, i, j, dir, yf;
+       const char *msg = msg; // for compiler
+       char c1, *p, *q, *save_dot;
+       char buf[12];
+       int dir = dir; // for compiler
+       int cnt, i, j;
 
-       c1 = c;                         // quiet the compiler
-       cnt = yf = dir = 0;     // quiet the compiler
-       msg = p = q = save_dot = buf;   // quiet the compiler
-       memset(buf, '\0', 9);   // clear buf
+//     c1 = c; // quiet the compiler
+//     cnt = yf = 0; // quiet the compiler
+//     msg = p = q = save_dot = buf; // quiet the compiler
+       memset(buf, '\0', 12);
 
        show_status_line();
 
@@ -2984,7 +3061,6 @@ static void do_cmd(char c)
                //case '(':     // (-
                //case ')':     // )-
                //case '*':     // *-
-               //case ',':     // ,-
                //case '=':     // =-
                //case '@':     // @-
                //case 'F':     // F-
@@ -2998,7 +3074,6 @@ static void do_cmd(char c)
                //case ']':     // ]-
                //case '_':     // _-
                //case '`':     // `-
-               //case 'g':     // g-
                //case 'u':     // u- FIXME- there is no undo
                //case 'v':     // v-
        default:                        // unrecognised command
@@ -3009,7 +3084,7 @@ static void do_cmd(char c)
                        buf[1] = c + '@';
                        buf[2] = '\0';
                }
-               ni(buf);
+               not_implemented(buf);
                end_cmd_q();    // stop adding to q
        case 0x00:                      // nul- ignore
                break;
@@ -3017,14 +3092,6 @@ static void do_cmd(char c)
        case VI_K_PAGEUP:       // Cursor Key Page Up
                dot_scroll(rows - 2, -1);
                break;
-#if ENABLE_FEATURE_VI_USE_SIGNALS
-       case 0x03:                      // ctrl-C   interrupt
-               longjmp(restart, 1);
-               break;
-       case 26:                        // ctrl-Z suspend
-               suspend_sig(SIGTSTP);
-               break;
-#endif
        case 4:                 // ctrl-D  scroll down half screen
                dot_scroll((rows - 2) / 2, 1);
                break;
@@ -3143,7 +3210,7 @@ static void do_cmd(char c)
        case 'p':                       // p- put register after
                p = reg[YDreg];
                if (p == 0) {
-                       psbs("Nothing in register %c", what_reg());
+                       status_line_bold("Nothing in register %c", what_reg());
                        break;
                }
                // are we putting whole lines or strings
@@ -3205,7 +3272,7 @@ static void do_cmd(char c)
                //
                // dont separate these two commands. 'f' depends on ';'
                //
-               //**** fall thru to ... ';'
+               //**** fall through to ... ';'
        case ';':                       // ;- look at rest of line for last forward char
                if (cmdcnt-- > 1) {
                        do_cmd(';');
@@ -3219,6 +3286,20 @@ static void do_cmd(char c)
                if (*q == last_forward_char)
                        dot = q;
                break;
+       case ',':           // repeat latest 'f' in opposite direction
+               if (cmdcnt-- > 1) {
+                       do_cmd(',');
+               }                               // repeat cnt
+               if (last_forward_char == 0)
+                       break;
+               q = dot - 1;
+               while (q >= text && *q != '\n' && *q != last_forward_char) {
+                       q--;
+               }
+               if (q >= text && *q == last_forward_char)
+                       dot = q;
+               break;
+
        case '-':                       // -- goto prev line
                if (cmdcnt-- > 1) {
                        do_cmd(c);
@@ -3230,7 +3311,8 @@ static void do_cmd(char c)
        case '.':                       // .- repeat the last modifying command
                // Stuff the last_modifying_cmd back into stdin
                // and let it be re-executed.
-               if (last_modifying_cmd != 0) {
+               if (lmc_len > 0) {
+                       last_modifying_cmd[lmc_len] = 0;
                        ioq = ioq_start = xstrdup(last_modifying_cmd);
                }
                break;
@@ -3241,8 +3323,11 @@ static void do_cmd(char c)
                buf[0] = c;
                buf[1] = '\0';
                q = get_input_line(buf);        // get input line- use "status line"
-               if (q[0] && !q[1])
+               if (q[0] && !q[1]) {
+                       if (last_search_pattern[0])
+                           last_search_pattern[0] = c;
                        goto dc3; // if no pat re-use old pat
+               }
                if (q[0]) {       // strlen(q) > 1: new pat- save it and find
                        // there is a new pat
                        free(last_search_pattern);
@@ -3306,7 +3391,7 @@ static void do_cmd(char c)
                }
  dc2:
                if (*msg)
-                       psbs("%s", msg);
+                       status_line_bold("%s", msg);
                break;
        case '{':                       // {- move backward paragraph
                q = char_search(dot, "\n\n", BACK, FULL);
@@ -3351,7 +3436,7 @@ static void do_cmd(char c)
                 || strncasecmp(p, "q!", cnt) == 0   // delete lines
                ) {
                        if (file_modified && p[1] != '!') {
-                               psbs("No write since last change (:quit! overrides)");
+                               status_line_bold("No write since last change (:quit! overrides)");
                        } else {
                                editing = 0;
                        }
@@ -3363,11 +3448,11 @@ static void do_cmd(char c)
                        cnt = file_write(current_filename, text, end - 1);
                        if (cnt < 0) {
                                if (cnt == -1)
-                                       psbs("Write error: %s", strerror(errno));
+                                       status_line_bold("Write error: %s", strerror(errno));
                        } else {
                                file_modified = 0;
                                last_file_modified = -1;
-                               psb("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
+                               status_line("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
                                if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
                                 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
                                ) {
@@ -3380,7 +3465,7 @@ static void do_cmd(char c)
                        dot = find_line(j);             // go to line # j
                        dot_skip_over_ws();
                } else {                // unrecognised cmd
-                       ni(p);
+                       not_implemented(p);
                }
 #endif /* !FEATURE_VI_COLON */
                break;
@@ -3416,7 +3501,7 @@ static void do_cmd(char c)
                break;
        case 'A':                       // A- append at e-o-l
                dot_end();              // go to e-o-l
-               //**** fall thru to ... 'a'
+               //**** fall through to ... 'a'
        case 'a':                       // a- append after current char
                if (*dot != '\n')
                        dot++;
@@ -3451,6 +3536,19 @@ static void do_cmd(char c)
                        end_cmd_q();    // stop adding to q
 #endif
                break;
+       case 'g':                       // 'gg' goto a line number (from vim)
+                                       // (default to first line in file)
+               c1 = get_one_char();
+               if (c1 != 'g') {
+                       buf[0] = 'g';
+                       buf[1] = c1;
+                       buf[2] = '\0';
+                       not_implemented(buf);
+                       break;
+               }
+               if (cmdcnt == 0)
+                       cmdcnt = 1;
+               /* fall through */
        case 'G':               // G- goto to a line number (default= E-O-F)
                dot = end - 1;                          // assume E-O-F
                if (cmdcnt > 0) {
@@ -3471,7 +3569,7 @@ static void do_cmd(char c)
        case 'I':                       // I- insert before first non-blank
                dot_begin();    // 0
                dot_skip_over_ws();
-               //**** fall thru to ... 'i'
+               //**** fall through to ... 'i'
        case 'i':                       // i- insert before current char
        case VI_K_INSERT:       // Cursor Key Insert
  dc_i:
@@ -3526,6 +3624,9 @@ static void do_cmd(char c)
  dc5:
                cmd_mode = 2;
                break;
+       case VI_K_DELETE:
+               c = 'x';
+               // fall through
        case 'X':                       // X- delete char before dot
        case 'x':                       // x- delete the current char
        case 's':                       // s- substitute the current char
@@ -3553,13 +3654,13 @@ static void do_cmd(char c)
                }
                if (file_modified) {
                        if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
-                               psbs("\"%s\" File is read only", current_filename);
+                               status_line_bold("\"%s\" File is read only", current_filename);
                                break;
                        }
                        cnt = file_write(current_filename, text, end - 1);
                        if (cnt < 0) {
                                if (cnt == -1)
-                                       psbs("Write error: %s", strerror(errno));
+                                       status_line_bold("Write error: %s", strerror(errno));
                        } else if (cnt == (end - 1 - text + 1)) {
                                editing = 0;
                        }
@@ -3597,6 +3698,8 @@ static void do_cmd(char c)
        case 'y':                       // y- yank   something
        case 'Y':                       // Y- Yank a line
 #endif
+               {
+               int yf, ml, whole = 0;
                yf = YANKDEL;   // assume either "c" or "d"
 #if ENABLE_FEATURE_VI_YANKMARK
                if (c == 'y' || c == 'Y')
@@ -3605,7 +3708,8 @@ static void do_cmd(char c)
                c1 = 'y';
                if (c != 'Y')
                        c1 = get_one_char();    // get the type of thing to delete
-               find_range(&p, &q, c1);
+               // determine range, and whether it spans lines
+               ml = find_range(&p, &q, c1);
                if (c1 == 27) { // ESC- user changed mind and wants out
                        c = c1 = 27;    // Escape- do nothing
                } else if (strchr("wW", c1)) {
@@ -3617,27 +3721,31 @@ static void do_cmd(char c)
                                        q--;
                                }
                        }
-                       dot = yank_delete(p, q, 0, yf); // delete word
-               } else if (strchr("^0bBeEft$", c1)) {
-                       // single line copy text into a register and delete
-                       dot = yank_delete(p, q, 0, yf); // delete word
-               } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
-                       // multiple line copy text into a register and delete
-                       dot = yank_delete(p, q, 1, yf); // delete lines
+                       dot = yank_delete(p, q, ml, yf);        // delete word
+               } else if (strchr("^0bBeEft%$ lh\b\177", c1)) {
+                       // partial line copy text into a register and delete
+                       dot = yank_delete(p, q, ml, yf);        // delete word
+               } else if (strchr("cdykjHL+-{}\r\n", c1)) {
+                       // whole line copy text into a register and delete
+                       dot = yank_delete(p, q, ml, yf);        // delete lines
+                       whole = 1;
+               } else {
+                       // could not recognize object
+                       c = c1 = 27;    // error-
+                       ml = 0;
+                       indicate_error(c);
+               }
+               if (ml && whole) {
                        if (c == 'c') {
                                dot = char_insert(dot, '\n');
                                // on the last line of file don't move to prev line
-                               if (dot != (end-1)) {
+                               if (whole && dot != (end-1)) {
                                        dot_prev();
                                }
                        } else if (c == 'd') {
                                dot_begin();
                                dot_skip_over_ws();
                        }
-               } else {
-                       // could not recognize object
-                       c = c1 = 27;    // error-
-                       indicate_error(c);
                }
                if (c1 != 27) {
                        // if CHANGING, not deleting, start inserting after the delete
@@ -3658,11 +3766,12 @@ static void do_cmd(char c)
                                if (*p == '\n')
                                        cnt++;
                        }
-                       psb("%s %d lines (%d chars) using [%c]",
+                       status_line("%s %d lines (%d chars) using [%c]",
                                buf, cnt, strlen(reg[YDreg]), what_reg());
 #endif
                        end_cmd_q();    // stop adding to q
                }
+               }
                break;
        case 'k':                       // k- goto prev line, same col
        case VI_K_UP:           // cursor key Up
@@ -3676,7 +3785,7 @@ static void do_cmd(char c)
                c1 = get_one_char();    // get the replacement char
                if (*dot != '\n') {
                        *dot = c1;
-                       file_modified++;        // has the file been modified
+                       file_modified++;
                }
                end_cmd_q();    // stop adding to q
                break;
@@ -3721,10 +3830,10 @@ static void do_cmd(char c)
                }                               // repeat cnt
                if (islower(*dot)) {
                        *dot = toupper(*dot);
-                       file_modified++;        // has the file been modified
+                       file_modified++;
                } else if (isupper(*dot)) {
                        *dot = tolower(*dot);
-                       file_modified++;        // has the file been modified
+                       file_modified++;
                }
                dot_right();
                end_cmd_q();    // stop adding to q
@@ -3771,6 +3880,7 @@ static void do_cmd(char c)
                dot--;
 }
 
+/* NB!  the CRASHME code is unmaintained, and doesn't currently build */
 #if ENABLE_FEATURE_VI_CRASHME
 static int totalcmds = 0;
 static int Mp = 85;             // Movement command Probability
@@ -3780,15 +3890,15 @@ static int Ip = 97;             // Insert command Probability
 static int Yp = 98;             // Yank command Probability
 static int Pp = 99;             // Put command Probability
 static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
-const char chars[20] = "\t012345 abcdABCD-=.$";
-const char *const words[20] = {
+static const char chars[20] = "\t012345 abcdABCD-=.$";
+static const char *const words[20] = {
        "this", "is", "a", "test",
        "broadcast", "the", "emergency", "of",
        "system", "quick", "brown", "fox",
        "jumped", "over", "lazy", "dogs",
        "back", "January", "Febuary", "March"
 };
-const char *const lines[20] = {
+static const char *const lines[20] = {
        "You should have received a copy of the GNU General Public License\n",
        "char c, cm, *cmd, *cmd1;\n",
        "generate a command by percentages\n",
@@ -3810,7 +3920,7 @@ const char *const lines[20] = {
        "The last command will be automatically run.\n",
        "This is too much english for a computer geek.\n",
 };
-char *multilines[20] = {
+static char *multilines[20] = {
        "You should have received a copy of the GNU General Public License\n",
        "char c, cm, *cmd, *cmd1;\n",
        "generate a command by percentages\n",
@@ -3844,12 +3954,12 @@ static void crash_dummy()
        cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
 
        // is there already a command running?
-       if (readed_for_parse > 0)
+       if (chars_to_parse > 0)
                goto cd1;
  cd0:
        startrbi = rbi = 0;
        sleeptime = 0;          // how long to pause between commands
-       memset(readbuffer, '\0', MAX_LINELEN);   // clear the read buffer
+       memset(readbuffer, '\0', sizeof(readbuffer));
        // generate a command by percentages
        percent = (int) lrand48() % 100;        // get a number from 0-99
        if (percent < Mp) {     //  Movement commands
@@ -3921,7 +4031,7 @@ static void crash_dummy()
                }
                strcat(readbuffer, "\033");
        }
-       readed_for_parse = strlen(readbuffer);
+       chars_to_parse = strlen(readbuffer);
  cd1:
        totalcmds++;
        if (sleeptime > 0)
@@ -3934,7 +4044,7 @@ static void crash_test()
        static time_t oldtim;
 
        time_t tim;
-       char d[2], msg[MAX_LINELEN];
+       char d[2], msg[80];
 
        msg[0] = '\0';
        if (end < text) {
@@ -3957,17 +4067,15 @@ static void crash_test()
        }
 
        if (msg[0]) {
-               alarm(0);
                printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
                        totalcmds, last_input_char, msg, SOs, SOn);
                fflush(stdout);
-               while (read(0, d, 1) > 0) {
+               while (safe_read(STDIN_FILENO, d, 1) > 0) {
                        if (d[0] == '\n' || d[0] == '\r')
                                break;
                }
-               alarm(3);
        }
-       tim = (time_t) time((time_t *) 0);
+       tim = time(NULL);
        if (tim >= (oldtim + 3)) {
                sprintf(status_buffer,
                                "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",