[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     ! 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