[BACK]Return to virus.c CVS log [TXT][DIR] Up to [contributed] / dgamelaunch-openbsd

Annotation of dgamelaunch-openbsd/virus.c, Revision 1.1.1.1

1.1       rubenllo    1: /* vi: set sw=8 ts=8: */
                      2: /*
                      3:  * virus - vi resembling utility skeleton - based on
                      4:  * tiny vi.c: A small 'vi' clone (from busybox 0.52)
                      5:  *
                      6:  * Copyright (C) 2001 - 2003 Stefan Koerner <ripclaw@rocklinux.org>
                      7:  * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
                      8:  *
                      9:  *  This program is free software; you can redistribute it and/or modify
                     10:  *  it under the terms of the GNU General Public License as published by
                     11:  *  the Free Software Foundation; either version 2 of the License, or
                     12:  *  (at your option) any later version.
                     13:  *
                     14:  *  This program is distributed in the hope that it will be useful,
                     15:  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
                     16:  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     17:  *  GNU General Public License for more details.
                     18:  *
                     19:  *  You should have received a copy of the GNU General Public License
                     20:  *  along with this program; if not, write to the Free Software
                     21:  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
                     22:  */
                     23:
                     24: #include "config.h"
                     25:
                     26: char *vi_Version = "0.0.6+dgamelaunch " PACKAGE_VERSION;
                     27:
                     28: /*
                     29:  * To compile:
                     30:  *     gcc -Wall -Os -s -o vi virus.c
                     31:  *     strip vi
                     32:  */
                     33:
                     34: /* this was in here from busybox, will sort out later FIXME
                     35:  * Things To Do:
                     36:  *     EXINIT
                     37:  *     $HOME/.exrc  and  ./.exrc
                     38:  *     add magic to search     /foo.*bar
                     39:  *     add :help command
                     40:  *     :map macros
                     41:  *     how about mode lines:   vi: set sw=8 ts=8:
                     42:  *     if mark[] values were line numbers rather than pointers
                     43:  *        it would be easier to change the mark when add/delete lines
                     44:  *     More intelligence in refresh()
                     45:  *     ":r !cmd"  and  "!cmd"  to filter text through an external command
                     46:  *     A true "undo" facility
                     47:  *     An "ex" line oriented mode- maybe using "cmdedit"
                     48:  */
                     49:
                     50: //----  Feature --------------  Bytes to immplement
                     51: #define vi_main                        main
                     52: #define BB_FEATURE_VI_COLON     // 4288
                     53: #define BB_FEATURE_VI_YANKMARK  // 1408
                     54: #define BB_FEATURE_VI_SEARCH    // 1088
                     55: // #define BB_FEATURE_VI_USE_SIGNALS    // 1056
                     56: #define BB_FEATURE_VI_DOT_CMD   //  576
                     57: #define BB_FEATURE_VI_READONLY  //  128
                     58: //#define BB_FEATURE_VI_SETOPTS   //  576
                     59: #define BB_FEATURE_VI_SET       //  224
                     60: #define BB_FEATURE_VI_WIN_RESIZE  //  256  WIN_RESIZE
                     61: // To test editor using CRASHME:
                     62: //    vi -C filename
                     63: // To stop testing, wait until all to text[] is deleted, or
                     64: //    Ctrl-Z and kill -9 %1
                     65: // while in the editor Ctrl-T will toggle the crashme function on and off.
                     66: //#define BB_FEATURE_VI_CRASHME         // randomly pick commands to execute
                     67:
                     68: #include <stdio.h>
                     69: #include <stdlib.h>
                     70: #include <string.h>
                     71: #include <termios.h>
                     72: #include <unistd.h>
                     73: #include <sys/ioctl.h>
                     74: #include <sys/time.h>
                     75: #include <sys/types.h>
                     76: #include <sys/stat.h>
                     77: #include <time.h>
                     78: #include <fcntl.h>
                     79: #include <signal.h>
                     80: #include <setjmp.h>
                     81: #include <regex.h>
                     82: #include <ctype.h>
                     83: #include <assert.h>
                     84: #include <errno.h>
                     85: #include <err.h>
                     86: #include <stdarg.h>
                     87:
                     88: #include "last_char_is.c"
                     89: #include "strlcat.c"
                     90: #include "strlcpy.c"
                     91:
                     92: #ifndef TRUE
                     93: #define TRUE                   ((int)1)
                     94: #define FALSE                  ((int)0)
                     95: #endif /* TRUE */
                     96: #define MAX_SCR_COLS           BUFSIZ
                     97: #define BUFSIZ_STATBUF         200
                     98:
                     99: // Misc. non-Ascii keys that report an escape sequence
                    100: #define VI_K_UP                        128         // cursor key Up
                    101: #define VI_K_DOWN              129         // cursor key Down
                    102: #define VI_K_RIGHT             130       // Cursor Key Right
                    103: #define VI_K_LEFT              131         // cursor key Left
                    104: #define VI_K_HOME              132         // Cursor Key Home
                    105: #define VI_K_END               133         // Cursor Key End
                    106: #define VI_K_INSERT            134       // Cursor Key Insert
                    107: #define VI_K_PAGEUP            135       // Cursor Key Page Up
                    108: #define VI_K_PAGEDOWN          136     // Cursor Key Page Down
                    109: #define VI_K_FUN1              137         // Function Key F1
                    110: #define VI_K_FUN2              138         // Function Key F2
                    111: #define VI_K_FUN3              139         // Function Key F3
                    112: #define VI_K_FUN4              140         // Function Key F4
                    113: #define VI_K_FUN5              141         // Function Key F5
                    114: #define VI_K_FUN6              142         // Function Key F6
                    115: #define VI_K_FUN7              143         // Function Key F7
                    116: #define VI_K_FUN8              144         // Function Key F8
                    117: #define VI_K_FUN9              145         // Function Key F9
                    118: #define VI_K_FUN10             146       // Function Key F10
                    119: #define VI_K_FUN11             147       // Function Key F11
                    120: #define VI_K_FUN12             148       // Function Key F12
                    121:
                    122: static const int YANKONLY = FALSE;
                    123: static const int YANKDEL = TRUE;
                    124: static const int FORWARD = 1;   // code depends on "1"  for array index
                    125: static const int BACK = -1;     // code depends on "-1" for array index
                    126: static const int LIMITED = 0;   // how much of text[] in char_search
                    127: static const int FULL = 1;      // how much of text[] in char_search
                    128:
                    129: static const int S_BEFORE_WS = 1; // used in skip_thing() for moving "dot"
                    130: static const int S_TO_WS = 2;   // used in skip_thing() for moving "dot"
                    131: static const int S_OVER_WS = 3; // used in skip_thing() for moving "dot"
                    132: static const int S_END_PUNCT = 4; // used in skip_thing() for moving "dot"
                    133: static const int S_END_ALNUM = 5; // used in skip_thing() for moving "dot"
                    134:
                    135: typedef unsigned char Byte;
                    136:
                    137:
                    138: static int editing;             // >0 while we are editing a file
                    139: static int cmd_mode;            // 0=command  1=insert
                    140: static int file_modified;       // buffer contents changed
                    141: static int err_method;          // indicate error with beep or flash
                    142: static int fn_start;            // index of first cmd line file name
                    143: static int save_argc;           // how many file names on cmd line
                    144: static int cmdcnt;              // repetition count
                    145: static fd_set rfds;             // use select() for small sleeps
                    146: static struct timeval tv;       // use select() for small sleeps
                    147: static char erase_char;         // the users erase character
                    148: static int rows, columns;       // the terminal screen is this size
                    149: static int crow, ccol, offset;  // cursor is on Crow x Ccol with Horz Ofset
                    150: static char *SOs, *SOn;         // terminal standout start/normal ESC sequence
                    151: static char *bell;              // terminal bell sequence
                    152: static char *Ceol, *Ceos;       // Clear-end-of-line and Clear-end-of-screen ESC sequence
                    153: static char *CMrc;              // Cursor motion arbitrary destination ESC sequence
                    154: static char *CMup, *CMdown;     // Cursor motion up and down ESC sequence
                    155: static Byte *status_buffer;     // mesages to the user
                    156: static Byte last_input_char;    // last char read from user
                    157: static Byte last_forward_char;  // last char searched for with 'f'
                    158: static Byte *cfn;               // previous, current, and next file name
                    159: static Byte *text, *end, *textend;  // pointers to the user data in memory
                    160: static Byte *screen;            // pointer to the virtual screen buffer
                    161: static int screensize;          //            and its size
                    162: static Byte *screenbegin;       // index into text[], of top line on the screen
                    163: static Byte *dot;               // where all the action takes place
                    164: static int tabstop;
                    165: static struct termios term_orig, term_vi; // remember what the cooked mode was
                    166:
                    167: #ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR
                    168: static int last_row;            // where the cursor was last moved to
                    169: #endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */
                    170: #ifdef BB_FEATURE_VI_USE_SIGNALS
                    171: static jmp_buf restart;         // catch_sig()
                    172: #endif /* BB_FEATURE_VI_USE_SIGNALS */
                    173: #ifdef BB_FEATURE_VI_WIN_RESIZE
                    174: static struct winsize winsize;  // remember the window size
                    175: #endif /* BB_FEATURE_VI_WIN_RESIZE */
                    176: #ifdef BB_FEATURE_VI_DOT_CMD
                    177: static int adding2q;            // are we currently adding user input to q
                    178: static Byte *last_modifying_cmd;  // last modifying cmd for "."
                    179: static Byte *ioq, *ioq_start;   // pointer to string for get_one_char to "read"
                    180: #endif /* BB_FEATURE_VI_DOT_CMD */
                    181: #if    defined(BB_FEATURE_VI_DOT_CMD) || defined(BB_FEATURE_VI_YANKMARK)
                    182: static Byte *modifying_cmds;    // cmds that modify text[]
                    183: #endif /* BB_FEATURE_VI_DOT_CMD || BB_FEATURE_VI_YANKMARK */
                    184: #ifdef BB_FEATURE_VI_READONLY
                    185: static int vi_readonly, readonly;
                    186: #endif /* BB_FEATURE_VI_READONLY */
                    187: #ifdef BB_FEATURE_VI_SETOPTS
                    188: static int autoindent;
                    189: static int showmatch;
                    190: static int ignorecase;
                    191: #endif /* BB_FEATURE_VI_SETOPTS */
                    192: #ifdef BB_FEATURE_VI_YANKMARK
                    193: static Byte *reg[28];           // named register a-z, "D", and "U" 0-25,26,27
                    194: static int YDreg, Ureg;         // default delete register and orig line for "U"
                    195: static Byte *mark[28];          // user marks points somewhere in text[]-  a-z and previous context ''
                    196: static Byte *context_start, *context_end;
                    197: #endif /* BB_FEATURE_VI_YANKMARK */
                    198: #ifdef BB_FEATURE_VI_SEARCH
                    199: static Byte *last_search_pattern; // last pattern from a '/' or '?' search
                    200: #endif /* BB_FEATURE_VI_SEARCH */
                    201:
                    202:
                    203: static void edit_file (Byte *); // edit one file
                    204: static void do_cmd (Byte);      // execute a command
                    205: static void sync_cursor (Byte *, int *, int *); // synchronize the screen cursor to dot
                    206: static Byte *begin_line (Byte *); // return pointer to cur line B-o-l
                    207: static Byte *end_line (Byte *); // return pointer to cur line E-o-l
                    208: static Byte *dollar_line (Byte *);  // return pointer to just before NL
                    209: static Byte *prev_line (Byte *);  // return pointer to prev line B-o-l
                    210: static Byte *next_line (Byte *);  // return pointer to next line B-o-l
                    211: static Byte *end_screen (void); // get pointer to last char on screen
                    212: static int count_lines (Byte *, Byte *);  // count line from start to stop
                    213: static Byte *find_line (int);   // find begining of line #li
                    214: static Byte *move_to_col (Byte *, int); // move "p" to column l
                    215: static int isblnk (Byte);       // is the char a blank or tab
                    216: static void dot_left (void);    // move dot left- dont leave line
                    217: static void dot_right (void);   // move dot right- dont leave line
                    218: static void dot_begin (void);   // move dot to B-o-l
                    219: static void dot_end (void);     // move dot to E-o-l
                    220: static void dot_next (void);    // move dot to next line B-o-l
                    221: static void dot_prev (void);    // move dot to prev line B-o-l
                    222: static void dot_scroll (int, int);  // move the screen up or down
                    223: static void dot_skip_over_ws (void);  // move dot pat WS
                    224: static void dot_delete (void);  // delete the char at 'dot'
                    225: static Byte *bound_dot (Byte *);  // make sure  text[0] <= P < "end"
                    226: static Byte *new_screen (int, int); // malloc virtual screen memory
                    227: static Byte *new_text (int);    // malloc memory for text[] buffer
                    228: static Byte *char_insert (Byte *, Byte);  // insert the char c at 'p'
                    229: static Byte *stupid_insert (Byte *, Byte);  // stupidly insert the char c at 'p'
                    230: static Byte find_range (Byte **, Byte **, Byte);  // return pointers for an object
                    231: static int st_test (Byte *, int, int, Byte *);  // helper for skip_thing()
                    232: static Byte *skip_thing (Byte *, int, int, int);  // skip some object
                    233: static Byte *find_pair (Byte *, Byte);  // find matching pair ()  []  {}
                    234: static Byte *text_hole_delete (Byte *, Byte *); // at "p", delete a 'size' byte hole
                    235: static Byte *text_hole_make (Byte *, int);  // at "p", make a 'size' byte hole
                    236: static Byte *yank_delete (Byte *, Byte *, int, int);  // yank text[] into register then delete
                    237: static void show_help (void);   // display some help info
                    238: static void print_literal (Byte *, Byte *); // copy s to buf, convert unprintable
                    239: static void rawmode (void);     // set "raw" mode on tty
                    240: static void cookmode (void);    // return to "cooked" mode on tty
                    241: static int mysleep (int);       // sleep for 'h' 1/100 seconds
                    242: static Byte readit (void);      // read (maybe cursor) key from stdin
                    243: static Byte get_one_char (void);  // read 1 char from stdin
                    244: static int file_size (Byte *);  // what is the byte size of "fn"
                    245: static int file_insert (Byte *, Byte *, int);
                    246: static int file_write (Byte *, Byte *, Byte *);
                    247: static void place_cursor (int, int, int);
                    248: static void screen_erase ();
                    249: static void clear_to_eol (void);
                    250: static void clear_to_eos (void);
                    251: static void standout_start (void);  // send "start reverse video" sequence
                    252: static void standout_end (void);  // send "end reverse video" sequence
                    253: static void flash (int);        // flash the terminal screen
                    254: static void beep (void);        // beep the terminal
                    255: static void indicate_error (char);  // use flash or beep to indicate error
                    256: static void show_status_line (void);  // put a message on the bottom line
                    257: static void psb (char *, ...);  // Print Status Buf
                    258: static void psbs (char *, ...); // Print Status Buf in standout mode
                    259: static void ni (Byte *);        // display messages
                    260: static void edit_status (void); // show file status on status line
                    261: static void redraw (int);       // force a full screen refresh
                    262: static void format_line (Byte *, Byte *, int);
                    263: static void refresh (int);      // update the terminal from screen[]
                    264:
                    265: #ifdef BB_FEATURE_VI_SEARCH
                    266: static Byte *char_search (Byte *, Byte *, int, int);  // search for pattern starting at p
                    267: static int mycmp (Byte *, Byte *, int); // string cmp based in "ignorecase"
                    268: #endif /* BB_FEATURE_VI_SEARCH */
                    269: #ifdef BB_FEATURE_VI_COLON
                    270: static void Hit_Return (void);
                    271: static Byte *get_one_address (Byte *, int *); // get colon addr, if present
                    272: static Byte *get_address (Byte *, int *, int *);  // get two colon addrs, if present
                    273: static void colon (Byte *);     // execute the "colon" mode cmds
                    274: #endif /* BB_FEATURE_VI_COLON */
                    275: static Byte *get_input_line (Byte *); // get input line- use "status line"
                    276: #ifdef BB_FEATURE_VI_USE_SIGNALS
                    277: static void winch_sig (int);    // catch window size changes
                    278: static void suspend_sig (int);  // catch ctrl-Z
                    279: static void alarm_sig (int);    // catch alarm time-outs
                    280: static void catch_sig (int);    // catch ctrl-C
                    281: static void core_sig (int);     // catch a core dump signal
                    282: #endif /* BB_FEATURE_VI_USE_SIGNALS */
                    283: #ifdef BB_FEATURE_VI_DOT_CMD
                    284: static void start_new_cmd_q (Byte); // new queue for command
                    285: static void end_cmd_q ();       // stop saving input chars
                    286: #else /* BB_FEATURE_VI_DOT_CMD */
                    287: #define end_cmd_q()
                    288: #endif /* BB_FEATURE_VI_DOT_CMD */
                    289: #ifdef BB_FEATURE_VI_WIN_RESIZE
                    290: static void window_size_get (int);  // find out what size the window is
                    291: #endif /* BB_FEATURE_VI_WIN_RESIZE */
                    292: #ifdef BB_FEATURE_VI_SETOPTS
                    293: static void showmatching (Byte *);  // show the matching pair ()  []  {}
                    294: #endif /* BB_FEATURE_VI_SETOPTS */
                    295: #if defined(BB_FEATURE_VI_YANKMARK) || defined(BB_FEATURE_VI_COLON) || defined(BB_FEATURE_VI_CRASHME)
                    296: static Byte *string_insert (Byte *, Byte *);  // insert the string at 'p'
                    297: #endif /* BB_FEATURE_VI_YANKMARK || BB_FEATURE_VI_COLON || BB_FEATURE_VI_CRASHME */
                    298: #ifdef BB_FEATURE_VI_YANKMARK
                    299: static Byte *text_yank (Byte *, Byte *, int); // save copy of "p" into a register
                    300: static Byte what_reg (void);    // what is letter of current YDreg
                    301: static void check_context (Byte); // remember context for '' command
                    302: static Byte *swap_context (Byte *); // goto new context for '' command
                    303: #endif /* BB_FEATURE_VI_YANKMARK */
                    304: #ifdef BB_FEATURE_VI_CRASHME
                    305: static void crash_dummy ();
                    306: static void crash_test ();
                    307: static int crashme = 0;
                    308: #endif /* BB_FEATURE_VI_CRASHME */
                    309:
                    310:
                    311: extern int
                    312: main (int argc, char **argv)
                    313: {
                    314: #ifdef BB_FEATURE_VI_YANKMARK
                    315:   int i;
                    316: #endif /* BB_FEATURE_VI_YANKMARK */
                    317:
                    318:   CMrc = "\033[%d;%dH";         // Terminal Crusor motion ESC sequence
                    319:   CMup = "\033[A";              // move cursor up one line, same col
                    320:   CMdown = "\n";                // move cursor down one line, same col
                    321:   Ceol = "\033[0K";             // Clear from cursor to end of line
                    322:   Ceos = "\033[0J";             // Clear from cursor to end of screen
                    323:   SOs = "\033[7m";              // Terminal standout mode on
                    324:   SOn = "\033[0m";              // Terminal standout mode off
                    325:   bell = "\007";                // Terminal bell sequence
                    326: #ifdef BB_FEATURE_VI_CRASHME
                    327:   (void) srand ((long) getpid ());
                    328: #endif /* BB_FEATURE_VI_CRASHME */
                    329:   status_buffer = (Byte *) malloc (BUFSIZ_STATBUF);  // hold messages to user
                    330: #ifdef BB_FEATURE_VI_READONLY
                    331:   vi_readonly = readonly = FALSE;
                    332:   if (strncmp (argv[0], "view", 4) == 0) {
                    333:       readonly = TRUE;
                    334:       vi_readonly = TRUE;
                    335:     }
                    336: #endif /* BB_FEATURE_VI_READONLY */
                    337: #ifdef BB_FEATURE_VI_SETOPTS
                    338:   autoindent = 1;
                    339:   ignorecase = 1;
                    340:   showmatch = 1;
                    341: #endif /* BB_FEATURE_VI_SETOPTS */
                    342: #ifdef BB_FEATURE_VI_YANKMARK
                    343:   for (i = 0; i < 28; i++) {
                    344:       reg[i] = 0;
                    345:     }                           // init the yank regs
                    346: #endif /* BB_FEATURE_VI_YANKMARK */
                    347: #ifdef BB_FEATURE_VI_DOT_CMD
                    348:   modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~";  // cmds modifying text[]
                    349: #endif /* BB_FEATURE_VI_DOT_CMD */
                    350:
                    351:   if (argc >= 2)
                    352:     {
                    353:       cfn = (Byte *) strdup (argv[1]);
                    354:       edit_file (cfn);
                    355:     }
                    356:   else
                    357:     {
                    358:       fprintf (stderr, "%s: no file to edit, bailing out\n", argv[0]);
                    359:       exit (-20);
                    360:     }
                    361:
                    362:   //-----------------------------------------------------------
                    363:
                    364:
                    365:   /* set these back to defaults. this was the infamous screen resize crash bug */
                    366:   signal (SIGWINCH, SIG_DFL);
                    367:   signal (SIGTSTP, SIG_DFL);
                    368:
                    369:   return (0);
                    370: }
                    371:
                    372: static void edit_file (Byte * fn)
                    373: {
                    374:   char c;
                    375:   int cnt, size, ch;
                    376:
                    377: #ifdef BB_FEATURE_VI_USE_SIGNALS
                    378:   char *msg;
                    379:   int sig;
                    380: #endif /* BB_FEATURE_VI_USE_SIGNALS */
                    381: #ifdef BB_FEATURE_VI_YANKMARK
                    382:   static Byte *cur_line;
                    383: #endif /* BB_FEATURE_VI_YANKMARK */
                    384:
                    385:   rawmode ();
                    386:   rows = 24;
                    387:   columns = 80;
                    388:   ch = -1;
                    389: #ifdef BB_FEATURE_VI_WIN_RESIZE
                    390:   window_size_get (0);
                    391: #endif /* BB_FEATURE_VI_WIN_RESIZE */
                    392:   new_screen (rows, columns);   // get memory for virtual screen
                    393:
                    394:   cnt = file_size (fn);         // file size
                    395:   size = 2 * cnt;               // 200% of file size
                    396:   new_text (size);              // get a text[] buffer
                    397:   screenbegin = dot = end = text;
                    398:   if (fn != 0) {
                    399:       ch = file_insert (fn, text, cnt);
                    400:     }
                    401:   if (ch < 1) {
                    402:       (void) char_insert (text, '\n');  // start empty buf with dummy line
                    403:     }
                    404:   file_modified = FALSE;
                    405: #ifdef BB_FEATURE_VI_YANKMARK
                    406:   YDreg = 26;                   // default Yank/Delete reg
                    407:   Ureg = 27;                    // hold orig line for "U" cmd
                    408:   for (cnt = 0; cnt < 28; cnt++) {
                    409:       mark[cnt] = 0;
                    410:     }                           // init the marks
                    411:   mark[26] = mark[27] = text;   // init "previous context"
                    412: #endif /* BB_FEATURE_VI_YANKMARK */
                    413:
                    414:   err_method = 1;               // flash
                    415:   last_forward_char = last_input_char = '\0';
                    416:   crow = 0;
                    417:   ccol = 0;
                    418:   edit_status ();
                    419:
                    420: #ifdef BB_FEATURE_VI_USE_SIGNALS
                    421:   signal (SIGHUP, catch_sig);
                    422:   signal (SIGINT, catch_sig);
                    423:   signal (SIGALRM, alarm_sig);
                    424:   signal (SIGTERM, catch_sig);
                    425:   signal (SIGQUIT, core_sig);
                    426:   signal (SIGILL, core_sig);
                    427:   signal (SIGTRAP, core_sig);
                    428:   signal (SIGIOT, core_sig);
                    429:   signal (SIGABRT, core_sig);
                    430:   signal (SIGFPE, core_sig);
                    431:   signal (SIGBUS, core_sig);
                    432:   signal (SIGSEGV, core_sig);
                    433: #ifdef SIGSYS
                    434:   signal (SIGSYS, core_sig);
                    435: #endif
                    436:   signal (SIGWINCH, winch_sig);
                    437:   signal (SIGTSTP, suspend_sig);
                    438:   sig = setjmp (restart);
                    439:   if (sig != 0) {
                    440:       msg = "";
                    441:       if (sig == SIGWINCH)
                    442:         msg = "(window resize)";
                    443:       if (sig == SIGHUP)
                    444:         msg = "(hangup)";
                    445:       if (sig == SIGINT)
                    446:         msg = "(interrupt)";
                    447:       if (sig == SIGTERM)
                    448:         msg = "(terminate)";
                    449:       if (sig == SIGBUS)
                    450:         msg = "(bus error)";
                    451:       if (sig == SIGSEGV)
                    452:         msg = "(I tried to touch invalid memory)";
                    453:       if (sig == SIGALRM)
                    454:         msg = "(alarm)";
                    455:
                    456:       psbs ("-- caught signal %d %s--", sig, msg);
                    457:       screenbegin = dot = text;
                    458:     }
                    459: #endif /* BB_FEATURE_VI_USE_SIGNALS */
                    460:
                    461:   editing = 1;
                    462:   cmd_mode = 0;                 // 0=command  1=insert  2='R'eplace
                    463:   cmdcnt = 0;
                    464:   tabstop = 8;
                    465:   offset = 0;                   // no horizontal offset
                    466:   c = '\0';
                    467: #ifdef BB_FEATURE_VI_DOT_CMD
                    468:   if (last_modifying_cmd != 0)
                    469:     free (last_modifying_cmd);
                    470:   if (ioq_start != NULL)
                    471:     free (ioq_start);
                    472:   ioq = ioq_start = last_modifying_cmd = 0;
                    473:   adding2q = 0;
                    474: #endif /* BB_FEATURE_VI_DOT_CMD */
                    475:   redraw (FALSE);               // dont force every col re-draw
                    476:   show_status_line ();
                    477:
                    478:   //------This is the main Vi cmd handling loop -----------------------
                    479:   while (editing > 0) {
                    480: #ifdef BB_FEATURE_VI_CRASHME
                    481:       if (crashme > 0) {
                    482:           if ((end - text) > 1) {
                    483:               crash_dummy ();   // generate a random command
                    484:             } else {
                    485:               crashme = 0;
                    486:               dot =
                    487:                  string_insert(text, (Byte *) "\n\n#####  Ran out of text to work on.  #####\n\n");    // insert the string
                    488:               refresh (FALSE);
                    489:             }
                    490:         }
                    491: #endif /* BB_FEATURE_VI_CRASHME */
                    492:       last_input_char = c = get_one_char ();  // get a cmd from user
                    493: #ifdef BB_FEATURE_VI_YANKMARK
                    494:       // save a copy of the current line- for the 'U" command
                    495:       if (begin_line (dot) != cur_line) {
                    496:           cur_line = begin_line (dot);
                    497:           text_yank (begin_line (dot), end_line (dot), Ureg);
                    498:         }
                    499: #endif /* BB_FEATURE_VI_YANKMARK */
                    500: #ifdef BB_FEATURE_VI_DOT_CMD
                    501:       // These are commands that change text[].
                    502:       // Remember the input for the "." command
                    503:       if (!adding2q && ioq_start == 0
                    504:           && strchr((char *) modifying_cmds, c) != NULL) {
                    505:           start_new_cmd_q (c);
                    506:         }
                    507: #endif /* BB_FEATURE_VI_DOT_CMD */
                    508:       do_cmd (c);               // execute the user command
                    509:       //
                    510:       // poll to see if there is input already waiting. if we are
                    511:       // not able to display output fast enough to keep up, skip
                    512:       // the display update until we catch up with input.
                    513:       if (mysleep(0) == 0) {
                    514:           // no input pending- so update output
                    515:           refresh (FALSE);
                    516:           show_status_line ();
                    517:         }
                    518: #ifdef BB_FEATURE_VI_CRASHME
                    519:       if (crashme > 0)
                    520:         crash_test ();          // test editor variables
                    521: #endif /* BB_FEATURE_VI_CRASHME */
                    522:     }
                    523:   //-------------------------------------------------------------------
                    524:
                    525:   place_cursor (rows, 0, FALSE);  // go to bottom of screen
                    526:   clear_to_eol ();              // Erase to end of line
                    527:   cookmode ();
                    528: }
                    529:
                    530: static Byte readbuffer[BUFSIZ];
                    531:
                    532: #ifdef BB_FEATURE_VI_CRASHME
                    533: static int totalcmds = 0;
                    534: static int Mp = 85;             // Movement command Probability
                    535: static int Np = 90;             // Non-movement command Probability
                    536: static int Dp = 96;             // Delete command Probability
                    537: static int Ip = 97;             // Insert command Probability
                    538: static int Yp = 98;             // Yank command Probability
                    539: static int Pp = 99;             // Put command Probability
                    540: static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
                    541: char chars[20] = "\t012345 abcdABCD-=.$";
                    542: char *words[20] = { "this", "is", "a", "test",
                    543:   "broadcast", "the", "emergency", "of",
                    544:   "system", "quick", "brown", "fox",
                    545:   "jumped", "over", "lazy", "dogs",
                    546:   "back", "January", "Febuary", "March"
                    547: };
                    548: char *lines[20] = {
                    549:   "You should have received a copy of the GNU General Public License\n",
                    550:   "char c, cm, *cmd, *cmd1;\n",
                    551:   "generate a command by percentages\n",
                    552:   "Numbers may be typed as a prefix to some commands.\n",
                    553:   "Quit, discarding changes!\n",
                    554:   "Forced write, if permission originally not valid.\n",
                    555:   "In general, any ex or ed command (such as substitute or delete).\n",
                    556:   "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
                    557:   "Please get w/ me and I will go over it with you.\n",
                    558:   "The following is a list of scheduled, committed changes.\n",
                    559:   "1.   Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
                    560:   "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
                    561:   "Any question about transactions please contact Sterling Huxley.\n",
                    562:   "I will try to get back to you by Friday, December 31.\n",
                    563:   "This Change will be implemented on Friday.\n",
                    564:   "Let me know if you have problems accessing this;\n",
                    565:   "Sterling Huxley recently added you to the access list.\n",
                    566:   "Would you like to go to lunch?\n",
                    567:   "The last command will be automatically run.\n",
                    568:   "This is too much english for a computer geek.\n",
                    569: };
                    570: char *multilines[20] = {
                    571:   "You should have received a copy of the GNU General Public License\n",
                    572:   "char c, cm, *cmd, *cmd1;\n",
                    573:   "generate a command by percentages\n",
                    574:   "Numbers may be typed as a prefix to some commands.\n",
                    575:   "Quit, discarding changes!\n",
                    576:   "Forced write, if permission originally not valid.\n",
                    577:   "In general, any ex or ed command (such as substitute or delete).\n",
                    578:   "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
                    579:   "Please get w/ me and I will go over it with you.\n",
                    580:   "The following is a list of scheduled, committed changes.\n",
                    581:   "1.   Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
                    582:   "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
                    583:   "Any question about transactions please contact Sterling Huxley.\n",
                    584:   "I will try to get back to you by Friday, December 31.\n",
                    585:   "This Change will be implemented on Friday.\n",
                    586:   "Let me know if you have problems accessing this;\n",
                    587:   "Sterling Huxley recently added you to the access list.\n",
                    588:   "Would you like to go to lunch?\n",
                    589:   "The last command will be automatically run.\n",
                    590:   "This is too much english for a computer geek.\n",
                    591: };
                    592:
                    593: // create a random command to execute
                    594: static void crash_dummy()
                    595: {
                    596:   static int sleeptime;         // how long to pause between commands
                    597:   char c, cm, *cmd, *cmd1;
                    598:   int i, cnt, thing, rbi, startrbi, percent;
                    599:
                    600:   // "dot" movement commands
                    601:   cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
                    602:
                    603:   // is there already a command running?
                    604:   if (strlen ((char *) readbuffer) > 0)
                    605:     goto cd1;
                    606: cd0:
                    607:   startrbi = rbi = 0;
                    608:   sleeptime = 0;                // how long to pause between commands
                    609:   memset (readbuffer, '\0', BUFSIZ - 1);  // clear the read buffer
                    610:   // generate a command by percentages
                    611:   percent = (int) lrand48 () % 100; // get a number from 0-99
                    612:   if (percent < Mp) {  //  Movement commands
                    613:       // available commands
                    614:       cmd = cmd1;
                    615:       M++;
                    616:     } else if (percent < Np) { //  non-movement commands
                    617:       cmd = "mz<>\'\"";         // available commands
                    618:       N++;
                    619:     } else if (percent < Dp) { //  Delete commands
                    620:       cmd = "dx";               // available commands
                    621:       D++;
                    622:     } else if (percent < Ip) { //  Inset commands
                    623:       cmd = "iIaAsrJ";          // available commands
                    624:       I++;
                    625:     } else if (percent < Yp) { //  Yank commands
                    626:       cmd = "yY";               // available commands
                    627:       Y++;
                    628:     } else if (percent < Pp) { //  Put commands
                    629:       cmd = "pP";               // available commands
                    630:       P++;
                    631:     } else {
                    632:       // We do not know how to handle this command, try again
                    633:       U++;
                    634:       goto cd0;
                    635:     }
                    636:   // randomly pick one of the available cmds from "cmd[]"
                    637:   i = (int) lrand48 () % strlen (cmd);
                    638:   cm = cmd[i];
                    639:   if (strchr (":\024", cm))
                    640:     goto cd0;                   // dont allow colon or ctrl-T commands
                    641:   readbuffer[rbi++] = cm;       // put cmd into input buffer
                    642:
                    643:   // now we have the command-
                    644:   // there are 1, 2, and multi char commands
                    645:   // find out which and generate the rest of command as necessary
                    646:   if (strchr ("dmryz<>\'\"", cm)) {    // 2-char commands
                    647:       cmd1 = " \n\r0$^-+wWeEbBhjklHL";
                    648:       if (cm == 'm' || cm == '\'' || cm == '\"') {     // pick a reg[]
                    649:           cmd1 = "abcdefghijklmnopqrstuvwxyz";
                    650:         }
                    651:       thing = (int) lrand48 () % strlen (cmd1); // pick a movement command
                    652:       c = cmd1[thing];
                    653:       readbuffer[rbi++] = c;    // add movement to input buffer
                    654:     }
                    655:   if (strchr ("iIaAsc", cm)) { // multi-char commands
                    656:       if (cm == 'c') {
                    657:           // change some thing
                    658:           thing = (int) lrand48 () % strlen (cmd1); // pick a movement command
                    659:           c = cmd1[thing];
                    660:           readbuffer[rbi++] = c;  // add movement to input buffer
                    661:         }
                    662:       thing = (int) lrand48 () % 4; // what thing to insert
                    663:       cnt = (int) lrand48 () % 10;  // how many to insert
                    664:       for (i = 0; i < cnt; i++) {
                    665:           if (thing == 0) {    // insert chars
                    666:               readbuffer[rbi++] = chars[((int) lrand48 () % strlen (chars))];
                    667:             } else if (thing == 1) {   // insert words
                    668:              strlcat((char *) readbuffer, words[(int) lrand48() % 20], BUFSIZ); /* FIXED added BUFSIZ and converted to strlcat() */
                    669:               strcat ((char *) readbuffer, " ");
                    670:               sleeptime = 0;    // how fast to type
                    671:             } else if (thing == 2) {   // insert lines
                    672:              strlcat((char *) readbuffer, lines[(int) lrand48() % 20], BUFSIZ); /* FIXED added BUFSIZ and converted to strlcat() */
                    673:               sleeptime = 0;    // how fast to type
                    674:             } else {   // insert multi-lines
                    675:              strlcat((char *) readbuffer, multilines[(int) lrand48() % 20], BUFSIZ); /* FIXED added BUFSIZ and converted to stlcat() */
                    676:               sleeptime = 0;    // how fast to type
                    677:             }
                    678:         }
                    679:       strcat ((char *) readbuffer, "\033");
                    680:     }
                    681: cd1:
                    682:   totalcmds++;
                    683:   if (sleeptime > 0)
                    684:     (void) mysleep (sleeptime); // sleep 1/100 sec
                    685: }
                    686:
                    687: // test to see if there are any errors
                    688: static void crash_test()
                    689: {
                    690:   static time_t oldtim;
                    691:   time_t tim;
                    692:   char d[2], buf[BUFSIZ], msg[BUFSIZ];
                    693:
                    694:   msg[0] = '\0';
                    695:   if (end < text) {
                    696:       strcat ((char *) msg, "end<text ");
                    697:     }
                    698:   if (end > textend) {
                    699:       strcat ((char *) msg, "end>textend ");
                    700:     }
                    701:   if (dot < text) {
                    702:       strcat ((char *) msg, "dot<text ");
                    703:     }
                    704:   if (dot > end) {
                    705:       strcat ((char *) msg, "dot>end ");
                    706:     }
                    707:   if (screenbegin < text) {
                    708:       strcat ((char *) msg, "screenbegin<text ");
                    709:     }
                    710:   if (screenbegin > end - 1) {
                    711:       strcat ((char *) msg, "screenbegin>end-1 ");
                    712:     }
                    713:
                    714:   if (strlen (msg) > 0) {
                    715:       // alarm(0);
                    716:       sprintf (buf, "\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
                    717:                totalcmds, last_input_char, msg, SOs, SOn);
                    718:       write (1, buf, strlen (buf));
                    719:       while (read (0, d, 1) > 0) {
                    720:           if (d[0] == '\n' || d[0] == '\r')
                    721:             break;
                    722:         }
                    723:       // alarm(3);
                    724:     }
                    725:   tim = (time_t) time ((time_t *) 0);
                    726:   if (tim >= (oldtim + 3)) {
                    727:       sprintf ((char *) status_buffer,
                    728:                "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
                    729:                totalcmds, M, N, I, D, Y, P, U, end - text + 1);
                    730:       oldtim = tim;
                    731:     }
                    732:   return;
                    733: }
                    734: #endif /* BB_FEATURE_VI_CRASHME */
                    735:
                    736: //---------------------------------------------------------------------
                    737: //----- the Ascii Chart -----------------------------------------------
                    738: //
                    739: //  00 nul   01 soh   02 stx   03 etx   04 eot   05 enq   06 ack   07 bel
                    740: //  08 bs    09 ht    0a nl    0b vt    0c np    0d cr    0e so    0f si
                    741: //  10 dle   11 dc1   12 dc2   13 dc3   14 dc4   15 nak   16 syn   17 etb
                    742: //  18 can   19 em    1a sub   1b esc   1c fs    1d gs    1e rs    1f us
                    743: //  20 sp    21 !     22 "     23 #     24 $     25 %     26 &     27 '
                    744: //  28 (     29 )     2a *     2b +     2c ,     2d -     2e .     2f /
                    745: //  30 0     31 1     32 2     33 3     34 4     35 5     36 6     37 7
                    746: //  38 8     39 9     3a :     3b ;     3c <     3d =     3e >     3f ?
                    747: //  40 @     41 A     42 B     43 C     44 D     45 E     46 F     47 G
                    748: //  48 H     49 I     4a J     4b K     4c L     4d M     4e N     4f O
                    749: //  50 P     51 Q     52 R     53 S     54 T     55 U     56 V     57 W
                    750: //  58 X     59 Y     5a Z     5b [     5c \     5d ]     5e ^     5f _
                    751: //  60 `     61 a     62 b     63 c     64 d     65 e     66 f     67 g
                    752: //  68 h     69 i     6a j     6b k     6c l     6d m     6e n     6f o
                    753: //  70 p     71 q     72 r     73 s     74 t     75 u     76 v     77 w
                    754: //  78 x     79 y     7a z     7b {     7c |     7d }     7e ~     7f del
                    755: //---------------------------------------------------------------------
                    756:
                    757: //----- Execute a Vi Command -----------------------------------
                    758: static void do_cmd(Byte c)
                    759: {
                    760:   Byte c1, *p, *q, *msg, buf[9], *save_dot;
                    761:   int cnt, i, j, dir, yf;
                    762:
                    763:   c1 = c;                       // quiet the compiler
                    764:   cnt = yf = dir = 0;           // quiet the compiler
                    765:   p = q = save_dot = msg = buf; // quiet the compiler
                    766:   memset (buf, '\0', 9);        // clear buf
                    767:
                    768:        /* if this is a cursor key, skip these checks */
                    769:        switch (c) {
                    770:                case VI_K_UP:
                    771:                case VI_K_DOWN:
                    772:                case VI_K_LEFT:
                    773:                case VI_K_RIGHT:
                    774:                case VI_K_HOME:
                    775:                case VI_K_END:
                    776:                case VI_K_PAGEUP:
                    777:                case VI_K_PAGEDOWN:
                    778:                        goto key_cmd_mode;
                    779:        }
                    780:
                    781:        if (cmd_mode == 2) {
                    782:       // we are 'R'eplacing the current *dot with new char
                    783:       if (*dot == '\n') {
                    784:           // don't Replace past E-o-l
                    785:           cmd_mode = 1;         // convert to insert
                    786:         } else {
                    787:           if (1 <= c && c <= 127) {    // only ASCII chars
                    788:               if (c != 27)
                    789:                 dot = yank_delete (dot, dot, 0, YANKDEL); // delete char
                    790:               dot = char_insert (dot, c); // insert new char
                    791:             }
                    792:           goto dc1;
                    793:         }
                    794:     }
                    795:   if (cmd_mode == 1) {
                    796:       //  hitting "Insert" twice means "R" replace mode
                    797:       if (c == VI_K_INSERT) goto dc5;
                    798:       // insert the char c at "dot"
                    799:       if (1 <= c && c <= 127) {
                    800:           dot = char_insert (dot, c); // only ASCII chars
                    801:         }
                    802:       goto dc1;
                    803:     }
                    804:
                    805: key_cmd_mode:
                    806:   switch (c) {
                    807:       //case 0x01:    // soh
                    808:       //case 0x09:    // ht
                    809:       //case 0x0b:    // vt
                    810:       //case 0x0e:    // so
                    811:       //case 0x0f:    // si
                    812:       //case 0x10:    // dle
                    813:       //case 0x11:    // dc1
                    814:       //case 0x13:    // dc3
                    815: #ifdef BB_FEATURE_VI_CRASHME
                    816:     case 0x14:                 // dc4  ctrl-T
                    817:       crashme = (crashme == 0) ? 1 : 0;
                    818:       break;
                    819: #endif /* BB_FEATURE_VI_CRASHME */
                    820:       //case 0x16:    // syn
                    821:       //case 0x17:    // etb
                    822:       //case 0x18:    // can
                    823:       //case 0x1c:    // fs
                    824:       //case 0x1d:    // gs
                    825:       //case 0x1e:    // rs
                    826:       //case 0x1f:    // us
                    827:       //case '!':     // !-
                    828:       //case '#':     // #-
                    829:       //case '&':     // &-
                    830:       //case '(':     // (-
                    831:       //case ')':     // )-
                    832:       //case '*':     // *-
                    833:       //case ',':     // ,-
                    834:       //case '=':     // =-
                    835:       //case '@':     // @-
                    836:       //case 'F':     // F-
                    837:       //case 'K':     // K-
                    838:       //case 'Q':     // Q-
                    839:       //case 'S':     // S-
                    840:       //case 'T':     // T-
                    841:       //case 'V':     // V-
                    842:       //case '[':     // [-
                    843:       //case '\\':    // \-
                    844:       //case ']':     // ]-
                    845:       //case '_':     // _-
                    846:       //case '`':     // `-
                    847:       //case 'g':     // g-
                    848:       //case 'u':     // u- FIXME- there is no undo
                    849:       //case 'v':     // v-
                    850:     default:                   // unrecognised command
                    851:       buf[0] = c;
                    852:       buf[1] = '\0';
                    853:       if (c <= ' ') {
                    854:           buf[0] = '^';
                    855:           buf[1] = c + '@';
                    856:           buf[2] = '\0';
                    857:         }
                    858:       ni ((Byte *) buf);
                    859:       end_cmd_q ();             // stop adding to q
                    860:     case 0x00:                 // nul- ignore
                    861:       break;
                    862:     case 2:                    // ctrl-B  scroll up   full screen
                    863:     case VI_K_PAGEUP:          // Cursor Key Page Up
                    864:       dot_scroll (rows - 2, -1);
                    865:       break;
                    866: #ifdef BB_FEATURE_VI_USE_SIGNALS
                    867:     case 0x03:                 // ctrl-C   interrupt
                    868:       longjmp (restart, 1);
                    869:       break;
                    870:     case 26:                   // ctrl-Z suspend
                    871:       suspend_sig (SIGTSTP);
                    872:       break;
                    873: #endif /* BB_FEATURE_VI_USE_SIGNALS */
                    874:     case 4:                    // ctrl-D  scroll down half screen
                    875:       dot_scroll ((rows - 2) / 2, 1);
                    876:       break;
                    877:     case 5:                    // ctrl-E  scroll down one line
                    878:       dot_scroll (1, 1);
                    879:       break;
                    880:     case 6:                    // ctrl-F  scroll down full screen
                    881:     case VI_K_PAGEDOWN:        // Cursor Key Page Down
                    882:       dot_scroll (rows - 2, 1);
                    883:       break;
                    884:     case 7:                    // ctrl-G  show current status
                    885:       edit_status ();
                    886:       break;
                    887:     case 'h':                  // h- move left
                    888:     case VI_K_LEFT:            // cursor key Left
                    889:     case 8:                    // ctrl-H- move left    (This may be ERASE char)
                    890:     case 127:                  // DEL- move left   (This may be ERASE char)
                    891:        do {
                    892:            dot_left ();
                    893:        } while (cmdcnt-- > 1);
                    894:        break;
                    895:     case 10:                   // Newline ^J
                    896:     case 'j':                  // j- goto next line, same col
                    897:     case VI_K_DOWN:            // cursor key Down
                    898:        do {
                    899:            dot_next();              // go to next B-o-l
                    900:            dot = move_to_col(dot, ccol + offset); // try stay in same col
                    901:        } while (cmdcnt-- > 1);
                    902:       break;
                    903:     case 12:                   // ctrl-L  force redraw whole screen
                    904:     case 18:                   // ctrl-R  force redraw
                    905:       place_cursor (0, 0, FALSE); // put cursor in correct place
                    906:       clear_to_eos ();          // tel terminal to erase display
                    907:       (void) mysleep (10);
                    908:       screen_erase ();          // erase the internal screen buffer
                    909:       refresh (TRUE);           // this will redraw the entire display
                    910:       break;
                    911:     case 13:                   // Carriage Return ^M
                    912:     case '+':                  // +- goto next line
                    913:       do {
                    914:          dot_next();
                    915:          dot_skip_over_ws();
                    916:       } while (cmdcnt-- > 1);
                    917:       break;
                    918:     case 21:                   // ctrl-U  scroll up   half screen
                    919:       dot_scroll ((rows - 2) / 2, -1);
                    920:       break;
                    921:     case 25:                   // ctrl-Y  scroll up one line
                    922:       dot_scroll (1, -1);
                    923:       break;
                    924:     case 27:                   // esc
                    925:       if (cmd_mode == 0)
                    926:         indicate_error (c);
                    927:       cmd_mode = 0;             // stop insrting
                    928:       end_cmd_q ();
                    929:       *status_buffer = '\0';    // clear status buffer
                    930:       break;
                    931:     case ' ':                  // move right
                    932:     case 'l':                  // move right
                    933:     case VI_K_RIGHT:           // Cursor Key Right
                    934:        do {
                    935:            dot_right();
                    936:        } while (cmdcnt-- > 1);
                    937:       break;
                    938: #ifdef BB_FEATURE_VI_YANKMARK
                    939:     case '"':                  // "- name a register to use for Delete/Yank
                    940:       c1 = get_one_char ();
                    941:       c1 = tolower (c1);
                    942:       if (islower(c1)) {
                    943:           YDreg = c1 - 'a';
                    944:         } else {
                    945:           indicate_error (c);
                    946:         }
                    947:       break;
                    948:     case '\'':                 // '- goto a specific mark
                    949:       c1 = get_one_char ();
                    950:       c1 = tolower (c1);
                    951:       if (islower(c1)) {
                    952:           c1 = c1 - 'a';
                    953:           // get the b-o-l
                    954:           q = mark[(int) c1];
                    955:           if (text <= q && q < end) {
                    956:               dot = q;
                    957:               dot_begin ();     // go to B-o-l
                    958:               dot_skip_over_ws ();
                    959:             }
                    960:         } else if (c1 == '\'') {       // goto previous context
                    961:           dot = swap_context (dot); // swap current and previous context
                    962:           dot_begin ();         // go to B-o-l
                    963:           dot_skip_over_ws ();
                    964:         } else {
                    965:           indicate_error (c);
                    966:         }
                    967:       break;
                    968:     case 'm':                  // m- Mark a line
                    969:       // this is really stupid.  If there are any inserts or deletes
                    970:       // between text[0] and dot then this mark will not point to the
                    971:       // correct location! It could be off by many lines!
                    972:       // Well..., at least its quick and dirty.
                    973:       c1 = get_one_char ();
                    974:       c1 = tolower (c1);
                    975:       if (islower(c1)) {
                    976:           c1 = c1 - 'a';
                    977:           // remember the line
                    978:           mark[(int) c1] = dot;
                    979:         } else {
                    980:           indicate_error (c);
                    981:         }
                    982:       break;
                    983:     case 'P':                  // P- Put register before
                    984:     case 'p':                  // p- put register after
                    985:       p = reg[YDreg];
                    986:       if (p == 0) {
                    987:           psbs ("Nothing in register %c", what_reg ());
                    988:           break;
                    989:         }
                    990:       // are we putting whole lines or strings
                    991:       if (strchr((char *) p, '\n') != NULL) {
                    992:           if (c == 'P') {
                    993:               dot_begin ();     // putting lines- Put above
                    994:             }
                    995:           if (c == 'p') {
                    996:               // are we putting after very last line?
                    997:               if (end_line (dot) == (end - 1)) {
                    998:                   dot = end;    // force dot to end of text[]
                    999:                 } else {
                   1000:                   dot_next ();  // next line, then put before
                   1001:                 }
                   1002:             }
                   1003:         } else {
                   1004:           if (c == 'p')
                   1005:             dot_right ();       // move to right, can move to NL
                   1006:         }
                   1007:       dot = string_insert (dot, p); // insert the string
                   1008:       end_cmd_q ();             // stop adding to q
                   1009:       break;
                   1010:     case 'U':                  // U- Undo; replace current line with original version
                   1011:       if (reg[Ureg] != 0) {
                   1012:           p = begin_line (dot);
                   1013:           q = end_line (dot);
                   1014:           p = text_hole_delete (p, q);  // delete cur line
                   1015:           p = string_insert (p, reg[Ureg]); // insert orig line
                   1016:           dot = p;
                   1017:           dot_skip_over_ws ();
                   1018:         }
                   1019:       break;
                   1020: #endif /* BB_FEATURE_VI_YANKMARK */
                   1021:     case '$':                  // $- goto end of line
                   1022:     case VI_K_END:             // Cursor Key End
                   1023:        do {
                   1024:            dot = end_line(dot + 1);
                   1025:        } while (cmdcnt-- > 1);
                   1026:       break;
                   1027:     case '%':                  // %- find matching char of pair () [] {}
                   1028:       for (q = dot; q < end && *q != '\n'; q++) {
                   1029:           if (strchr("()[]{}", *q) != NULL) {
                   1030:               // we found half of a pair
                   1031:               p = find_pair (q, *q);
                   1032:               if (p == NULL) {
                   1033:                   indicate_error (c);
                   1034:                 } else {
                   1035:                   dot = p;
                   1036:                 }
                   1037:               break;
                   1038:             }
                   1039:         }
                   1040:       if (*q == '\n')
                   1041:         indicate_error (c);
                   1042:       break;
                   1043:     case 'f':                  // f- forward to a user specified char
                   1044:       last_forward_char = get_one_char ();  // get the search char
                   1045:       //
                   1046:       // dont seperate these two commands. 'f' depends on ';'
                   1047:       //
                   1048:       //**** fall thru to ... 'i'
                   1049:     case ';':                  // ;- look at rest of line for last forward char
                   1050:        if (last_forward_char == 0) break;
                   1051:        do {
                   1052:            q = dot + 1;
                   1053:            while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
                   1054:                q++;
                   1055:            }
                   1056:            if (*q == last_forward_char)
                   1057:                dot = q;
                   1058:        } while (cmdcnt-- > 1);
                   1059:       break;
                   1060:     case '-':                  // -- goto prev line
                   1061:        do {
                   1062:            dot_prev();
                   1063:            dot_skip_over_ws();
                   1064:        } while (cmdcnt-- > 1);
                   1065:       break;
                   1066: #ifdef BB_FEATURE_VI_DOT_CMD
                   1067:     case '.':                  // .- repeat the last modifying command
                   1068:       // Stuff the last_modifying_cmd back into stdin
                   1069:       // and let it be re-executed.
                   1070:       if (last_modifying_cmd != 0) {
                   1071:           ioq = ioq_start = (Byte *) strdup ((char *) last_modifying_cmd);
                   1072:         }
                   1073:       break;
                   1074: #endif /* BB_FEATURE_VI_DOT_CMD */
                   1075: #ifdef BB_FEATURE_VI_SEARCH
                   1076:     case '?':                  // /- search for a pattern
                   1077:     case '/':                  // /- search for a pattern
                   1078:       buf[0] = c;
                   1079:       buf[1] = '\0';
                   1080:       q = get_input_line (buf); // get input line- use "status line"
                   1081:       if (strlen ((char *) q) == 1)
                   1082:         goto dc3;               // if no pat re-use old pat
                   1083:       if (strlen ((char *) q) > 1) {   // new pat- save it and find
                   1084:           // there is a new pat
                   1085:           if (last_search_pattern != 0) {
                   1086:               free (last_search_pattern);
                   1087:             }
                   1088:           last_search_pattern = (Byte *) strdup ((char *) q);
                   1089:           goto dc3;             // now find the pattern
                   1090:         }
                   1091:       // user changed mind and erased the "/"-  do nothing
                   1092:       break;
                   1093:     case 'N':                  // N- backward search for last pattern
                   1094:       if (last_search_pattern == 0) break;
                   1095:       dir = BACK;               // assume BACKWARD search
                   1096:       p = dot - 1;
                   1097:       if (last_search_pattern[0] == '?') {
                   1098:           dir = FORWARD;
                   1099:           p = dot + 1;
                   1100:         }
                   1101:       goto dc4;                 // now search for pattern
                   1102:       break;
                   1103:     case 'n':                  // n- repeat search for last pattern
                   1104:       // search rest of text[] starting at next char
                   1105:       // if search fails return orignal "p" not the "p+1" address
                   1106:     dc3:
                   1107:       if (last_search_pattern == 0) {
                   1108:           msg = (Byte *) "No previous regular expression";
                   1109:           goto dc2;
                   1110:         }
                   1111:       if (last_search_pattern[0] == '/') {
                   1112:           dir = FORWARD;        // assume FORWARD search
                   1113:           p = dot + 1;
                   1114:         }
                   1115:       if (last_search_pattern[0] == '?') {
                   1116:           dir = BACK;
                   1117:           p = dot - 1;
                   1118:         }
                   1119:     dc4:
                   1120:       msg = NULL;
                   1121:       do {
                   1122:          q = char_search (p, last_search_pattern + 1, dir, FULL);
                   1123:          if (q != NULL) {
                   1124:              dot = q;              // good search, update "dot"
                   1125:              if (cmdcnt-- > 1) {
                   1126:                  p = dot + ((dir == FORWARD) ? 1 : -1);
                   1127:                  goto dc4;
                   1128:              }
                   1129:              msg = (Byte *) " ";
                   1130:              goto dc2;
                   1131:          }
                   1132:          // no pattern found between "dot" and "end"- continue at top
                   1133:          p = text;
                   1134:          if (dir == BACK) {
                   1135:              p = end - 1;
                   1136:          }
                   1137:          q = char_search (p, last_search_pattern + 1, dir, FULL);
                   1138:          if (q != NULL) {      // found something
                   1139:              dot = q;              // found new pattern- goto it
                   1140:              msg = (Byte *) "search hit BOTTOM, continuing at TOP";
                   1141:              if (dir == BACK) {
                   1142:                  msg = (Byte *) "search hit TOP, continuing at BOTTOM";
                   1143:              }
                   1144:          } else {
                   1145:              msg = (Byte *) "Pattern not found";
                   1146:          }
                   1147:       } while (msg == NULL && (cmdcnt-- > 1));
                   1148:       dc2:
                   1149:          psbs ("%s", msg);
                   1150:       break;
                   1151:     case '{':                  // {- move backward paragraph
                   1152:        do {
                   1153:            q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
                   1154:            if (q != NULL) {    // found blank line
                   1155:                dot = next_line(q);  // move to next blank line
                   1156:            }
                   1157:        } while (cmdcnt-- > 1);
                   1158:       break;
                   1159:     case '}':                  // }- move forward paragraph
                   1160:        do {
                   1161:            q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
                   1162:            if (q != NULL) {    // found blank line
                   1163:                dot = next_line(q);  // move to next blank line
                   1164:            }
                   1165:        } while (cmdcnt-- > 1);
                   1166:       break;
                   1167: #endif /* BB_FEATURE_VI_SEARCH */
                   1168:     case '0':                  // 0- goto begining of line
                   1169:     case '1':                  // 1-
                   1170:     case '2':                  // 2-
                   1171:     case '3':                  // 3-
                   1172:     case '4':                  // 4-
                   1173:     case '5':                  // 5-
                   1174:     case '6':                  // 6-
                   1175:     case '7':                  // 7-
                   1176:     case '8':                  // 8-
                   1177:     case '9':                  // 9-
                   1178:       if (c == '0' && cmdcnt < 1) {
                   1179:           dot_begin ();         // this was a standalone zero
                   1180:         } else {
                   1181:           cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
                   1182:          if (cmdcnt > 65536) cmdcnt = 65536; // arbitrary
                   1183:         }
                   1184:       break;
                   1185:     case ':':                  // :- the colon mode commands
                   1186:       p = get_input_line ((Byte *) ":");  // get input line- use "status line"
                   1187: #ifdef BB_FEATURE_VI_COLON
                   1188:       colon (p);                // execute the command
                   1189: #else /* BB_FEATURE_VI_COLON */
                   1190:       if (*p == ':')
                   1191:         p++;                    // move past the ':'
                   1192:       cnt = strlen ((char *) p);
                   1193:       if (cnt <= 0)
                   1194:         break;
                   1195:       if (strncasecmp((char *) p, "quit", cnt) == 0 ||
                   1196:           strncasecmp((char *) p, "q!", cnt) == 0) {   // delete lines
                   1197:           if (file_modified == TRUE && p[1] != '!') {
                   1198:               psbs ("No write since last change (:quit! overrides)");
                   1199:             } else {
                   1200:               editing = 0;
                   1201:             }
                   1202:       } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
                   1203:                 strncasecmp((char *) p, "wq", cnt) == 0) {
                   1204:           cnt = file_write (cfn, text, end - 1);
                   1205:           file_modified = FALSE;
                   1206:           psb ("\"%s\" %dL, %dC", cfn, count_lines (text, end - 1), cnt);
                   1207:           if (p[1] == 'q') {
                   1208:               editing = 0;
                   1209:             }
                   1210:       } else if (strncasecmp((char *) p, "file", cnt) == 0) {
                   1211:           edit_status ();       // show current file status
                   1212:       } else if (sscanf ((char *) p, "%d", &j) > 0) {
                   1213:           dot = find_line (j);  // go to line # j
                   1214:           dot_skip_over_ws ();
                   1215:       } else { // unrecognised cmd
                   1216:           ni ((Byte *) p);
                   1217:       }
                   1218: #endif /* BB_FEATURE_VI_COLON */
                   1219:       break;
                   1220:     case '<':                  // <- Left  shift something
                   1221:     case '>':                  // >- Right shift something
                   1222:       cnt = count_lines (text, dot);  // remember what line we are on
                   1223:       c1 = get_one_char ();     // get the type of thing to delete
                   1224:       find_range (&p, &q, c1);
                   1225:       (void) yank_delete (p, q, 1, YANKONLY); // save copy before change
                   1226:       p = begin_line (p);
                   1227:       q = end_line (q);
                   1228:       i = count_lines (p, q);   // # of lines we are shifting
                   1229:       for (; i > 0; i--, p = next_line(p)) {
                   1230:           if (c == '<') {
                   1231:               // shift left- remove tab or 8 spaces
                   1232:               if (*p == '\t') {
                   1233:                   // shrink buffer 1 char
                   1234:                   (void) text_hole_delete (p, p);
                   1235:                 } else if (*p == ' ') {
                   1236:                   // we should be calculating columns, not just SPACE
                   1237:                   for (j = 0; *p == ' ' && j < tabstop; j++) {
                   1238:                       (void) text_hole_delete (p, p);
                   1239:                     }
                   1240:                 }
                   1241:             } else if (c == '>') {
                   1242:               // shift right -- add tab or 8 spaces
                   1243:               (void) char_insert (p, '\t');
                   1244:             }
                   1245:         }
                   1246:       dot = find_line (cnt);    // what line were we on
                   1247:       dot_skip_over_ws ();
                   1248:       end_cmd_q ();             // stop adding to q
                   1249:       break;
                   1250:     case 'A':                  // A- append at e-o-l
                   1251:       dot_end ();               // go to e-o-l
                   1252:       //**** fall thru to ... 'a'
                   1253:     case 'a':                  // a- append after current char
                   1254:       if (*dot != '\n')
                   1255:         dot++;
                   1256:       goto dc_i;
                   1257:       break;
                   1258:     case 'B':                  // B- back a blank-delimited Word
                   1259:     case 'E':                  // E- end of a blank-delimited word
                   1260:     case 'W':                  // W- forward a blank-delimited word
                   1261:        dir = ((c == 'B') ? BACK : FORWARD);
                   1262:        do {
                   1263:          if (c == 'W' || isspace(dot[dir])) {
                   1264:              dot = skip_thing (dot, 1, dir, S_TO_WS);
                   1265:              dot = skip_thing (dot, 2, dir, S_OVER_WS);
                   1266:          }
                   1267:          if (c != 'W')
                   1268:              dot = skip_thing (dot, 1, dir, S_BEFORE_WS);
                   1269:        } while (cmdcnt-- > 1);
                   1270:       break;
                   1271:     case 'C':                  // C- Change to e-o-l
                   1272:     case 'D':                  // D- delete to e-o-l
                   1273:       save_dot = dot;
                   1274:       dot = dollar_line (dot);  // move to before NL
                   1275:       // copy text into a register and delete
                   1276:       dot = yank_delete (save_dot, dot, 0, YANKDEL);  // delete to e-o-l
                   1277:       if (c == 'C')
                   1278:         goto dc_i;              // start inserting
                   1279: #ifdef BB_FEATURE_VI_DOT_CMD
                   1280:       if (c == 'D')
                   1281:         end_cmd_q ();           // stop adding to q
                   1282: #endif /* BB_FEATURE_VI_DOT_CMD */
                   1283:       break;
                   1284:     case 'G':                  // G- goto to a line number (default= E-O-F)
                   1285:       dot = end - 1;            // assume E-O-F
                   1286:       if (cmdcnt > 0) {
                   1287:           dot = find_line (cmdcnt); // what line is #cmdcnt
                   1288:         }
                   1289:       dot_skip_over_ws ();
                   1290:       break;
                   1291:     case 'H':                  // H- goto top line on screen
                   1292:       dot = screenbegin;
                   1293:       if (cmdcnt > (rows - 1)) {
                   1294:           cmdcnt = (rows - 1);
                   1295:         }
                   1296:       if (cmdcnt-- > 1) {
                   1297:           do_cmd ('+');
                   1298:         }                       // repeat cnt
                   1299:       dot_skip_over_ws ();
                   1300:       break;
                   1301:     case 'I':                  // I- insert before first non-blank
                   1302:       dot_begin ();             // 0
                   1303:       dot_skip_over_ws ();
                   1304:       //**** fall thru to ... 'i'
                   1305:     case 'i':                  // i- insert before current char
                   1306:     case VI_K_INSERT:          // Cursor Key Insert
                   1307:     dc_i:
                   1308:       cmd_mode = 1;             // start insrting
                   1309:       psb ("-- Insert --");
                   1310:       break;
                   1311:     case 'J':                  // J- join current and next lines together
                   1312:       do {
                   1313:          dot_end ();               // move to NL
                   1314:          if (dot < end - 1) {  // make sure not last char in text[]
                   1315:              *dot++ = ' ';         // replace NL with space
                   1316:              while (isblnk(*dot)) {    // delete leading WS
                   1317:                  dot_delete ();
                   1318:              }
                   1319:          }
                   1320:          end_cmd_q ();             // stop adding to q
                   1321:       } while (cmdcnt-- > 2);
                   1322:       break;
                   1323:     case 'L':                  // L- goto bottom line on screen
                   1324:       dot = end_screen ();
                   1325:       if (cmdcnt > (rows - 1)) {
                   1326:           cmdcnt = (rows - 1);
                   1327:         }
                   1328:       if (cmdcnt-- > 1) {
                   1329:           do_cmd ('-');
                   1330:         }                       // repeat cnt
                   1331:       dot_begin ();
                   1332:       dot_skip_over_ws ();
                   1333:       break;
                   1334:     case 'M':                  // M- goto middle line on screen
                   1335:       dot = screenbegin;
                   1336:       for (cnt = 0; cnt < (rows - 1) / 2; cnt++)
                   1337:         dot = next_line (dot);
                   1338:       break;
                   1339:     case 'O':                  // O- open a empty line above
                   1340:       //    0i\n ESC -i
                   1341:       p = begin_line (dot);
                   1342:       if (p[-1] == '\n') {
                   1343:           dot_prev ();
                   1344:     case 'o':                  // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
                   1345:           dot_end ();
                   1346:           dot = char_insert (dot, '\n');
                   1347:         } else {
                   1348:           dot_begin ();         // 0
                   1349:           dot = char_insert (dot, '\n');  // i\n ESC
                   1350:           dot_prev ();          // -
                   1351:         }
                   1352:       goto dc_i;
                   1353:       break;
                   1354:     case 'R':                  // R- continuous Replace char
                   1355:     dc5:
                   1356:       cmd_mode = 2;
                   1357:       psb ("-- Replace --");
                   1358:       break;
                   1359:     case 'X':                  // X- delete char before dot
                   1360:     case 'x':                  // x- delete the current char
                   1361:     case 's':                  // s- substitute the current char
                   1362:        dir = (c == 'X') ? -1 : 0;
                   1363:        do {
                   1364:          if (dot[dir] != '\n') {
                   1365:              if (c == 'X')
                   1366:                  dot--;              // delete prev char
                   1367:              dot = yank_delete (dot, dot, 0, YANKDEL); // delete char
                   1368:          }
                   1369:        } while (cmdcnt-- > 1);
                   1370:       if (c == 's')
                   1371:         goto dc_i;              // start insrting
                   1372:       end_cmd_q ();             // stop adding to q
                   1373:       break;
                   1374:     case 'Z':                  // Z- if modified, {write}; exit
                   1375:       // ZZ means to save file (if necessary), then exit
                   1376:       c1 = get_one_char ();
                   1377:       if (c1 != 'Z') {
                   1378:           indicate_error (c);
                   1379:           break;
                   1380:         }
                   1381:       if (file_modified == TRUE
                   1382: #ifdef BB_FEATURE_VI_READONLY
                   1383:           && vi_readonly == FALSE
                   1384:          && readonly == FALSE
                   1385: #endif /* BB_FEATURE_VI_READONLY */
                   1386:         ) {
                   1387:           cnt = file_write (cfn, text, end - 1);
                   1388:           if (cnt == (end - 1 - text + 1)) {
                   1389:               editing = 0;
                   1390:             }
                   1391:         } else {
                   1392:           editing = 0;
                   1393:         }
                   1394:       break;
                   1395:     case '^':                  // ^- move to first non-blank on line
                   1396:       dot_begin ();
                   1397:       dot_skip_over_ws ();
                   1398:       break;
                   1399:     case 'b':                  // b- back a word
                   1400:     case 'e':                  // e- end of word
                   1401:       dir = (c == 'b') ? BACK : FORWARD;
                   1402:       do {
                   1403:          if ((dot + dir) < text || (dot + dir) > end - 1)
                   1404:              break;
                   1405:          dot += dir;
                   1406:          if (isspace(*dot)) {
                   1407:              dot = skip_thing (dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
                   1408:          }
                   1409:          if (isalnum(*dot) || *dot == '_') {
                   1410:              dot = skip_thing (dot, 1, dir, S_END_ALNUM);
                   1411:          } else if (ispunct(*dot)) {
                   1412:              dot = skip_thing (dot, 1, dir, S_END_PUNCT);
                   1413:          }
                   1414:       } while (cmdcnt-- > 1);
                   1415:       break;
                   1416:     case 'c':                  // c- change something
                   1417:     case 'd':                  // d- delete something
                   1418: #ifdef BB_FEATURE_VI_YANKMARK
                   1419:     case 'y':                  // y- yank   something
                   1420:     case 'Y':                  // Y- Yank a line
                   1421: #endif /* BB_FEATURE_VI_YANKMARK */
                   1422:       yf = YANKDEL;             // assume either "c" or "d"
                   1423: #ifdef BB_FEATURE_VI_YANKMARK
                   1424:       if (c == 'y' || c == 'Y')
                   1425:         yf = YANKONLY;
                   1426: #endif /* BB_FEATURE_VI_YANKMARK */
                   1427:       c1 = 'y';
                   1428:       if (c != 'Y')
                   1429:         c1 = get_one_char ();   // get the type of thing to delete
                   1430:       find_range (&p, &q, c1);
                   1431:       if (c1 == 27) {  // ESC- user changed mind and wants out
                   1432:           c = c1 = 27;          // Escape- do nothing
                   1433:       } else if (strchr("wW", c1)) {
                   1434:           if (c == 'c') {
                   1435:               // don't include trailing WS as part of word
                   1436:               while (isblnk(*q)) {
                   1437:                   if (q <= text || q[-1] == '\n')
                   1438:                     break;
                   1439:                   q--;
                   1440:                 }
                   1441:             }
                   1442:           dot = yank_delete (p, q, 0, yf);  // delete word
                   1443:       } else if (strchr("^0bBeEft$", c1)) {
                   1444:           // single line copy text into a register and delete
                   1445:           dot = yank_delete (p, q, 0, yf);  // delete word
                   1446:       } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
                   1447:           // multiple line copy text into a register and delete
                   1448:           dot = yank_delete (p, q, 1, yf);  // delete lines
                   1449:           if (c == 'c') {
                   1450:               dot = char_insert (dot, '\n');
                   1451:               // on the last line of file don't move to prev line
                   1452:               if (dot != (end - 1)) {
                   1453:                   dot_prev ();
                   1454:                 }
                   1455:          } else if (c == 'd') {
                   1456:               dot_begin ();
                   1457:               dot_skip_over_ws ();
                   1458:          }
                   1459:       } else {
                   1460:          // could not recognize object
                   1461:           c = c1 = 27; // error-
                   1462:           indicate_error (c);
                   1463:       }
                   1464:       if (c1 != 27) {
                   1465:           // if CHANGING, not deleting, start inserting after the delete
                   1466:           if (c == 'c') {
                   1467:               strcpy ((char *) buf, "Change");
                   1468:               goto dc_i;        // start inserting
                   1469:          }
                   1470:           if (c == 'd') {
                   1471:               strcpy ((char *) buf, "Delete");
                   1472:          }
                   1473: #ifdef BB_FEATURE_VI_YANKMARK
                   1474:           if (c == 'y' || c == 'Y') {
                   1475:               strcpy ((char *) buf, "Yank");
                   1476:          }
                   1477:           p = reg[YDreg];
                   1478:           q = p + strlen ((char *) p);
                   1479:           for (cnt = 0; p <= q; p++) {
                   1480:               if (*p == '\n')
                   1481:                 cnt++;
                   1482:          }
                   1483:           psb ("%s %d lines (%d chars) using [%c]",
                   1484:                buf, cnt, strlen ((char *) reg[YDreg]), what_reg ());
                   1485: #endif /* BB_FEATURE_VI_YANKMARK */
                   1486:           end_cmd_q ();         // stop adding to q
                   1487:         }
                   1488:       break;
                   1489:     case 'k':                  // k- goto prev line, same col
                   1490:     case VI_K_UP:              // cursor key Up
                   1491:       do {
                   1492:          dot_prev();
                   1493:          dot = move_to_col(dot, ccol + offset); // try stay in same col
                   1494:       } while (cmdcnt-- > 1);
                   1495:       break;
                   1496:     case 'r':                  // r- replace the current char with user input
                   1497:       c1 = get_one_char ();     // get the replacement char
                   1498:       if (*dot != '\n') {
                   1499:           *dot = c1;
                   1500:           file_modified = TRUE; // has the file been modified
                   1501:       }
                   1502:       end_cmd_q ();             // stop adding to q
                   1503:       break;
                   1504:     case 't':                  // t- move to char prior to next x
                   1505:       last_forward_char = get_one_char ();
                   1506:       do_cmd (';');
                   1507:       if (*dot == last_forward_char)
                   1508:         dot_left ();
                   1509:       last_forward_char = 0;
                   1510:       break;
                   1511:     case 'w':                  // w- forward a word
                   1512:        do {
                   1513:            if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
                   1514:                dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
                   1515:            } else if (ispunct(*dot)) { // we are on PUNCT
                   1516:                dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
                   1517:            }
                   1518:            if (dot < end - 1)
                   1519:                dot++;                  // move over word
                   1520:            if (isspace(*dot)) {
                   1521:                dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
                   1522:            }
                   1523:        } while (cmdcnt-- > 1);
                   1524:       break;
                   1525:     case 'z':                  // z-
                   1526:       c1 = get_one_char ();     // get the replacement char
                   1527:       cnt = 0;
                   1528:       if (c1 == '.')
                   1529:         cnt = (rows - 2) / 2;   // put dot at center
                   1530:       if (c1 == '-')
                   1531:         cnt = rows - 2;         // put dot at bottom
                   1532:       screenbegin = begin_line (dot); // start dot at top
                   1533:       dot_scroll (cnt, -1);
                   1534:       break;
                   1535:     case '|':                  // |- move to column "cmdcnt"
                   1536:       dot = move_to_col (dot, cmdcnt - 1);  // try to move to column
                   1537:       break;
                   1538:     case '~':                  // ~- flip the case of letters   a-z -> A-Z
                   1539:       do {
                   1540:          if (islower(*dot)) {
                   1541:              *dot = toupper(*dot);
                   1542:              file_modified = TRUE; // has the file been modified
                   1543:          } else if (isupper(*dot)) {
                   1544:              *dot = tolower(*dot);
                   1545:              file_modified = TRUE; // has the file been modified
                   1546:          }
                   1547:          dot_right();
                   1548:       } while (cmdcnt-- > 1);
                   1549:       end_cmd_q ();             // stop adding to q
                   1550:       break;
                   1551:       //----- The Cursor and Function Keys -----------------------------
                   1552:     case VI_K_HOME:            // Cursor Key Home
                   1553:       dot_begin ();
                   1554:       break;
                   1555:       // The Fn keys could point to do_macro which could translate them
                   1556:     case VI_K_FUN1:            // Function Key F1
                   1557:     case VI_K_FUN2:            // Function Key F2
                   1558:     case VI_K_FUN3:            // Function Key F3
                   1559:     case VI_K_FUN4:            // Function Key F4
                   1560:     case VI_K_FUN5:            // Function Key F5
                   1561:     case VI_K_FUN6:            // Function Key F6
                   1562:     case VI_K_FUN7:            // Function Key F7
                   1563:     case VI_K_FUN8:            // Function Key F8
                   1564:     case VI_K_FUN9:            // Function Key F9
                   1565:     case VI_K_FUN10:           // Function Key F10
                   1566:     case VI_K_FUN11:           // Function Key F11
                   1567:     case VI_K_FUN12:           // Function Key F12
                   1568:       break;
                   1569:     }
                   1570:
                   1571: dc1:
                   1572:   // if text[] just became empty, add back an empty line
                   1573:   if (end == text) {
                   1574:       (void) char_insert (text, '\n');  // start empty buf with dummy line
                   1575:       dot = text;
                   1576:   }
                   1577:   // it is OK for dot to exactly equal to end, otherwise check dot validity
                   1578:   if (dot != end) {
                   1579:       dot = bound_dot (dot);    // make sure "dot" is valid
                   1580:   }
                   1581: #ifdef BB_FEATURE_VI_YANKMARK
                   1582:   check_context (c);            // update the current context
                   1583: #endif /* BB_FEATURE_VI_YANKMARK */
                   1584:
                   1585:   if (!isdigit (c))
                   1586:     cmdcnt = 0;                 // cmd was not a number, reset cmdcnt
                   1587:   cnt = dot - begin_line (dot);
                   1588:   // Try to stay off of the Newline
                   1589:   if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
                   1590:     dot--;
                   1591: }
                   1592:
                   1593: //----- The Colon commands -------------------------------------
                   1594: #ifdef BB_FEATURE_VI_COLON
                   1595: static Byte *get_one_address(Byte * p, int *addr)      // get colon addr, if present
                   1596: {
                   1597:   int st;
                   1598:   Byte *q;
                   1599:
                   1600: #ifdef BB_FEATURE_VI_YANKMARK
                   1601:   Byte c;
                   1602: #endif /* BB_FEATURE_VI_YANKMARK */
                   1603: #ifdef BB_FEATURE_VI_SEARCH
                   1604:   Byte *pat, buf[BUFSIZ];
                   1605: #endif /* BB_FEATURE_VI_SEARCH */
                   1606:
                   1607:   *addr = -1;                   // assume no addr
                   1608:   if (*p == '.') {     // the current line
                   1609:       p++;
                   1610:       q = begin_line (dot);
                   1611:       *addr = count_lines (text, q);
                   1612: #ifdef BB_FEATURE_VI_YANKMARK
                   1613:   } else if (*p == '\'') {     // is this a mark addr
                   1614:       p++;
                   1615:       c = tolower (*p);
                   1616:       p++;
                   1617:       if (c >= 'a' && c <= 'z') {
                   1618:           // we have a mark
                   1619:           c = c - 'a';
                   1620:           q = mark[(int) c];
                   1621:           if (q != NULL) {     // is mark valid
                   1622:               *addr = count_lines (text, q);  // count lines
                   1623:          }
                   1624:       }
                   1625: #endif /* BB_FEATURE_VI_YANKMARK */
                   1626: #ifdef BB_FEATURE_VI_SEARCH
                   1627:   } else if (*p == '/') {      // a search pattern
                   1628:       q = buf;
                   1629:       for (p++; *p; p++) {
                   1630:           if (*p == '/')
                   1631:             break;
                   1632:           *q++ = *p;
                   1633:           *q = '\0';
                   1634:       }
                   1635:       pat = (Byte *) strdup ((char *) buf); // save copy of pattern
                   1636:       if (*p == '/')
                   1637:         p++;
                   1638:       q = char_search (dot, pat, FORWARD, FULL);
                   1639:       if (q != NULL) {
                   1640:           *addr = count_lines (text, q);
                   1641:       }
                   1642:       free (pat);
                   1643: #endif /* BB_FEATURE_VI_SEARCH */
                   1644:   } else if (*p == '$') {      // the last line in file
                   1645:       p++;
                   1646:       q = begin_line (end - 1);
                   1647:       *addr = count_lines (text, q);
                   1648:   } else if (isdigit(*p)) {    // specific line number
                   1649:       sscanf ((char *) p, "%d%n", addr, &st);
                   1650:       p += st;
                   1651:   } else {     // I don't reconise this
                   1652:       // unrecognised address- assume -1
                   1653:       *addr = -1;
                   1654:   }
                   1655:   return (p);
                   1656: }
                   1657:
                   1658: static Byte *get_address(Byte * p, int *b, int *e)     // get two colon addrs, if present
                   1659: {
                   1660:   //----- get the address' i.e., 1,3   'a,'b  -----
                   1661:   // get FIRST addr, if present
                   1662:   while (isblnk (*p))
                   1663:     p++;                        // skip over leading spaces
                   1664:   if (*p == '%') {     // alias for 1,$
                   1665:       p++;
                   1666:       *b = 1;
                   1667:       *e = count_lines (text, end - 1);
                   1668:       goto ga0;
                   1669:   }
                   1670:   p = get_one_address (p, b);
                   1671:   while (isblnk (*p))
                   1672:     p++;
                   1673:   if (*p == ',') {     // is there a address seperator
                   1674:       p++;
                   1675:       while (isblnk (*p))
                   1676:         p++;
                   1677:       // get SECOND addr, if present
                   1678:       p = get_one_address (p, e);
                   1679:   }
                   1680: ga0:
                   1681:   while (isblnk (*p))
                   1682:     p++;                        // skip over trailing spaces
                   1683:   return (p);
                   1684: }
                   1685:
                   1686: static void colon(Byte * buf)
                   1687: {
                   1688:   Byte c, *orig_buf, *buf1, *q, *r;
                   1689:   Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
                   1690:   int i, l, li, ch, st, b, e;
                   1691:   int useforce, forced;
                   1692:   struct stat st_buf;
                   1693:
                   1694:   // :3154        // if (-e line 3154) goto it  else stay put
                   1695:   // :4,33w! foo  // write a portion of buffer to file "foo"
                   1696:   // :w           // write all of buffer to current file
                   1697:   // :q           // quit
                   1698:   // :q!          // quit- dont care about modified file
                   1699:   // :'a,'z!sort -u   // filter block through sort
                   1700:   // :'f          // goto mark "f"
                   1701:   // :'fl         // list literal the mark "f" line
                   1702:   // :.r bar      // read file "bar" into buffer before dot
                   1703:   // :/123/,/abc/d    // delete lines from "123" line to "abc" line
                   1704:   // :/xyz/       // goto the "xyz" line
                   1705:   // :s/find/replace/ // substitute pattern "find" with "replace"
                   1706:   // :!<cmd>      // run <cmd> then return
                   1707:   //
                   1708:   if (strlen ((char *) buf) <= 0)
                   1709:     goto vc1;
                   1710:   if (*buf == ':')
                   1711:     buf++;                      // move past the ':'
                   1712:
                   1713:   forced = useforce = FALSE;
                   1714:   li = st = ch = i = 0;
                   1715:   b = e = -1;
                   1716:   q = text;                     // assume 1,$ for the range
                   1717:   r = end - 1;
                   1718:   li = count_lines (text, end - 1);
                   1719:   fn = cfn;                     // default to current file
                   1720:   memset (cmd, '\0', BUFSIZ);   // clear cmd[]
                   1721:   memset (args, '\0', BUFSIZ);  // clear args[]
                   1722:
                   1723:   // look for optional address(es)  :.  :1  :1,9   :'q,'a   :%
                   1724:   buf = get_address (buf, &b, &e);
                   1725:
                   1726:   // remember orig command line
                   1727:   orig_buf = buf;
                   1728:
                   1729:   // get the COMMAND into cmd[]
                   1730:   buf1 = cmd;
                   1731:   while (*buf != '\0') {
                   1732:       if (isspace (*buf))
                   1733:         break;
                   1734:       *buf1++ = *buf++;
                   1735:   }
                   1736:   // get any ARGuments
                   1737:   while (isblnk (*buf))
                   1738:     buf++;
                   1739:   /* FIXED strcpy((char *) args, (char *) buf); */
                   1740:   if (strlcpy((char *) args, (char *) buf, sizeof((char *)args)) > sizeof((char *)args)) err(1, "strlcpy overflow in function colon");
                   1741:   buf1 = (Byte *)last_char_is((char *)cmd, '!');
                   1742:   if (buf1) {
                   1743:       useforce = TRUE;
                   1744:       *buf1 = '\0';             // get rid of !
                   1745:   }
                   1746:   if (b >= 0) {
                   1747:       // if there is only one addr, then the addr
                   1748:       // is the line number of the single line the
                   1749:       // user wants. So, reset the end
                   1750:       // pointer to point at end of the "b" line
                   1751:       q = find_line (b);        // what line is #b
                   1752:       r = end_line (q);
                   1753:       li = 1;
                   1754:   }
                   1755:   if (e >= 0) {
                   1756:       // we were given two addrs.  change the
                   1757:       // end pointer to the addr given by user.
                   1758:       r = find_line (e);        // what line is #e
                   1759:       r = end_line (r);
                   1760:       li = e - b + 1;
                   1761:   }
                   1762:   // ------------ now look for the command ------------
                   1763:   i = strlen ((char *) cmd);
                   1764:   if (i == 0) {                // :123CR goto line #123
                   1765:       if (b >= 0) {
                   1766:           dot = find_line (b);  // what line is #b
                   1767:           dot_skip_over_ws ();
                   1768:       }
                   1769:   } else if (0) {
                   1770:       // } else if (strncmp((char *) cmd, "!", 1) == 0) {     // run a cmd
                   1771:       // :!ls   run the <cmd>
                   1772:       (void) alarm(0);             // wait for input- no alarms
                   1773:       place_cursor (rows - 1, 0, FALSE);  // go to Status line
                   1774:       clear_to_eol ();          // clear the line
                   1775:       cookmode ();
                   1776:       system((char *)orig_buf+1);    // run the cmd
                   1777:       rawmode ();
                   1778:       Hit_Return ();            // let user see results
                   1779:       (void) alarm(3);             // done waiting for input
                   1780:   } else if (strncmp((char *) cmd, "=", i) == 0) {     // where is the address
                   1781:       if (b < 0) {     // no addr given- use defaults
                   1782:           b = e = count_lines (text, dot);
                   1783:       }
                   1784:       psb ("%d", b);
                   1785:   } else if (strncasecmp((char *) cmd, "delete", i) == 0) {    // delete lines
                   1786:       if (b < 0) {     // no addr given- use defaults
                   1787:           q = begin_line (dot); // assume .,. for the range
                   1788:           r = end_line (dot);
                   1789:       }
                   1790:       dot = yank_delete (q, r, 1, YANKDEL); // save, then delete lines
                   1791:       dot_skip_over_ws ();
                   1792:   } else if (0) {
                   1793:       // } else if (strncasecmp((char *) cmd, "edit", i) == 0) {      // Edit a file
                   1794:       int sr;
                   1795:       sr = 0;
                   1796:       // don't edit, if the current file has been modified
                   1797:       if (file_modified == TRUE && useforce != TRUE) {
                   1798:           psbs ("No write since last change (:edit! overrides)");
                   1799:           goto vc1;
                   1800:       }
                   1801:       if (strlen((char *)args) > 0) {
                   1802:           // the user supplied a file name
                   1803:           fn = args;
                   1804:       } else if (cfn != 0 && strlen((char *)cfn) > 0) {
                   1805:           // no user supplied name- use the current filename
                   1806:           fn = cfn;
                   1807:           goto vc5;
                   1808:       } else {
                   1809:           // no user file name, no current name- punt
                   1810:           psbs ("No current filename");
                   1811:           goto vc1;
                   1812:       }
                   1813:
                   1814:       // see if file exists- if not, its just a new file request
                   1815:       if ((sr = stat((char *) fn, &st_buf)) < 0) {
                   1816:           // This is just a request for a new file creation.
                   1817:           // The file_insert below will fail but we get
                   1818:           // an empty buffer with a file name.  Then the "write"
                   1819:           // command can do the create.
                   1820:       } else {
                   1821:           if ((st_buf.st_mode & (S_IFREG)) == 0) {
                   1822:               // This is not a regular file
                   1823:               psbs ("\"%s\" is not a regular file", fn);
                   1824:               goto vc1;
                   1825:          }
                   1826:           if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
                   1827:               // dont have any read permissions
                   1828:               psbs ("\"%s\" is not readable", fn);
                   1829:               goto vc1;
                   1830:          }
                   1831:       }
                   1832:
                   1833:       // There is a read-able regular file
                   1834:       // make this the current file
                   1835:       q = (Byte *) strdup ((char *) fn);  // save the cfn
                   1836:       if (cfn != 0)
                   1837:         free (cfn);             // free the old name
                   1838:       cfn = q;                  // remember new cfn
                   1839:
                   1840:     vc5:
                   1841:       // delete all the contents of text[]
                   1842:       new_text (2 * file_size (fn));
                   1843:       screenbegin = dot = end = text;
                   1844:
                   1845:       // insert new file
                   1846:       ch = file_insert (fn, text, file_size (fn));
                   1847:
                   1848:       if (ch < 1) {
                   1849:           // start empty buf with dummy line
                   1850:           (void) char_insert (text, '\n');
                   1851:           ch = 1;
                   1852:       }
                   1853:       file_modified = FALSE;
                   1854: #ifdef BB_FEATURE_VI_YANKMARK
                   1855:       if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
                   1856:           free (reg[Ureg]);     //   free orig line reg- for 'U'
                   1857:           reg[Ureg] = 0;
                   1858:       }
                   1859:       if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
                   1860:           free (reg[YDreg]);    //   free default yank/delete register
                   1861:           reg[YDreg] = 0;
                   1862:       }
                   1863:       for (li = 0; li < 28; li++) {
                   1864:           mark[li] = 0;
                   1865:       }                                // init the marks
                   1866: #endif /* BB_FEATURE_VI_YANKMARK */
                   1867:       // how many lines in text[]?
                   1868:       li = count_lines (text, end - 1);
                   1869:       psb ("\"%s\"%s"
                   1870: #ifdef BB_FEATURE_VI_READONLY
                   1871:            "%s"
                   1872: #endif /* BB_FEATURE_VI_READONLY */
                   1873:            " %dL, %dC", cfn,
                   1874:           (sr < 0 ? " [New file]" : ""),
                   1875: #ifdef BB_FEATURE_VI_READONLY
                   1876:            ((vi_readonly == TRUE || readonly == TRUE) ? " [Read only]" : ""),
                   1877: #endif /* BB_FEATURE_VI_READONLY */
                   1878:            li, ch);
                   1879:     }
                   1880:   else if (0) {
                   1881:       // } else if (strncasecmp((char *) cmd, "file", i) == 0) {      // what File is this
                   1882:       if (b != -1 || e != -1) {
                   1883:           ni ((Byte *) "No address allowed on this command");
                   1884:           goto vc1;
                   1885:       }
                   1886:       if (strlen ((char *) args) > 0) {
                   1887:           // user wants a new filename
                   1888:           if (cfn != NULL)
                   1889:             free (cfn);
                   1890:           cfn = (Byte *) strdup ((char *) args);
                   1891:       } else {
                   1892:           // user wants file status info
                   1893:           edit_status ();
                   1894:       }
                   1895:   } else if (strncasecmp ((char *) cmd, "features", i) == 0) { // what features are available
                   1896:       // print out values of all features
                   1897:       place_cursor (rows - 1, 0, FALSE);  // go to Status line, bottom of screen
                   1898:       clear_to_eol ();          // clear the line
                   1899:       cookmode ();
                   1900:       show_help ();
                   1901:       rawmode ();
                   1902:       Hit_Return ();
                   1903:   } else if (strncasecmp ((char *) cmd, "list", i) == 0) {     // literal print line
                   1904:       if (b < 0) {     // no addr given- use defaults
                   1905:           q = begin_line (dot); // assume .,. for the range
                   1906:           r = end_line (dot);
                   1907:       }
                   1908:       place_cursor (rows - 1, 0, FALSE);  // go to Status line, bottom of screen
                   1909:       clear_to_eol ();          // clear the line
                   1910:       write (1, "\r\n", 2);
                   1911:       for (; q <= r; q++) {
                   1912:           c = *q;
                   1913:           if (c > '~')
                   1914:             standout_start ();
                   1915:           if (c == '\n') {
                   1916:               write (1, "$\r", 2);
                   1917:          } else if (*q < ' ') {
                   1918:               write (1, "^", 1);
                   1919:               c += '@';
                   1920:          }
                   1921:           write (1, &c, 1);
                   1922:           if (c > '~')
                   1923:             standout_end ();
                   1924:       }
                   1925: #ifdef BB_FEATURE_VI_SET
                   1926:     vc2:
                   1927: #endif /* BB_FEATURE_VI_SET */
                   1928:       Hit_Return ();
                   1929:     } else if ((strncasecmp ((char *) cmd, "quit", i) == 0) || // Quit
                   1930:            (strncasecmp ((char *) cmd, "next", i) == 0)) {     // edit next file
                   1931:       if (useforce == TRUE) {
                   1932:           // force end of argv list
                   1933:           if (*cmd == 'q') {
                   1934:               optind = save_argc;
                   1935:          }
                   1936:           editing = 0;
                   1937:           goto vc1;
                   1938:       }
                   1939:       // don't exit if the file been modified
                   1940:       if (file_modified == TRUE) {
                   1941:           psbs ("No write since last change (:%s! overrides)",
                   1942:                 (*cmd == 'q' ? "quit" : "next"));
                   1943:           goto vc1;
                   1944:       }
                   1945:       // are there other file to edit
                   1946:       /* if (*cmd == 'q' && optind < save_argc - 1) {
                   1947:          psbs("%d more file to edit", (save_argc - optind - 1));
                   1948:          goto vc1;
                   1949:          }
                   1950:          if (*cmd == 'n' && optind >= save_argc - 1) {
                   1951:          psbs("No more files to edit");
                   1952:          goto vc1;
                   1953:          } */
                   1954:       editing = 0;
                   1955:   } else if (0) {
                   1956:       // } else if (strncasecmp((char *) cmd, "read", i) == 0) {      // read file into text[]
                   1957:       fn = args;
                   1958:       if (strlen ((char *) fn) <= 0) {
                   1959:           psbs ("No filename given");
                   1960:           goto vc1;
                   1961:       }
                   1962:       if (b < 0) {     // no addr given- use defaults
                   1963:           q = begin_line (dot); // assume "dot"
                   1964:       }
                   1965:       // read after current line- unless user said ":0r foo"
                   1966:       if (b != 0)
                   1967:         q = next_line (q);
                   1968: #ifdef BB_FEATURE_VI_READONLY
                   1969:       l = readonly;             // remember current files' status
                   1970: #endif
                   1971:       ch = file_insert (fn, q, file_size (fn));
                   1972: #ifdef BB_FEATURE_VI_READONLY
                   1973:       readonly = l;
                   1974: #endif
                   1975:       if (ch < 0)
                   1976:         goto vc1;               // nothing was inserted
                   1977:       // how many lines in text[]?
                   1978:       li = count_lines (q, q + ch - 1);
                   1979:       psb ("\"%s\""
                   1980: #ifdef BB_FEATURE_VI_READONLY
                   1981:            "%s"
                   1982: #endif /* BB_FEATURE_VI_READONLY */
                   1983:            " %dL, %dC", fn,
                   1984: #ifdef BB_FEATURE_VI_READONLY
                   1985:            ((vi_readonly == TRUE || readonly == TRUE) ? " [Read only]" : ""),
                   1986: #endif /* BB_FEATURE_VI_READONLY */
                   1987:            li, ch);
                   1988:       if (ch > 0) {
                   1989:           // if the insert is before "dot" then we need to update
                   1990:           if (q <= dot)
                   1991:             dot += ch;
                   1992:           file_modified = TRUE;
                   1993:       }
                   1994:   } else if (strncasecmp ((char *) cmd, "rewind", i) == 0) {   // rewind cmd line args
                   1995:       if (file_modified == TRUE && useforce != TRUE) {
                   1996:           psbs ("No write since last change (:rewind! overrides)");
                   1997:       } else {
                   1998:           // reset the filenames to edit
                   1999:           optind = fn_start - 1;
                   2000:           editing = 0;
                   2001:       }
                   2002: #ifdef BB_FEATURE_VI_SET
                   2003:   } else if (strncasecmp ((char *) cmd, "set", i) == 0) {      // set or clear features
                   2004:       i = 0;                    // offset into args
                   2005:       if (strlen ((char *) args) == 0) {
                   2006:           // print out values of all options
                   2007:           place_cursor (rows - 1, 0, FALSE);  // go to Status line, bottom of screen
                   2008:           clear_to_eol ();      // clear the line
                   2009:           printf ("----------------------------------------\r\n");
                   2010: #ifdef BB_FEATURE_VI_SETOPTS
                   2011:           if (!autoindent)
                   2012:             printf ("no");
                   2013:           printf ("autoindent ");
                   2014:           if (!err_method)
                   2015:             printf ("no");
                   2016:           printf ("flash ");
                   2017:           if (!ignorecase)
                   2018:             printf ("no");
                   2019:           printf ("ignorecase ");
                   2020:           if (!showmatch)
                   2021:             printf ("no");
                   2022:           printf ("showmatch ");
                   2023:           printf ("tabstop=%d ", tabstop);
                   2024: #endif /* BB_FEATURE_VI_SETOPTS */
                   2025:           printf ("\r\n");
                   2026:           goto vc2;
                   2027:         }
                   2028:       if (strncasecmp ((char *) args, "no", 2) == 0)
                   2029:         i = 2;                  // ":set noautoindent"
                   2030: #ifdef BB_FEATURE_VI_SETOPTS
                   2031:       if (strncasecmp ((char *) args + i, "autoindent", 10) == 0 ||
                   2032:           strncasecmp ((char *) args + i, "ai", 2) == 0) {
                   2033:           autoindent = (i == 2) ? 0 : 1;
                   2034:       }
                   2035:       if (strncasecmp ((char *) args + i, "flash", 5) == 0 ||
                   2036:           strncasecmp ((char *) args + i, "fl", 2) == 0) {
                   2037:           err_method = (i == 2) ? 0 : 1;
                   2038:       }
                   2039:       if (strncasecmp ((char *) args + i, "ignorecase", 10) == 0 ||
                   2040:           strncasecmp ((char *) args + i, "ic", 2) == 0) {
                   2041:           ignorecase = (i == 2) ? 0 : 1;
                   2042:       }
                   2043:       if (strncasecmp ((char *) args + i, "showmatch", 9) == 0 ||
                   2044:           strncasecmp ((char *) args + i, "sm", 2) == 0) {
                   2045:           showmatch = (i == 2) ? 0 : 1;
                   2046:       }
                   2047:       if (strncasecmp ((char *) args + i, "tabstop", 7) == 0) {
                   2048:           sscanf (strchr ((char *) args + i, '='), "=%d", &ch);
                   2049:           if (ch > 0 && ch < columns - 1)
                   2050:             tabstop = ch;
                   2051:       }
                   2052: #endif /* BB_FEATURE_VI_SETOPTS */
                   2053: #endif /* BB_FEATURE_VI_SET */
                   2054: #ifdef BB_FEATURE_VI_SEARCH
                   2055:   } else if (strncasecmp ((char *) cmd, "s", 1) == 0) {        // substitute a pattern with a replacement pattern
                   2056:       Byte *ls, *F, *R;
                   2057:       int gflag;
                   2058:
                   2059:       // F points to the "find" pattern
                   2060:       // R points to the "replace" pattern
                   2061:       // replace the cmd line delimiters "/" with NULLs
                   2062:       gflag = 0;                // global replace flag
                   2063:       c = orig_buf[1];          // what is the delimiter
                   2064:       F = orig_buf + 2;         // start of "find"
                   2065:       R = (Byte *) strchr ((char *) F, c);  // middle delimiter
                   2066:       if (!R) goto colon_s_fail;
                   2067:       *R++ = '\0';              // terminate "find"
                   2068:       buf1 = (Byte *) strchr ((char *) R, c);
                   2069:       if (!buf1) goto colon_s_fail;
                   2070:       *buf1++ = '\0';           // terminate "replace"
                   2071:       if (*buf1 == 'g') {      // :s/foo/bar/g
                   2072:           buf1++;
                   2073:           gflag++;              // turn on gflag
                   2074:       }
                   2075:       q = begin_line (q);
                   2076:       if (b < 0) {             // maybe :s/foo/bar/
                   2077:           q = begin_line (dot); // start with cur line
                   2078:           b = count_lines (text, q);  // cur line number
                   2079:       }
                   2080:       if (e < 0)
                   2081:         e = b;                  // maybe :.s/foo/bar/
                   2082:       for (i = b; i <= e; i++) {       // so, :20,23 s \0 find \0 replace \0
                   2083:           ls = q;               // orig line start
                   2084:         vc4:
                   2085:           buf1 = char_search (q, F, FORWARD, LIMITED);  // search cur line only for "find"
                   2086:           if (buf1 != NULL) {
                   2087:               // we found the "find" pattern- delete it
                   2088:               (void) text_hole_delete (buf1, buf1 + strlen ((char *) F) - 1);
                   2089:               // inset the "replace" patern
                   2090:               (void) string_insert (buf1, R); // insert the string
                   2091:               // check for "global"  :s/foo/bar/g
                   2092:               if (gflag == 1) {
                   2093:                   if ((buf1 + strlen ((char *) R)) < end_line(ls)) {
                   2094:                       q = buf1 + strlen ((char *) R);
                   2095:                       goto vc4; // don't let q move past cur line
                   2096:                  }
                   2097:              }
                   2098:          }
                   2099:           q = next_line (ls);
                   2100:       }
                   2101: #endif /* BB_FEATURE_VI_SEARCH */
                   2102:   } else if (strncasecmp ((char *) cmd, "version", i) == 0) {  // show software version
                   2103:       psb ("%s", vi_Version);
                   2104:   } else if ((strncasecmp ((char *) cmd, "write", i) == 0) || // write text to file
                   2105:            (strncasecmp ((char *) cmd, "wq", i) == 0)) {       // write text to file
                   2106:       // is there a file name to write to?
                   2107:       /* if (strlen((char *) args) > 0) {
                   2108:          fn = args;
                   2109:          } */
                   2110: #ifdef BB_FEATURE_VI_READONLY
                   2111:       if ((vi_readonly == TRUE || readonly == TRUE) && useforce == FALSE) {
                   2112:           psbs ("\"%s\" File is read only", fn);
                   2113:           goto vc3;
                   2114:       }
                   2115: #endif /* BB_FEATURE_VI_READONLY */
                   2116:       // how many lines in text[]?
                   2117:       li = count_lines (q, r);
                   2118:       ch = r - q + 1;
                   2119:       // see if file exists- if not, its just a new file request
                   2120:       if (useforce == TRUE) {
                   2121:           // if "fn" is not write-able, chmod u+w
                   2122:           // sprintf(syscmd, "chmod u+w %s", fn);
                   2123:           // system(syscmd);
                   2124:           forced = TRUE;
                   2125:       }
                   2126:       l = file_write (fn, q, r);
                   2127:       if (useforce == TRUE && forced == TRUE) {
                   2128:           // chmod u-w
                   2129:           // sprintf(syscmd, "chmod u-w %s", fn);
                   2130:           // system(syscmd);
                   2131:           forced = FALSE;
                   2132:       }
                   2133:       psb ("\"%s\" %dL, %dC", fn, li, l);
                   2134:       if (q == text && r == end - 1 && l == ch)
                   2135:         file_modified = FALSE;
                   2136:       if (cmd[1] == 'q' && l == ch) {
                   2137:           editing = 0;
                   2138:       }
                   2139: #ifdef BB_FEATURE_VI_READONLY
                   2140:     vc3:;
                   2141: #endif /* BB_FEATURE_VI_READONLY */
                   2142: #ifdef BB_FEATURE_VI_YANKMARK
                   2143:   } else if (strncasecmp ((char *) cmd, "yank", i) == 0) {     // yank lines
                   2144:       if (b < 0) {     // no addr given- use defaults
                   2145:           q = begin_line (dot); // assume .,. for the range
                   2146:           r = end_line (dot);
                   2147:       }
                   2148:       text_yank (q, r, YDreg);
                   2149:       li = count_lines (q, r);
                   2150:       psb ("Yank %d lines (%d chars) into [%c]",
                   2151:            li, strlen ((char *) reg[YDreg]), what_reg ());
                   2152: #endif /* BB_FEATURE_VI_YANKMARK */
                   2153:   } else {
                   2154:       // cmd unknown
                   2155:       ni ((Byte *) cmd);
                   2156:   }
                   2157: vc1:
                   2158:   dot = bound_dot (dot);        // make sure "dot" is valid
                   2159:   return;
                   2160: #ifdef BB_FEATURE_VI_SEARCH
                   2161: colon_s_fail:
                   2162:   psb (":s expression missing delimiters");
                   2163:   return;
                   2164: #endif
                   2165:
                   2166: }
                   2167:
                   2168: static void Hit_Return(void)
                   2169: {
                   2170:   char c;
                   2171:
                   2172:   standout_start ();            // start reverse video
                   2173:   write (1, "[Hit return to continue]", 24);
                   2174:   standout_end ();              // end reverse video
                   2175:   while ((c = get_one_char ()) != '\n' && c != '\r')  /*do nothing */
                   2176:     ;
                   2177:   redraw (TRUE);                // force redraw all
                   2178: }
                   2179: #endif /* BB_FEATURE_VI_COLON */
                   2180:
                   2181: //----- Synchronize the cursor to Dot --------------------------
                   2182: static void sync_cursor(Byte * d, int *row, int *col)
                   2183: {
                   2184:   Byte *beg_cur, *end_cur;      // begin and end of "d" line
                   2185:   Byte *beg_scr, *end_scr;      // begin and end of screen
                   2186:   Byte *tp;
                   2187:   int cnt, ro, co;
                   2188:
                   2189:   beg_cur = begin_line (d);     // first char of cur line
                   2190:   end_cur = end_line (d);       // last char of cur line
                   2191:
                   2192:   beg_scr = end_scr = screenbegin;  // first char of screen
                   2193:   end_scr = end_screen ();      // last char of screen
                   2194:
                   2195:   if (beg_cur < screenbegin) {
                   2196:       // "d" is before  top line on screen
                   2197:       // how many lines do we have to move
                   2198:       cnt = count_lines (beg_cur, screenbegin);
                   2199:     sc1:
                   2200:       screenbegin = beg_cur;
                   2201:       if (cnt > (rows - 1) / 2) {
                   2202:           // we moved too many lines. put "dot" in middle of screen
                   2203:           for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
                   2204:               screenbegin = prev_line (screenbegin);
                   2205:          }
                   2206:       }
                   2207:   } else if (beg_cur > end_scr) {
                   2208:       // "d" is after bottom line on screen
                   2209:       // how many lines do we have to move
                   2210:       cnt = count_lines (end_scr, beg_cur);
                   2211:       if (cnt > (rows - 1) / 2)
                   2212:         goto sc1;               // too many lines
                   2213:       for (ro = 0; ro < cnt - 1; ro++) {
                   2214:           // move screen begin the same amount
                   2215:           screenbegin = next_line (screenbegin);
                   2216:           // now, move the end of screen
                   2217:           end_scr = next_line (end_scr);
                   2218:           end_scr = end_line (end_scr);
                   2219:       }
                   2220:   }
                   2221:   // "d" is on screen- find out which row
                   2222:   tp = screenbegin;
                   2223:   for (ro = 0; ro < rows - 1; ro++) {  // drive "ro" to correct row
                   2224:       if (tp == beg_cur)
                   2225:         break;
                   2226:       tp = next_line (tp);
                   2227:     }
                   2228:
                   2229:   // find out what col "d" is on
                   2230:   co = 0;
                   2231:   do {                                 // drive "co" to correct column
                   2232:       if (*tp == '\n' || *tp == '\0')
                   2233:         break;
                   2234:       if (*tp == '\t') {
                   2235:           //         7       - (co %    8  )
                   2236:           co += ((tabstop - 1) - (co % tabstop));
                   2237:       } else if (*tp < ' ') {
                   2238:           co++;                 // display as ^X, use 2 columns
                   2239:       }
                   2240:   } while (tp++ < d && ++co);
                   2241:
                   2242:   // "co" is the column where "dot" is.
                   2243:   // The screen has "columns" columns.
                   2244:   // The currently displayed columns are  0+offset -- columns+ofset
                   2245:   // |-------------------------------------------------------------|
                   2246:   //               ^ ^                                ^
                   2247:   //        offset | |------- columns ----------------|
                   2248:   //
                   2249:   // If "co" is already in this range then we do not have to adjust offset
                   2250:   //      but, we do have to subtract the "offset" bias from "co".
                   2251:   // If "co" is outside this range then we have to change "offset".
                   2252:   // If the first char of a line is a tab the cursor will try to stay
                   2253:   //  in column 7, but we have to set offset to 0.
                   2254:
                   2255:   if (co < 0 + offset) {
                   2256:       offset = co;
                   2257:   }
                   2258:   if (co >= columns + offset) {
                   2259:       offset = co - columns + 1;
                   2260:   }
                   2261:   // if the first char of the line is a tab, and "dot" is sitting on it
                   2262:   //  force offset to 0.
                   2263:   if (d == beg_cur && *d == '\t') {
                   2264:       offset = 0;
                   2265:   }
                   2266:   co -= offset;
                   2267:
                   2268:   *row = ro;
                   2269:   *col = co;
                   2270: }
                   2271:
                   2272: //----- Text Movement Routines ---------------------------------
                   2273: static Byte *begin_line(Byte * p)           // return pointer to first char cur line
                   2274: {
                   2275:   while (p > text && p[-1] != '\n')
                   2276:     p--;                        // go to cur line B-o-l
                   2277:   return (p);
                   2278: }
                   2279:
                   2280: static Byte *end_line(Byte * p)             // return pointer to NL of cur line line
                   2281: {
                   2282:   while (p < end - 1 && *p != '\n')
                   2283:     p++;                        // go to cur line E-o-l
                   2284:   return (p);
                   2285: }
                   2286:
                   2287: static Byte *dollar_line(Byte * p)          // return pointer to just before NL line
                   2288: {
                   2289:   while (p < end - 1 && *p != '\n')
                   2290:     p++;                        // go to cur line E-o-l
                   2291:   // Try to stay off of the Newline
                   2292:   if (*p == '\n' && (p - begin_line (p)) > 0)
                   2293:     p--;
                   2294:   return (p);
                   2295: }
                   2296:
                   2297: static Byte *prev_line(Byte * p)            // return pointer first char prev line
                   2298: {
                   2299:   p = begin_line (p);           // goto begining of cur line
                   2300:   if (p[-1] == '\n' && p > text)
                   2301:     p--;                        // step to prev line
                   2302:   p = begin_line (p);           // goto begining of prev line
                   2303:   return (p);
                   2304: }
                   2305:
                   2306: static Byte *next_line(Byte * p)            // return pointer first char next line
                   2307: {
                   2308:   p = end_line (p);
                   2309:   if (*p == '\n' && p < end - 1)
                   2310:     p++;                        // step to next line
                   2311:   return (p);
                   2312: }
                   2313:
                   2314: //----- Text Information Routines ------------------------------
                   2315: static Byte *end_screen(void)
                   2316: {
                   2317:   Byte *q;
                   2318:   int cnt;
                   2319:
                   2320:   // find new bottom line
                   2321:   q = screenbegin;
                   2322:   for (cnt = 0; cnt < rows - 2; cnt++)
                   2323:     q = next_line (q);
                   2324:   q = end_line (q);
                   2325:   return (q);
                   2326: }
                   2327:
                   2328: static int count_lines(Byte * start, Byte * stop) // count line from start to stop
                   2329: {
                   2330:   Byte *q;
                   2331:   int cnt;
                   2332:
                   2333:   if (stop < start) {  // start and stop are backwards- reverse them
                   2334:       q = start;
                   2335:       start = stop;
                   2336:       stop = q;
                   2337:   }
                   2338:   cnt = 0;
                   2339:   stop = end_line (stop);       // get to end of this line
                   2340:   for (q = start; q <= stop && q <= end - 1; q++) {
                   2341:       if (*q == '\n')
                   2342:         cnt++;
                   2343:   }
                   2344:   return (cnt);
                   2345: }
                   2346:
                   2347: static Byte *find_line(int li)              // find begining of line #li
                   2348: {
                   2349:   Byte *q;
                   2350:
                   2351:   for (q = text; li > 1; li--) {
                   2352:       q = next_line (q);
                   2353:   }
                   2354:   return (q);
                   2355: }
                   2356:
                   2357: //----- Dot Movement Routines ----------------------------------
                   2358: static void dot_left(void)
                   2359: {
                   2360:   if (dot > text && dot[-1] != '\n')
                   2361:     dot--;
                   2362: }
                   2363:
                   2364: static void dot_right(void)
                   2365: {
                   2366:   if (dot < end - 1 && *dot != '\n')
                   2367:     dot++;
                   2368: }
                   2369:
                   2370: static void dot_begin(void)
                   2371: {
                   2372:   dot = begin_line (dot);       // return pointer to first char cur line
                   2373: }
                   2374:
                   2375: static void dot_end(void)
                   2376: {
                   2377:   dot = end_line (dot);         // return pointer to last char cur line
                   2378: }
                   2379:
                   2380: static Byte *move_to_col(Byte * p, int l)
                   2381: {
                   2382:   int co;
                   2383:
                   2384:   p = begin_line (p);
                   2385:   co = 0;
                   2386:   do {
                   2387:       if (*p == '\n' || *p == '\0')
                   2388:         break;
                   2389:       if (*p == '\t') {
                   2390:           //         7       - (co %    8  )
                   2391:           co += ((tabstop - 1) - (co % tabstop));
                   2392:       } else if (*p < ' ') {
                   2393:           co++;                 // display as ^X, use 2 columns
                   2394:       }
                   2395:   } while (++co <= l && p++ < end);
                   2396:   return (p);
                   2397: }
                   2398:
                   2399: static void dot_next(void)
                   2400: {
                   2401:   dot = next_line (dot);
                   2402: }
                   2403:
                   2404: static void dot_prev(void)
                   2405: {
                   2406:   dot = prev_line (dot);
                   2407: }
                   2408:
                   2409: static void dot_scroll(int cnt, int dir)
                   2410: {
                   2411:   Byte *q;
                   2412:
                   2413:   for (; cnt > 0; cnt--) {
                   2414:       if (dir < 0) {
                   2415:           // scroll Backwards
                   2416:           // ctrl-Y  scroll up one line
                   2417:           screenbegin = prev_line (screenbegin);
                   2418:       } else {
                   2419:           // scroll Forwards
                   2420:           // ctrl-E  scroll down one line
                   2421:           screenbegin = next_line (screenbegin);
                   2422:       }
                   2423:   }
                   2424:   // make sure "dot" stays on the screen so we dont scroll off
                   2425:   if (dot < screenbegin)
                   2426:     dot = screenbegin;
                   2427:   q = end_screen ();            // find new bottom line
                   2428:   if (dot > q)
                   2429:     dot = begin_line (q);       // is dot is below bottom line?
                   2430:   dot_skip_over_ws ();
                   2431: }
                   2432:
                   2433: static void dot_skip_over_ws(void)
                   2434: {
                   2435:   // skip WS
                   2436:   while (isspace (*dot) && *dot != '\n' && dot < end - 1)
                   2437:     dot++;
                   2438: }
                   2439:
                   2440: static void dot_delete(void)               // delete the char at 'dot'
                   2441: {
                   2442:   (void) text_hole_delete (dot, dot);
                   2443: }
                   2444:
                   2445: static Byte *bound_dot(Byte * p)            // make sure  text[0] <= P < "end"
                   2446: {
                   2447:   if (p >= end && end > text) {
                   2448:       p = end - 1;
                   2449:       indicate_error ('1');
                   2450:   }
                   2451:   if (p < text) {
                   2452:       p = text;
                   2453:       indicate_error ('2');
                   2454:   }
                   2455:   return (p);
                   2456: }
                   2457:
                   2458: //----- Helper Utility Routines --------------------------------
                   2459:
                   2460: //----------------------------------------------------------------
                   2461: //----- Char Routines --------------------------------------------
                   2462: /* Chars that are part of a word-
                   2463:  *    0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
                   2464:  * Chars that are Not part of a word (stoppers)
                   2465:  *    !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
                   2466:  * Chars that are WhiteSpace
                   2467:  *    TAB NEWLINE VT FF RETURN SPACE
                   2468:  * DO NOT COUNT NEWLINE AS WHITESPACE
                   2469:  */
                   2470:
                   2471: static Byte *new_screen(int ro, int co)
                   2472: {
                   2473:   int li;
                   2474:
                   2475:   if (screen != 0)
                   2476:     free (screen);
                   2477:   screensize = ro * co + 8;
                   2478:   screen = (Byte *) malloc (screensize);
                   2479:   // initialize the new screen. assume this will be a empty file.
                   2480:   screen_erase ();
                   2481:   //   non-existant text[] lines start with a tilde (~).
                   2482:   for (li = 1; li < ro - 1; li++) {
                   2483:       screen[(li * co) + 0] = '~';
                   2484:   }
                   2485:   return (screen);
                   2486: }
                   2487:
                   2488: static Byte *new_text(int size)
                   2489: {
                   2490:   if (size < 10240)
                   2491:     size = 10240;               // have a minimum size for new files
                   2492:   if (text != 0) {
                   2493:       //text -= 4;
                   2494:       free (text);
                   2495:   }
                   2496:   text = (Byte *) malloc (size + 8);
                   2497:   memset (text, '\0', size);    // clear new text[]
                   2498:   //text += 4;            // leave some room for "oops"
                   2499:   textend = text + size - 1;
                   2500:   //textend -= 4;         // leave some root for "oops"
                   2501:   return (text);
                   2502: }
                   2503:
                   2504: #ifdef BB_FEATURE_VI_SEARCH
                   2505: static int mycmp(Byte * s1, Byte * s2, int len)
                   2506: {
                   2507:   int i;
                   2508:
                   2509:   i = strncmp ((char *) s1, (char *) s2, len);
                   2510: #ifdef BB_FEATURE_VI_SETOPTS
                   2511:   if (ignorecase) {
                   2512:       i = strncasecmp ((char *) s1, (char *) s2, len);
                   2513:   }
                   2514: #endif /* BB_FEATURE_VI_SETOPTS */
                   2515:   return (i);
                   2516: }
                   2517:
                   2518: static Byte *char_search(Byte * p, Byte * pat, int dir, int range)  // search for pattern starting at p
                   2519: {
                   2520: #ifndef REGEX_SEARCH
                   2521:   Byte *start, *stop;
                   2522:   int len;
                   2523:
                   2524:   len = strlen ((char *) pat);
                   2525:   if (dir == FORWARD) {
                   2526:       stop = end - 1;           // assume range is p - end-1
                   2527:       if (range == LIMITED)
                   2528:         stop = next_line (p);   // range is to next line
                   2529:       for (start = p; start < stop; start++) {
                   2530:           if (mycmp (start, pat, len) == 0) {
                   2531:               return (start);
                   2532:          }
                   2533:       }
                   2534:   } else if (dir == BACK) {
                   2535:       stop = text;              // assume range is text - p
                   2536:       if (range == LIMITED)
                   2537:         stop = prev_line (p);   // range is to prev line
                   2538:       for (start = p - len; start >= stop; start--) {
                   2539:           if (mycmp (start, pat, len) == 0) {
                   2540:               return (start);
                   2541:          }
                   2542:       }
                   2543:   }
                   2544:   // pattern not found
                   2545:   return (NULL);
                   2546: #else /*REGEX_SEARCH */
                   2547:   char *q;
                   2548:   struct re_pattern_buffer preg;
                   2549:   int i;
                   2550:   int size, range;
                   2551:
                   2552:   re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
                   2553:   preg.translate = 0;
                   2554:   preg.fastmap = 0;
                   2555:   preg.buffer = 0;
                   2556:   preg.allocated = 0;
                   2557:
                   2558:   // assume a LIMITED forward search
                   2559:   q = next_line (p);
                   2560:   q = end_line (q);
                   2561:   q = end - 1;
                   2562:   if (dir == BACK) {
                   2563:       q = prev_line (p);
                   2564:       q = text;
                   2565:   }
                   2566:   // count the number of chars to search over, forward or backward
                   2567:   size = q - p;
                   2568:   if (size < 0)
                   2569:     size = p - q;
                   2570:   // RANGE could be negative if we are searching backwards
                   2571:   range = q - p;
                   2572:
                   2573:   q = (char *) re_compile_pattern (pat, strlen ((char *) pat), &preg);
                   2574:   if (q != 0) {
                   2575:       // The pattern was not compiled
                   2576:       psbs ("bad search pattern: \"%s\": %s", pat, q);
                   2577:       i = 0;                    // return p if pattern not compiled
                   2578:       goto cs1;
                   2579:   }
                   2580:
                   2581:   q = p;
                   2582:   if (range < 0) {
                   2583:       q = p - size;
                   2584:       if (q < text)
                   2585:         q = text;
                   2586:   }
                   2587:   // search for the compiled pattern, preg, in p[]
                   2588:   // range < 0-  search backward
                   2589:   // range > 0-  search forward
                   2590:   // 0 < start < size
                   2591:   // re_search() < 0  not found or error
                   2592:   // re_search() > 0  index of found pattern
                   2593:   //            struct pattern    char     int    int    int     struct reg
                   2594:   // re_search (*pattern_buffer,  *string, size,  start, range,  *regs)
                   2595:   i = re_search (&preg, q, size, 0, range, 0);
                   2596:   if (i == -1) {
                   2597:       p = 0;
                   2598:       i = 0;                    // return NULL if pattern not found
                   2599:   }
                   2600: cs1:
                   2601:   if (dir == FORWARD) {
                   2602:       p = p + i;
                   2603:   } else {
                   2604:       p = p - i;
                   2605:   }
                   2606:   return (p);
                   2607: #endif /*REGEX_SEARCH */
                   2608: }
                   2609: #endif /* BB_FEATURE_VI_SEARCH */
                   2610:
                   2611: static Byte *char_insert(Byte * p, Byte c)  // insert the char c at 'p'
                   2612: {
                   2613:   if (c == 22) {               // Is this an ctrl-V?
                   2614:       p = stupid_insert (p, '^'); // use ^ to indicate literal next
                   2615:       p--;                      // backup onto ^
                   2616:       refresh (FALSE);          // show the ^
                   2617:       c = get_one_char ();
                   2618:       *p = c;
                   2619:       p++;
                   2620:       file_modified = TRUE;     // has the file been modified
                   2621:   } else if (c == 27) {                // Is this an ESC?
                   2622:       cmd_mode = 0;
                   2623:       cmdcnt = 0;
                   2624:       end_cmd_q ();             // stop adding to q
                   2625:       strcpy ((char *) status_buffer, " "); // clear the status buffer
                   2626:       if ((p[-1] != '\n') && (dot > text)) {
                   2627:           p--;
                   2628:       }
                   2629:   } else if (c == erase_char) {        // Is this a BS
                   2630:       //     123456789
                   2631:       if ((p[-1] != '\n') && (dot > text)) {
                   2632:           p--;
                   2633:           p = text_hole_delete (p, p);  // shrink buffer 1 char
                   2634: #ifdef BB_FEATURE_VI_DOT_CMD
                   2635:           // also rmove char from last_modifying_cmd
                   2636:           if (last_modifying_cmd && (strlen((char *) last_modifying_cmd) > 0)) {
                   2637:               Byte *q;
                   2638:
                   2639:               q = last_modifying_cmd;
                   2640:               q[strlen ((char *) q) - 1] = '\0';  // erase BS
                   2641:               q[strlen ((char *) q) - 1] = '\0';  // erase prev char
                   2642:          }
                   2643: #endif /* BB_FEATURE_VI_DOT_CMD */
                   2644:       }
                   2645:   } else {
                   2646:       // insert a char into text[]
                   2647:       Byte *sp;                 // "save p"
                   2648:
                   2649:       if (c == 13)
                   2650:         c = '\n';               // translate \r to \n
                   2651:       sp = p;                   // remember addr of insert
                   2652:       p = stupid_insert (p, c); // insert the char
                   2653: #ifdef BB_FEATURE_VI_SETOPTS
                   2654:       if (showmatch && strchr (")]}", *sp) != NULL) {
                   2655:           showmatching (sp);
                   2656:       }
                   2657:       if (autoindent && c == '\n') {   // auto indent the new line
                   2658:           Byte *q;
                   2659:
                   2660:           q = prev_line (p);    // use prev line as templet
                   2661:           for (; isblnk (*q); q++) {
                   2662:               p = stupid_insert (p, *q);  // insert the char
                   2663:          }
                   2664:       }
                   2665: #endif /* BB_FEATURE_VI_SETOPTS */
                   2666:     }
                   2667:   return (p);
                   2668: }
                   2669:
                   2670: static Byte *stupid_insert(Byte * p, Byte c)  // stupidly insert the char c at 'p'
                   2671: {
                   2672:   p = text_hole_make (p, 1);
                   2673:   if (p != 0) {
                   2674:       *p = c;
                   2675:       file_modified = TRUE;     // has the file been modified
                   2676:       p++;
                   2677:   }
                   2678:   return (p);
                   2679: }
                   2680:
                   2681: static Byte find_range(Byte ** start, Byte ** stop, Byte c)
                   2682: {
                   2683:   Byte *save_dot, *p, *q;
                   2684:   int cnt;
                   2685:
                   2686:   save_dot = dot;
                   2687:   p = q = dot;
                   2688:
                   2689:   if (strchr ("cdy><", c)) {
                   2690:       // these cmds operate on whole lines
                   2691:       p = q = begin_line (p);
                   2692:       for (cnt = 1; cnt < cmdcnt; cnt++) {
                   2693:           q = next_line (q);
                   2694:       }
                   2695:       q = end_line (q);
                   2696:   } else if (strchr ("^%$0bBeEft", c)) {
                   2697:       // These cmds operate on char positions
                   2698:       do_cmd (c);               // execute movement cmd
                   2699:       q = dot;
                   2700:   } else if (strchr ("wW", c)) {
                   2701:       do_cmd (c);               // execute movement cmd
                   2702:       if (dot > text)
                   2703:         dot--;                  // move back off of next word
                   2704:       if (dot > text && *dot == '\n')
                   2705:         dot--;                  // stay off NL
                   2706:       q = dot;
                   2707:   } else if (strchr ("H-k{", c)) {
                   2708:       // these operate on multi-lines backwards
                   2709:       q = end_line (dot);       // find NL
                   2710:       do_cmd (c);               // execute movement cmd
                   2711:       dot_begin ();
                   2712:       p = dot;
                   2713:   } else if (strchr ("L+j}\r\n", c)) {
                   2714:       // these operate on multi-lines forwards
                   2715:       p = begin_line (dot);
                   2716:       do_cmd (c);               // execute movement cmd
                   2717:       dot_end ();               // find NL
                   2718:       q = dot;
                   2719:   } else {
                   2720:       c = 27;                   // error- return an ESC char
                   2721:       //break;
                   2722:   }
                   2723:   *start = p;
                   2724:   *stop = q;
                   2725:   if (q < p) {
                   2726:       *start = q;
                   2727:       *stop = p;
                   2728:   }
                   2729:   dot = save_dot;
                   2730:   return (c);
                   2731: }
                   2732:
                   2733: static int st_test(Byte * p, int type, int dir, Byte * tested)
                   2734: {
                   2735:   Byte c, c0, ci;
                   2736:   int test, inc;
                   2737:
                   2738:   inc = dir;
                   2739:   c = c0 = p[0];
                   2740:   ci = p[inc];
                   2741:   test = 0;
                   2742:
                   2743:   if (type == S_BEFORE_WS) {
                   2744:       c = ci;
                   2745:       test = ((!isspace (c)) || c == '\n');
                   2746:   }
                   2747:   if (type == S_TO_WS) {
                   2748:       c = c0;
                   2749:       test = ((!isspace (c)) || c == '\n');
                   2750:   }
                   2751:   if (type == S_OVER_WS) {
                   2752:       c = c0;
                   2753:       test = ((isspace (c)));
                   2754:   }
                   2755:   if (type == S_END_PUNCT) {
                   2756:       c = ci;
                   2757:       test = ((ispunct (c)));
                   2758:   }
                   2759:   if (type == S_END_ALNUM) {
                   2760:       c = ci;
                   2761:       test = ((isalnum (c)) || c == '_');
                   2762:   }
                   2763:   *tested = c;
                   2764:   return (test);
                   2765: }
                   2766:
                   2767: static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
                   2768: {
                   2769:   Byte c;
                   2770:
                   2771:   while (st_test(p, type, dir, &c)) {
                   2772:       // make sure we limit search to correct number of lines
                   2773:       if (c == '\n' && --linecnt < 1)
                   2774:         break;
                   2775:       if (dir >= 0 && p >= end - 1)
                   2776:         break;
                   2777:       if (dir < 0 && p <= text)
                   2778:         break;
                   2779:       p += dir;                 // move to next char
                   2780:   }
                   2781:   return (p);
                   2782: }
                   2783:
                   2784: // find matching char of pair  ()  []  {}
                   2785: static Byte *find_pair(Byte * p, Byte c)
                   2786: {
                   2787:   Byte match, *q;
                   2788:   int dir, level;
                   2789:
                   2790:   match = ')';
                   2791:   level = 1;
                   2792:   dir = 1;                      // assume forward
                   2793:   switch (c) {
                   2794:     case '(':
                   2795:       match = ')';
                   2796:       break;
                   2797:     case '[':
                   2798:       match = ']';
                   2799:       break;
                   2800:     case '{':
                   2801:       match = '}';
                   2802:       break;
                   2803:     case ')':
                   2804:       match = '(';
                   2805:       dir = -1;
                   2806:       break;
                   2807:     case ']':
                   2808:       match = '[';
                   2809:       dir = -1;
                   2810:       break;
                   2811:     case '}':
                   2812:       match = '{';
                   2813:       dir = -1;
                   2814:       break;
                   2815:   }
                   2816:   for (q = p + dir; text <= q && q < end; q += dir) {
                   2817:       // look for match, count levels of pairs  (( ))
                   2818:       if (*q == c)
                   2819:         level++;                // increase pair levels
                   2820:       if (*q == match)
                   2821:         level--;                // reduce pair level
                   2822:       if (level == 0)
                   2823:         break;                  // found matching pair
                   2824:   }
                   2825:   if (level != 0)
                   2826:     q = NULL;                   // indicate no match
                   2827:   return (q);
                   2828: }
                   2829:
                   2830: #ifdef BB_FEATURE_VI_SETOPTS
                   2831: // show the matching char of a pair,  ()  []  {}
                   2832: static void showmatching(Byte * p)
                   2833: {
                   2834:   Byte *q, *save_dot;
                   2835:
                   2836:   // we found half of a pair
                   2837:   q = find_pair (p, *p);        // get loc of matching char
                   2838:   if (q == NULL) {
                   2839:       indicate_error ('3');     // no matching char
                   2840:   } else {
                   2841:       // "q" now points to matching pair
                   2842:       save_dot = dot;           // remember where we are
                   2843:       dot = q;                  // go to new loc
                   2844:       refresh (FALSE);          // let the user see it
                   2845:       (void) mysleep (40);      // give user some time
                   2846:       dot = save_dot;           // go back to old loc
                   2847:       refresh (FALSE);
                   2848:   }
                   2849: }
                   2850: #endif /* BB_FEATURE_VI_SETOPTS */
                   2851:
                   2852: //  open a hole in text[]
                   2853: static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
                   2854: {
                   2855:   Byte *src, *dest;
                   2856:   int cnt;
                   2857:
                   2858:   if (size <= 0)
                   2859:     goto thm0;
                   2860:   src = p;
                   2861:   dest = p + size;
                   2862:   cnt = end - src;              // the rest of buffer
                   2863:   if (memmove(dest, src, cnt) != dest) {
                   2864:       psbs ("can't create room for new characters");
                   2865:   }
                   2866:   memset (p, ' ', size);        // clear new hole
                   2867:   end = end + size;             // adjust the new END
                   2868:   file_modified = TRUE;         // has the file been modified
                   2869: thm0:
                   2870:   return (p);
                   2871: }
                   2872:
                   2873: //  close a hole in text[]
                   2874: static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
                   2875: {
                   2876:   Byte *src, *dest;
                   2877:   int cnt, hole_size;
                   2878:
                   2879:   // move forwards, from beginning
                   2880:   // assume p <= q
                   2881:   src = q + 1;
                   2882:   dest = p;
                   2883:   if (q < p) {                 // they are backward- swap them
                   2884:       src = p + 1;
                   2885:       dest = q;
                   2886:   }
                   2887:   hole_size = q - p + 1;
                   2888:   cnt = end - src;
                   2889:   if (src < text || src > end)
                   2890:     goto thd0;
                   2891:   if (dest < text || dest >= end)
                   2892:     goto thd0;
                   2893:   if (src >= end)
                   2894:     goto thd_atend;             // just delete the end of the buffer
                   2895:   if (memmove(dest, src, cnt) != dest) {
                   2896:       psbs ("can't delete the character");
                   2897:   }
                   2898: thd_atend:
                   2899:   end = end - hole_size;        // adjust the new END
                   2900:   if (dest >= end)
                   2901:     dest = end - 1;             // make sure dest in below end-1
                   2902:   if (end <= text)
                   2903:     dest = end = text;          // keep pointers valid
                   2904:   file_modified = TRUE;         // has the file been modified
                   2905: thd0:
                   2906:   return (dest);
                   2907: }
                   2908:
                   2909: // copy text into register, then delete text.
                   2910: // if dist <= 0, do not include, or go past, a NewLine
                   2911: //
                   2912: static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
                   2913: {
                   2914:   Byte *p;
                   2915:
                   2916:   // make sure start <= stop
                   2917:   if (start > stop) {
                   2918:       // they are backwards, reverse them
                   2919:       p = start;
                   2920:       start = stop;
                   2921:       stop = p;
                   2922:   }
                   2923:   if (dist <= 0) {
                   2924:       // we can not cross NL boundaries
                   2925:       p = start;
                   2926:       if (*p == '\n')
                   2927:         return (p);
                   2928:       // dont go past a NewLine
                   2929:       for (; p + 1 <= stop; p++) {
                   2930:           if (p[1] == '\n') {
                   2931:               stop = p;         // "stop" just before NewLine
                   2932:               break;
                   2933:          }
                   2934:       }
                   2935:   }
                   2936:   p = start;
                   2937: #ifdef BB_FEATURE_VI_YANKMARK
                   2938:   text_yank (start, stop, YDreg);
                   2939: #endif /* BB_FEATURE_VI_YANKMARK */
                   2940:   if (yf == YANKDEL) {
                   2941:       p = text_hole_delete (start, stop);
                   2942:   }                           // delete lines
                   2943:   return (p);
                   2944: }
                   2945:
                   2946: static void show_help(void)
                   2947: {
                   2948:   fprintf (stderr, "version: %s\n", vi_Version);
                   2949:   puts ("These features are available:"
                   2950: #ifdef BB_FEATURE_VI_SEARCH
                   2951:         "\n\tPattern searches with / and ?"
                   2952: #endif /* BB_FEATURE_VI_SEARCH */
                   2953: #ifdef BB_FEATURE_VI_DOT_CMD
                   2954:         "\n\tLast command repeat with \'.\'"
                   2955: #endif /* BB_FEATURE_VI_DOT_CMD */
                   2956: #ifdef BB_FEATURE_VI_YANKMARK
                   2957:         "\n\tLine marking with  'x"
                   2958:        "\n\tNamed buffers with  \"x"
                   2959: #endif /* BB_FEATURE_VI_YANKMARK */
                   2960: #ifdef BB_FEATURE_VI_READONLY
                   2961:         "\n\tReadonly if vi is called as \"view\""
                   2962:         "\n\tReadonly with -R command line arg"
                   2963: #endif /* BB_FEATURE_VI_READONLY */
                   2964: #ifdef BB_FEATURE_VI_SET
                   2965:         "\n\tSome colon mode commands with \':\'"
                   2966: #endif /* BB_FEATURE_VI_SET */
                   2967: #ifdef BB_FEATURE_VI_SETOPTS
                   2968:         "\n\tSettable options with \":set\""
                   2969: #endif /* BB_FEATURE_VI_SETOPTS */
                   2970: #ifdef BB_FEATURE_VI_USE_SIGNALS
                   2971:         "\n\tSignal catching- ^C"
                   2972:        "\n\tJob suspend and resume with ^Z"
                   2973: #endif /* BB_FEATURE_VI_USE_SIGNALS */
                   2974: #ifdef BB_FEATURE_VI_WIN_RESIZE
                   2975:         "\n\tAdapt to window re-sizes"
                   2976: #endif /* BB_FEATURE_VI_WIN_RESIZE */
                   2977:     );
                   2978: }
                   2979:
                   2980: static void print_literal(Byte * buf, Byte * s)  // copy s to buf, convert unprintable
                   2981: {
                   2982:   Byte c, b[2];
                   2983:
                   2984:   b[1] = '\0';
                   2985:   strcpy ((char *) buf, "");    // init buf
                   2986:   if (strlen ((char *) s) <= 0)
                   2987:     s = (Byte *) "(NULL)";
                   2988:   for (; *s > '\0'; s++) {
                   2989:       c = *s;
                   2990:       if (*s > '~') {
                   2991:          strlcat((char *) buf, SOs, BUFSIZ); /* FIXED strlcat BUFSIZ */
                   2992:           c = *s - 128;
                   2993:       }
                   2994:       if (*s < ' ') {
                   2995:           strcat ((char *) buf, "^");
                   2996:           c += '@';
                   2997:       }
                   2998:       b[0] = c;
                   2999:       strlcat((char *) buf, (char *) b, BUFSIZ); /* FIXED added BUFSIZ and converted to strlcat() */
                   3000:       if (*s > '~')
                   3001:          strlcat((char *) buf, SOn, BUFSIZ); /* FIXED strlcat BUFSIZ */
                   3002:       if (*s == '\n') {
                   3003:           strcat ((char *) buf, "$");
                   3004:       }
                   3005:   }
                   3006: }
                   3007:
                   3008: #ifdef BB_FEATURE_VI_DOT_CMD
                   3009: static void start_new_cmd_q(Byte c)
                   3010: {
                   3011:   // release old cmd
                   3012:   if (last_modifying_cmd != 0)
                   3013:     free (last_modifying_cmd);
                   3014:   // get buffer for new cmd
                   3015:   last_modifying_cmd = (Byte *) malloc (BUFSIZ);
                   3016:   memset (last_modifying_cmd, '\0', BUFSIZ);  // clear new cmd queue
                   3017:   // if there is a current cmd count put it in the buffer first
                   3018:   if (cmdcnt > 0)
                   3019:     sprintf ((char *) last_modifying_cmd, "%d", cmdcnt);
                   3020:   // save char c onto queue
                   3021:   last_modifying_cmd[strlen ((char *) last_modifying_cmd)] = c;
                   3022:   adding2q = 1;
                   3023:   return;
                   3024: }
                   3025:
                   3026: static void end_cmd_q()
                   3027: {
                   3028: #ifdef BB_FEATURE_VI_YANKMARK
                   3029:   YDreg = 26;                   // go back to default Yank/Delete reg
                   3030: #endif /* BB_FEATURE_VI_YANKMARK */
                   3031:   adding2q = 0;
                   3032:   return;
                   3033: }
                   3034: #endif /* BB_FEATURE_VI_DOT_CMD */
                   3035:
                   3036: #if defined(BB_FEATURE_VI_YANKMARK) || defined(BB_FEATURE_VI_COLON) || defined(BB_FEATURE_VI_CRASHME)
                   3037: static Byte * string_insert(Byte * p, Byte * s)  // insert the string at 'p'
                   3038: {
                   3039:   int cnt, i;
                   3040:
                   3041:   i = strlen ((char *) s);
                   3042:   p = text_hole_make (p, i);
                   3043:   strncpy ((char *) p, (char *) s, i);
                   3044:   for (cnt = 0; *s != '\0'; s++) {
                   3045:       if (*s == '\n')
                   3046:         cnt++;
                   3047:   }
                   3048: #ifdef BB_FEATURE_VI_YANKMARK
                   3049:   psb ("Put %d lines (%d chars) from [%c]", cnt, i, what_reg ());
                   3050: #endif /* BB_FEATURE_VI_YANKMARK */
                   3051:   return (p);
                   3052: }
                   3053: #endif /* BB_FEATURE_VI_YANKMARK || BB_FEATURE_VI_COLON || BB_FEATURE_VI_CRASHME */
                   3054:
                   3055: #ifdef BB_FEATURE_VI_YANKMARK
                   3056: static Byte *text_yank(Byte * p, Byte * q, int dest)  // copy text into a register
                   3057: {
                   3058:   Byte *t;
                   3059:   int cnt;
                   3060:
                   3061:   if (q < p) {                 // they are backwards- reverse them
                   3062:       t = q;
                   3063:       q = p;
                   3064:       p = t;
                   3065:   }
                   3066:   cnt = q - p + 1;
                   3067:   t = reg[dest];
                   3068:   if (t != 0) {                        // if already a yank register
                   3069:       free (t);                 //   free it
                   3070:   }
                   3071:   t = (Byte *) malloc (cnt + 1);  // get a new register
                   3072:   memset (t, '\0', cnt + 1);    // clear new text[]
                   3073:   strncpy ((char *) t, (char *) p, cnt);  // copy text[] into bufer
                   3074:   reg[dest] = t;
                   3075:   return (p);
                   3076: }
                   3077:
                   3078: static Byte what_reg(void)
                   3079: {
                   3080:   Byte c;
                   3081:   int i;
                   3082:
                   3083:   i = 0;
                   3084:   c = 'D';                      // default to D-reg
                   3085:   if (0 <= YDreg && YDreg <= 25)
                   3086:     c = 'a' + (Byte) YDreg;
                   3087:   if (YDreg == 26)
                   3088:     c = 'D';
                   3089:   if (YDreg == 27)
                   3090:     c = 'U';
                   3091:   return (c);
                   3092: }
                   3093:
                   3094: static void check_context(Byte cmd)
                   3095: {
                   3096:   // A context is defined to be "modifying text"
                   3097:   // Any modifying command establishes a new context.
                   3098:
                   3099:   if (dot < context_start || dot > context_end) {
                   3100:       if (strchr((char *) modifying_cmds, cmd) != NULL) {
                   3101:           // we are trying to modify text[]- make this the current context
                   3102:           mark[27] = mark[26];  // move cur to prev
                   3103:           mark[26] = dot;       // move local to cur
                   3104:           context_start = prev_line (prev_line (dot));
                   3105:           context_end = next_line (next_line (dot));
                   3106:           //loiter= start_loiter= now;
                   3107:       }
                   3108:   }
                   3109:   return;
                   3110: }
                   3111:
                   3112: static Byte *swap_context(Byte * p)         // goto new context for '' command make this the current context
                   3113: {
                   3114:   Byte *tmp;
                   3115:
                   3116:   // the current context is in mark[26]
                   3117:   // the previous context is in mark[27]
                   3118:   // only swap context if other context is valid
                   3119:   if (text <= mark[27] && mark[27] <= end - 1) {
                   3120:       tmp = mark[27];
                   3121:       mark[27] = mark[26];
                   3122:       mark[26] = tmp;
                   3123:       p = mark[26];             // where we are going- previous context
                   3124:       context_start = prev_line (prev_line (prev_line (p)));
                   3125:       context_end = next_line (next_line (next_line (p)));
                   3126:   }
                   3127:   return (p);
                   3128: }
                   3129: #endif /* BB_FEATURE_VI_YANKMARK */
                   3130:
                   3131: static int isblnk(Byte c)                 // is the char a blank or tab
                   3132: {
                   3133:   return (c == ' ' || c == '\t');
                   3134: }
                   3135:
                   3136: //----- Set terminal attributes --------------------------------
                   3137: static void rawmode(void)
                   3138: {
                   3139:   tcgetattr (0, &term_orig);
                   3140:   term_vi = term_orig;
                   3141:   term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
                   3142:   term_vi.c_iflag &= (~IXON & ~ICRNL);
                   3143:   term_vi.c_oflag &= (~ONLCR);
                   3144: #ifndef linux
                   3145:   term_vi.c_cc[VMIN] = 1;
                   3146:   term_vi.c_cc[VTIME] = 0;
                   3147: #endif
                   3148:   erase_char = term_vi.c_cc[VERASE];
                   3149:   tcsetattr (0, TCSANOW, &term_vi);
                   3150: }
                   3151:
                   3152: static void cookmode(void)
                   3153: {
                   3154:   tcsetattr (0, TCSANOW, &term_orig);
                   3155: }
                   3156:
                   3157: #ifdef BB_FEATURE_VI_WIN_RESIZE
                   3158: //----- See what the window size currently is --------------------
                   3159: static void window_size_get(int sig)
                   3160: {
                   3161:   int i;
                   3162:
                   3163:   i = ioctl (0, TIOCGWINSZ, &winsize);
                   3164:   if (i != 0) {
                   3165:       // force 24x80
                   3166:       winsize.ws_row = 24;
                   3167:       winsize.ws_col = 80;
                   3168:   }
                   3169:   if (winsize.ws_row <= 1) {
                   3170:       winsize.ws_row = 24;
                   3171:   }
                   3172:   if (winsize.ws_col <= 1) {
                   3173:       winsize.ws_col = 80;
                   3174:   }
                   3175:   rows = (int) winsize.ws_row;
                   3176:   columns = (int) winsize.ws_col;
                   3177: }
                   3178: #endif /* BB_FEATURE_VI_WIN_RESIZE */
                   3179:
                   3180: //----- Come here when we get a window resize signal ---------
                   3181: #ifdef BB_FEATURE_VI_USE_SIGNALS
                   3182: static void winch_sig(int sig)
                   3183: {
                   3184:   signal (SIGWINCH, winch_sig);
                   3185: #ifdef BB_FEATURE_VI_WIN_RESIZE
                   3186:   window_size_get (0);
                   3187: #endif /* BB_FEATURE_VI_WIN_RESIZE */
                   3188:   new_screen (rows, columns);   // get memory for virtual screen
                   3189:   redraw (TRUE);                // re-draw the screen
                   3190: }
                   3191:
                   3192: //----- Come here when we get a continue signal -------------------
                   3193: static void cont_sig(int sig)
                   3194: {
                   3195:   rawmode ();                   // terminal to "raw"
                   3196:   *status_buffer = '\0';        // clear the status buffer
                   3197:   redraw (TRUE);                // re-draw the screen
                   3198:
                   3199:   signal (SIGTSTP, suspend_sig);
                   3200:   signal (SIGCONT, SIG_DFL);
                   3201:   kill (getpid (), SIGCONT);
                   3202: }
                   3203:
                   3204: //----- Come here when we get a Suspend signal -------------------
                   3205: static void suspend_sig(int sig)
                   3206: {
                   3207:   place_cursor (rows - 1, 0, FALSE);  // go to bottom of screen
                   3208:   clear_to_eol ();              // Erase to end of line
                   3209:   cookmode ();                  // terminal to "cooked"
                   3210:
                   3211:   signal (SIGCONT, cont_sig);
                   3212:   signal (SIGTSTP, SIG_DFL);
                   3213:   kill (getpid (), SIGTSTP);
                   3214: }
                   3215:
                   3216: //----- Come here when we get a signal ---------------------------
                   3217: static void catch_sig(int sig)
                   3218: {
                   3219:   signal (SIGHUP, catch_sig);
                   3220:   signal (SIGINT, catch_sig);
                   3221:   signal (SIGTERM, catch_sig);
                   3222:   longjmp (restart, sig);
                   3223: }
                   3224:
                   3225: static void alarm_sig(int sig)
                   3226: {
                   3227:   signal (SIGALRM, catch_sig);
                   3228:   longjmp (restart, sig);
                   3229: }
                   3230:
                   3231: //----- Come here when we get a core dump signal -----------------
                   3232: static void core_sig(int sig)
                   3233: {
                   3234:   signal (SIGQUIT, core_sig);
                   3235:   signal (SIGILL, core_sig);
                   3236:   signal (SIGTRAP, core_sig);
                   3237:   signal (SIGIOT, core_sig);
                   3238:   signal (SIGABRT, core_sig);
                   3239:   signal (SIGFPE, core_sig);
                   3240:   signal (SIGBUS, core_sig);
                   3241:   signal (SIGSEGV, core_sig);
                   3242: #ifdef SIGSYS
                   3243:   signal (SIGSYS, core_sig);
                   3244: #endif
                   3245:
                   3246:   dot = bound_dot (dot);        // make sure "dot" is valid
                   3247:
                   3248:   longjmp (restart, sig);
                   3249: }
                   3250: #endif /* BB_FEATURE_VI_USE_SIGNALS */
                   3251:
                   3252: static int mysleep(int hund)              // sleep for 'h' 1/100 seconds
                   3253: {
                   3254:   // Don't hang- Wait 5/100 seconds-  1 Sec= 1000000
                   3255:   FD_ZERO (&rfds);
                   3256:   FD_SET (0, &rfds);
                   3257:   tv.tv_sec = 0;
                   3258:   tv.tv_usec = hund * 10000;
                   3259:   select (1, &rfds, NULL, NULL, &tv);
                   3260:   return (FD_ISSET (0, &rfds));
                   3261: }
                   3262:
                   3263: //----- IO Routines --------------------------------------------
                   3264: static Byte readit(void)                   // read (maybe cursor) key from stdin
                   3265: {
                   3266:   Byte c;
                   3267:   int i, bufsiz, cnt, cmdindex;
                   3268:   struct esc_cmds {
                   3269:     Byte *seq;
                   3270:     Byte val;
                   3271:   };
                   3272:
                   3273:   static struct esc_cmds esccmds[] = {
                   3274:     {(Byte *) "OA", (Byte) VI_K_UP}, // cursor key Up
                   3275:     {(Byte *) "OB", (Byte) VI_K_DOWN}, // cursor key Down
                   3276:     {(Byte *) "OC", (Byte) VI_K_RIGHT},  // Cursor Key Right
                   3277:     {(Byte *) "OD", (Byte) VI_K_LEFT}, // cursor key Left
                   3278:     {(Byte *) "OH", (Byte) VI_K_HOME}, // Cursor Key Home
                   3279:     {(Byte *) "OF", (Byte) VI_K_END},  // Cursor Key End
                   3280:     {(Byte *) "", (Byte) VI_K_UP}, // cursor key Up
                   3281:     {(Byte *) "", (Byte) VI_K_DOWN}, // cursor key Down
                   3282:     {(Byte *) "", (Byte) VI_K_RIGHT},  // Cursor Key Right
                   3283:     {(Byte *) "", (Byte) VI_K_LEFT}, // cursor key Left
                   3284:     {(Byte *) "", (Byte) VI_K_HOME}, // Cursor Key Home
                   3285:     {(Byte *) "", (Byte) VI_K_END},  // Cursor Key End
                   3286:     {(Byte *) "[2~", (Byte) VI_K_INSERT},  // Cursor Key Insert
                   3287:     {(Byte *) "[5~", (Byte) VI_K_PAGEUP},  // Cursor Key Page Up
                   3288:     {(Byte *) "[6~", (Byte) VI_K_PAGEDOWN},  // Cursor Key Page Down
                   3289:     {(Byte *) "OP", (Byte) VI_K_FUN1}, // Function Key F1
                   3290:     {(Byte *) "OQ", (Byte) VI_K_FUN2}, // Function Key F2
                   3291:     {(Byte *) "OR", (Byte) VI_K_FUN3}, // Function Key F3
                   3292:     {(Byte *) "OS", (Byte) VI_K_FUN4}, // Function Key F4
                   3293:     {(Byte *) "[15~", (Byte) VI_K_FUN5}, // Function Key F5
                   3294:     {(Byte *) "[17~", (Byte) VI_K_FUN6}, // Function Key F6
                   3295:     {(Byte *) "[18~", (Byte) VI_K_FUN7}, // Function Key F7
                   3296:     {(Byte *) "[19~", (Byte) VI_K_FUN8}, // Function Key F8
                   3297:     {(Byte *) "[20~", (Byte) VI_K_FUN9}, // Function Key F9
                   3298:     {(Byte *) "[21~", (Byte) VI_K_FUN10},  // Function Key F10
                   3299:     {(Byte *) "[23~", (Byte) VI_K_FUN11},  // Function Key F11
                   3300:     {(Byte *) "[24~", (Byte) VI_K_FUN12},  // Function Key F12
                   3301:     {(Byte *) "[11~", (Byte) VI_K_FUN1}, // Function Key F1
                   3302:     {(Byte *) "[12~", (Byte) VI_K_FUN2}, // Function Key F2
                   3303:     {(Byte *) "[13~", (Byte) VI_K_FUN3}, // Function Key F3
                   3304:     {(Byte *) "[14~", (Byte) VI_K_FUN4}, // Function Key F4
                   3305:   };
                   3306:
                   3307: #define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
                   3308:
                   3309:   // (void) alarm(0);     // turn alarm OFF while we wait for input
                   3310:   // get input from User- are there already input chars in Q?
                   3311:   bufsiz = strlen ((char *) readbuffer);
                   3312:   if (bufsiz <= 0) {
                   3313:     ri0:
                   3314:       // the Q is empty, wait for a typed char
                   3315:       bufsiz = read (0, readbuffer, BUFSIZ - 1);
                   3316:       if (bufsiz < 0) {
                   3317:           if (errno == EINTR)
                   3318:             goto ri0;           // interrupted sys call
                   3319:           if (errno == EBADF)
                   3320:             editing = 0;
                   3321:           if (errno == EFAULT)
                   3322:             editing = 0;
                   3323:           if (errno == EINVAL)
                   3324:             editing = 0;
                   3325:           if (errno == EIO)
                   3326:             editing = 0;
                   3327:           errno = 0;
                   3328:           bufsiz = 0;
                   3329:       }
                   3330:       readbuffer[bufsiz] = '\0';
                   3331:   }
                   3332:   // return char if it is not part of ESC sequence
                   3333:   if (readbuffer[0] != 27)
                   3334:     goto ri1;
                   3335:
                   3336:   // This is an ESC char. Is this Esc sequence?
                   3337:   // Could be bare Esc key. See if there are any
                   3338:   // more chars to read after the ESC. This would
                   3339:   // be a Function or Cursor Key sequence.
                   3340:   FD_ZERO (&rfds);
                   3341:   FD_SET (0, &rfds);
                   3342:   tv.tv_sec = 0;
                   3343:   tv.tv_usec = 50000;           // Wait 5/100 seconds- 1 Sec=1000000
                   3344:
                   3345:   // keep reading while there are input chars and room in buffer
                   3346:   while (select(1, &rfds, NULL, NULL, &tv) > 0 && bufsiz <= (BUFSIZ - 5)) {
                   3347:       // read the rest of the ESC string
                   3348:       i = read (0, (void *) (readbuffer + bufsiz), BUFSIZ - bufsiz);
                   3349:       if (i > 0) {
                   3350:           bufsiz += i;
                   3351:           readbuffer[bufsiz] = '\0';  // Terminate the string
                   3352:       }
                   3353:   }
                   3354:   // Maybe cursor or function key?
                   3355:   for (cmdindex = 0; cmdindex < ESCCMDS_COUNT; cmdindex++) {
                   3356:       cnt = strlen ((char *) esccmds[cmdindex].seq);
                   3357:       i = strncmp ((char *) esccmds[cmdindex].seq, (char *) readbuffer, cnt);
                   3358:       if (i == 0) {
                   3359:           // is a Cursor key- put derived value back into Q
                   3360:           readbuffer[0] = esccmds[cmdindex].val;
                   3361:           // squeeze out the ESC sequence
                   3362:           for (i = 1; i < cnt; i++) {
                   3363:               memmove (readbuffer + 1, readbuffer + 2, BUFSIZ - 2);
                   3364:               readbuffer[BUFSIZ - 1] = '\0';
                   3365:          }
                   3366:           break;
                   3367:       }
                   3368:   }
                   3369: ri1:
                   3370:   c = readbuffer[0];
                   3371:   // remove one char from Q
                   3372:   memmove (readbuffer, readbuffer + 1, BUFSIZ - 1);
                   3373:   readbuffer[BUFSIZ - 1] = '\0';
                   3374:   // (void) alarm(3);     // we are done waiting for input, turn alarm ON
                   3375:   return (c);
                   3376: }
                   3377:
                   3378: //----- IO Routines --------------------------------------------
                   3379: static Byte get_one_char()
                   3380: {
                   3381:   static Byte c;
                   3382:
                   3383: #ifdef BB_FEATURE_VI_DOT_CMD
                   3384:   // ! adding2q  && ioq == 0  read()
                   3385:   // ! adding2q  && ioq != 0  *ioq
                   3386:   // adding2q         *last_modifying_cmd= read()
                   3387:   if (!adding2q) {
                   3388:       // we are not adding to the q.
                   3389:       // but, we may be reading from a q
                   3390:       if (ioq == 0) {
                   3391:           // there is no current q, read from STDIN
                   3392:           c = readit ();        // get the users input
                   3393:       } else {
                   3394:           // there is a queue to get chars from first
                   3395:           c = *ioq++;
                   3396:           if (c == '\0') {
                   3397:               // the end of the q, read from STDIN
                   3398:               free (ioq_start);
                   3399:               ioq_start = ioq = 0;
                   3400:               c = readit ();    // get the users input
                   3401:          }
                   3402:       }
                   3403:   } else {
                   3404:       // adding STDIN chars to q
                   3405:       c = readit ();            // get the users input
                   3406:       if (last_modifying_cmd != 0) {
                   3407:          int len = strlen((char *) last_modifying_cmd);
                   3408:          if (len + 1 >= BUFSIZ) {
                   3409:              /* FIXME last_modifying_cmd needs a shaving */
                   3410:              fprintf(stderr,"last_modifying_cmd overrun");
                   3411:              exit(EXIT_FAILURE);
                   3412:          } else {
                   3413:              // add new char to q
                   3414:              last_modifying_cmd[len] = c;
                   3415:          }
                   3416:
                   3417:       }
                   3418:   }
                   3419: #else /* BB_FEATURE_VI_DOT_CMD */
                   3420:   c = readit ();                // get the users input
                   3421: #endif /* BB_FEATURE_VI_DOT_CMD */
                   3422:   return (c);                   // return the char, where ever it came from
                   3423: }
                   3424:
                   3425: static Byte *get_input_line(Byte * prompt)  // get input line- use "status line"
                   3426: {
                   3427:   Byte buf[BUFSIZ];
                   3428:   Byte c;
                   3429:   int i;
                   3430:   static Byte *obufp = NULL;
                   3431:
                   3432:   /* FIXED strcpy((char *) buf, (char *) prompt); */
                   3433:   if (strlcpy((char *) buf, (char *) prompt, sizeof((char *)buf)) > sizeof ((char *) buf))  err(1, "strlcpy overflow in function get_input_line");
                   3434:   *status_buffer = '\0';        // clear the status buffer
                   3435:   place_cursor (rows - 1, 0, FALSE);  // go to Status line, bottom of screen
                   3436:   clear_to_eol ();              // clear the line
                   3437:   write (1, prompt, strlen ((char *) prompt));  // write out the :, /, or ? prompt
                   3438:
                   3439:   for (i = strlen ((char *) buf); i < BUFSIZ;) {
                   3440:       c = get_one_char ();      // read user input
                   3441:       if (c == '\n' || c == '\r' || c == 27)
                   3442:         break;                  // is this end of input
                   3443:       if (c == erase_char) {   // user wants to erase prev char
                   3444:           i--;                  // backup to prev char
                   3445:           buf[i] = '\0';        // erase the char
                   3446:           buf[i + 1] = '\0';    // null terminate buffer
                   3447:           write (1, " ", 3);      // erase char on screen
                   3448:           if (i <= 0) {        // user backs up before b-o-l, exit
                   3449:               break;
                   3450:          }
                   3451:       } else {
                   3452:           buf[i] = c;           // save char in buffer
                   3453:           buf[i + 1] = '\0';    // make sure buffer is null terminated
                   3454:           write (1, buf + i, 1);  // echo the char back to user
                   3455:           i++;
                   3456:       }
                   3457:   }
                   3458:   refresh (FALSE);
                   3459:   if (obufp != NULL)
                   3460:     free (obufp);
                   3461:   obufp = (Byte *) strdup ((char *) buf);
                   3462:   return (obufp);
                   3463: }
                   3464:
                   3465: static int file_size(Byte * fn)           // what is the byte size of "fn"
                   3466: {
                   3467:   struct stat st_buf;
                   3468:   int cnt, sr;
                   3469:
                   3470:   if (fn == 0 || strlen (fn) <= 0)
                   3471:     return (-1);
                   3472:   cnt = -1;
                   3473:   sr = stat ((char *) fn, &st_buf); // see if file exists
                   3474:   if (sr >= 0) {
                   3475:       cnt = (int) st_buf.st_size;
                   3476:   }
                   3477:   return (cnt);
                   3478: }
                   3479:
                   3480: static int file_insert(Byte * fn, Byte * p, int size)
                   3481: {
                   3482:   int fd, cnt;
                   3483:
                   3484:   cnt = -1;
                   3485: #ifdef BB_FEATURE_VI_READONLY
                   3486:   readonly = FALSE;
                   3487: #endif /* BB_FEATURE_VI_READONLY */
                   3488:   if (fn == 0 || strlen ((char *) fn) <= 0) {
                   3489:       psbs ("No filename given");
                   3490:       goto fi0;
                   3491:   }
                   3492:   if (size == 0) {
                   3493:       // OK- this is just a no-op
                   3494:       cnt = 0;
                   3495:       goto fi0;
                   3496:   }
                   3497:   if (size < 0) {
                   3498:       psbs ("Trying to insert a negative number (%d) of characters", size);
                   3499:       goto fi0;
                   3500:   }
                   3501:   if (p < text || p > end) {
                   3502:       psbs ("Trying to insert file outside of memory");
                   3503:       goto fi0;
                   3504:   }
                   3505:
                   3506:   // see if we can open the file
                   3507: #ifdef BB_FEATURE_VI_READONLY
                   3508:   if (vi_readonly == TRUE) goto fi1;                   // do not try write-mode
                   3509: #endif
                   3510:   fd = open ((char *) fn, O_RDWR);  // assume read & write
                   3511:   if (fd < 0) {
                   3512:       // could not open for writing- maybe file is read only
                   3513: #ifdef BB_FEATURE_VI_READONLY
                   3514:     fi1:
                   3515: #endif
                   3516:       fd = open ((char *) fn, O_RDONLY);  // try read-only
                   3517:       if (fd < 0) {
                   3518:           psbs ("\"%s\" %s", fn, "could not open file");
                   3519:           goto fi0;
                   3520:       }
                   3521: #ifdef BB_FEATURE_VI_READONLY
                   3522:       // got the file- read-only
                   3523:       readonly = TRUE;
                   3524: #endif /* BB_FEATURE_VI_READONLY */
                   3525:   }
                   3526:   p = text_hole_make (p, size);
                   3527:   cnt = read (fd, p, size);
                   3528:   close (fd);
                   3529:   if (cnt < 0) {
                   3530:       cnt = -1;
                   3531:       p = text_hole_delete (p, p + size - 1); // un-do buffer insert
                   3532:       psbs ("could not read file \"%s\"", fn);
                   3533:   } else if (cnt < size) {
                   3534:       // There was a partial read, shrink unused space text[]
                   3535:       p = text_hole_delete (p + cnt, p + (size - cnt) - 1); // un-do buffer insert
                   3536:       psbs ("could not read all of file \"%s\"", fn);
                   3537:   }
                   3538:   if (cnt >= size)
                   3539:     file_modified = TRUE;
                   3540: fi0:
                   3541:   return (cnt);
                   3542: }
                   3543:
                   3544: static int file_write(Byte * fn, Byte * first, Byte * last)
                   3545: {
                   3546:   int fd, cnt, charcnt;
                   3547:
                   3548:   if (fn == 0) {
                   3549:       psbs ("No current filename");
                   3550:       return (-1);
                   3551:   }
                   3552:   charcnt = 0;
                   3553:   // FIXIT- use the correct umask()
                   3554:   fd = open ((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
                   3555:   if (fd < 0)
                   3556:     return (-1);
                   3557:   cnt = last - first + 1;
                   3558:   charcnt = write (fd, first, cnt);
                   3559:   if (charcnt == cnt) {
                   3560:       // good write
                   3561:       //file_modified= FALSE; // the file has not been modified
                   3562:   } else {
                   3563:       charcnt = 0;
                   3564:   }
                   3565:   close (fd);
                   3566:   return (charcnt);
                   3567: }
                   3568:
                   3569: //----- Terminal Drawing ---------------------------------------
                   3570: // The terminal is made up of 'rows' line of 'columns' columns.
                   3571: // classicly this would be 24 x 80.
                   3572: //  screen coordinates
                   3573: //  0,0     ...     0,79
                   3574: //  1,0     ...     1,79
                   3575: //  .       ...     .
                   3576: //  .       ...     .
                   3577: //  22,0    ...     22,79
                   3578: //  23,0    ...     23,79   status line
                   3579: //
                   3580:
                   3581: //----- Move the cursor to row x col (count from 0, not 1) -------
                   3582: static void place_cursor(int row, int col, int opti)
                   3583: {
                   3584:   char cm1[BUFSIZ];
                   3585:   char *cm;
                   3586:   int l;
                   3587: #ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR
                   3588:   char cm2[BUFSIZ];
                   3589:   Byte *screenp;
                   3590:   // char cm3[BUFSIZ];
                   3591:   int Rrow = last_row;
                   3592: #endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */
                   3593:
                   3594:   memset (cm1, '\0', BUFSIZ - 1); // clear the buffer
                   3595:
                   3596:   if (row < 0) row = 0;
                   3597:   if (row >= rows) row = rows - 1;
                   3598:   if (col < 0) col = 0;
                   3599:   if (col >= columns) col = columns - 1;
                   3600:
                   3601:   //----- 1.  Try the standard terminal ESC sequence
                   3602:   sprintf ((char *) cm1, CMrc, row + 1, col + 1);
                   3603:   cm = cm1;
                   3604:   if (opti == FALSE) goto pc0;
                   3605:
                   3606: #ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR
                   3607:   //----- find the minimum # of chars to move cursor -------------
                   3608:   //----- 2.  Try moving with discreet chars (Newline, [back]space, ...)
                   3609:   memset (cm2, '\0', BUFSIZ - 1); // clear the buffer
                   3610:   /* FIXME - possible replace with strlc* ? */
                   3611:
                   3612:   // move to the correct row
                   3613:   while (row < Rrow) {
                   3614:       // the cursor has to move up
                   3615:       strlcat(cm2, CMup, BUFSIZ); /* FIXED strlcat BUFSIZ */
                   3616:       Rrow--;
                   3617:   }
                   3618:   while (row > Rrow) {
                   3619:       // the cursor has to move down
                   3620:       strlcat(cm2, CMdown, BUFSIZ); /* FIXED strlcat BUFSIZE */
                   3621:       Rrow++;
                   3622:   }
                   3623:
                   3624:   // now move to the correct column
                   3625:   strcat (cm2, "\r");           // start at col 0
                   3626:   // just send out orignal source char to get to correct place
                   3627:   screenp = &screen[row * columns]; // start of screen line
                   3628:   strncat (cm2, screenp, col);
                   3629:
                   3630:   //----- 3.  Try some other way of moving cursor
                   3631:   //---------------------------------------------
                   3632:
                   3633:   // pick the shortest cursor motion to send out
                   3634:   cm = cm1;
                   3635:   if (strlen (cm2) < strlen (cm)) {
                   3636:       cm = cm2;
                   3637:   }                           /* else if (strlen(cm3) < strlen(cm)) {
                   3638:                                    cm= cm3;
                   3639:                                    } */
                   3640: #endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */
                   3641: pc0:
                   3642:   l = strlen (cm);
                   3643:   if (l) write (1, cm, l);           // move the cursor
                   3644: }
                   3645:
                   3646: //----- Erase from cursor to end of line -----------------------
                   3647: static void clear_to_eol()
                   3648: {
                   3649:   write (1, Ceol, strlen (Ceol)); // Erase from cursor to end of line
                   3650: }
                   3651:
                   3652: //----- Erase from cursor to end of screen -----------------------
                   3653: static void clear_to_eos()
                   3654: {
                   3655:   write (1, Ceos, strlen (Ceos)); // Erase from cursor to end of screen
                   3656: }
                   3657:
                   3658: //----- Start standout mode ------------------------------------
                   3659: static void standout_start()               // send "start reverse video" sequence
                   3660: {
                   3661:   write (1, SOs, strlen (SOs)); // Start reverse video mode
                   3662: }
                   3663:
                   3664: //----- End standout mode --------------------------------------
                   3665: static void standout_end()                 // send "end reverse video" sequence
                   3666: {
                   3667:   write (1, SOn, strlen (SOn)); // End reverse video mode
                   3668: }
                   3669:
                   3670: //----- Flash the screen  --------------------------------------
                   3671: static void flash(int h)
                   3672: {
                   3673:   standout_start ();            // send "start reverse video" sequence
                   3674:   redraw (TRUE);
                   3675:   (void) mysleep (h);
                   3676:   standout_end ();              // send "end reverse video" sequence
                   3677:   redraw (TRUE);
                   3678: }
                   3679:
                   3680: static void beep()
                   3681: {
                   3682:   write (1, bell, strlen (bell)); // send out a bell character
                   3683: }
                   3684:
                   3685: static void indicate_error(char c)
                   3686: {
                   3687: #ifdef BB_FEATURE_VI_CRASHME
                   3688:   if (crashme > 0)
                   3689:     return;                     // generate a random command
                   3690: #endif /* BB_FEATURE_VI_CRASHME */
                   3691:   if (err_method == 0) {
                   3692:       beep ();
                   3693:   } else {
                   3694:       flash (10);
                   3695:   }
                   3696: }
                   3697:
                   3698: //----- Screen[] Routines --------------------------------------
                   3699: //----- Erase the Screen[] memory ------------------------------
                   3700: static void screen_erase()
                   3701: {
                   3702:   memset (screen, ' ', screensize); // clear new screen
                   3703: }
                   3704:
                   3705: //----- Draw the status line at bottom of the screen -------------
                   3706: static void show_status_line(void)
                   3707: {
                   3708:   static int last_cksum;
                   3709:   int l, cnt, cksum;
                   3710:
                   3711:   cnt = strlen ((char *) status_buffer);
                   3712:   for (cksum = l = 0; l < cnt; l++) { cksum += (int) (status_buffer[l]); }
                   3713:   // don't write the status line unless it changes
                   3714:   if (cnt > 0 && last_cksum != cksum) {
                   3715:       last_cksum = cksum;       // remember if we have seen this line
                   3716:       place_cursor (rows - 1, 0, FALSE);  // put cursor on status line
                   3717:       write (1, status_buffer, cnt);
                   3718:       clear_to_eol ();
                   3719:       place_cursor (crow, ccol, FALSE); // put cursor back in correct place
                   3720:   }
                   3721: }
                   3722:
                   3723: //----- format the status buffer, the bottom line of screen ------
                   3724: // print status buffer, with STANDOUT mode
                   3725: static void psbs(char *format, ...)
                   3726: {
                   3727:   va_list args;
                   3728:
                   3729:   va_start (args, format);
                   3730:   /* FIXED strcpy((char *) status_buffer, SOs); */
                   3731:   strlcpy((char *) status_buffer, SOs, BUFSIZ_STATBUF);   // Terminal standout mode on
                   3732:   vsprintf ((char *) status_buffer + strlen ((char *) status_buffer), format,
                   3733:             args);
                   3734:   /* FIXED strcat((char *) status_buffer, SOn); */
                   3735:   strlcat((char *) status_buffer, SOn, BUFSIZ_STATBUF);   // Terminal standout mode off
                   3736:   va_end (args);
                   3737:
                   3738:   return;
                   3739: }
                   3740:
                   3741: // print status buffer
                   3742: static void psb(char *format, ...)
                   3743: {
                   3744:   va_list args;
                   3745:
                   3746:   va_start (args, format);
                   3747:   vsprintf ((char *) status_buffer, format, args);
                   3748:   va_end (args);
                   3749:   return;
                   3750: }
                   3751:
                   3752: static void ni(Byte * s)                   // display messages
                   3753: {
                   3754:   Byte buf[BUFSIZ];
                   3755:
                   3756:   print_literal (buf, s);
                   3757:   psbs ("\'%s\' is not implemented", buf);
                   3758: }
                   3759:
                   3760: static void edit_status(void)              // show file status on status line
                   3761: {
                   3762:   int cur, tot, percent;
                   3763:
                   3764:   cur = count_lines (text, dot);
                   3765:   tot = count_lines (text, end - 1);
                   3766:   //    current line         percent
                   3767:   //   -------------    ~~ ----------
                   3768:   //    total lines            100
                   3769:   if (tot > 0) {
                   3770:       percent = (100 * cur) / tot;
                   3771:   } else {
                   3772:       cur = tot = 0;
                   3773:       percent = 100;
                   3774:   }
                   3775:   psb ("\"%s\""
                   3776: #ifdef BB_FEATURE_VI_READONLY
                   3777:        "%s"
                   3778: #endif /* BB_FEATURE_VI_READONLY */
                   3779:        "%s line %d of %d --%d%%--",
                   3780:        (cfn != 0 ? (char *) cfn : "No file"),
                   3781: #ifdef BB_FEATURE_VI_READONLY
                   3782:        ((vi_readonly == TRUE || readonly == TRUE) ? " [Read only]" : ""),
                   3783: #endif /* BB_FEATURE_VI_READONLY */
                   3784:        (file_modified == TRUE ? " [modified]" : ""),
                   3785:        cur, tot, percent);
                   3786: }
                   3787:
                   3788: //----- Force refresh of all Lines -----------------------------
                   3789: static void redraw(int full_screen)
                   3790: {
                   3791:   place_cursor (0, 0, FALSE);   // put cursor in correct place
                   3792:   clear_to_eos ();              // tel terminal to erase display
                   3793:   screen_erase ();              // erase the internal screen buffer
                   3794:   refresh (full_screen);        // this will redraw the entire display
                   3795: }
                   3796:
                   3797: //----- Format a text[] line into a buffer ---------------------
                   3798: static void format_line(Byte * dest, Byte * src, int li)
                   3799: {
                   3800:   int co;
                   3801:   Byte c;
                   3802:
                   3803:   for (co = 0; co < MAX_SCR_COLS; co++) {
                   3804:       c = ' ';                  // assume blank
                   3805:       if (li > 0 && co == 0) {
                   3806:           c = '~';              // not first line, assume Tilde
                   3807:       }
                   3808:       // are there chars in text[] and have we gone past the end
                   3809:       if (text < end && src < end) {
                   3810:           c = *src++;
                   3811:       }
                   3812:       if (c == '\n')
                   3813:         break;
                   3814:       if (c < ' ' || c > '~') {
                   3815:           if (c == '\t') {
                   3816:               c = ' ';
                   3817:               //       co %    8     !=     7
                   3818:               for (; (co % tabstop) != (tabstop - 1); co++) {
                   3819:                   dest[co] = c;
                   3820:              }
                   3821:          } else {
                   3822:               dest[co++] = '^';
                   3823:               c |= '@';         // make it visible
                   3824:               c &= 0x7f;        // get rid of hi bit
                   3825:          }
                   3826:       }
                   3827:       // the co++ is done here so that the column will
                   3828:       // not be overwritten when we blank-out the rest of line
                   3829:       dest[co] = c;
                   3830:       if (src >= end)
                   3831:         break;
                   3832:   }
                   3833: }
                   3834:
                   3835: //----- Refresh the changed screen lines -----------------------
                   3836: // Copy the source line from text[] into the buffer and note
                   3837: // if the current screenline is different from the new buffer.
                   3838: // If they differ then that line needs redrawing on the terminal.
                   3839: //
                   3840: static void refresh(int full_screen)
                   3841: {
                   3842:   static int old_offset;
                   3843:   int li, changed;
                   3844:   Byte buf[MAX_SCR_COLS];
                   3845:   Byte *tp, *sp;                // pointer into text[] and screen[]
                   3846: #ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR
                   3847:   int last_li = -2;             // last line that changed- for optimizing cursor movement
                   3848: #endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */
                   3849:
                   3850: #ifdef BB_FEATURE_VI_WIN_RESIZE
                   3851:   window_size_get (0);
                   3852: #endif /* BB_FEATURE_VI_WIN_RESIZE */
                   3853:   sync_cursor (dot, &crow, &ccol);  // where cursor will be (on "dot")
                   3854:   tp = screenbegin;             // index into text[] of top line
                   3855:
                   3856:   // compare text[] to screen[] and mark screen[] lines that need updating
                   3857:   for (li = 0; li < rows - 1; li++) {
                   3858:       int cs, ce;               // column start & end
                   3859:       memset (buf, ' ', MAX_SCR_COLS);  // blank-out the buffer
                   3860:       buf[MAX_SCR_COLS - 1] = 0;  // NULL terminate the buffer
                   3861:       // format current text line into buf
                   3862:       format_line (buf, tp, li);
                   3863:
                   3864:       // skip to the end of the current text[] line
                   3865:       while (tp < end && *tp++ != '\n') /*no-op */ ;
                   3866:
                   3867:       // see if there are any changes between vitual screen and buf
                   3868:       changed = FALSE;          // assume no change
                   3869:       cs = 0;
                   3870:       ce = columns - 1;
                   3871:       sp = &screen[li * columns]; // start of screen line
                   3872:       if (full_screen == TRUE) {
                   3873:           // force re-draw of every single column from 0 - columns-1
                   3874:           goto re0;
                   3875:       }
                   3876:       // compare newly formatted buffer with virtual screen
                   3877:       // look forward for first difference between buf and screen
                   3878:       for (; cs <= ce; cs++) {
                   3879:           if (buf[cs + offset] != sp[cs]) {
                   3880:               changed = TRUE;   // mark for redraw
                   3881:               break;
                   3882:          }
                   3883:       }
                   3884:
                   3885:       // look backward for last difference between buf and screen
                   3886:       for (; ce >= cs; ce--) {
                   3887:           if (buf[ce + offset] != sp[ce]) {
                   3888:               changed = TRUE;   // mark for redraw
                   3889:               break;
                   3890:          }
                   3891:       }
                   3892:       // now, cs is index of first diff, and ce is index of last diff
                   3893:
                   3894:       // if horz offset has changed, force a redraw
                   3895:       if (offset != old_offset) {
                   3896:         re0:
                   3897:           changed = TRUE;
                   3898:       }
                   3899:
                   3900:       // make a sanity check of columns indexes
                   3901:       if (cs < 0) cs = 0;
                   3902:       if (ce > columns - 1) ce = columns - 1;
                   3903:       if (cs > ce) { cs = 0; ce = columns - 1; }
                   3904:       // is there a change between vitual screen and buf
                   3905:       if (changed == TRUE) {
                   3906:           //  copy changed part of buffer to virtual screen
                   3907:           memmove (sp + cs, buf + (cs + offset), ce - cs + 1);
                   3908:
                   3909:           // move cursor to column of first change
                   3910:           if (offset != old_offset) {
                   3911:               // opti_cur_move is still too stupid
                   3912:               // to handle offsets correctly
                   3913:               place_cursor (li, cs, FALSE);
                   3914:          } else {
                   3915: #ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR
                   3916:               // if this just the next line
                   3917:               //  try to optimize cursor movement
                   3918:               //  otherwise, use standard ESC sequence
                   3919:               place_cursor (li, cs, li == (last_li + 1) ? TRUE : FALSE);
                   3920:               last_li = li;
                   3921: #else /* BB_FEATURE_VI_OPTIMIZE_CURSOR */
                   3922:               place_cursor (li, cs, FALSE); // use standard ESC sequence
                   3923: #endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */
                   3924:          }
                   3925:
                   3926:           // write line out to terminal
                   3927:           write (1, sp + cs, ce - cs + 1);
                   3928: #ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR
                   3929:           last_row = li;
                   3930: #endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */
                   3931:       }
                   3932:   }
                   3933:
                   3934: #ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR
                   3935:   place_cursor (crow, ccol, (crow == last_row) ? TRUE : FALSE);
                   3936:   last_row = crow;
                   3937: #else
                   3938:   place_cursor (crow, ccol, FALSE);
                   3939: #endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */
                   3940:
                   3941:   if (offset != old_offset)
                   3942:     old_offset = offset;
                   3943: }

CVSweb