Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/editors/vi.c


Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (13 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.9/mindi-busybox/editors/vi.c

    r1765 r2725  
    44 * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
    55 *
    6  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
     6 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    77 */
    88
     
    2424#include "libbb.h"
    2525
     26/* the CRASHME code is unmaintained, and doesn't currently build */
    2627#define ENABLE_FEATURE_VI_CRASHME 0
    2728
     29
    2830#if ENABLE_LOCALE_SUPPORT
    29 #define Isprint(c) isprint((c))
     31
     32#if ENABLE_FEATURE_VI_8BIT
     33//FIXME: this does not work properly for Unicode anyway
     34# define Isprint(c) (isprint)(c)
    3035#else
     36# define Isprint(c) isprint_asciionly(c)
     37#endif
     38
     39#else
     40
    3141/* 0x9b is Meta-ESC */
     42#if ENABLE_FEATURE_VI_8BIT
    3243#define Isprint(c) ((unsigned char)(c) >= ' ' && (c) != 0x7f && (unsigned char)(c) != 0x9b)
    33 #endif
     44#else
     45#define Isprint(c) ((unsigned char)(c) >= ' ' && (unsigned char)(c) < 0x7f)
     46#endif
     47
     48#endif
     49
    3450
    3551enum {
    36     MAX_LINELEN = CONFIG_FEATURE_VI_MAX_LEN,
     52    MAX_TABSTOP = 32, // sanity limit
     53    // User input len. Need not be extra big.
     54    // Lines in file being edited *can* be bigger than this.
     55    MAX_INPUT_LEN = 128,
     56    // Sanity limits. We have only one buffer of this size.
    3757    MAX_SCR_COLS = CONFIG_FEATURE_VI_MAX_LEN,
     58    MAX_SCR_ROWS = CONFIG_FEATURE_VI_MAX_LEN,
    3859};
    39 
    40 // Misc. non-Ascii keys that report an escape sequence
    41 #define VI_K_UP         (char)128   // cursor key Up
    42 #define VI_K_DOWN       (char)129   // cursor key Down
    43 #define VI_K_RIGHT      (char)130   // Cursor Key Right
    44 #define VI_K_LEFT       (char)131   // cursor key Left
    45 #define VI_K_HOME       (char)132   // Cursor Key Home
    46 #define VI_K_END        (char)133   // Cursor Key End
    47 #define VI_K_INSERT     (char)134   // Cursor Key Insert
    48 #define VI_K_PAGEUP     (char)135   // Cursor Key Page Up
    49 #define VI_K_PAGEDOWN       (char)136   // Cursor Key Page Down
    50 #define VI_K_FUN1       (char)137   // Function Key F1
    51 #define VI_K_FUN2       (char)138   // Function Key F2
    52 #define VI_K_FUN3       (char)139   // Function Key F3
    53 #define VI_K_FUN4       (char)140   // Function Key F4
    54 #define VI_K_FUN5       (char)141   // Function Key F5
    55 #define VI_K_FUN6       (char)142   // Function Key F6
    56 #define VI_K_FUN7       (char)143   // Function Key F7
    57 #define VI_K_FUN8       (char)144   // Function Key F8
    58 #define VI_K_FUN9       (char)145   // Function Key F9
    59 #define VI_K_FUN10      (char)146   // Function Key F10
    60 #define VI_K_FUN11      (char)147   // Function Key F11
    61 #define VI_K_FUN12      (char)148   // Function Key F12
    6260
    6361/* vt102 typical ESC sequence */
    6462/* terminal standout start/normal ESC sequence */
    65 static const char SOs[] ALIGN1 = "\033[7m";
    66 static const char SOn[] ALIGN1 = "\033[0m";
     63#define SOs "\033[7m"
     64#define SOn "\033[0m"
    6765/* terminal bell sequence */
    68 static const char bell[] ALIGN1 = "\007";
     66#define bell "\007"
    6967/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
    70 static const char Ceol[] ALIGN1 = "\033[0K";
    71 static const char Ceos[] ALIGN1 = "\033[0J";
     68#define Ceol "\033[K"
     69#define Ceos "\033[J"
    7270/* Cursor motion arbitrary destination ESC sequence */
    73 static const char CMrc[] ALIGN1 = "\033[%d;%dH";
     71#define CMrc "\033[%u;%uH"
    7472/* Cursor motion up and down ESC sequence */
    75 static const char CMup[] ALIGN1 = "\033[A";
    76 static const char CMdown[] ALIGN1 = "\n";
    77 
     73#define CMup "\033[A"
     74#define CMdown "\n"
     75
     76#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
     77// cmds modifying text[]
     78// vda: removed "aAiIs" as they switch us into insert mode
     79// and remembering input for replay after them makes no sense
     80static const char modifying_cmds[] = "cCdDJoOpPrRxX<>~";
     81#endif
    7882
    7983enum {
     
    9296};
    9397
     98
    9499/* vi.c expects chars to be unsigned. */
    95100/* busybox build system provides that, but it's better */
    96101/* to audit and fix the source */
    97102
    98 static smallint vi_setops;
     103struct globals {
     104    /* many references - keep near the top of globals */
     105    char *text, *end;       // pointers to the user data in memory
     106    char *dot;              // where all the action takes place
     107    int text_size;      // size of the allocated buffer
     108
     109    /* the rest */
     110    smallint vi_setops;
    99111#define VI_AUTOINDENT 1
    100112#define VI_SHOWMATCH  2
     
    107119#define err_method (vi_setops & VI_ERR_METHOD)
    108120
    109 
    110 static smallint editing;        // >0 while we are editing a file
    111                                 // [code audit says "can be 0 or 1 only"]
    112 static smallint cmd_mode;       // 0=command  1=insert 2=replace
    113 static smallint file_modified;  // buffer contents changed
    114 static smallint last_file_modified = -1;
    115 static int fn_start;            // index of first cmd line file name
    116 static int save_argc;           // how many file names on cmd line
    117 static int cmdcnt;              // repetition count
    118 static int rows, columns;       // the terminal screen is this size
    119 static int crow, ccol, offset;  // cursor is on Crow x Ccol with Horz Ofset
    120 static char *status_buffer;     // mesages to the user
    121 #define STATUS_BUFFER_LEN  200
    122 static int have_status_msg;     // is default edit status needed?
    123                                 // [don't make smallint!]
    124 static int last_status_cksum;   // hash of current status line
    125 static char *current_filename;               // current file name
    126 //static char *text, *end;        // pointers to the user data in memory
    127 static char *screen;            // pointer to the virtual screen buffer
    128 static int screensize;          //            and its size
    129 static char *screenbegin;       // index into text[], of top line on the screen
    130 //static char *dot;               // where all the action takes place
    131 static int tabstop;
    132 static char erase_char;         // the users erase character
    133 static char last_input_char;    // last char read from user
    134 static char last_forward_char;  // last char searched for with 'f'
    135 
    136121#if ENABLE_FEATURE_VI_READONLY
    137 //static smallint vi_readonly, readonly;
    138 static smallint readonly_mode = 0;
     122    smallint readonly_mode;
    139123#define SET_READONLY_FILE(flags)        ((flags) |= 0x01)
    140124#define SET_READONLY_MODE(flags)        ((flags) |= 0x02)
    141125#define UNSET_READONLY_FILE(flags)      ((flags) &= 0xfe)
    142126#else
    143 #define readonly_mode 0
    144 #define SET_READONLY_FILE(flags)
    145 #define SET_READONLY_MODE(flags)
    146 #define UNSET_READONLY_FILE(flags)
    147 #endif
     127#define SET_READONLY_FILE(flags)        ((void)0)
     128#define SET_READONLY_MODE(flags)        ((void)0)
     129#define UNSET_READONLY_FILE(flags)      ((void)0)
     130#endif
     131
     132    smallint editing;        // >0 while we are editing a file
     133                             // [code audit says "can be 0, 1 or 2 only"]
     134    smallint cmd_mode;       // 0=command  1=insert 2=replace
     135    int file_modified;       // buffer contents changed (counter, not flag!)
     136    int last_file_modified;  // = -1;
     137    int fn_start;            // index of first cmd line file name
     138    int save_argc;           // how many file names on cmd line
     139    int cmdcnt;              // repetition count
     140    unsigned rows, columns;  // the terminal screen is this size
     141#if ENABLE_FEATURE_VI_ASK_TERMINAL
     142    int get_rowcol_error;
     143#endif
     144    int crow, ccol;          // cursor is on Crow x Ccol
     145    int offset;              // chars scrolled off the screen to the left
     146    int have_status_msg;     // is default edit status needed?
     147                             // [don't make smallint!]
     148    int last_status_cksum;   // hash of current status line
     149    char *current_filename;
     150    char *screenbegin;       // index into text[], of top line on the screen
     151    char *screen;            // pointer to the virtual screen buffer
     152    int screensize;          //            and its size
     153    int tabstop;
     154    int last_forward_char;   // last char searched for with 'f' (int because of Unicode)
     155    char erase_char;         // the users erase character
     156    char last_input_char;    // last char read from user
    148157
    149158#if ENABLE_FEATURE_VI_DOT_CMD
    150 static smallint adding2q;       // are we currently adding user input to q
    151 static char *last_modifying_cmd;    // last modifying cmd for "."
    152 static char *ioq, *ioq_start;           // pointer to string for get_one_char to "read"
     159    smallint adding2q;   // are we currently adding user input to q
     160    int lmc_len;             // length of last_modifying_cmd
     161    char *ioq, *ioq_start;   // pointer to string for get_one_char to "read"
    153162#endif
    154163#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
    155 static int last_row;        // where the cursor was last moved to
     164    int last_row;        // where the cursor was last moved to
    156165#endif
    157166#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
    158 static int my_pid;
    159 #endif
    160 #if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
    161 static char *modifying_cmds;            // cmds that modify text[]
     167    int my_pid;
    162168#endif
    163169#if ENABLE_FEATURE_VI_SEARCH
    164 static char *last_search_pattern;   // last pattern from a '/' or '?' search
    165 #endif
    166 
    167 /* Moving biggest data to malloced space... */
    168 struct globals {
    169     /* many references - keep near the top of globals */
    170     char *text, *end;       // pointers to the user data in memory
    171     int text_size;      // size of the allocated buffer
    172     char *dot;              // where all the action takes place
     170    char *last_search_pattern; // last pattern from a '/' or '?' search
     171#endif
     172
     173    /* former statics */
    173174#if ENABLE_FEATURE_VI_YANKMARK
     175    char *edit_file__cur_line;
     176#endif
     177    int refresh__old_offset;
     178    int format_edit_status__tot;
     179
     180    /* a few references only */
     181#if ENABLE_FEATURE_VI_YANKMARK
     182    int YDreg, Ureg;        // default delete register and orig line for "U"
    174183    char *reg[28];          // named register a-z, "D", and "U" 0-25,26,27
    175     int YDreg, Ureg;        // default delete register and orig line for "U"
    176184    char *mark[28];         // user marks points somewhere in text[]-  a-z and previous context ''
    177185    char *context_start, *context_end;
    178186#endif
    179     /* a few references only */
    180187#if ENABLE_FEATURE_VI_USE_SIGNALS
    181     jmp_buf restart;        // catch_sig()
    182 #endif
    183     struct termios term_orig, term_vi;  // remember what the cooked mode was
     188    sigjmp_buf restart;     // catch_sig()
     189#endif
     190    struct termios term_orig, term_vi; // remember what the cooked mode was
    184191#if ENABLE_FEATURE_VI_COLON
    185192    char *initial_cmds[3];  // currently 2 entries, NULL terminated
    186193#endif
     194    // Should be just enough to hold a key sequence,
     195    // but CRASHME mode uses it as generated command buffer too
     196#if ENABLE_FEATURE_VI_CRASHME
     197    char readbuffer[128];
     198#else
     199    char readbuffer[KEYCODE_BUFFER_SIZE];
     200#endif
     201#define STATUS_BUFFER_LEN  200
     202    char status_buffer[STATUS_BUFFER_LEN]; // messages to the user
     203#if ENABLE_FEATURE_VI_DOT_CMD
     204    char last_modifying_cmd[MAX_INPUT_LEN]; // last modifying cmd for "."
     205#endif
     206    char get_input_line__buf[MAX_INPUT_LEN]; /* former static */
     207
     208    char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2];
    187209};
    188210#define G (*ptr_to_globals)
     
    192214#define dot            (G.dot           )
    193215#define reg            (G.reg           )
     216
     217#define vi_setops               (G.vi_setops          )
     218#define editing                 (G.editing            )
     219#define cmd_mode                (G.cmd_mode           )
     220#define file_modified           (G.file_modified      )
     221#define last_file_modified      (G.last_file_modified )
     222#define fn_start                (G.fn_start           )
     223#define save_argc               (G.save_argc          )
     224#define cmdcnt                  (G.cmdcnt             )
     225#define rows                    (G.rows               )
     226#define columns                 (G.columns            )
     227#define crow                    (G.crow               )
     228#define ccol                    (G.ccol               )
     229#define offset                  (G.offset             )
     230#define status_buffer           (G.status_buffer      )
     231#define have_status_msg         (G.have_status_msg    )
     232#define last_status_cksum       (G.last_status_cksum  )
     233#define current_filename        (G.current_filename   )
     234#define screen                  (G.screen             )
     235#define screensize              (G.screensize         )
     236#define screenbegin             (G.screenbegin        )
     237#define tabstop                 (G.tabstop            )
     238#define last_forward_char       (G.last_forward_char  )
     239#define erase_char              (G.erase_char         )
     240#define last_input_char         (G.last_input_char    )
     241#if ENABLE_FEATURE_VI_READONLY
     242#define readonly_mode           (G.readonly_mode      )
     243#else
     244#define readonly_mode           0
     245#endif
     246#define adding2q                (G.adding2q           )
     247#define lmc_len                 (G.lmc_len            )
     248#define ioq                     (G.ioq                )
     249#define ioq_start               (G.ioq_start          )
     250#define last_row                (G.last_row           )
     251#define my_pid                  (G.my_pid             )
     252#define last_search_pattern     (G.last_search_pattern)
     253
     254#define edit_file__cur_line     (G.edit_file__cur_line)
     255#define refresh__old_offset     (G.refresh__old_offset)
     256#define format_edit_status__tot (G.format_edit_status__tot)
     257
    194258#define YDreg          (G.YDreg         )
    195259#define Ureg           (G.Ureg          )
     
    201265#define term_vi        (G.term_vi       )
    202266#define initial_cmds   (G.initial_cmds  )
     267#define readbuffer     (G.readbuffer    )
     268#define scr_out_buf    (G.scr_out_buf   )
     269#define last_modifying_cmd  (G.last_modifying_cmd )
     270#define get_input_line__buf (G.get_input_line__buf)
     271
     272#define INIT_G() do { \
     273    SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
     274    last_file_modified = -1; \
     275    /* "" but has space for 2 chars: */ \
     276    IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \
     277} while (0)
     278
    203279
    204280static int init_text_buffer(char *); // init from file or create new
    205281static void edit_file(char *);  // edit one file
    206 static void do_cmd(char);   // execute a command
     282static void do_cmd(int);    // execute a command
    207283static int next_tabstop(int);
    208284static void sync_cursor(char *, int *, int *);  // synchronize the screen cursor to dot
     
    227303static char *new_screen(int, int);  // malloc virtual screen memory
    228304static char *char_insert(char *, char); // insert the char c at 'p'
    229 static char *stupid_insert(char *, char);   // stupidly insert the char c at 'p'
    230 static char find_range(char **, char **, char); // return pointers for an object
     305// might reallocate text[]! use p += stupid_insert(p, ...),
     306// and be careful to not use pointers into potentially freed text[]!
     307static uintptr_t stupid_insert(char *, char);   // stupidly insert the char c at 'p'
     308static int find_range(char **, char **, char);  // return pointers for an object
    231309static int st_test(char *, int, int, char *);   // helper for skip_thing()
    232310static char *skip_thing(char *, int, int, int); // skip some object
    233311static char *find_pair(char *, char);   // find matching pair ()  []  {}
    234312static char *text_hole_delete(char *, char *);  // at "p", delete a 'size' byte hole
    235 static char *text_hole_make(char *, int);   // at "p", make a 'size' byte hole
     313// might reallocate text[]! use p += text_hole_make(p, ...),
     314// and be careful to not use pointers into potentially freed text[]!
     315static uintptr_t text_hole_make(char *, int);   // at "p", make a 'size' byte hole
    236316static char *yank_delete(char *, char *, int, int); // yank text[] into register then delete
    237317static void show_help(void);    // display some help info
    238318static void rawmode(void);  // set "raw" mode on tty
    239319static void cookmode(void); // return to "cooked" mode on tty
    240 static int mysleep(int);    // sleep for 'h' 1/100 seconds
    241 static char readit(void);   // read (maybe cursor) key from stdin
    242 static char get_one_char(void); // read 1 char from stdin
     320// sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready)
     321static int mysleep(int);
     322static int readit(void);    // read (maybe cursor) key from stdin
     323static int get_one_char(void);  // read 1 char from stdin
    243324static int file_size(const char *);   // what is the byte size of "fn"
    244 #if ENABLE_FEATURE_VI_READONLY
     325#if !ENABLE_FEATURE_VI_READONLY
     326#define file_insert(fn, p, update_ro_status) file_insert(fn, p)
     327#endif
     328// file_insert might reallocate text[]!
    245329static int file_insert(const char *, char *, int);
    246 #else
    247 static int file_insert(const char *, char *);
    248 #endif
    249330static int file_write(char *, char *, char *);
     331#if !ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
     332#define place_cursor(a, b, optimize) place_cursor(a, b)
     333#endif
    250334static void place_cursor(int, int, int);
    251335static void screen_erase(void);
    252336static void clear_to_eol(void);
    253337static void clear_to_eos(void);
     338static void go_bottom_and_clear_to_eol(void);
    254339static void standout_start(void);   // send "start reverse video" sequence
    255340static void standout_end(void); // send "end reverse video" sequence
    256341static void flash(int);     // flash the terminal screen
    257342static void show_status_line(void); // put a message on the bottom line
    258 static void psb(const char *, ...);     // Print Status Buf
    259 static void psbs(const char *, ...);    // Print Status Buf in standout mode
    260 static void ni(const char *);       // display messages
     343static void status_line(const char *, ...);     // print to status buf
     344static void status_line_bold(const char *, ...);
     345static void not_implemented(const char *); // display "Not implemented" message
    261346static int format_edit_status(void);    // format file status on status line
    262347static void redraw(int);    // force a full screen refresh
    263 static void format_line(char*, char*, int);
     348static char* format_line(char* /*, int*/);
    264349static void refresh(int);   // update the terminal from screen[]
    265350
     
    292377#endif
    293378#if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME
    294 static char *string_insert(char *, char *); // insert the string at 'p'
     379// might reallocate text[]! use p += string_insert(p, ...),
     380// and be careful to not use pointers into potentially freed text[]!
     381static uintptr_t string_insert(char *, const char *);   // insert the string at 'p'
    295382#endif
    296383#if ENABLE_FEATURE_VI_YANKMARK
     
    311398}
    312399
    313 int vi_main(int argc, char **argv);
     400int vi_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
    314401int vi_main(int argc, char **argv)
    315402{
    316403    int c;
    317     RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN);
     404
     405    INIT_G();
    318406
    319407#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
    320408    my_pid = getpid();
    321409#endif
    322 
    323     PTR_TO_GLOBALS = xzalloc(sizeof(G));
    324 
    325410#if ENABLE_FEATURE_VI_CRASHME
    326411    srand((long) my_pid);
    327412#endif
    328 
    329     status_buffer = STATUS_BUFFER;
    330     last_status_cksum = 0;
    331     text = NULL;
    332 
    333413#ifdef NO_SUCH_APPLET_YET
    334414    /* If we aren't "vi", we are "view" */
     
    338418#endif
    339419
    340     vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
    341 #if ENABLE_FEATURE_VI_YANKMARK
    342     memset(reg, 0, sizeof(reg)); // init the yank regs
    343 #endif
    344 #if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
    345     modifying_cmds = (char *) "aAcCdDiIJoOpPrRsxX<>~";  // cmds modifying text[]
    346 #endif
    347 
     420    vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE;
    348421    //  1-  process $HOME/.exrc file (not inplemented yet)
    349422    //  2-  process EXINIT variable from environment
     
    353426        char *p = getenv("EXINIT");
    354427        if (p && *p)
    355             initial_cmds[0] = xstrdup(p);
    356     }
    357 #endif
    358     while ((c = getopt(argc, argv, "hCR" USE_FEATURE_VI_COLON("c:"))) != -1) {
     428            initial_cmds[0] = xstrndup(p, MAX_INPUT_LEN);
     429    }
     430#endif
     431    while ((c = getopt(argc, argv, "hCRH" IF_FEATURE_VI_COLON("c:"))) != -1) {
    359432        switch (c) {
    360433#if ENABLE_FEATURE_VI_CRASHME
     
    368441            break;
    369442#endif
    370             //case 'r': // recover flag-  ignore- we don't use tmp file
    371             //case 'x': // encryption flag- ignore
    372             //case 'c': // execute command first
    373443#if ENABLE_FEATURE_VI_COLON
    374444        case 'c':       // cmd line vi command
    375445            if (*optarg)
    376                 initial_cmds[initial_cmds[0] != 0] = xstrdup(optarg);
     446                initial_cmds[initial_cmds[0] != 0] = xstrndup(optarg, MAX_INPUT_LEN);
    377447            break;
    378             //case 'h': // help -- just use default
    379 #endif
     448#endif
     449        case 'H':
     450            show_help();
     451            /* fall through */
    380452        default:
    381             show_help();
     453            bb_show_usage();
    382454            return 1;
    383455        }
     
    390462
    391463    //----- This is the main file handling loop --------------
    392     if (optind >= argc) {
    393         edit_file(0);
    394     } else {
    395         for (; optind < argc; optind++) {
    396             edit_file(argv[optind]);
    397         }
     464    while (1) {
     465        edit_file(argv[optind]); /* param might be NULL */
     466        if (++optind >= argc)
     467            break;
    398468    }
    399469    //-----------------------------------------------------------
     
    411481    /* allocate/reallocate text buffer */
    412482    free(text);
    413     text_size = size * 2;
    414     if (text_size < 10240)
    415         text_size = 10240;  // have a minimum size for new files
     483    text_size = size + 10240;
    416484    screenbegin = dot = end = text = xzalloc(text_size);
    417485
     
    425493        rc = 0;
    426494    } else {
    427         rc = file_insert(fn, text
    428             USE_FEATURE_VI_READONLY(, 1));
     495        rc = file_insert(fn, text, 1);
    429496    }
    430497    file_modified = 0;
     
    437504}
    438505
    439 static void edit_file(char * fn)
    440 {
    441     char c;
     506#if ENABLE_FEATURE_VI_WIN_RESIZE
     507static int query_screen_dimensions(void)
     508{
     509    int err = get_terminal_width_height(STDIN_FILENO, &columns, &rows);
     510    if (rows > MAX_SCR_ROWS)
     511        rows = MAX_SCR_ROWS;
     512    if (columns > MAX_SCR_COLS)
     513        columns = MAX_SCR_COLS;
     514    return err;
     515}
     516#else
     517# define query_screen_dimensions() (0)
     518#endif
     519
     520static void edit_file(char *fn)
     521{
     522#if ENABLE_FEATURE_VI_YANKMARK
     523#define cur_line edit_file__cur_line
     524#endif
     525    int c;
    442526    int size;
    443 
    444527#if ENABLE_FEATURE_VI_USE_SIGNALS
    445528    int sig;
    446529#endif
    447 #if ENABLE_FEATURE_VI_YANKMARK
    448     static char *cur_line;
    449 #endif
    450 
    451     editing = 1;    // 0= exit,  1= one file, 2= multiple files
     530
     531    editing = 1;    // 0 = exit, 1 = one file, 2 = multiple files
    452532    rawmode();
    453533    rows = 24;
    454534    columns = 80;
    455535    size = 0;
    456     if (ENABLE_FEATURE_VI_WIN_RESIZE)
    457         get_terminal_width_height(0, &columns, &rows);
     536    IF_FEATURE_VI_ASK_TERMINAL(G.get_rowcol_error =) query_screen_dimensions();
     537#if ENABLE_FEATURE_VI_ASK_TERMINAL
     538    if (G.get_rowcol_error /* TODO? && no input on stdin */) {
     539        uint64_t k;
     540        write1("\033[999;999H" "\033[6n");
     541        fflush_all();
     542        k = read_key(STDIN_FILENO, readbuffer, /*timeout_ms:*/ 100);
     543        if ((int32_t)k == KEYCODE_CURSOR_POS) {
     544            uint32_t rc = (k >> 32);
     545            columns = (rc & 0x7fff);
     546            if (columns > MAX_SCR_COLS)
     547                columns = MAX_SCR_COLS;
     548            rows = ((rc >> 16) & 0x7fff);
     549            if (rows > MAX_SCR_ROWS)
     550                rows = MAX_SCR_ROWS;
     551        }
     552    }
     553#endif
    458554    new_screen(rows, columns);  // get memory for virtual screen
    459555    init_text_buffer(fn);
     
    470566
    471567#if ENABLE_FEATURE_VI_USE_SIGNALS
    472     catch_sig(0);
     568    signal(SIGINT, catch_sig);
    473569    signal(SIGWINCH, winch_sig);
    474570    signal(SIGTSTP, suspend_sig);
    475     sig = setjmp(restart);
     571    sig = sigsetjmp(restart, 1);
    476572    if (sig != 0) {
    477573        screenbegin = dot = text;
     
    485581    c = '\0';
    486582#if ENABLE_FEATURE_VI_DOT_CMD
    487     free(last_modifying_cmd);
    488583    free(ioq_start);
    489     ioq = ioq_start = last_modifying_cmd = NULL;
     584    ioq = ioq_start = NULL;
     585    lmc_len = 0;
    490586    adding2q = 0;
    491587#endif
    492     redraw(FALSE);          // dont force every col re-draw
    493588
    494589#if ENABLE_FEATURE_VI_COLON
     
    497592        int n = 0;
    498593
    499         while ((p = initial_cmds[n])) {
     594        while ((p = initial_cmds[n]) != NULL) {
    500595            do {
    501596                q = p;
    502                 p = strchr(q,'\n');
     597                p = strchr(q, '\n');
    503598                if (p)
    504599                    while (*p == '\n')
     
    513608    }
    514609#endif
     610    redraw(FALSE);          // dont force every col re-draw
    515611    //------This is the main Vi cmd handling loop -----------------------
    516612    while (editing > 0) {
     
    521617            } else {
    522618                crashme = 0;
    523                 dot = string_insert(text, "\n\n#####  Ran out of text to work on.  #####\n\n"); // insert the string
     619                string_insert(text, "\n\n#####  Ran out of text to work on.  #####\n\n"); // insert the string
     620                dot = text;
    524621                refresh(FALSE);
    525622            }
     
    537634        // These are commands that change text[].
    538635        // Remember the input for the "." command
    539         if (!adding2q && ioq_start == 0
     636        if (!adding2q && ioq_start == NULL
     637         && cmd_mode == 0 // command mode
     638         && c > '\0' // exclude NUL and non-ASCII chars
     639         && c < 0x7f // (Unicode and such)
    540640         && strchr(modifying_cmds, c)
    541641        ) {
     
    544644#endif
    545645        do_cmd(c);      // execute the user command
    546         //
     646
    547647        // poll to see if there is input already waiting. if we are
    548648        // not able to display output fast enough to keep up, skip
    549649        // the display update until we catch up with input.
    550         if (mysleep(0) == 0) {
    551             // no input pending- so update output
     650        if (!readbuffer[0] && mysleep(0) == 0) {
     651            // no input pending - so update output
    552652            refresh(FALSE);
    553653            show_status_line();
     
    560660    //-------------------------------------------------------------------
    561661
    562     place_cursor(rows, 0, FALSE);   // go to bottom of screen
    563     clear_to_eol();     // Erase to end of line
     662    go_bottom_and_clear_to_eol();
    564663    cookmode();
     664#undef cur_line
    565665}
    566666
    567667//----- The Colon commands -------------------------------------
    568668#if ENABLE_FEATURE_VI_COLON
    569 static char *get_one_address(char * p, int *addr)   // get colon addr, if present
     669static char *get_one_address(char *p, int *addr)    // get colon addr, if present
    570670{
    571671    int st;
    572672    char *q;
    573 
    574 #if ENABLE_FEATURE_VI_YANKMARK
    575     char c;
    576 #endif
    577 #if ENABLE_FEATURE_VI_SEARCH
    578     char *pat, buf[MAX_LINELEN];
    579 #endif
     673    IF_FEATURE_VI_YANKMARK(char c;)
     674    IF_FEATURE_VI_SEARCH(char *pat;)
    580675
    581676    *addr = -1;         // assume no addr
     
    584679        q = begin_line(dot);
    585680        *addr = count_lines(text, q);
     681    }
    586682#if ENABLE_FEATURE_VI_YANKMARK
    587     } else if (*p == '\'') {    // is this a mark addr
     683    else if (*p == '\'') {  // is this a mark addr
    588684        p++;
    589685        c = tolower(*p);
     
    594690            q = mark[(unsigned char) c];
    595691            if (q != NULL) {    // is mark valid
    596                 *addr = count_lines(text, q);   // count lines
     692                *addr = count_lines(text, q);
    597693            }
    598694        }
     695    }
    599696#endif
    600697#if ENABLE_FEATURE_VI_SEARCH
    601     } else if (*p == '/') { // a search pattern
    602         q = buf;
    603         for (p++; *p; p++) {
    604             if (*p == '/')
    605                 break;
    606             *q++ = *p;
    607             *q = '\0';
    608         }
    609         pat = xstrdup(buf); // save copy of pattern
     698    else if (*p == '/') {   // a search pattern
     699        q = strchrnul(++p, '/');
     700        pat = xstrndup(p, q - p); // save copy of pattern
     701        p = q;
    610702        if (*p == '/')
    611703            p++;
     
    615707        }
    616708        free(pat);
    617 #endif
    618     } else if (*p == '$') { // the last line in file
     709    }
     710#endif
     711    else if (*p == '$') {   // the last line in file
    619712        p++;
    620713        q = begin_line(end - 1);
     
    623716        sscanf(p, "%d%n", addr, &st);
    624717        p += st;
    625     } else {            // I don't reconise this
    626         // unrecognised address- assume -1
     718    } else {
     719        // unrecognized address - assume -1
    627720        *addr = -1;
    628721    }
     
    665758    int l = strlen(opname) - 1; /* opname have + ' ' */
    666759
     760    // maybe strncmp? we had tons of erroneous strncasecmp's...
    667761    if (strncasecmp(a, opname, l) == 0
    668762     || strncasecmp(a, short_opname, 2) == 0
     
    676770#endif
    677771
    678 static void colon(char * buf)
     772// buf must be no longer than MAX_INPUT_LEN!
     773static void colon(char *buf)
    679774{
    680775    char c, *orig_buf, *buf1, *q, *r;
    681     char *fn, cmd[MAX_LINELEN], args[MAX_LINELEN];
     776    char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN];
    682777    int i, l, li, ch, b, e;
    683     int useforce = FALSE, forced = FALSE;
     778    int useforce, forced = FALSE;
    684779
    685780    // :3154    // if (-e line 3154) goto it  else stay put
     
    699794
    700795    if (!buf[0])
    701         goto vc1;
     796        goto ret;
    702797    if (*buf == ':')
    703798        buf++;          // move past the ':'
     
    708803    r = end - 1;
    709804    li = count_lines(text, end - 1);
    710     fn = current_filename;          // default to current file
    711     memset(cmd, '\0', MAX_LINELEN); // clear cmd[]
    712     memset(args, '\0', MAX_LINELEN);    // clear args[]
     805    fn = current_filename;
    713806
    714807    // look for optional address(es)  :.  :1  :1,9   :'q,'a   :%
     
    725818        *buf1++ = *buf++;
    726819    }
     820    *buf1 = '\0';
    727821    // get any ARGuments
    728822    while (isblank(*buf))
    729823        buf++;
    730824    strcpy(args, buf);
     825    useforce = FALSE;
    731826    buf1 = last_char_is(cmd, '!');
    732827    if (buf1) {
     
    759854    }
    760855#if ENABLE_FEATURE_ALLOW_EXEC
    761     else if (strncmp(cmd, "!", 1) == 0) {   // run a cmd
     856    else if (cmd[0] == '!') {   // run a cmd
    762857        int retcode;
    763858        // :!ls   run the <cmd>
    764         alarm(0);       // wait for input- no alarms
    765         place_cursor(rows - 1, 0, FALSE);   // go to Status line
    766         clear_to_eol();         // clear the line
     859        go_bottom_and_clear_to_eol();
    767860        cookmode();
    768861        retcode = system(orig_buf + 1); // run the cmd
     
    771864        rawmode();
    772865        Hit_Return();           // let user see results
    773         alarm(3);       // done waiting for input
    774     }
    775 #endif
    776     else if (strncmp(cmd, "=", i) == 0) {   // where is the address
     866    }
     867#endif
     868    else if (cmd[0] == '=' && !cmd[1]) {    // where is the address
    777869        if (b < 0) {    // no addr given- use defaults
    778870            b = e = count_lines(text, dot);
    779871        }
    780         psb("%d", b);
    781     } else if (strncasecmp(cmd, "delete", i) == 0) {    // delete lines
     872        status_line("%d", b);
     873    } else if (strncmp(cmd, "delete", i) == 0) {    // delete lines
    782874        if (b < 0) {    // no addr given- use defaults
    783875            q = begin_line(dot);    // assume .,. for the range
     
    786878        dot = yank_delete(q, r, 1, YANKDEL);    // save, then delete lines
    787879        dot_skip_over_ws();
    788     } else if (strncasecmp(cmd, "edit", i) == 0) {  // Edit a file
     880    } else if (strncmp(cmd, "edit", i) == 0) {  // Edit a file
    789881        // don't edit, if the current file has been modified
    790         if (file_modified && ! useforce) {
    791             psbs("No write since last change (:edit! overrides)");
    792             goto vc1;
     882        if (file_modified && !useforce) {
     883            status_line_bold("No write since last change (:edit! overrides)");
     884            goto ret;
    793885        }
    794886        if (args[0]) {
     
    800892        } else {
    801893            // no user file name, no current name- punt
    802             psbs("No current filename");
    803             goto vc1;
     894            status_line_bold("No current filename");
     895            goto ret;
    804896        }
    805897
    806898        if (init_text_buffer(fn) < 0)
    807             goto vc1;
     899            goto ret;
    808900
    809901#if ENABLE_FEATURE_VI_YANKMARK
     
    819911        // how many lines in text[]?
    820912        li = count_lines(text, end - 1);
    821         psb("\"%s\"%s"
    822             USE_FEATURE_VI_READONLY("%s")
     913        status_line("\"%s\"%s"
     914            IF_FEATURE_VI_READONLY("%s")
    823915            " %dL, %dC", current_filename,
    824916            (file_size(fn) < 0 ? " [New file]" : ""),
    825             USE_FEATURE_VI_READONLY(
     917            IF_FEATURE_VI_READONLY(
    826918                ((readonly_mode) ? " [Readonly]" : ""),
    827919            )
    828920            li, ch);
    829     } else if (strncasecmp(cmd, "file", i) == 0) {  // what File is this
     921    } else if (strncmp(cmd, "file", i) == 0) {  // what File is this
    830922        if (b != -1 || e != -1) {
    831             ni("No address allowed on this command");
    832             goto vc1;
     923            status_line_bold("No address allowed on this command");
     924            goto ret;
    833925        }
    834926        if (args[0]) {
     
    840932            last_status_cksum = 0;  // force status update
    841933        }
    842     } else if (strncasecmp(cmd, "features", i) == 0) {  // what features are available
     934    } else if (strncmp(cmd, "features", i) == 0) {  // what features are available
    843935        // print out values of all features
    844         place_cursor(rows - 1, 0, FALSE);   // go to Status line, bottom of screen
    845         clear_to_eol(); // clear the line
     936        go_bottom_and_clear_to_eol();
    846937        cookmode();
    847938        show_help();
    848939        rawmode();
    849940        Hit_Return();
    850     } else if (strncasecmp(cmd, "list", i) == 0) {  // literal print line
     941    } else if (strncmp(cmd, "list", i) == 0) {  // literal print line
    851942        if (b < 0) {    // no addr given- use defaults
    852943            q = begin_line(dot);    // assume .,. for the range
    853944            r = end_line(dot);
    854945        }
    855         place_cursor(rows - 1, 0, FALSE);   // go to Status line, bottom of screen
    856         clear_to_eol(); // clear the line
     946        go_bottom_and_clear_to_eol();
    857947        puts("\r");
    858948        for (; q <= r; q++) {
     
    864954                c = '.';
    865955                standout_start();
    866                 }
     956            }
    867957            if (c == '\n') {
    868958                write1("$\r");
    869959            } else if (c < ' ' || c == 127) {
    870                 putchar('^');
     960                bb_putchar('^');
    871961                if (c == 127)
    872962                    c = '?';
     
    874964                    c += '@';
    875965            }
    876             putchar(c);
     966            bb_putchar(c);
    877967            if (c_is_no_print)
    878968                standout_end();
    879969        }
    880 #if ENABLE_FEATURE_VI_SET
    881  vc2:
    882 #endif
    883970        Hit_Return();
    884     } else if (strncasecmp(cmd, "quit", i) == 0 // Quit
    885             || strncasecmp(cmd, "next", i) == 0 // edit next file
     971    } else if (strncmp(cmd, "quit", i) == 0 // quit
     972            || strncmp(cmd, "next", i) == 0 // edit next file
    886973    ) {
     974        int n;
    887975        if (useforce) {
    888976            // force end of argv list
     
    891979            }
    892980            editing = 0;
    893             goto vc1;
     981            goto ret;
    894982        }
    895983        // don't exit if the file been modified
    896984        if (file_modified) {
    897             psbs("No write since last change (:%s! overrides)",
     985            status_line_bold("No write since last change (:%s! overrides)",
    898986                 (*cmd == 'q' ? "quit" : "next"));
    899             goto vc1;
     987            goto ret;
    900988        }
    901989        // are there other file to edit
    902         if (*cmd == 'q' && optind < save_argc - 1) {
    903             psbs("%d more file to edit", (save_argc - optind - 1));
    904             goto vc1;
    905         }
    906         if (*cmd == 'n' && optind >= save_argc - 1) {
    907             psbs("No more files to edit");
    908             goto vc1;
     990        n = save_argc - optind - 1;
     991        if (*cmd == 'q' && n > 0) {
     992            status_line_bold("%d more file(s) to edit", n);
     993            goto ret;
     994        }
     995        if (*cmd == 'n' && n <= 0) {
     996            status_line_bold("No more files to edit");
     997            goto ret;
    909998        }
    910999        editing = 0;
    911     } else if (strncasecmp(cmd, "read", i) == 0) {  // read file into text[]
     1000    } else if (strncmp(cmd, "read", i) == 0) {  // read file into text[]
    9121001        fn = args;
    9131002        if (!fn[0]) {
    914             psbs("No filename given");
    915             goto vc1;
     1003            status_line_bold("No filename given");
     1004            goto ret;
    9161005        }
    9171006        if (b < 0) {    // no addr given- use defaults
     
    9211010        if (b != 0)
    9221011            q = next_line(q);
    923         ch = file_insert(fn, q  USE_FEATURE_VI_READONLY(, 0));
     1012        { // dance around potentially-reallocated text[]
     1013            uintptr_t ofs = q - text;
     1014            ch = file_insert(fn, q, 0);
     1015            q = text + ofs;
     1016        }
    9241017        if (ch < 0)
    925             goto vc1;   // nothing was inserted
     1018            goto ret;   // nothing was inserted
    9261019        // how many lines in text[]?
    9271020        li = count_lines(q, q + ch - 1);
    928         psb("\"%s\""
    929             USE_FEATURE_VI_READONLY("%s")
     1021        status_line("\"%s\""
     1022            IF_FEATURE_VI_READONLY("%s")
    9301023            " %dL, %dC", fn,
    931             USE_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
     1024            IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
    9321025            li, ch);
    9331026        if (ch > 0) {
     
    9351028            if (q <= dot)
    9361029                dot += ch;
    937             file_modified++;
    938         }
    939     } else if (strncasecmp(cmd, "rewind", i) == 0) {    // rewind cmd line args
    940         if (file_modified && ! useforce) {
    941             psbs("No write since last change (:rewind! overrides)");
     1030            /*file_modified++; - done by file_insert */
     1031        }
     1032    } else if (strncmp(cmd, "rewind", i) == 0) {    // rewind cmd line args
     1033        if (file_modified && !useforce) {
     1034            status_line_bold("No write since last change (:rewind! overrides)");
    9421035        } else {
    9431036            // reset the filenames to edit
     
    9461039        }
    9471040#if ENABLE_FEATURE_VI_SET
    948     } else if (strncasecmp(cmd, "set", i) == 0) {   // set or clear features
     1041    } else if (strncmp(cmd, "set", i) == 0) {   // set or clear features
    9491042#if ENABLE_FEATURE_VI_SETOPTS
    9501043        char *argp;
     
    9541047        if (!args[0] || strcasecmp(args, "all") == 0) {
    9551048            // print out values of all options
    956             place_cursor(rows - 1, 0, FALSE);   // go to Status line, bottom of screen
    957             clear_to_eol(); // clear the line
    958             printf("----------------------------------------\r\n");
    9591049#if ENABLE_FEATURE_VI_SETOPTS
    960             if (!autoindent)
    961                 printf("no");
    962             printf("autoindent ");
    963             if (!err_method)
    964                 printf("no");
    965             printf("flash ");
    966             if (!ignorecase)
    967                 printf("no");
    968             printf("ignorecase ");
    969             if (!showmatch)
    970                 printf("no");
    971             printf("showmatch ");
    972             printf("tabstop=%d ", tabstop);
    973 #endif
    974             printf("\r\n");
    975             goto vc2;
     1050            status_line_bold(
     1051                "%sautoindent "
     1052                "%sflash "
     1053                "%signorecase "
     1054                "%sshowmatch "
     1055                "tabstop=%u",
     1056                autoindent ? "" : "no",
     1057                err_method ? "" : "no",
     1058                ignorecase ? "" : "no",
     1059                showmatch ? "" : "no",
     1060                tabstop
     1061            );
     1062#endif
     1063            goto ret;
    9761064        }
    9771065#if ENABLE_FEATURE_VI_SETOPTS
    9781066        argp = args;
    9791067        while (*argp) {
    980             if (strncasecmp(argp, "no", 2) == 0)
     1068            if (strncmp(argp, "no", 2) == 0)
    9811069                i = 2;      // ":set noautoindent"
    9821070            setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT);
    983             setops(argp, "flash ", i, "fl", VI_ERR_METHOD);
     1071            setops(argp, "flash "     , i, "fl", VI_ERR_METHOD);
    9841072            setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE);
    985             setops(argp, "showmatch ", i, "ic", VI_SHOWMATCH);
    986             /* tabstopXXXX */
    987             if (strncasecmp(argp + i, "tabstop=%d ", 7) == 0) {
    988                 sscanf(strchr(argp + i, '='), "tabstop=%d" + 7, &ch);
    989                 if (ch > 0 && ch < columns - 1)
    990                     tabstop = ch;
     1073            setops(argp, "showmatch " , i, "sm", VI_SHOWMATCH );
     1074            if (strncmp(argp + i, "tabstop=", 8) == 0) {
     1075                int t = 0;
     1076                sscanf(argp + i+8, "%u", &t);
     1077                if (t > 0 && t <= MAX_TABSTOP)
     1078                    tabstop = t;
    9911079            }
    992             while (*argp && *argp != ' ')
    993                 argp++; // skip to arg delimiter (i.e. blank)
    994             while (*argp && *argp == ' ')
    995                 argp++; // skip all delimiting blanks
     1080            argp = skip_non_whitespace(argp);
     1081            argp = skip_whitespace(argp);
    9961082        }
    9971083#endif /* FEATURE_VI_SETOPTS */
    9981084#endif /* FEATURE_VI_SET */
    9991085#if ENABLE_FEATURE_VI_SEARCH
    1000     } else if (strncasecmp(cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
     1086    } else if (cmd[0] == 's') { // substitute a pattern with a replacement pattern
    10011087        char *ls, *F, *R;
    10021088        int gflag;
     
    10091095        F = orig_buf + 2;   // start of "find"
    10101096        R = strchr(F, c);   // middle delimiter
    1011         if (!R) goto colon_s_fail;
     1097        if (!R)
     1098            goto colon_s_fail;
    10121099        *R++ = '\0';    // terminate "find"
    10131100        buf1 = strchr(R, c);
    1014         if (!buf1) goto colon_s_fail;
     1101        if (!buf1)
     1102            goto colon_s_fail;
    10151103        *buf1++ = '\0'; // terminate "replace"
    10161104        if (*buf1 == 'g') { // :s/foo/bar/g
     
    10301118            buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
    10311119            if (buf1) {
     1120                uintptr_t bias;
    10321121                // we found the "find" pattern - delete it
    10331122                text_hole_delete(buf1, buf1 + strlen(F) - 1);
    10341123                // inset the "replace" patern
    1035                 string_insert(buf1, R); // insert the string
     1124                bias = string_insert(buf1, R);  // insert the string
     1125                buf1 += bias;
     1126                ls += bias;
     1127                /*q += bias; - recalculated anyway */
    10361128                // check for "global"  :s/foo/bar/g
    10371129                if (gflag == 1) {
     
    10451137        }
    10461138#endif /* FEATURE_VI_SEARCH */
    1047     } else if (strncasecmp(cmd, "version", i) == 0) {  // show software version
    1048         psb("%s", BB_VER " " BB_BT);
    1049     } else if (strncasecmp(cmd, "write", i) == 0  // write text to file
    1050             || strncasecmp(cmd, "wq", i) == 0
    1051             || strncasecmp(cmd, "wn", i) == 0
    1052             || strncasecmp(cmd, "x", i) == 0
     1139    } else if (strncmp(cmd, "version", i) == 0) {  // show software version
     1140        status_line(BB_VER " " BB_BT);
     1141    } else if (strncmp(cmd, "write", i) == 0  // write text to file
     1142            || strncmp(cmd, "wq", i) == 0
     1143            || strncmp(cmd, "wn", i) == 0
     1144            || (cmd[0] == 'x' && !cmd[1])
    10531145    ) {
    10541146        // is there a file name to write to?
     
    10581150#if ENABLE_FEATURE_VI_READONLY
    10591151        if (readonly_mode && !useforce) {
    1060             psbs("\"%s\" File is read only", fn);
    1061             goto vc3;
     1152            status_line_bold("\"%s\" File is read only", fn);
     1153            goto ret;
    10621154        }
    10631155#endif
     
    10811173        if (l < 0) {
    10821174            if (l == -1)
    1083                 psbs("\"%s\" %s", fn, strerror(errno));
     1175                status_line_bold("\"%s\" %s", fn, strerror(errno));
    10841176        } else {
    1085             psb("\"%s\" %dL, %dC", fn, li, l);
     1177            status_line("\"%s\" %dL, %dC", fn, li, l);
    10861178            if (q == text && r == end - 1 && l == ch) {
    10871179                file_modified = 0;
    10881180                last_file_modified = -1;
    10891181            }
    1090             if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' ||
    1091                  cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N')
    1092                  && l == ch) {
     1182            if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n'
     1183                || cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N'
     1184                )
     1185             && l == ch
     1186            ) {
    10931187                editing = 0;
    10941188            }
    10951189        }
    1096 #if ENABLE_FEATURE_VI_READONLY
    1097  vc3:;
    1098 #endif
    10991190#if ENABLE_FEATURE_VI_YANKMARK
    1100     } else if (strncasecmp(cmd, "yank", i) == 0) {  // yank lines
     1191    } else if (strncmp(cmd, "yank", i) == 0) {  // yank lines
    11011192        if (b < 0) {    // no addr given- use defaults
    11021193            q = begin_line(dot);    // assume .,. for the range
     
    11051196        text_yank(q, r, YDreg);
    11061197        li = count_lines(q, r);
    1107         psb("Yank %d lines (%d chars) into [%c]",
     1198        status_line("Yank %d lines (%d chars) into [%c]",
    11081199                li, strlen(reg[YDreg]), what_reg());
    11091200#endif
    11101201    } else {
    11111202        // cmd unknown
    1112         ni(cmd);
    1113     }
    1114  vc1:
     1203        not_implemented(cmd);
     1204    }
     1205 ret:
    11151206    dot = bound_dot(dot);   // make sure "dot" is valid
    11161207    return;
    11171208#if ENABLE_FEATURE_VI_SEARCH
    11181209 colon_s_fail:
    1119     psb(":s expression missing delimiters");
     1210    status_line(":s expression missing delimiters");
    11201211#endif
    11211212}
     
    11251216static void Hit_Return(void)
    11261217{
    1127     char c;
    1128 
    1129     standout_start();   // start reverse video
     1218    int c;
     1219
     1220    standout_start();
    11301221    write1("[Hit return to continue]");
    1131     standout_end();     // end reverse video
    1132     while ((c = get_one_char()) != '\n' && c != '\r')   /*do nothing */
    1133         ;
     1222    standout_end();
     1223    while ((c = get_one_char()) != '\n' && c != '\r')
     1224        continue;
    11341225    redraw(TRUE);       // force redraw all
    11351226}
     
    11411232
    11421233//----- Synchronize the cursor to Dot --------------------------
    1143 static void sync_cursor(char * d, int *row, int *col)
     1234static NOINLINE void sync_cursor(char *d, int *row, int *col)
    11441235{
    11451236    char *beg_cur;  // begin and end of "d" line
    1146     char *end_scr;  // begin and end of screen
    11471237    char *tp;
    11481238    int cnt, ro, co;
     
    11501240    beg_cur = begin_line(d);    // first char of cur line
    11511241
    1152     end_scr = end_screen(); // last char of screen
    1153 
    11541242    if (beg_cur < screenbegin) {
    1155         // "d" is before  top line on screen
     1243        // "d" is before top line on screen
    11561244        // how many lines do we have to move
    11571245        cnt = count_lines(beg_cur, screenbegin);
     
    11641252            }
    11651253        }
    1166     } else if (beg_cur > end_scr) {
    1167         // "d" is after bottom line on screen
    1168         // how many lines do we have to move
    1169         cnt = count_lines(end_scr, beg_cur);
    1170         if (cnt > (rows - 1) / 2)
    1171             goto sc1;   // too many lines
    1172         for (ro = 0; ro < cnt - 1; ro++) {
    1173             // move screen begin the same amount
    1174             screenbegin = next_line(screenbegin);
    1175             // now, move the end of screen
    1176             end_scr = next_line(end_scr);
    1177             end_scr = end_line(end_scr);
     1254    } else {
     1255        char *end_scr;  // begin and end of screen
     1256        end_scr = end_screen(); // last char of screen
     1257        if (beg_cur > end_scr) {
     1258            // "d" is after bottom line on screen
     1259            // how many lines do we have to move
     1260            cnt = count_lines(end_scr, beg_cur);
     1261            if (cnt > (rows - 1) / 2)
     1262                goto sc1;   // too many lines
     1263            for (ro = 0; ro < cnt - 1; ro++) {
     1264                // move screen begin the same amount
     1265                screenbegin = next_line(screenbegin);
     1266                // now, move the end of screen
     1267                end_scr = next_line(end_scr);
     1268                end_scr = end_line(end_scr);
     1269            }
    11781270        }
    11791271    }
     
    11881280    // find out what col "d" is on
    11891281    co = 0;
    1190     do {                // drive "co" to correct column
    1191         if (*tp == '\n' || *tp == '\0')
     1282    while (tp < d) { // drive "co" to correct column
     1283        if (*tp == '\n') //vda || *tp == '\0')
    11921284            break;
    11931285        if (*tp == '\t') {
    1194             if (d == tp && cmd_mode) { /* handle tabs like real vi */
     1286            // handle tabs like real vi
     1287            if (d == tp && cmd_mode) {
    11951288                break;
    1196             } else {
    1197                 co = next_tabstop(co);
    11981289            }
    1199         } else if (*tp < ' ' || *tp == 127) {
    1200             co++;       // display as ^X, use 2 columns
    1201         }
    1202     } while (tp++ < d && ++co);
     1290            co = next_tabstop(co);
     1291        } else if ((unsigned char)*tp < ' ' || *tp == 0x7f) {
     1292            co++; // display as ^X, use 2 columns
     1293        }
     1294        co++;
     1295        tp++;
     1296    }
    12031297
    12041298    // "co" is the column where "dot" is.
     
    12331327
    12341328//----- Text Movement Routines ---------------------------------
    1235 static char *begin_line(char * p) // return pointer to first char cur line
    1236 {
    1237     while (p > text && p[-1] != '\n')
    1238         p--;            // go to cur line B-o-l
     1329static char *begin_line(char *p) // return pointer to first char cur line
     1330{
     1331    if (p > text) {
     1332        p = memrchr(text, '\n', p - text);
     1333        if (!p)
     1334            return text;
     1335        return p + 1;
     1336    }
    12391337    return p;
    12401338}
    12411339
    1242 static char *end_line(char * p) // return pointer to NL of cur line line
    1243 {
    1244     while (p < end - 1 && *p != '\n')
    1245         p++;            // go to cur line E-o-l
     1340static char *end_line(char *p) // return pointer to NL of cur line
     1341{
     1342    if (p < end - 1) {
     1343        p = memchr(p, '\n', end - p - 1);
     1344        if (!p)
     1345            return end - 1;
     1346    }
    12461347    return p;
    12471348}
    12481349
    1249 static inline char *dollar_line(char * p) // return pointer to just before NL line
    1250 {
    1251     while (p < end - 1 && *p != '\n')
    1252         p++;            // go to cur line E-o-l
     1350static char *dollar_line(char *p) // return pointer to just before NL line
     1351{
     1352    p = end_line(p);
    12531353    // Try to stay off of the Newline
    12541354    if (*p == '\n' && (p - begin_line(p)) > 0)
     
    12571357}
    12581358
    1259 static char *prev_line(char * p) // return pointer first char prev line
     1359static char *prev_line(char *p) // return pointer first char prev line
    12601360{
    12611361    p = begin_line(p);  // goto begining of cur line
    1262     if (p[-1] == '\n' && p > text)
     1362    if (p > text && p[-1] == '\n')
    12631363        p--;            // step to prev line
    12641364    p = begin_line(p);  // goto begining of prev line
     
    12661366}
    12671367
    1268 static char *next_line(char * p) // return pointer first char next line
     1368static char *next_line(char *p) // return pointer first char next line
    12691369{
    12701370    p = end_line(p);
    1271     if (*p == '\n' && p < end - 1)
     1371    if (p < end - 1 && *p == '\n')
    12721372        p++;            // step to next line
    12731373    return p;
     
    12881388}
    12891389
    1290 static int count_lines(char * start, char * stop) // count line from start to stop
     1390// count line from start to stop
     1391static int count_lines(char *start, char *stop)
    12911392{
    12921393    char *q;
    12931394    int cnt;
    12941395
    1295     if (stop < start) { // start and stop are backwards- reverse them
     1396    if (stop < start) { // start and stop are backwards- reverse them
    12961397        q = start;
    12971398        start = stop;
     
    12991400    }
    13001401    cnt = 0;
    1301     stop = end_line(stop);  // get to end of this line
    1302     for (q = start; q <= stop && q <= end - 1; q++) {
    1303         if (*q == '\n')
     1402    stop = end_line(stop);
     1403    while (start <= stop && start <= end - 1) {
     1404        start = end_line(start);
     1405        if (*start == '\n')
    13041406            cnt++;
     1407        start++;
    13051408    }
    13061409    return cnt;
     
    13401443}
    13411444
    1342 static char *move_to_col(char * p, int l)
     1445static char *move_to_col(char *p, int l)
    13431446{
    13441447    int co;
     
    13461449    p = begin_line(p);
    13471450    co = 0;
    1348     do {
    1349         if (*p == '\n' || *p == '\0')
     1451    while (co < l && p < end) {
     1452        if (*p == '\n') //vda || *p == '\0')
    13501453            break;
    13511454        if (*p == '\t') {
    13521455            co = next_tabstop(co);
    13531456        } else if (*p < ' ' || *p == 127) {
    1354             co++;       // display as ^X, use 2 columns
    1355         }
    1356     } while (++co <= l && p++ < end);
     1457            co++; // display as ^X, use 2 columns
     1458        }
     1459        co++;
     1460        p++;
     1461    }
    13571462    return p;
    13581463}
     
    13751480        if (dir < 0) {
    13761481            // scroll Backwards
    1377             // ctrl-Y  scroll up one line
     1482            // ctrl-Y scroll up one line
    13781483            screenbegin = prev_line(screenbegin);
    13791484        } else {
    13801485            // scroll Forwards
    1381             // ctrl-E  scroll down one line
     1486            // ctrl-E scroll down one line
    13821487            screenbegin = next_line(screenbegin);
    13831488        }
     
    14041509}
    14051510
    1406 static char *bound_dot(char * p) // make sure  text[0] <= P < "end"
     1511static char *bound_dot(char *p) // make sure  text[0] <= P < "end"
    14071512{
    14081513    if (p >= end && end > text) {
     
    14471552
    14481553#if ENABLE_FEATURE_VI_SEARCH
    1449 static int mycmp(const char * s1, const char * s2, int len)
    1450 {
    1451     int i;
    1452 
    1453     i = strncmp(s1, s2, len);
     1554static int mycmp(const char *s1, const char *s2, int len)
     1555{
    14541556    if (ENABLE_FEATURE_VI_SETOPTS && ignorecase) {
    1455         i = strncasecmp(s1, s2, len);
    1456     }
    1457     return i;
     1557        return strncasecmp(s1, s2, len);
     1558    }
     1559    return strncmp(s1, s2, len);
    14581560}
    14591561
    14601562// search for pattern starting at p
    1461 static char *char_search(char * p, const char * pat, int dir, int range)
     1563static char *char_search(char *p, const char *pat, int dir, int range)
    14621564{
    14631565#ifndef REGEX_SEARCH
     
    15171619    if (q != 0) {
    15181620        // The pattern was not compiled
    1519         psbs("bad search pattern: \"%s\": %s", pat, q);
     1621        status_line_bold("bad search pattern: \"%s\": %s", pat, q);
    15201622        i = 0;          // return p if pattern not compiled
    15211623        goto cs1;
     
    15521654#endif /* FEATURE_VI_SEARCH */
    15531655
    1554 static char *char_insert(char * p, char c) // insert the char c at 'p'
     1656static char *char_insert(char *p, char c) // insert the char c at 'p'
    15551657{
    15561658    if (c == 22) {      // Is this an ctrl-V?
    1557         p = stupid_insert(p, '^');  // use ^ to indicate literal next
    1558         p--;            // backup onto ^
     1659        p += stupid_insert(p, '^'); // use ^ to indicate literal next
    15591660        refresh(FALSE); // show the ^
    15601661        c = get_one_char();
    15611662        *p = c;
    15621663        p++;
    1563         file_modified++;    // has the file been modified
     1664        file_modified++;
    15641665    } else if (c == 27) {   // Is this an ESC?
    15651666        cmd_mode = 0;
     
    15831684            c = '\n';   // translate \r to \n
    15841685        sp = p;         // remember addr of insert
    1585         p = stupid_insert(p, c);    // insert the char
     1686        p += 1 + stupid_insert(p, c);   // insert the char
    15861687#if ENABLE_FEATURE_VI_SETOPTS
    15871688        if (showmatch && strchr(")]}", *sp) != NULL) {
     
    15901691        if (autoindent && c == '\n') {  // auto indent the new line
    15911692            char *q;
    1592 
    1593             q = prev_line(p);   // use prev line as templet
    1594             for (; isblank(*q); q++) {
    1595                 p = stupid_insert(p, *q);   // insert the char
     1693            size_t len;
     1694            q = prev_line(p);   // use prev line as template
     1695            len = strspn(q, " \t"); // space or tab
     1696            if (len) {
     1697                uintptr_t bias;
     1698                bias = text_hole_make(p, len);
     1699                p += bias;
     1700                q += bias;
     1701                memcpy(p, q, len);
     1702                p += len;
    15961703            }
    15971704        }
     
    16011708}
    16021709
    1603 static char *stupid_insert(char * p, char c) // stupidly insert the char c at 'p'
    1604 {
    1605     p = text_hole_make(p, 1);
    1606     if (p != 0) {
    1607         *p = c;
    1608         file_modified++;    // has the file been modified
    1609         p++;
    1610     }
    1611     return p;
    1612 }
    1613 
    1614 static char find_range(char ** start, char ** stop, char c)
    1615 {
    1616     char *save_dot, *p, *q;
    1617     int cnt;
     1710// might reallocate text[]! use p += stupid_insert(p, ...),
     1711// and be careful to not use pointers into potentially freed text[]!
     1712static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at 'p'
     1713{
     1714    uintptr_t bias;
     1715    bias = text_hole_make(p, 1);
     1716    p += bias;
     1717    *p = c;
     1718    //file_modified++; - done by text_hole_make()
     1719    return bias;
     1720}
     1721
     1722static int find_range(char **start, char **stop, char c)
     1723{
     1724    char *save_dot, *p, *q, *t;
     1725    int cnt, multiline = 0;
    16181726
    16191727    save_dot = dot;
     
    16271735        }
    16281736        q = end_line(q);
    1629     } else if (strchr("^%$0bBeEft", c)) {
     1737    } else if (strchr("^%$0bBeEfth\b\177", c)) {
    16301738        // These cmds operate on char positions
    16311739        do_cmd(c);      // execute movement cmd
     
    16561764        q = dot;
    16571765    } else {
    1658         c = 27;         // error- return an ESC char
    1659         //break;
    1660     }
     1766        // nothing -- this causes any other values of c to
     1767        // represent the one-character range under the
     1768        // cursor.  this is correct for ' ' and 'l', but
     1769        // perhaps no others.
     1770        //
     1771    }
     1772    if (q < p) {
     1773        t = q;
     1774        q = p;
     1775        p = t;
     1776    }
     1777
     1778    // backward char movements don't include start position
     1779    if (q > p && strchr("^0bBh\b\177", c)) q--;
     1780
     1781    multiline = 0;
     1782    for (t = p; t <= q; t++) {
     1783        if (*t == '\n') {
     1784            multiline = 1;
     1785            break;
     1786        }
     1787    }
     1788
    16611789    *start = p;
    16621790    *stop = q;
    1663     if (q < p) {
    1664         *start = q;
    1665         *stop = p;
    1666     }
    16671791    dot = save_dot;
    1668     return c;
    1669 }
    1670 
    1671 static int st_test(char * p, int type, int dir, char * tested)
     1792    return multiline;
     1793}
     1794
     1795static int st_test(char *p, int type, int dir, char *tested)
    16721796{
    16731797    char c, c0, ci;
     
    16811805    if (type == S_BEFORE_WS) {
    16821806        c = ci;
    1683         test = ((!isspace(c)) || c == '\n');
     1807        test = (!isspace(c) || c == '\n');
    16841808    }
    16851809    if (type == S_TO_WS) {
    16861810        c = c0;
    1687         test = ((!isspace(c)) || c == '\n');
     1811        test = (!isspace(c) || c == '\n');
    16881812    }
    16891813    if (type == S_OVER_WS) {
    16901814        c = c0;
    1691         test = ((isspace(c)));
     1815        test = isspace(c);
    16921816    }
    16931817    if (type == S_END_PUNCT) {
    16941818        c = ci;
    1695         test = ((ispunct(c)));
     1819        test = ispunct(c);
    16961820    }
    16971821    if (type == S_END_ALNUM) {
    16981822        c = ci;
    1699         test = ((isalnum(c)) || c == '_');
     1823        test = (isalnum(c) || c == '_');
    17001824    }
    17011825    *tested = c;
     
    17031827}
    17041828
    1705 static char *skip_thing(char * p, int linecnt, int dir, int type)
     1829static char *skip_thing(char *p, int linecnt, int dir, int type)
    17061830{
    17071831    char c;
     
    17211845
    17221846// find matching char of pair  ()  []  {}
    1723 static char *find_pair(char * p, char c)
     1847static char *find_pair(char *p, const char c)
    17241848{
    17251849    char match, *q;
     
    17301854    dir = 1;            // assume forward
    17311855    switch (c) {
    1732     case '(':
    1733         match = ')';
    1734         break;
    1735     case '[':
    1736         match = ']';
    1737         break;
    1738     case '{':
    1739         match = '}';
    1740         break;
    1741     case ')':
    1742         match = '(';
    1743         dir = -1;
    1744         break;
    1745     case ']':
    1746         match = '[';
    1747         dir = -1;
    1748         break;
    1749     case '}':
    1750         match = '{';
    1751         dir = -1;
    1752         break;
     1856    case '(': match = ')'; break;
     1857    case '[': match = ']'; break;
     1858    case '{': match = '}'; break;
     1859    case ')': match = '('; dir = -1; break;
     1860    case ']': match = '['; dir = -1; break;
     1861    case '}': match = '{'; dir = -1; break;
    17531862    }
    17541863    for (q = p + dir; text <= q && q < end; q += dir) {
     
    17681877#if ENABLE_FEATURE_VI_SETOPTS
    17691878// show the matching char of a pair,  ()  []  {}
    1770 static void showmatching(char * p)
     1879static void showmatching(char *p)
    17711880{
    17721881    char *q, *save_dot;
     
    17881897#endif /* FEATURE_VI_SETOPTS */
    17891898
    1790 //  open a hole in text[]
    1791 static char *text_hole_make(char * p, int size) // at "p", make a 'size' byte hole
    1792 {
    1793     char *src, *dest;
    1794     int cnt;
     1899// open a hole in text[]
     1900// might reallocate text[]! use p += text_hole_make(p, ...),
     1901// and be careful to not use pointers into potentially freed text[]!
     1902static uintptr_t text_hole_make(char *p, int size)  // at "p", make a 'size' byte hole
     1903{
     1904    uintptr_t bias = 0;
    17951905
    17961906    if (size <= 0)
    1797         goto thm0;
    1798     src = p;
    1799     dest = p + size;
    1800     cnt = end - src;    // the rest of buffer
    1801     if ( ((end + size) >= (text + text_size)) // TODO: realloc here
    1802             || memmove(dest, src, cnt) != dest) {
    1803         psbs("can't create room for new characters");
    1804         p = NULL;
    1805         goto thm0;
    1806     }
     1907        return bias;
     1908    end += size;        // adjust the new END
     1909    if (end >= (text + text_size)) {
     1910        char *new_text;
     1911        text_size += end - (text + text_size) + 10240;
     1912        new_text = xrealloc(text, text_size);
     1913        bias = (new_text - text);
     1914        screenbegin += bias;
     1915        dot         += bias;
     1916        end         += bias;
     1917        p           += bias;
     1918        text = new_text;
     1919    }
     1920    memmove(p + size, p, end - size - p);
    18071921    memset(p, ' ', size);   // clear new hole
    1808     end += size;        // adjust the new END
    1809     file_modified++;    // has the file been modified
    1810  thm0:
    1811     return p;
     1922    file_modified++;
     1923    return bias;
    18121924}
    18131925
    18141926//  close a hole in text[]
    1815 static char *text_hole_delete(char * p, char * q) // delete "p" thru "q", inclusive
     1927static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclusive
    18161928{
    18171929    char *src, *dest;
     
    18341946    if (src >= end)
    18351947        goto thd_atend; // just delete the end of the buffer
    1836     if (memmove(dest, src, cnt) != dest) {
    1837         psbs("can't delete the character");
    1838     }
     1948    memmove(dest, src, cnt);
    18391949 thd_atend:
    18401950    end = end - hole_size;  // adjust the new END
     
    18431953    if (end <= text)
    18441954        dest = end = text;  // keep pointers valid
    1845     file_modified++;    // has the file been modified
     1955    file_modified++;
    18461956 thd0:
    18471957    return dest;
     
    18511961// if dist <= 0, do not include, or go past, a NewLine
    18521962//
    1853 static char *yank_delete(char * start, char * stop, int dist, int yf)
     1963static char *yank_delete(char *start, char *stop, int dist, int yf)
    18541964{
    18551965    char *p;
     
    18952005#endif
    18962006#if ENABLE_FEATURE_VI_YANKMARK
    1897     "\n\tLine marking with  'x"
    1898     "\n\tNamed buffers with  \"x"
     2007    "\n\tLine marking with 'x"
     2008    "\n\tNamed buffers with \"x"
    18992009#endif
    19002010#if ENABLE_FEATURE_VI_READONLY
     
    19182028}
    19192029
    1920 static inline void print_literal(char * buf, const char * s) // copy s to buf, convert unprintable
    1921 {
    1922     unsigned char c;
    1923     char b[2];
    1924 
    1925     b[1] = '\0';
    1926     buf[0] = '\0';
    1927     if (!s[0])
    1928         s = "(NULL)";
    1929     for (; *s; s++) {
    1930         int c_is_no_print;
    1931 
    1932         c = *s;
    1933         c_is_no_print = (c & 0x80) && !Isprint(c);
    1934         if (c_is_no_print) {
    1935             strcat(buf, SOn);
    1936             c = '.';
    1937         }
    1938         if (c < ' ' || c == 127) {
    1939             strcat(buf, "^");
    1940             if (c == 127)
    1941                 c = '?';
    1942             else
    1943                 c += '@';
    1944         }
    1945         b[0] = c;
    1946         strcat(buf, b);
    1947         if (c_is_no_print)
    1948             strcat(buf, SOs);
    1949         if (*s == '\n')
    1950             strcat(buf, "$");
    1951     }
    1952 }
    1953 
    19542030#if ENABLE_FEATURE_VI_DOT_CMD
    19552031static void start_new_cmd_q(char c)
    19562032{
    1957     // release old cmd
    1958     free(last_modifying_cmd);
    19592033    // get buffer for new cmd
    1960     last_modifying_cmd = xzalloc(MAX_LINELEN);
    19612034    // if there is a current cmd count put it in the buffer first
    1962     if (cmdcnt > 0)
    1963         sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
    1964     else // just save char c onto queue
     2035    if (cmdcnt > 0) {
     2036        lmc_len = sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
     2037    } else { // just save char c onto queue
    19652038        last_modifying_cmd[0] = c;
     2039        lmc_len = 1;
     2040    }
    19662041    adding2q = 1;
    19672042}
     
    19792054 || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
    19802055 || ENABLE_FEATURE_VI_CRASHME
    1981 static char *string_insert(char * p, char * s) // insert the string at 'p'
    1982 {
    1983     int cnt, i;
     2056// might reallocate text[]! use p += string_insert(p, ...),
     2057// and be careful to not use pointers into potentially freed text[]!
     2058static uintptr_t string_insert(char *p, const char *s) // insert the string at 'p'
     2059{
     2060    uintptr_t bias;
     2061    int i;
    19842062
    19852063    i = strlen(s);
    1986     if (text_hole_make(p, i)) {
    1987         strncpy(p, s, i);
     2064    bias = text_hole_make(p, i);
     2065    p += bias;
     2066    memcpy(p, s, i);
     2067#if ENABLE_FEATURE_VI_YANKMARK
     2068    {
     2069        int cnt;
    19882070        for (cnt = 0; *s != '\0'; s++) {
    19892071            if (*s == '\n')
    19902072                cnt++;
    19912073        }
     2074        status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
     2075    }
     2076#endif
     2077    return bias;
     2078}
     2079#endif
     2080
    19922081#if ENABLE_FEATURE_VI_YANKMARK
    1993         psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
    1994 #endif
    1995     }
    1996     return p;
    1997 }
    1998 #endif
    1999 
    2000 #if ENABLE_FEATURE_VI_YANKMARK
    2001 static char *text_yank(char * p, char * q, int dest)    // copy text into a register
    2002 {
    2003     char *t;
    2004     int cnt;
    2005 
    2006     if (q < p) {        // they are backwards- reverse them
    2007         t = q;
    2008         q = p;
    2009         p = t;
    2010     }
    2011     cnt = q - p + 1;
    2012     t = reg[dest];
    2013     free(t);        //  if already a yank register, free it
    2014     t = xmalloc(cnt + 1);   // get a new register
    2015     memset(t, '\0', cnt + 1);   // clear new text[]
    2016     strncpy(t, p, cnt); // copy text[] into bufer
    2017     reg[dest] = t;
     2082static char *text_yank(char *p, char *q, int dest)  // copy text into a register
     2083{
     2084    int cnt = q - p;
     2085    if (cnt < 0) {      // they are backwards- reverse them
     2086        p = q;
     2087        cnt = -cnt;
     2088    }
     2089    free(reg[dest]);    //  if already a yank register, free it
     2090    reg[dest] = xstrndup(p, cnt + 1);
    20182091    return p;
    20192092}
     
    20502123}
    20512124
    2052 static inline char *swap_context(char * p) // goto new context for '' command make this the current context
     2125static char *swap_context(char *p) // goto new context for '' command make this the current context
    20532126{
    20542127    char *tmp;
     
    20802153    term_vi.c_cc[VTIME] = 0;
    20812154    erase_char = term_vi.c_cc[VERASE];
    2082     tcsetattr(0, TCSANOW, &term_vi);
     2155    tcsetattr_stdin_TCSANOW(&term_vi);
    20832156}
    20842157
    20852158static void cookmode(void)
    20862159{
    2087     fflush(stdout);
    2088     tcsetattr(0, TCSANOW, &term_orig);
    2089 }
    2090 
     2160    fflush_all();
     2161    tcsetattr_stdin_TCSANOW(&term_orig);
     2162}
     2163
     2164#if ENABLE_FEATURE_VI_USE_SIGNALS
    20912165//----- Come here when we get a window resize signal ---------
    2092 #if ENABLE_FEATURE_VI_USE_SIGNALS
    2093 static void winch_sig(int sig ATTRIBUTE_UNUSED)
    2094 {
     2166static void winch_sig(int sig UNUSED_PARAM)
     2167{
     2168    int save_errno = errno;
     2169    // FIXME: do it in main loop!!!
    20952170    signal(SIGWINCH, winch_sig);
    2096     if (ENABLE_FEATURE_VI_WIN_RESIZE)
    2097         get_terminal_width_height(0, &columns, &rows);
     2171    query_screen_dimensions();
    20982172    new_screen(rows, columns);  // get memory for virtual screen
    20992173    redraw(TRUE);       // re-draw the screen
     2174    errno = save_errno;
    21002175}
    21012176
    21022177//----- Come here when we get a continue signal -------------------
    2103 static void cont_sig(int sig ATTRIBUTE_UNUSED)
    2104 {
    2105     rawmode();          // terminal to "raw"
    2106     last_status_cksum = 0;  // force status update
    2107     redraw(TRUE);       // re-draw the screen
     2178static void cont_sig(int sig UNUSED_PARAM)
     2179{
     2180    int save_errno = errno;
     2181    rawmode(); // terminal to "raw"
     2182    last_status_cksum = 0; // force status update
     2183    redraw(TRUE); // re-draw the screen
    21082184
    21092185    signal(SIGTSTP, suspend_sig);
    21102186    signal(SIGCONT, SIG_DFL);
    2111     kill(my_pid, SIGCONT);
     2187    //kill(my_pid, SIGCONT); // huh? why? we are already "continued"...
     2188    errno = save_errno;
    21122189}
    21132190
    21142191//----- Come here when we get a Suspend signal -------------------
    2115 static void suspend_sig(int sig ATTRIBUTE_UNUSED)
    2116 {
    2117     place_cursor(rows - 1, 0, FALSE);   // go to bottom of screen
    2118     clear_to_eol();     // Erase to end of line
    2119     cookmode();         // terminal to "cooked"
     2192static void suspend_sig(int sig UNUSED_PARAM)
     2193{
     2194    int save_errno = errno;
     2195    go_bottom_and_clear_to_eol();
     2196    cookmode(); // terminal to "cooked"
    21202197
    21212198    signal(SIGCONT, cont_sig);
    21222199    signal(SIGTSTP, SIG_DFL);
    21232200    kill(my_pid, SIGTSTP);
     2201    errno = save_errno;
    21242202}
    21252203
     
    21282206{
    21292207    signal(SIGINT, catch_sig);
    2130     if (sig)
    2131         longjmp(restart, sig);
     2208    siglongjmp(restart, sig);
    21322209}
    21332210#endif /* FEATURE_VI_USE_SIGNALS */
    21342211
    2135 static int mysleep(int hund)    // sleep for 'h' 1/100 seconds
    2136 {
    2137     fd_set rfds;
    2138     struct timeval tv;
    2139 
    2140     // Don't hang- Wait 5/100 seconds-  1 Sec= 1000000
    2141     fflush(stdout);
    2142     FD_ZERO(&rfds);
    2143     FD_SET(0, &rfds);
    2144     tv.tv_sec = 0;
    2145     tv.tv_usec = hund * 10000;
    2146     select(1, &rfds, NULL, NULL, &tv);
    2147     return FD_ISSET(0, &rfds);
    2148 }
    2149 
    2150 #define readbuffer bb_common_bufsiz1
    2151 
    2152 static int readed_for_parse;
     2212static int mysleep(int hund)    // sleep for 'hund' 1/100 seconds or stdin ready
     2213{
     2214    struct pollfd pfd[1];
     2215
     2216    pfd[0].fd = STDIN_FILENO;
     2217    pfd[0].events = POLLIN;
     2218    return safe_poll(pfd, 1, hund*10) > 0;
     2219}
    21532220
    21542221//----- IO Routines --------------------------------------------
    2155 static char readit(void)    // read (maybe cursor) key from stdin
    2156 {
    2157     char c;
    2158     int n;
    2159     struct esc_cmds {
    2160         const char *seq;
    2161         char val;
    2162     };
    2163 
    2164     static const struct esc_cmds esccmds[] = {
    2165         {"OA", VI_K_UP},       // cursor key Up
    2166         {"OB", VI_K_DOWN},     // cursor key Down
    2167         {"OC", VI_K_RIGHT},    // Cursor Key Right
    2168         {"OD", VI_K_LEFT},     // cursor key Left
    2169         {"OH", VI_K_HOME},     // Cursor Key Home
    2170         {"OF", VI_K_END},      // Cursor Key End
    2171         {"[A", VI_K_UP},       // cursor key Up
    2172         {"[B", VI_K_DOWN},     // cursor key Down
    2173         {"[C", VI_K_RIGHT},    // Cursor Key Right
    2174         {"[D", VI_K_LEFT},     // cursor key Left
    2175         {"[H", VI_K_HOME},     // Cursor Key Home
    2176         {"[F", VI_K_END},      // Cursor Key End
    2177         {"[1~", VI_K_HOME},    // Cursor Key Home
    2178         {"[2~", VI_K_INSERT},  // Cursor Key Insert
    2179         {"[4~", VI_K_END},     // Cursor Key End
    2180         {"[5~", VI_K_PAGEUP},  // Cursor Key Page Up
    2181         {"[6~", VI_K_PAGEDOWN},// Cursor Key Page Down
    2182         {"OP", VI_K_FUN1},     // Function Key F1
    2183         {"OQ", VI_K_FUN2},     // Function Key F2
    2184         {"OR", VI_K_FUN3},     // Function Key F3
    2185         {"OS", VI_K_FUN4},     // Function Key F4
    2186         {"[15~", VI_K_FUN5},   // Function Key F5
    2187         {"[17~", VI_K_FUN6},   // Function Key F6
    2188         {"[18~", VI_K_FUN7},   // Function Key F7
    2189         {"[19~", VI_K_FUN8},   // Function Key F8
    2190         {"[20~", VI_K_FUN9},   // Function Key F9
    2191         {"[21~", VI_K_FUN10},  // Function Key F10
    2192         {"[23~", VI_K_FUN11},  // Function Key F11
    2193         {"[24~", VI_K_FUN12},  // Function Key F12
    2194         {"[11~", VI_K_FUN1},   // Function Key F1
    2195         {"[12~", VI_K_FUN2},   // Function Key F2
    2196         {"[13~", VI_K_FUN3},   // Function Key F3
    2197         {"[14~", VI_K_FUN4},   // Function Key F4
    2198     };
    2199     enum { ESCCMDS_COUNT = ARRAY_SIZE(esccmds) };
    2200 
    2201     alarm(0);   // turn alarm OFF while we wait for input
    2202     fflush(stdout);
    2203     n = readed_for_parse;
    2204     // get input from User- are there already input chars in Q?
    2205     if (n <= 0) {
    2206  ri0:
    2207         // the Q is empty, wait for a typed char
    2208         n = read(0, readbuffer, MAX_LINELEN - 1);
    2209         if (n < 0) {
    2210             if (errno == EINTR)
    2211                 goto ri0;   // interrupted sys call
    2212             if (errno == EBADF || errno == EFAULT || errno == EINVAL
    2213                     || errno == EIO)
    2214                 editing = 0;
    2215             errno = 0;
    2216         }
    2217         if (n <= 0)
    2218             return 0;       // error
    2219         if (readbuffer[0] == 27) {
    2220             fd_set rfds;
    2221             struct timeval tv;
    2222 
    2223             // This is an ESC char. Is this Esc sequence?
    2224             // Could be bare Esc key. See if there are any
    2225             // more chars to read after the ESC. This would
    2226             // be a Function or Cursor Key sequence.
    2227             FD_ZERO(&rfds);
    2228             FD_SET(0, &rfds);
    2229             tv.tv_sec = 0;
    2230             tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
    2231 
    2232             // keep reading while there are input chars and room in buffer
    2233             while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (MAX_LINELEN - 5)) {
    2234                 // read the rest of the ESC string
    2235                 int r = read(0, (void *) (readbuffer + n), MAX_LINELEN - n);
    2236                 if (r > 0) {
    2237                     n += r;
    2238                 }
    2239             }
    2240         }
    2241         readed_for_parse = n;
    2242     }
    2243     c = readbuffer[0];
    2244     if (c == 27 && n > 1) {
    2245         // Maybe cursor or function key?
    2246         const struct esc_cmds *eindex;
    2247 
    2248         for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
    2249             int cnt = strlen(eindex->seq);
    2250 
    2251             if (n <= cnt)
    2252                 continue;
    2253             if (strncmp(eindex->seq, readbuffer + 1, cnt))
    2254                 continue;
    2255             // is a Cursor key- put derived value back into Q
    2256             c = eindex->val;
    2257             // for squeeze out the ESC sequence
    2258             n = cnt + 1;
    2259             break;
    2260         }
    2261         if (eindex == &esccmds[ESCCMDS_COUNT]) {
    2262             /* defined ESC sequence not found, set only one ESC */
    2263             n = 1;
    2264     }
    2265     } else {
    2266         n = 1;
    2267     }
    2268     // remove key sequence from Q
    2269     readed_for_parse -= n;
    2270     memmove(readbuffer, readbuffer + n, MAX_LINELEN - n);
    2271     alarm(3);   // we are done waiting for input, turn alarm ON
     2222static int readit(void) // read (maybe cursor) key from stdin
     2223{
     2224    int c;
     2225
     2226    fflush_all();
     2227    c = read_key(STDIN_FILENO, readbuffer, /*timeout off:*/ -2);
     2228    if (c == -1) { // EOF/error
     2229        go_bottom_and_clear_to_eol();
     2230        cookmode(); // terminal to "cooked"
     2231        bb_error_msg_and_die("can't read user input");
     2232    }
    22722233    return c;
    22732234}
    22742235
    22752236//----- IO Routines --------------------------------------------
    2276 static char get_one_char(void)
    2277 {
    2278     static char c;
     2237static int get_one_char(void)
     2238{
     2239    int c;
    22792240
    22802241#if ENABLE_FEATURE_VI_DOT_CMD
    2281     // ! adding2q  && ioq == 0  read()
    2282     // ! adding2q  && ioq != 0  *ioq
    2283     // adding2q         *last_modifying_cmd= read()
    22842242    if (!adding2q) {
    22852243        // we are not adding to the q.
     
    22902248        } else {
    22912249            // there is a queue to get chars from first
    2292             c = *ioq++;
     2250            // careful with correct sign expansion!
     2251            c = (unsigned char)*ioq++;
    22932252            if (c == '\0') {
    22942253                // the end of the q, read from STDIN
     
    23012260        // adding STDIN chars to q
    23022261        c = readit();   // get the users input
    2303         if (last_modifying_cmd != 0) {
    2304             int len = strlen(last_modifying_cmd);
    2305             if (len >= MAX_LINELEN - 1) {
    2306                 psbs("last_modifying_cmd overrun");
    2307             } else {
    2308                 // add new char to q
    2309                 last_modifying_cmd[len] = c;
    2310             }
     2262        if (lmc_len >= MAX_INPUT_LEN - 1) {
     2263            status_line_bold("last_modifying_cmd overrun");
     2264        } else {
     2265            // add new char to q
     2266            last_modifying_cmd[lmc_len++] = c;
    23112267        }
    23122268    }
     
    23142270    c = readit();       // get the users input
    23152271#endif /* FEATURE_VI_DOT_CMD */
    2316     return c;           // return the char, where ever it came from
    2317 }
    2318 
    2319 static char *get_input_line(const char * prompt) // get input line- use "status line"
    2320 {
    2321     static char *obufp;
    2322 
    2323     char buf[MAX_LINELEN];
    2324     char c;
     2272    return c;
     2273}
     2274
     2275// Get input line (uses "status line" area)
     2276static char *get_input_line(const char *prompt)
     2277{
     2278    // char [MAX_INPUT_LEN]
     2279#define buf get_input_line__buf
     2280
     2281    int c;
    23252282    int i;
    23262283
    23272284    strcpy(buf, prompt);
    23282285    last_status_cksum = 0;  // force status update
    2329     place_cursor(rows - 1, 0, FALSE);   // go to Status line, bottom of screen
    2330     clear_to_eol();     // clear the line
     2286    go_bottom_and_clear_to_eol();
    23312287    write1(prompt);      // write out the :, /, or ? prompt
    23322288
    23332289    i = strlen(buf);
    2334     while (i < MAX_LINELEN) {
    2335         c = get_one_char(); // read user input
     2290    while (i < MAX_INPUT_LEN) {
     2291        c = get_one_char();
    23362292        if (c == '\n' || c == '\r' || c == 27)
    2337             break;      // is this end of input
     2293            break;      // this is end of input
    23382294        if (c == erase_char || c == 8 || c == 127) {
    23392295            // user wants to erase prev char
    2340             i--;        // backup to prev char
    2341             buf[i] = '\0';  // erase the char
    2342             buf[i + 1] = '\0';  // null terminate buffer
    2343             write1("\b \b");     // erase char on screen
    2344             if (i <= 0) {   // user backs up before b-o-l, exit
     2296            buf[--i] = '\0';
     2297            write1("\b \b"); // erase char on screen
     2298            if (i <= 0) // user backs up before b-o-l, exit
    23452299                break;
    2346             }
    2347         } else {
    2348             buf[i] = c; // save char in buffer
    2349             buf[i + 1] = '\0';  // make sure buffer is null terminated
    2350             putchar(c);   // echo the char back to user
    2351             i++;
     2300        } else if (c > 0 && c < 256) { // exclude Unicode
     2301            // (TODO: need to handle Unicode)
     2302            buf[i] = c;
     2303            buf[++i] = '\0';
     2304            bb_putchar(c);
    23522305        }
    23532306    }
    23542307    refresh(FALSE);
    2355     free(obufp);
    2356     obufp = xstrdup(buf);
    2357     return obufp;
     2308    return buf;
     2309#undef buf
    23582310}
    23592311
     
    23642316
    23652317    cnt = -1;
    2366     if (fn && fn[0] && stat(fn, &st_buf) == 0)  // see if file exists
     2318    if (fn && stat(fn, &st_buf) == 0)   // see if file exists
    23672319        cnt = (int) st_buf.st_size;
    23682320    return cnt;
    23692321}
    23702322
    2371 static int file_insert(const char * fn, char *p
    2372         USE_FEATURE_VI_READONLY(, int update_ro_status))
     2323// might reallocate text[]!
     2324static int file_insert(const char *fn, char *p, int update_ro_status)
    23732325{
    23742326    int cnt = -1;
     
    23782330    /* Validate file */
    23792331    if (stat(fn, &statbuf) < 0) {
    2380         psbs("\"%s\" %s", fn, strerror(errno));
     2332        status_line_bold("\"%s\" %s", fn, strerror(errno));
    23812333        goto fi0;
    23822334    }
    2383     if ((statbuf.st_mode & S_IFREG) == 0) {
     2335    if (!S_ISREG(statbuf.st_mode)) {
    23842336        // This is not a regular file
    2385         psbs("\"%s\" Not a regular file", fn);
     2337        status_line_bold("\"%s\" Not a regular file", fn);
    23862338        goto fi0;
    23872339    }
    2388     /* // this check is done by open()
    2389     if ((statbuf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
    2390         // dont have any read permissions
    2391         psbs("\"%s\" Not readable", fn);
    2392         goto fi0;
    2393     }
    2394     */
    23952340    if (p < text || p > end) {
    2396         psbs("Trying to insert file outside of memory");
     2341        status_line_bold("Trying to insert file outside of memory");
    23972342        goto fi0;
    23982343    }
     
    24012346    fd = open(fn, O_RDONLY);
    24022347    if (fd < 0) {
    2403         psbs("\"%s\" %s", fn, strerror(errno));
     2348        status_line_bold("\"%s\" %s", fn, strerror(errno));
    24042349        goto fi0;
    24052350    }
    24062351    size = statbuf.st_size;
    2407     p = text_hole_make(p, size);
    2408     if (p == NULL)
    2409         goto fi0;
    2410     cnt = read(fd, p, size);
     2352    p += text_hole_make(p, size);
     2353    cnt = safe_read(fd, p, size);
    24112354    if (cnt < 0) {
    2412         psbs("\"%s\" %s", fn, strerror(errno));
     2355        status_line_bold("\"%s\" %s", fn, strerror(errno));
    24132356        p = text_hole_delete(p, p + size - 1);  // un-do buffer insert
    24142357    } else if (cnt < size) {
    24152358        // There was a partial read, shrink unused space text[]
    24162359        p = text_hole_delete(p + cnt, p + (size - cnt) - 1);    // un-do buffer insert
    2417         psbs("cannot read all of file \"%s\"", fn);
     2360        status_line_bold("can't read all of file \"%s\"", fn);
    24182361    }
    24192362    if (cnt >= size)
     
    24352378}
    24362379
    2437 
    2438 static int file_write(char * fn, char * first, char * last)
     2380static int file_write(char *fn, char *first, char *last)
    24392381{
    24402382    int fd, cnt, charcnt;
    24412383
    24422384    if (fn == 0) {
    2443         psbs("No current filename");
     2385        status_line_bold("No current filename");
    24442386        return -2;
    24452387    }
    2446     charcnt = 0;
    2447     // FIXIT- use the correct umask()
    2448     fd = open(fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
     2388    /* By popular request we do not open file with O_TRUNC,
     2389     * but instead ftruncate() it _after_ successful write.
     2390     * Might reduce amount of data lost on power fail etc.
     2391     */
     2392    fd = open(fn, (O_WRONLY | O_CREAT), 0666);
    24492393    if (fd < 0)
    24502394        return -1;
    24512395    cnt = last - first + 1;
    2452     charcnt = write(fd, first, cnt);
     2396    charcnt = full_write(fd, first, cnt);
     2397    ftruncate(fd, charcnt);
    24532398    if (charcnt == cnt) {
    24542399        // good write
    2455         //file_modified = FALSE; // the file has not been modified
     2400        //file_modified = FALSE;
    24562401    } else {
    24572402        charcnt = 0;
     
    24702415//  .       ...     .
    24712416//  22,0    ...     22,79
    2472 //  23,0    ...     23,79   status line
    2473 //
     2417//  23,0    ...     23,79   <- status line
    24742418
    24752419//----- Move the cursor to row x col (count from 0, not 1) -------
    2476 static void place_cursor(int row, int col, int opti)
    2477 {
    2478     char cm1[MAX_LINELEN];
     2420static void place_cursor(int row, int col, int optimize)
     2421{
     2422    char cm1[sizeof(CMrc) + sizeof(int)*3 * 2];
     2423#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
     2424    enum {
     2425        SZ_UP = sizeof(CMup),
     2426        SZ_DN = sizeof(CMdown),
     2427        SEQ_SIZE = SZ_UP > SZ_DN ? SZ_UP : SZ_DN,
     2428    };
     2429    char cm2[SEQ_SIZE * 5 + 32]; // bigger than worst case size
     2430#endif
    24792431    char *cm;
    2480 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
    2481     char cm2[MAX_LINELEN];
    2482     char *screenp;
    2483     // char cm3[MAX_LINELEN];
    2484     int Rrow = last_row;
    2485 #endif
    2486 
    2487     memset(cm1, '\0', MAX_LINELEN);  // clear the buffer
    24882432
    24892433    if (row < 0) row = 0;
     
    24952439    sprintf(cm1, CMrc, row + 1, col + 1);
    24962440    cm = cm1;
    2497     if (!opti)
    2498         goto pc0;
    24992441
    25002442#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
    2501     //----- find the minimum # of chars to move cursor -------------
    2502     //----- 2.  Try moving with discreet chars (Newline, [back]space, ...)
    2503     memset(cm2, '\0', MAX_LINELEN);  // clear the buffer
    2504 
    2505     // move to the correct row
    2506     while (row < Rrow) {
    2507         // the cursor has to move up
    2508         strcat(cm2, CMup);
    2509         Rrow--;
    2510     }
    2511     while (row > Rrow) {
    2512         // the cursor has to move down
    2513         strcat(cm2, CMdown);
    2514         Rrow++;
    2515     }
    2516 
    2517     // now move to the correct column
    2518     strcat(cm2, "\r");          // start at col 0
    2519     // just send out orignal source char to get to correct place
    2520     screenp = &screen[row * columns];   // start of screen line
    2521     strncat(cm2, screenp, col);
    2522 
    2523     //----- 3.  Try some other way of moving cursor
    2524     //---------------------------------------------
    2525 
    2526     // pick the shortest cursor motion to send out
    2527     cm = cm1;
    2528     if (strlen(cm2) < strlen(cm)) {
    2529         cm = cm2;
    2530     }  /* else if (strlen(cm3) < strlen(cm)) {
    2531         cm= cm3;
    2532     } */
     2443    if (optimize && col < 16) {
     2444        char *screenp;
     2445        int Rrow = last_row;
     2446        int diff = Rrow - row;
     2447
     2448        if (diff < -5 || diff > 5)
     2449            goto skip;
     2450
     2451        //----- find the minimum # of chars to move cursor -------------
     2452        //----- 2.  Try moving with discreet chars (Newline, [back]space, ...)
     2453        cm2[0] = '\0';
     2454
     2455        // move to the correct row
     2456        while (row < Rrow) {
     2457            // the cursor has to move up
     2458            strcat(cm2, CMup);
     2459            Rrow--;
     2460        }
     2461        while (row > Rrow) {
     2462            // the cursor has to move down
     2463            strcat(cm2, CMdown);
     2464            Rrow++;
     2465        }
     2466
     2467        // now move to the correct column
     2468        strcat(cm2, "\r");          // start at col 0
     2469        // just send out orignal source char to get to correct place
     2470        screenp = &screen[row * columns];   // start of screen line
     2471        strncat(cm2, screenp, col);
     2472
     2473        // pick the shortest cursor motion to send out
     2474        if (strlen(cm2) < strlen(cm)) {
     2475            cm = cm2;
     2476        }
     2477 skip: ;
     2478    }
     2479    last_row = row;
    25332480#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
    2534  pc0:
    2535     write1(cm);                 // move the cursor
     2481    write1(cm);
    25362482}
    25372483
     
    25402486{
    25412487    write1(Ceol);   // Erase from cursor to end of line
     2488}
     2489
     2490static void go_bottom_and_clear_to_eol(void)
     2491{
     2492    place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
     2493    clear_to_eol(); // erase to end of line
    25422494}
    25432495
     
    26122564    }
    26132565    if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
    2614         last_status_cksum= cksum;       // remember if we have seen this line
    2615         place_cursor(rows - 1, 0, FALSE);   // put cursor on status line
     2566        last_status_cksum = cksum;      // remember if we have seen this line
     2567        go_bottom_and_clear_to_eol();
    26162568        write1(status_buffer);
    2617         clear_to_eol();
    26182569        if (have_status_msg) {
    26192570            if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
     
    26262577        place_cursor(crow, ccol, FALSE);    // put cursor back in correct place
    26272578    }
    2628     fflush(stdout);
     2579    fflush_all();
    26292580}
    26302581
    26312582//----- format the status buffer, the bottom line of screen ------
    26322583// format status buffer, with STANDOUT mode
    2633 static void psbs(const char *format, ...)
     2584static void status_line_bold(const char *format, ...)
    26342585{
    26352586    va_list args;
     
    26372588    va_start(args, format);
    26382589    strcpy(status_buffer, SOs); // Terminal standout mode on
    2639     vsprintf(status_buffer + strlen(status_buffer), format, args);
     2590    vsprintf(status_buffer + sizeof(SOs)-1, format, args);
    26402591    strcat(status_buffer, SOn); // Terminal standout mode off
    26412592    va_end(args);
     
    26452596
    26462597// format status buffer
    2647 static void psb(const char *format, ...)
     2598static void status_line(const char *format, ...)
    26482599{
    26492600    va_list args;
     
    26562607}
    26572608
    2658 static void ni(const char * s) // display messages
    2659 {
    2660     char buf[MAX_LINELEN];
     2609// copy s to buf, convert unprintable
     2610static void print_literal(char *buf, const char *s)
     2611{
     2612    char *d;
     2613    unsigned char c;
     2614
     2615    buf[0] = '\0';
     2616    if (!s[0])
     2617        s = "(NULL)";
     2618
     2619    d = buf;
     2620    for (; *s; s++) {
     2621        int c_is_no_print;
     2622
     2623        c = *s;
     2624        c_is_no_print = (c & 0x80) && !Isprint(c);
     2625        if (c_is_no_print) {
     2626            strcpy(d, SOn);
     2627            d += sizeof(SOn)-1;
     2628            c = '.';
     2629        }
     2630        if (c < ' ' || c == 0x7f) {
     2631            *d++ = '^';
     2632            c |= '@'; /* 0x40 */
     2633            if (c == 0x7f)
     2634                c = '?';
     2635        }
     2636        *d++ = c;
     2637        *d = '\0';
     2638        if (c_is_no_print) {
     2639            strcpy(d, SOs);
     2640            d += sizeof(SOs)-1;
     2641        }
     2642        if (*s == '\n') {
     2643            *d++ = '$';
     2644            *d = '\0';
     2645        }
     2646        if (d - buf > MAX_INPUT_LEN - 10) // paranoia
     2647            break;
     2648    }
     2649}
     2650
     2651static void not_implemented(const char *s)
     2652{
     2653    char buf[MAX_INPUT_LEN];
    26612654
    26622655    print_literal(buf, s);
    2663     psbs("\'%s\' is not implemented", buf);
    2664 }
    2665 
    2666 static int format_edit_status(void) // show file status on status line
    2667 {
    2668     static int tot;
     2656    status_line_bold("\'%s\' is not implemented", buf);
     2657}
     2658
     2659// show file status on status line
     2660static int format_edit_status(void)
     2661{
    26692662    static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
     2663
     2664#define tot format_edit_status__tot
     2665
    26702666    int cur, percent, ret, trunc_at;
    26712667
     
    27182714
    27192715    return trunc_at;  /* had to truncate */
     2716#undef tot
    27202717}
    27212718
     
    27242721{
    27252722    place_cursor(0, 0, FALSE);  // put cursor in correct place
    2726     clear_to_eos();     // tel terminal to erase display
     2723    clear_to_eos();     // tell terminal to erase display
    27272724    screen_erase();     // erase the internal screen buffer
    27282725    last_status_cksum = 0;  // force status update
     
    27322729
    27332730//----- Format a text[] line into a buffer ---------------------
    2734 static void format_line(char *dest, char *src, int li)
    2735 {
     2731static char* format_line(char *src /*, int li*/)
     2732{
     2733    unsigned char c;
    27362734    int co;
    2737     char c;
    2738 
    2739     for (co = 0; co < MAX_SCR_COLS; co++) {
    2740         c = ' ';        // assume blank
    2741         if (li > 0 && co == 0) {
    2742             c = '~';        // not first line, assume Tilde
    2743         }
    2744         // are there chars in text[] and have we gone past the end
    2745         if (text < end && src < end) {
     2735    int ofs = offset;
     2736    char *dest = scr_out_buf; // [MAX_SCR_COLS + MAX_TABSTOP * 2]
     2737
     2738    c = '~'; // char in col 0 in non-existent lines is '~'
     2739    co = 0;
     2740    while (co < columns + tabstop) {
     2741        // have we gone past the end?
     2742        if (src < end) {
    27462743            c = *src++;
    2747         }
    2748         if (c == '\n')
    2749             break;
    2750         if ((c & 0x80) && !Isprint(c)) {
    2751             c = '.';
    2752         }
    2753         if ((unsigned char)(c) < ' ' || c == 0x7f) {
    2754             if (c == '\t') {
    2755                 c = ' ';
    2756                 //       co %    8     !=     7
    2757                 for (; (co % tabstop) != (tabstop - 1); co++) {
    2758                     dest[co] = c;
     2744            if (c == '\n')
     2745                break;
     2746            if ((c & 0x80) && !Isprint(c)) {
     2747                c = '.';
     2748            }
     2749            if (c < ' ' || c == 0x7f) {
     2750                if (c == '\t') {
     2751                    c = ' ';
     2752                    //      co %    8     !=     7
     2753                    while ((co % tabstop) != (tabstop - 1)) {
     2754                        dest[co++] = c;
     2755                    }
     2756                } else {
     2757                    dest[co++] = '^';
     2758                    if (c == 0x7f)
     2759                        c = '?';
     2760                    else
     2761                        c += '@'; // Ctrl-X -> 'X'
    27592762                }
    2760             } else {
    2761                 dest[co++] = '^';
    2762                 if (c == 0x7f)
    2763                     c = '?';
    2764                 else
    2765                     c += '@';       // make it visible
    27662763            }
    27672764        }
    2768         // the co++ is done here so that the column will
    2769         // not be overwritten when we blank-out the rest of line
    2770         dest[co] = c;
     2765        dest[co++] = c;
     2766        // discard scrolled-off-to-the-left portion,
     2767        // in tabstop-sized pieces
     2768        if (ofs >= tabstop && co >= tabstop) {
     2769            memmove(dest, dest + tabstop, co);
     2770            co -= tabstop;
     2771            ofs -= tabstop;
     2772        }
    27712773        if (src >= end)
    27722774            break;
    27732775    }
     2776    // check "short line, gigantic offset" case
     2777    if (co < ofs)
     2778        ofs = co;
     2779    // discard last scrolled off part
     2780    co -= ofs;
     2781    dest += ofs;
     2782    // fill the rest with spaces
     2783    if (co < columns)
     2784        memset(&dest[co], ' ', columns - co);
     2785    return dest;
    27742786}
    27752787
     
    27812793static void refresh(int full_screen)
    27822794{
    2783     static int old_offset;
     2795#define old_offset refresh__old_offset
    27842796
    27852797    int li, changed;
    2786     char buf[MAX_SCR_COLS];
    27872798    char *tp, *sp;      // pointer into text[] and screen[]
    2788 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
    2789     int last_li = -2; // last line that changed- for optimizing cursor movement
    2790 #endif
    2791 
    2792     if (ENABLE_FEATURE_VI_WIN_RESIZE)
    2793         get_terminal_width_height(0, &columns, &rows);
     2799
     2800    if (ENABLE_FEATURE_VI_WIN_RESIZE IF_FEATURE_VI_ASK_TERMINAL(&& !G.get_rowcol_error) ) {
     2801        unsigned c = columns, r = rows;
     2802        query_screen_dimensions();
     2803        full_screen |= (c - columns) | (r - rows);
     2804    }
    27942805    sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
    27952806    tp = screenbegin;   // index into text[] of top line
     
    27982809    for (li = 0; li < rows - 1; li++) {
    27992810        int cs, ce;             // column start & end
    2800         memset(buf, ' ', MAX_SCR_COLS);     // blank-out the buffer
    2801         buf[MAX_SCR_COLS-1] = 0;        // NULL terminate the buffer
    2802         // format current text line into buf
    2803         format_line(buf, tp, li);
     2811        char *out_buf;
     2812        // format current text line
     2813        out_buf = format_line(tp /*, li*/);
    28042814
    28052815        // skip to the end of the current text[] line
    2806         while (tp < end && *tp++ != '\n') /*no-op*/;
    2807 
    2808         // see if there are any changes between vitual screen and buf
     2816        if (tp < end) {
     2817            char *t = memchr(tp, '\n', end - tp);
     2818            if (!t) t = end - 1;
     2819            tp = t + 1;
     2820        }
     2821
     2822        // see if there are any changes between vitual screen and out_buf
    28092823        changed = FALSE;    // assume no change
    2810         cs= 0;
    2811         ce= columns-1;
     2824        cs = 0;
     2825        ce = columns - 1;
    28122826        sp = &screen[li * columns]; // start of screen line
    28132827        if (full_screen) {
     
    28182832        // look forward for first difference between buf and screen
    28192833        for (; cs <= ce; cs++) {
    2820             if (buf[cs + offset] != sp[cs]) {
     2834            if (out_buf[cs] != sp[cs]) {
    28212835                changed = TRUE; // mark for redraw
    28222836                break;
     
    28242838        }
    28252839
    2826         // look backward for last difference between buf and screen
    2827         for ( ; ce >= cs; ce--) {
    2828             if (buf[ce + offset] != sp[ce]) {
     2840        // look backward for last difference between out_buf and screen
     2841        for (; ce >= cs; ce--) {
     2842            if (out_buf[ce] != sp[ce]) {
    28292843                changed = TRUE; // mark for redraw
    28302844                break;
     
    28402854
    28412855        // make a sanity check of columns indexes
    2842         if (cs < 0) cs= 0;
    2843         if (ce > columns-1) ce= columns-1;
    2844         if (cs > ce) {  cs= 0;  ce= columns-1; }
    2845         // is there a change between vitual screen and buf
     2856        if (cs < 0) cs = 0;
     2857        if (ce > columns - 1) ce = columns - 1;
     2858        if (cs > ce) { cs = 0; ce = columns - 1; }
     2859        // is there a change between vitual screen and out_buf
    28462860        if (changed) {
    2847             //  copy changed part of buffer to virtual screen
    2848             memmove(sp+cs, buf+(cs+offset), ce-cs+1);
     2861            // copy changed part of buffer to virtual screen
     2862            memcpy(sp+cs, out_buf+cs, ce-cs+1);
    28492863
    28502864            // move cursor to column of first change
    2851             if (offset != old_offset) {
    2852                 // opti_cur_move is still too stupid
    2853                 // to handle offsets correctly
    2854                 place_cursor(li, cs, FALSE);
    2855             } else {
    2856 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
    2857                 // if this just the next line
    2858                 //  try to optimize cursor movement
    2859                 //  otherwise, use standard ESC sequence
    2860                 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
    2861                 last_li= li;
    2862 #else
    2863                 place_cursor(li, cs, FALSE);    // use standard ESC sequence
    2864 #endif /* FEATURE_VI_OPTIMIZE_CURSOR */
    2865             }
     2865            //if (offset != old_offset) {
     2866            //  // place_cursor is still too stupid
     2867            //  // to handle offsets correctly
     2868            //  place_cursor(li, cs, FALSE);
     2869            //} else {
     2870                place_cursor(li, cs, TRUE);
     2871            //}
    28662872
    28672873            // write line out to terminal
    2868             {
    2869                 int nic = ce - cs + 1;
    2870                 char *out = sp + cs;
    2871 
    2872                 while (nic-- > 0) {
    2873                     putchar(*out);
    2874                     out++;
    2875                 }
    2876             }
    2877 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
    2878             last_row = li;
    2879 #endif
    2880         }
    2881     }
    2882 
    2883 #if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
    2884     place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
    2885     last_row = crow;
    2886 #else
    2887     place_cursor(crow, ccol, FALSE);
    2888 #endif
    2889 
    2890     if (offset != old_offset)
    2891         old_offset = offset;
     2874            fwrite(&sp[cs], ce - cs + 1, 1, stdout);
     2875        }
     2876    }
     2877
     2878    place_cursor(crow, ccol, TRUE);
     2879
     2880    old_offset = offset;
     2881#undef old_offset
    28922882}
    28932883
     
    29142904
    29152905//----- Execute a Vi Command -----------------------------------
    2916 static void do_cmd(char c)
    2917 {
    2918     const char *msg;
    2919     char c1, *p, *q, buf[9], *save_dot;
    2920     int cnt, i, j, dir, yf;
    2921 
    2922     c1 = c;             // quiet the compiler
    2923     cnt = yf = dir = 0; // quiet the compiler
    2924     msg = p = q = save_dot = buf;   // quiet the compiler
    2925     memset(buf, '\0', 9);   // clear buf
     2906static void do_cmd(int c)
     2907{
     2908    const char *msg = msg; // for compiler
     2909    char *p, *q, *save_dot;
     2910    char buf[12];
     2911    int dir;
     2912    int cnt, i, j;
     2913    int c1;
     2914
     2915//  c1 = c; // quiet the compiler
     2916//  cnt = yf = 0; // quiet the compiler
     2917//  msg = p = q = save_dot = buf; // quiet the compiler
     2918    memset(buf, '\0', 12);
    29262919
    29272920    show_status_line();
     
    29292922    /* if this is a cursor key, skip these checks */
    29302923    switch (c) {
    2931         case VI_K_UP:
    2932         case VI_K_DOWN:
    2933         case VI_K_LEFT:
    2934         case VI_K_RIGHT:
    2935         case VI_K_HOME:
    2936         case VI_K_END:
    2937         case VI_K_PAGEUP:
    2938         case VI_K_PAGEDOWN:
     2924        case KEYCODE_UP:
     2925        case KEYCODE_DOWN:
     2926        case KEYCODE_LEFT:
     2927        case KEYCODE_RIGHT:
     2928        case KEYCODE_HOME:
     2929        case KEYCODE_END:
     2930        case KEYCODE_PAGEUP:
     2931        case KEYCODE_PAGEDOWN:
     2932        case KEYCODE_DELETE:
    29392933            goto key_cmd_mode;
    29402934    }
     
    29422936    if (cmd_mode == 2) {
    29432937        //  flip-flop Insert/Replace mode
    2944         if (c == VI_K_INSERT)
     2938        if (c == KEYCODE_INSERT)
    29452939            goto dc_i;
    29462940        // we are 'R'eplacing the current *dot with new char
     
    29592953    if (cmd_mode == 1) {
    29602954        //  hitting "Insert" twice means "R" replace mode
    2961         if (c == VI_K_INSERT) goto dc5;
     2955        if (c == KEYCODE_INSERT) goto dc5;
    29622956        // insert the char c at "dot"
    29632957        if (1 <= c || Isprint(c)) {
     
    29952989        //case ')': // )-
    29962990        //case '*': // *-
    2997         //case ',': // ,-
    29982991        //case '=': // =-
    29992992        //case '@': // @-
     
    30093002        //case '_': // _-
    30103003        //case '`': // `-
    3011         //case 'g': // g-
    30123004        //case 'u': // u- FIXME- there is no undo
    30133005        //case 'v': // v-
    3014     default:            // unrecognised command
     3006    default:            // unrecognized command
    30153007        buf[0] = c;
    30163008        buf[1] = '\0';
    3017         if (c < ' ') {
    3018             buf[0] = '^';
    3019             buf[1] = c + '@';
    3020             buf[2] = '\0';
    3021         }
    3022         ni(buf);
     3009        not_implemented(buf);
    30233010        end_cmd_q();    // stop adding to q
    30243011    case 0x00:          // nul- ignore
    30253012        break;
    30263013    case 2:         // ctrl-B  scroll up   full screen
    3027     case VI_K_PAGEUP:   // Cursor Key Page Up
     3014    case KEYCODE_PAGEUP:    // Cursor Key Page Up
    30283015        dot_scroll(rows - 2, -1);
    30293016        break;
    3030 #if ENABLE_FEATURE_VI_USE_SIGNALS
    3031     case 0x03:          // ctrl-C   interrupt
    3032         longjmp(restart, 1);
    3033         break;
    3034     case 26:            // ctrl-Z suspend
    3035         suspend_sig(SIGTSTP);
    3036         break;
    3037 #endif
    30383017    case 4:         // ctrl-D  scroll down half screen
    30393018        dot_scroll((rows - 2) / 2, 1);
     
    30433022        break;
    30443023    case 6:         // ctrl-F  scroll down full screen
    3045     case VI_K_PAGEDOWN: // Cursor Key Page Down
     3024    case KEYCODE_PAGEDOWN:  // Cursor Key Page Down
    30463025        dot_scroll(rows - 2, 1);
    30473026        break;
     
    30503029        break;
    30513030    case 'h':           // h- move left
    3052     case VI_K_LEFT: // cursor key Left
     3031    case KEYCODE_LEFT:  // cursor key Left
    30533032    case 8:     // ctrl-H- move left    (This may be ERASE char)
    30543033    case 0x7f:  // DEL- move left   (This may be ERASE char)
    3055         if (cmdcnt-- > 1) {
     3034        if (--cmdcnt > 0) {
    30563035            do_cmd(c);
    3057         }               // repeat cnt
     3036        }
    30583037        dot_left();
    30593038        break;
    30603039    case 10:            // Newline ^J
    30613040    case 'j':           // j- goto next line, same col
    3062     case VI_K_DOWN: // cursor key Down
    3063         if (cmdcnt-- > 1) {
     3041    case KEYCODE_DOWN:  // cursor key Down
     3042        if (--cmdcnt > 0) {
    30643043            do_cmd(c);
    3065         }               // repeat cnt
     3044        }
    30663045        dot_next();     // go to next B-o-l
    30673046        dot = move_to_col(dot, ccol + offset);  // try stay in same col
     
    30783057    case 13:            // Carriage Return ^M
    30793058    case '+':           // +- goto next line
    3080         if (cmdcnt-- > 1) {
     3059        if (--cmdcnt > 0) {
    30813060            do_cmd(c);
    3082         }               // repeat cnt
     3061        }
    30833062        dot_next();
    30843063        dot_skip_over_ws();
     
    30993078    case ' ':           // move right
    31003079    case 'l':           // move right
    3101     case VI_K_RIGHT:    // Cursor Key Right
    3102         if (cmdcnt-- > 1) {
     3080    case KEYCODE_RIGHT: // Cursor Key Right
     3081        if (--cmdcnt > 0) {
    31033082            do_cmd(c);
    3104         }               // repeat cnt
     3083        }
    31053084        dot_right();
    31063085        break;
    31073086#if ENABLE_FEATURE_VI_YANKMARK
    31083087    case '"':           // "- name a register to use for Delete/Yank
    3109         c1 = get_one_char();
    3110         c1 = tolower(c1);
    3111         if (islower(c1)) {
    3112             YDreg = c1 - 'a';
     3088        c1 = (get_one_char() | 0x20) - 'a'; // | 0x20 is tolower()
     3089        if ((unsigned)c1 <= 25) { // a-z?
     3090            YDreg = c1;
    31133091        } else {
    31143092            indicate_error(c);
     
    31163094        break;
    31173095    case '\'':          // '- goto a specific mark
    3118         c1 = get_one_char();
    3119         c1 = tolower(c1);
    3120         if (islower(c1)) {
    3121             c1 = c1 - 'a';
     3096        c1 = (get_one_char() | 0x20) - 'a';
     3097        if ((unsigned)c1 <= 25) { // a-z?
    31223098            // get the b-o-l
    3123             q = mark[(unsigned char) c1];
     3099            q = mark[c1];
    31243100            if (text <= q && q < end) {
    31253101                dot = q;
     
    31403116        // correct location! It could be off by many lines!
    31413117        // Well..., at least its quick and dirty.
    3142         c1 = get_one_char();
    3143         c1 = tolower(c1);
    3144         if (islower(c1)) {
    3145             c1 = c1 - 'a';
     3118        c1 = (get_one_char() | 0x20) - 'a';
     3119        if ((unsigned)c1 <= 25) { // a-z?
    31463120            // remember the line
    3147             mark[(int) c1] = dot;
     3121            mark[c1] = dot;
    31483122        } else {
    31493123            indicate_error(c);
     
    31533127    case 'p':           // p- put register after
    31543128        p = reg[YDreg];
    3155         if (p == 0) {
    3156             psbs("Nothing in register %c", what_reg());
     3129        if (p == NULL) {
     3130            status_line_bold("Nothing in register %c", what_reg());
    31573131            break;
    31583132        }
     
    31743148                dot_right();    // move to right, can move to NL
    31753149        }
    3176         dot = string_insert(dot, p);    // insert the string
     3150        string_insert(dot, p);  // insert the string
    31773151        end_cmd_q();    // stop adding to q
    31783152        break;
     
    31823156            q = end_line(dot);
    31833157            p = text_hole_delete(p, q); // delete cur line
    3184             p = string_insert(p, reg[Ureg]);    // insert orig line
     3158            p += string_insert(p, reg[Ureg]);   // insert orig line
    31853159            dot = p;
    31863160            dot_skip_over_ws();
     
    31893163#endif /* FEATURE_VI_YANKMARK */
    31903164    case '$':           // $- goto end of line
    3191     case VI_K_END:      // Cursor Key End
    3192         if (cmdcnt-- > 1) {
     3165    case KEYCODE_END:       // Cursor Key End
     3166        if (--cmdcnt > 0) {
     3167            dot_next();
    31933168            do_cmd(c);
    3194         }               // repeat cnt
     3169        }
    31953170        dot = end_line(dot);
    31963171        break;
     
    32163191        // dont separate these two commands. 'f' depends on ';'
    32173192        //
    3218         //**** fall thru to ... ';'
     3193        //**** fall through to ... ';'
    32193194    case ';':           // ;- look at rest of line for last forward char
    3220         if (cmdcnt-- > 1) {
     3195        if (--cmdcnt > 0) {
    32213196            do_cmd(';');
    3222         }               // repeat cnt
     3197        }
    32233198        if (last_forward_char == 0)
    32243199            break;
     
    32303205            dot = q;
    32313206        break;
     3207    case ',':           // repeat latest 'f' in opposite direction
     3208        if (--cmdcnt > 0) {
     3209            do_cmd(',');
     3210        }
     3211        if (last_forward_char == 0)
     3212            break;
     3213        q = dot - 1;
     3214        while (q >= text && *q != '\n' && *q != last_forward_char) {
     3215            q--;
     3216        }
     3217        if (q >= text && *q == last_forward_char)
     3218            dot = q;
     3219        break;
     3220
    32323221    case '-':           // -- goto prev line
    3233         if (cmdcnt-- > 1) {
     3222        if (--cmdcnt > 0) {
    32343223            do_cmd(c);
    3235         }               // repeat cnt
     3224        }
    32363225        dot_prev();
    32373226        dot_skip_over_ws();
     
    32413230        // Stuff the last_modifying_cmd back into stdin
    32423231        // and let it be re-executed.
    3243         if (last_modifying_cmd != 0) {
     3232        if (lmc_len > 0) {
     3233            last_modifying_cmd[lmc_len] = 0;
    32443234            ioq = ioq_start = xstrdup(last_modifying_cmd);
    32453235        }
     
    32523242        buf[1] = '\0';
    32533243        q = get_input_line(buf);    // get input line- use "status line"
    3254         if (q[0] && !q[1])
     3244        if (q[0] && !q[1]) {
     3245            if (last_search_pattern[0])
     3246                last_search_pattern[0] = c;
    32553247            goto dc3; // if no pat re-use old pat
     3248        }
    32563249        if (q[0]) {       // strlen(q) > 1: new pat- save it and find
    32573250            // there is a new pat
     
    32633256        break;
    32643257    case 'N':           // N- backward search for last pattern
    3265         if (cmdcnt-- > 1) {
     3258        if (--cmdcnt > 0) {
    32663259            do_cmd(c);
    3267         }               // repeat cnt
     3260        }
    32683261        dir = BACK;     // assume BACKWARD search
    32693262        p = dot - 1;
     
    32773270        // search rest of text[] starting at next char
    32783271        // if search fails return orignal "p" not the "p+1" address
    3279         if (cmdcnt-- > 1) {
     3272        if (--cmdcnt > 0) {
    32803273            do_cmd(c);
    3281         }               // repeat cnt
     3274        }
    32823275 dc3:
    3283         if (last_search_pattern == 0) {
    3284             msg = "No previous regular expression";
    3285             goto dc2;
    3286         }
    3287         if (last_search_pattern[0] == '/') {
    3288             dir = FORWARD;  // assume FORWARD search
    3289             p = dot + 1;
    3290         }
     3276        dir = FORWARD;  // assume FORWARD search
     3277        p = dot + 1;
    32913278        if (last_search_pattern[0] == '?') {
    32923279            dir = BACK;
     
    33173304 dc2:
    33183305        if (*msg)
    3319             psbs("%s", msg);
     3306            status_line_bold("%s", msg);
    33203307        break;
    33213308    case '{':           // {- move backward paragraph
     
    33583345        if (cnt <= 0)
    33593346            break;
    3360         if (strncasecmp(p, "quit", cnt) == 0
    3361          || strncasecmp(p, "q!", cnt) == 0   // delete lines
     3347        if (strncmp(p, "quit", cnt) == 0
     3348         || strncmp(p, "q!", cnt) == 0   // delete lines
    33623349        ) {
    33633350            if (file_modified && p[1] != '!') {
    3364                 psbs("No write since last change (:quit! overrides)");
     3351                status_line_bold("No write since last change (:quit! overrides)");
    33653352            } else {
    33663353                editing = 0;
    33673354            }
    3368         } else if (strncasecmp(p, "write", cnt) == 0
    3369                 || strncasecmp(p, "wq", cnt) == 0
    3370                 || strncasecmp(p, "wn", cnt) == 0
    3371                 || strncasecmp(p, "x", cnt) == 0
     3355        } else if (strncmp(p, "write", cnt) == 0
     3356                || strncmp(p, "wq", cnt) == 0
     3357                || strncmp(p, "wn", cnt) == 0
     3358                || (p[0] == 'x' && !p[1])
    33723359        ) {
    33733360            cnt = file_write(current_filename, text, end - 1);
    33743361            if (cnt < 0) {
    33753362                if (cnt == -1)
    3376                     psbs("Write error: %s", strerror(errno));
     3363                    status_line_bold("Write error: %s", strerror(errno));
    33773364            } else {
    33783365                file_modified = 0;
    33793366                last_file_modified = -1;
    3380                 psb("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
     3367                status_line("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
    33813368                if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
    33823369                 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
     
    33853372                }
    33863373            }
    3387         } else if (strncasecmp(p, "file", cnt) == 0) {
     3374        } else if (strncmp(p, "file", cnt) == 0) {
    33883375            last_status_cksum = 0;  // force status update
    33893376        } else if (sscanf(p, "%d", &j) > 0) {
    33903377            dot = find_line(j);     // go to line # j
    33913378            dot_skip_over_ws();
    3392         } else {        // unrecognised cmd
    3393             ni(p);
     3379        } else {        // unrecognized cmd
     3380            not_implemented(p);
    33943381        }
    33953382#endif /* !FEATURE_VI_COLON */
     
    34273414    case 'A':           // A- append at e-o-l
    34283415        dot_end();      // go to e-o-l
    3429         //**** fall thru to ... 'a'
     3416        //**** fall through to ... 'a'
    34303417    case 'a':           // a- append after current char
    34313418        if (*dot != '\n')
     
    34363423    case 'E':           // E- end of a blank-delimited word
    34373424    case 'W':           // W- forward a blank-delimited word
    3438         if (cmdcnt-- > 1) {
     3425        if (--cmdcnt > 0) {
    34393426            do_cmd(c);
    3440         }               // repeat cnt
     3427        }
    34413428        dir = FORWARD;
    34423429        if (c == 'B')
     
    34623449#endif
    34633450        break;
     3451    case 'g': // 'gg' goto a line number (vim) (default: very first line)
     3452        c1 = get_one_char();
     3453        if (c1 != 'g') {
     3454            buf[0] = 'g';
     3455            buf[1] = c1; // TODO: if Unicode?
     3456            buf[2] = '\0';
     3457            not_implemented(buf);
     3458            break;
     3459        }
     3460        if (cmdcnt == 0)
     3461            cmdcnt = 1;
     3462        /* fall through */
    34643463    case 'G':       // G- goto to a line number (default= E-O-F)
    34653464        dot = end - 1;              // assume E-O-F
     
    34743473            cmdcnt = (rows - 1);
    34753474        }
    3476         if (cmdcnt-- > 1) {
     3475        if (--cmdcnt > 0) {
    34773476            do_cmd('+');
    3478         }               // repeat cnt
     3477        }
    34793478        dot_skip_over_ws();
    34803479        break;
     
    34823481        dot_begin();    // 0
    34833482        dot_skip_over_ws();
    3484         //**** fall thru to ... 'i'
     3483        //**** fall through to ... 'i'
    34853484    case 'i':           // i- insert before current char
    3486     case VI_K_INSERT:   // Cursor Key Insert
     3485    case KEYCODE_INSERT:    // Cursor Key Insert
    34873486 dc_i:
    34883487        cmd_mode = 1;   // start insrting
    34893488        break;
    34903489    case 'J':           // J- join current and next lines together
    3491         if (cmdcnt-- > 2) {
     3490        if (--cmdcnt > 1) {
    34923491            do_cmd(c);
    3493         }               // repeat cnt
     3492        }
    34943493        dot_end();      // move to NL
    34953494        if (dot < end - 1) {    // make sure not last char in text[]
     
    35073506            cmdcnt = (rows - 1);
    35083507        }
    3509         if (cmdcnt-- > 1) {
     3508        if (--cmdcnt > 0) {
    35103509            do_cmd('-');
    3511         }               // repeat cnt
     3510        }
    35123511        dot_begin();
    35133512        dot_skip_over_ws();
     
    35373536        cmd_mode = 2;
    35383537        break;
     3538    case KEYCODE_DELETE:
     3539        c = 'x';
     3540        // fall through
    35393541    case 'X':           // X- delete char before dot
    35403542    case 'x':           // x- delete the current char
    35413543    case 's':           // s- substitute the current char
    3542         if (cmdcnt-- > 1) {
     3544        if (--cmdcnt > 0) {
    35433545            do_cmd(c);
    3544         }               // repeat cnt
     3546        }
    35453547        dir = 0;
    35463548        if (c == 'X')
     
    35643566        if (file_modified) {
    35653567            if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
    3566                 psbs("\"%s\" File is read only", current_filename);
     3568                status_line_bold("\"%s\" File is read only", current_filename);
    35673569                break;
    35683570            }
     
    35703572            if (cnt < 0) {
    35713573                if (cnt == -1)
    3572                     psbs("Write error: %s", strerror(errno));
     3574                    status_line_bold("Write error: %s", strerror(errno));
    35733575            } else if (cnt == (end - 1 - text + 1)) {
    35743576                editing = 0;
     
    35843586    case 'b':           // b- back a word
    35853587    case 'e':           // e- end of word
    3586         if (cmdcnt-- > 1) {
     3588        if (--cmdcnt > 0) {
    35873589            do_cmd(c);
    3588         }               // repeat cnt
     3590        }
    35893591        dir = FORWARD;
    35903592        if (c == 'b')
     
    36083610    case 'Y':           // Y- Yank a line
    36093611#endif
     3612    {
     3613        int yf, ml, whole = 0;
    36103614        yf = YANKDEL;   // assume either "c" or "d"
    36113615#if ENABLE_FEATURE_VI_YANKMARK
     
    36163620        if (c != 'Y')
    36173621            c1 = get_one_char();    // get the type of thing to delete
    3618         find_range(&p, &q, c1);
     3622        // determine range, and whether it spans lines
     3623        ml = find_range(&p, &q, c1);
    36193624        if (c1 == 27) { // ESC- user changed mind and wants out
    36203625            c = c1 = 27;    // Escape- do nothing
     
    36283633                }
    36293634            }
    3630             dot = yank_delete(p, q, 0, yf); // delete word
    3631         } else if (strchr("^0bBeEft$", c1)) {
    3632             // single line copy text into a register and delete
    3633             dot = yank_delete(p, q, 0, yf); // delete word
    3634         } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
    3635             // multiple line copy text into a register and delete
    3636             dot = yank_delete(p, q, 1, yf); // delete lines
     3635            dot = yank_delete(p, q, ml, yf);    // delete word
     3636        } else if (strchr("^0bBeEft%$ lh\b\177", c1)) {
     3637            // partial line copy text into a register and delete
     3638            dot = yank_delete(p, q, ml, yf);    // delete word
     3639        } else if (strchr("cdykjHL+-{}\r\n", c1)) {
     3640            // whole line copy text into a register and delete
     3641            dot = yank_delete(p, q, ml, yf);    // delete lines
     3642            whole = 1;
     3643        } else {
     3644            // could not recognize object
     3645            c = c1 = 27;    // error-
     3646            ml = 0;
     3647            indicate_error(c);
     3648        }
     3649        if (ml && whole) {
    36373650            if (c == 'c') {
    36383651                dot = char_insert(dot, '\n');
    36393652                // on the last line of file don't move to prev line
    3640                 if (dot != (end-1)) {
     3653                if (whole && dot != (end-1)) {
    36413654                    dot_prev();
    36423655                }
     
    36453658                dot_skip_over_ws();
    36463659            }
    3647         } else {
    3648             // could not recognize object
    3649             c = c1 = 27;    // error-
    3650             indicate_error(c);
    36513660        }
    36523661        if (c1 != 27) {
     
    36693678                    cnt++;
    36703679            }
    3671             psb("%s %d lines (%d chars) using [%c]",
     3680            status_line("%s %d lines (%d chars) using [%c]",
    36723681                buf, cnt, strlen(reg[YDreg]), what_reg());
    36733682#endif
     
    36753684        }
    36763685        break;
     3686    }
    36773687    case 'k':           // k- goto prev line, same col
    3678     case VI_K_UP:       // cursor key Up
    3679         if (cmdcnt-- > 1) {
     3688    case KEYCODE_UP:        // cursor key Up
     3689        if (--cmdcnt > 0) {
    36803690            do_cmd(c);
    3681         }               // repeat cnt
     3691        }
    36823692        dot_prev();
    36833693        dot = move_to_col(dot, ccol + offset);  // try stay in same col
     
    36873697        if (*dot != '\n') {
    36883698            *dot = c1;
    3689             file_modified++;    // has the file been modified
     3699            file_modified++;
    36903700        }
    36913701        end_cmd_q();    // stop adding to q
     
    36963706        if (*dot == last_forward_char)
    36973707            dot_left();
    3698         last_forward_char= 0;
     3708        last_forward_char = 0;
    36993709        break;
    37003710    case 'w':           // w- forward a word
    3701         if (cmdcnt-- > 1) {
     3711        if (--cmdcnt > 0) {
    37023712            do_cmd(c);
    3703         }               // repeat cnt
     3713        }
    37043714        if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
    37053715            dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
     
    37273737        break;
    37283738    case '~':           // ~- flip the case of letters   a-z -> A-Z
    3729         if (cmdcnt-- > 1) {
     3739        if (--cmdcnt > 0) {
    37303740            do_cmd(c);
    3731         }               // repeat cnt
     3741        }
    37323742        if (islower(*dot)) {
    37333743            *dot = toupper(*dot);
    3734             file_modified++;    // has the file been modified
     3744            file_modified++;
    37353745        } else if (isupper(*dot)) {
    37363746            *dot = tolower(*dot);
    3737             file_modified++;    // has the file been modified
     3747            file_modified++;
    37383748        }
    37393749        dot_right();
     
    37413751        break;
    37423752        //----- The Cursor and Function Keys -----------------------------
    3743     case VI_K_HOME: // Cursor Key Home
     3753    case KEYCODE_HOME:  // Cursor Key Home
    37443754        dot_begin();
    37453755        break;
    37463756        // The Fn keys could point to do_macro which could translate them
    3747     case VI_K_FUN1: // Function Key F1
    3748     case VI_K_FUN2: // Function Key F2
    3749     case VI_K_FUN3: // Function Key F3
    3750     case VI_K_FUN4: // Function Key F4
    3751     case VI_K_FUN5: // Function Key F5
    3752     case VI_K_FUN6: // Function Key F6
    3753     case VI_K_FUN7: // Function Key F7
    3754     case VI_K_FUN8: // Function Key F8
    3755     case VI_K_FUN9: // Function Key F9
    3756     case VI_K_FUN10:    // Function Key F10
    3757     case VI_K_FUN11:    // Function Key F11
    3758     case VI_K_FUN12:    // Function Key F12
    3759         break;
     3757#if 0
     3758    case KEYCODE_FUN1:  // Function Key F1
     3759    case KEYCODE_FUN2:  // Function Key F2
     3760    case KEYCODE_FUN3:  // Function Key F3
     3761    case KEYCODE_FUN4:  // Function Key F4
     3762    case KEYCODE_FUN5:  // Function Key F5
     3763    case KEYCODE_FUN6:  // Function Key F6
     3764    case KEYCODE_FUN7:  // Function Key F7
     3765    case KEYCODE_FUN8:  // Function Key F8
     3766    case KEYCODE_FUN9:  // Function Key F9
     3767    case KEYCODE_FUN10: // Function Key F10
     3768    case KEYCODE_FUN11: // Function Key F11
     3769    case KEYCODE_FUN12: // Function Key F12
     3770        break;
     3771#endif
    37603772    }
    37613773
     
    37823794}
    37833795
     3796/* NB!  the CRASHME code is unmaintained, and doesn't currently build */
    37843797#if ENABLE_FEATURE_VI_CRASHME
    37853798static int totalcmds = 0;
     
    37913804static int Pp = 99;             // Put command Probability
    37923805static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
    3793 const char chars[20] = "\t012345 abcdABCD-=.$";
    3794 const char *const words[20] = {
     3806static const char chars[20] = "\t012345 abcdABCD-=.$";
     3807static const char *const words[20] = {
    37953808    "this", "is", "a", "test",
    37963809    "broadcast", "the", "emergency", "of",
     
    37993812    "back", "January", "Febuary", "March"
    38003813};
    3801 const char *const lines[20] = {
     3814static const char *const lines[20] = {
    38023815    "You should have received a copy of the GNU General Public License\n",
    38033816    "char c, cm, *cmd, *cmd1;\n",
     
    38213834    "This is too much english for a computer geek.\n",
    38223835};
    3823 char *multilines[20] = {
     3836static char *multilines[20] = {
    38243837    "You should have received a copy of the GNU General Public License\n",
    38253838    "char c, cm, *cmd, *cmd1;\n",
     
    38553868
    38563869    // is there already a command running?
    3857     if (readed_for_parse > 0)
     3870    if (readbuffer[0] > 0)
    38583871        goto cd1;
    38593872 cd0:
    3860     startrbi = rbi = 0;
     3873    readbuffer[0] = 'X';
     3874    startrbi = rbi = 1;
    38613875    sleeptime = 0;          // how long to pause between commands
    3862     memset(readbuffer, '\0', MAX_LINELEN);   // clear the read buffer
     3876    memset(readbuffer, '\0', sizeof(readbuffer));
    38633877    // generate a command by percentages
    38643878    percent = (int) lrand48() % 100;        // get a number from 0-99
     
    39323946        strcat(readbuffer, "\033");
    39333947    }
    3934     readed_for_parse = strlen(readbuffer);
     3948    readbuffer[0] = strlen(readbuffer + 1);
    39353949 cd1:
    39363950    totalcmds++;
     
    39453959
    39463960    time_t tim;
    3947     char d[2], msg[MAX_LINELEN];
     3961    char d[2], msg[80];
    39483962
    39493963    msg[0] = '\0';
     
    39683982
    39693983    if (msg[0]) {
    3970         alarm(0);
    39713984        printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
    39723985            totalcmds, last_input_char, msg, SOs, SOn);
    3973         fflush(stdout);
    3974         while (read(0, d, 1) > 0) {
     3986        fflush_all();
     3987        while (safe_read(STDIN_FILENO, d, 1) > 0) {
    39753988            if (d[0] == '\n' || d[0] == '\r')
    39763989                break;
    39773990        }
    3978         alarm(3);
    3979     }
    3980     tim = (time_t) time((time_t *) 0);
     3991    }
     3992    tim = time(NULL);
    39813993    if (tim >= (oldtim + 3)) {
    39823994        sprintf(status_buffer,
Note: See TracChangeset for help on using the changeset viewer.