blob: 9aacae439f9779efc27ceb643cb62aae5305f3f3 [file] [log] [blame]
Eric Andersen3f980402001-04-04 17:31:15 +00001/* vi: set sw=8 ts=8: */
2/*
3 * tiny vi.c: A small 'vi' clone
4 * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
5 *
Paul Foxdbf935d2006-03-27 20:29:33 +00006 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersen3f980402001-04-04 17:31:15 +00007 */
8
Eric Andersen3f980402001-04-04 17:31:15 +00009/*
10 * To compile for standalone use:
Eric Andersend402edf2001-04-04 19:29:48 +000011 * gcc -Wall -Os -s -DSTANDALONE -o vi vi.c
Eric Andersen3f980402001-04-04 17:31:15 +000012 * or
Eric Andersenbdfd0d72001-10-24 05:00:29 +000013 * gcc -Wall -Os -s -DSTANDALONE -DCONFIG_FEATURE_VI_CRASHME -o vi vi.c # include testing features
Eric Andersen3f980402001-04-04 17:31:15 +000014 * strip vi
15 */
16
17/*
18 * Things To Do:
19 * EXINIT
Eric Andersen1c0d3112001-04-16 15:46:44 +000020 * $HOME/.exrc and ./.exrc
Eric Andersen3f980402001-04-04 17:31:15 +000021 * add magic to search /foo.*bar
22 * add :help command
23 * :map macros
24 * how about mode lines: vi: set sw=8 ts=8:
25 * if mark[] values were line numbers rather than pointers
26 * it would be easier to change the mark when add/delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +000027 * More intelligence in refresh()
28 * ":r !cmd" and "!cmd" to filter text through an external command
29 * A true "undo" facility
30 * An "ex" line oriented mode- maybe using "cmdedit"
Eric Andersen3f980402001-04-04 17:31:15 +000031 */
32
Eric Andersenaff114c2004-04-14 17:51:38 +000033//---- Feature -------------- Bytes to implement
Eric Andersen3f980402001-04-04 17:31:15 +000034#ifdef STANDALONE
Eric Andersen822c3832001-05-07 17:37:43 +000035#define vi_main main
Eric Andersenbdfd0d72001-10-24 05:00:29 +000036#define CONFIG_FEATURE_VI_COLON // 4288
37#define CONFIG_FEATURE_VI_YANKMARK // 1408
38#define CONFIG_FEATURE_VI_SEARCH // 1088
39#define CONFIG_FEATURE_VI_USE_SIGNALS // 1056
40#define CONFIG_FEATURE_VI_DOT_CMD // 576
41#define CONFIG_FEATURE_VI_READONLY // 128
42#define CONFIG_FEATURE_VI_SETOPTS // 576
43#define CONFIG_FEATURE_VI_SET // 224
44#define CONFIG_FEATURE_VI_WIN_RESIZE // 256 WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +000045// To test editor using CRASHME:
46// vi -C filename
47// To stop testing, wait until all to text[] is deleted, or
48// Ctrl-Z and kill -9 %1
49// while in the editor Ctrl-T will toggle the crashme function on and off.
Eric Andersenbdfd0d72001-10-24 05:00:29 +000050//#define CONFIG_FEATURE_VI_CRASHME // randomly pick commands to execute
Eric Andersen3f980402001-04-04 17:31:15 +000051#endif /* STANDALONE */
52
Eric Andersen3f980402001-04-04 17:31:15 +000053#include <stdio.h>
54#include <stdlib.h>
55#include <string.h>
56#include <termios.h>
57#include <unistd.h>
58#include <sys/ioctl.h>
59#include <sys/time.h>
60#include <sys/types.h>
61#include <sys/stat.h>
62#include <time.h>
63#include <fcntl.h>
64#include <signal.h>
65#include <setjmp.h>
66#include <regex.h>
67#include <ctype.h>
68#include <assert.h>
69#include <errno.h>
70#include <stdarg.h>
Eric Andersene0c07572001-06-23 13:49:14 +000071#ifndef STANDALONE
72#include "busybox.h"
Paul Foxdbf935d2006-03-27 20:29:33 +000073#define vi_Version BB_VER " " BB_BT
74#else
75#define vi_Version "standalone"
Eric Andersene0c07572001-06-23 13:49:14 +000076#endif /* STANDALONE */
Eric Andersen3f980402001-04-04 17:31:15 +000077
Glenn L McGrath09adaca2002-12-02 21:18:10 +000078#ifdef CONFIG_LOCALE_SUPPORT
79#define Isprint(c) isprint((c))
80#else
81#define Isprint(c) ( (c) >= ' ' && (c) != 127 && (c) != ((unsigned char)'\233') )
82#endif
83
Eric Andersen3f980402001-04-04 17:31:15 +000084#ifndef TRUE
85#define TRUE ((int)1)
86#define FALSE ((int)0)
87#endif /* TRUE */
Eric Andersen1c0d3112001-04-16 15:46:44 +000088#define MAX_SCR_COLS BUFSIZ
Eric Andersen3f980402001-04-04 17:31:15 +000089
90// Misc. non-Ascii keys that report an escape sequence
91#define VI_K_UP 128 // cursor key Up
92#define VI_K_DOWN 129 // cursor key Down
93#define VI_K_RIGHT 130 // Cursor Key Right
94#define VI_K_LEFT 131 // cursor key Left
95#define VI_K_HOME 132 // Cursor Key Home
96#define VI_K_END 133 // Cursor Key End
97#define VI_K_INSERT 134 // Cursor Key Insert
98#define VI_K_PAGEUP 135 // Cursor Key Page Up
99#define VI_K_PAGEDOWN 136 // Cursor Key Page Down
100#define VI_K_FUN1 137 // Function Key F1
101#define VI_K_FUN2 138 // Function Key F2
102#define VI_K_FUN3 139 // Function Key F3
103#define VI_K_FUN4 140 // Function Key F4
104#define VI_K_FUN5 141 // Function Key F5
105#define VI_K_FUN6 142 // Function Key F6
106#define VI_K_FUN7 143 // Function Key F7
107#define VI_K_FUN8 144 // Function Key F8
108#define VI_K_FUN9 145 // Function Key F9
109#define VI_K_FUN10 146 // Function Key F10
110#define VI_K_FUN11 147 // Function Key F11
111#define VI_K_FUN12 148 // Function Key F12
112
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000113/* vt102 typical ESC sequence */
114/* terminal standout start/normal ESC sequence */
115static const char SOs[] = "\033[7m";
116static const char SOn[] = "\033[0m";
117/* terminal bell sequence */
118static const char bell[] = "\007";
119/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
120static const char Ceol[] = "\033[0K";
121static const char Ceos [] = "\033[0J";
122/* Cursor motion arbitrary destination ESC sequence */
123static const char CMrc[] = "\033[%d;%dH";
124/* Cursor motion up and down ESC sequence */
125static const char CMup[] = "\033[A";
126static const char CMdown[] = "\n";
127
128
Rob Landleybc68cd12006-03-10 19:22:06 +0000129enum {
130 YANKONLY = FALSE,
131 YANKDEL = TRUE,
132 FORWARD = 1, // code depends on "1" for array index
133 BACK = -1, // code depends on "-1" for array index
134 LIMITED = 0, // how much of text[] in char_search
135 FULL = 1, // how much of text[] in char_search
Eric Andersen3f980402001-04-04 17:31:15 +0000136
Rob Landleybc68cd12006-03-10 19:22:06 +0000137 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
138 S_TO_WS = 2, // used in skip_thing() for moving "dot"
139 S_OVER_WS = 3, // used in skip_thing() for moving "dot"
140 S_END_PUNCT = 4, // used in skip_thing() for moving "dot"
141 S_END_ALNUM = 5 // used in skip_thing() for moving "dot"
142};
Eric Andersen3f980402001-04-04 17:31:15 +0000143
144typedef unsigned char Byte;
145
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000146static int vi_setops;
147#define VI_AUTOINDENT 1
148#define VI_SHOWMATCH 2
149#define VI_IGNORECASE 4
150#define VI_ERR_METHOD 8
151#define autoindent (vi_setops & VI_AUTOINDENT)
152#define showmatch (vi_setops & VI_SHOWMATCH )
153#define ignorecase (vi_setops & VI_IGNORECASE)
154/* indicate error with beep or flash */
155#define err_method (vi_setops & VI_ERR_METHOD)
156
Eric Andersen3f980402001-04-04 17:31:15 +0000157
158static int editing; // >0 while we are editing a file
Paul Fox8552aec2005-09-16 12:20:05 +0000159static int cmd_mode; // 0=command 1=insert 2=replace
Eric Andersen3f980402001-04-04 17:31:15 +0000160static int file_modified; // buffer contents changed
Paul Fox8552aec2005-09-16 12:20:05 +0000161static int last_file_modified = -1;
Eric Andersen3f980402001-04-04 17:31:15 +0000162static int fn_start; // index of first cmd line file name
163static int save_argc; // how many file names on cmd line
164static int cmdcnt; // repetition count
165static fd_set rfds; // use select() for small sleeps
166static struct timeval tv; // use select() for small sleeps
Eric Andersen3f980402001-04-04 17:31:15 +0000167static int rows, columns; // the terminal screen is this size
168static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset
Eric Andersen3f980402001-04-04 17:31:15 +0000169static Byte *status_buffer; // mesages to the user
Paul Fox8552aec2005-09-16 12:20:05 +0000170#define STATUS_BUFFER_LEN 200
171static int have_status_msg; // is default edit status needed?
172static int last_status_cksum; // hash of current status line
Eric Andersen3f980402001-04-04 17:31:15 +0000173static Byte *cfn; // previous, current, and next file name
174static Byte *text, *end, *textend; // pointers to the user data in memory
175static Byte *screen; // pointer to the virtual screen buffer
176static int screensize; // and its size
177static Byte *screenbegin; // index into text[], of top line on the screen
178static Byte *dot; // where all the action takes place
179static int tabstop;
180static struct termios term_orig, term_vi; // remember what the cooked mode was
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000181static Byte erase_char; // the users erase character
182static Byte last_input_char; // last char read from user
183static Byte last_forward_char; // last char searched for with 'f'
Eric Andersen3f980402001-04-04 17:31:15 +0000184
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000185#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +0000186static int last_row; // where the cursor was last moved to
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000187#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
188#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000189static jmp_buf restart; // catch_sig()
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000190#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000191#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
192static int my_pid;
193#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000194#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000195static int adding2q; // are we currently adding user input to q
196static Byte *last_modifying_cmd; // last modifying cmd for "."
197static Byte *ioq, *ioq_start; // pointer to string for get_one_char to "read"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000198#endif /* CONFIG_FEATURE_VI_DOT_CMD */
199#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
Eric Andersen3f980402001-04-04 17:31:15 +0000200static Byte *modifying_cmds; // cmds that modify text[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000201#endif /* CONFIG_FEATURE_VI_DOT_CMD || CONFIG_FEATURE_VI_YANKMARK */
202#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000203static int vi_readonly, readonly;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000204#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000205#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000206static Byte *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
207static int YDreg, Ureg; // default delete register and orig line for "U"
208static Byte *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
209static Byte *context_start, *context_end;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000210#endif /* CONFIG_FEATURE_VI_YANKMARK */
211#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000212static Byte *last_search_pattern; // last pattern from a '/' or '?' search
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000213#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +0000214
215
216static void edit_file(Byte *); // edit one file
217static void do_cmd(Byte); // execute a command
218static void sync_cursor(Byte *, int *, int *); // synchronize the screen cursor to dot
219static Byte *begin_line(Byte *); // return pointer to cur line B-o-l
220static Byte *end_line(Byte *); // return pointer to cur line E-o-l
Eric Andersen3f980402001-04-04 17:31:15 +0000221static Byte *prev_line(Byte *); // return pointer to prev line B-o-l
222static Byte *next_line(Byte *); // return pointer to next line B-o-l
223static Byte *end_screen(void); // get pointer to last char on screen
224static int count_lines(Byte *, Byte *); // count line from start to stop
225static Byte *find_line(int); // find begining of line #li
226static Byte *move_to_col(Byte *, int); // move "p" to column l
227static int isblnk(Byte); // is the char a blank or tab
228static void dot_left(void); // move dot left- dont leave line
229static void dot_right(void); // move dot right- dont leave line
230static void dot_begin(void); // move dot to B-o-l
231static void dot_end(void); // move dot to E-o-l
232static void dot_next(void); // move dot to next line B-o-l
233static void dot_prev(void); // move dot to prev line B-o-l
234static void dot_scroll(int, int); // move the screen up or down
235static void dot_skip_over_ws(void); // move dot pat WS
236static void dot_delete(void); // delete the char at 'dot'
237static Byte *bound_dot(Byte *); // make sure text[0] <= P < "end"
238static Byte *new_screen(int, int); // malloc virtual screen memory
239static Byte *new_text(int); // malloc memory for text[] buffer
240static Byte *char_insert(Byte *, Byte); // insert the char c at 'p'
241static Byte *stupid_insert(Byte *, Byte); // stupidly insert the char c at 'p'
242static Byte find_range(Byte **, Byte **, Byte); // return pointers for an object
243static int st_test(Byte *, int, int, Byte *); // helper for skip_thing()
244static Byte *skip_thing(Byte *, int, int, int); // skip some object
245static Byte *find_pair(Byte *, Byte); // find matching pair () [] {}
246static Byte *text_hole_delete(Byte *, Byte *); // at "p", delete a 'size' byte hole
247static Byte *text_hole_make(Byte *, int); // at "p", make a 'size' byte hole
248static Byte *yank_delete(Byte *, Byte *, int, int); // yank text[] into register then delete
249static void show_help(void); // display some help info
Eric Andersen3f980402001-04-04 17:31:15 +0000250static void rawmode(void); // set "raw" mode on tty
251static void cookmode(void); // return to "cooked" mode on tty
252static int mysleep(int); // sleep for 'h' 1/100 seconds
253static Byte readit(void); // read (maybe cursor) key from stdin
254static Byte get_one_char(void); // read 1 char from stdin
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000255static int file_size(const Byte *); // what is the byte size of "fn"
Eric Andersen3f980402001-04-04 17:31:15 +0000256static int file_insert(Byte *, Byte *, int);
257static int file_write(Byte *, Byte *, Byte *);
Eric Andersen822c3832001-05-07 17:37:43 +0000258static void place_cursor(int, int, int);
Eric Andersenbff7a602001-11-17 07:15:43 +0000259static void screen_erase(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000260static void clear_to_eol(void);
261static void clear_to_eos(void);
262static void standout_start(void); // send "start reverse video" sequence
263static void standout_end(void); // send "end reverse video" sequence
264static void flash(int); // flash the terminal screen
Eric Andersen3f980402001-04-04 17:31:15 +0000265static void show_status_line(void); // put a message on the bottom line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000266static void psb(const char *, ...); // Print Status Buf
267static void psbs(const char *, ...); // Print Status Buf in standout mode
Eric Andersen3f980402001-04-04 17:31:15 +0000268static void ni(Byte *); // display messages
Paul Fox8552aec2005-09-16 12:20:05 +0000269static int format_edit_status(void); // format file status on status line
Eric Andersen3f980402001-04-04 17:31:15 +0000270static void redraw(int); // force a full screen refresh
Eric Andersen822c3832001-05-07 17:37:43 +0000271static void format_line(Byte*, Byte*, int);
Eric Andersen3f980402001-04-04 17:31:15 +0000272static void refresh(int); // update the terminal from screen[]
273
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000274static void Indicate_Error(void); // use flash or beep to indicate error
275#define indicate_error(c) Indicate_Error()
Paul Fox90372ed2005-10-09 14:26:26 +0000276static void Hit_Return(void);
277
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000278#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000279static Byte *char_search(Byte *, Byte *, int, int); // search for pattern starting at p
280static int mycmp(Byte *, Byte *, int); // string cmp based in "ignorecase"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000281#endif /* CONFIG_FEATURE_VI_SEARCH */
282#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen1c0d3112001-04-16 15:46:44 +0000283static Byte *get_one_address(Byte *, int *); // get colon addr, if present
284static Byte *get_address(Byte *, int *, int *); // get two colon addrs, if present
Eric Andersen3f980402001-04-04 17:31:15 +0000285static void colon(Byte *); // execute the "colon" mode cmds
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000286#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000287#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000288static void winch_sig(int); // catch window size changes
289static void suspend_sig(int); // catch ctrl-Z
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000290static void catch_sig(int); // catch ctrl-C and alarm time-outs
Eric Andersen3f980402001-04-04 17:31:15 +0000291static void core_sig(int); // catch a core dump signal
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000292#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
293#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000294static void start_new_cmd_q(Byte); // new queue for command
Eric Andersenbff7a602001-11-17 07:15:43 +0000295static void end_cmd_q(void); // stop saving input chars
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000296#else /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000297#define end_cmd_q()
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000298#endif /* CONFIG_FEATURE_VI_DOT_CMD */
299#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000300static void window_size_get(int); // find out what size the window is
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000301#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
302#ifdef CONFIG_FEATURE_VI_SETOPTS
Eric Andersen3f980402001-04-04 17:31:15 +0000303static void showmatching(Byte *); // show the matching pair () [] {}
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000304#endif /* CONFIG_FEATURE_VI_SETOPTS */
Paul Foxb7b24d62006-04-05 14:17:24 +0000305#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_SEARCH) || defined(CONFIG_FEATURE_VI_CRASHME)
Eric Andersen3f980402001-04-04 17:31:15 +0000306static Byte *string_insert(Byte *, Byte *); // insert the string at 'p'
Paul Foxb7b24d62006-04-05 14:17:24 +0000307#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_SEARCH || CONFIG_FEATURE_VI_CRASHME */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000308#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000309static Byte *text_yank(Byte *, Byte *, int); // save copy of "p" into a register
310static Byte what_reg(void); // what is letter of current YDreg
311static void check_context(Byte); // remember context for '' command
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000312#endif /* CONFIG_FEATURE_VI_YANKMARK */
313#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000314static void crash_dummy();
315static void crash_test();
316static int crashme = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000317#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000318
319
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000320static void write1(const char *out)
321{
322 fputs(out, stdout);
323}
324
Rob Landleydfba7412006-03-06 20:47:33 +0000325int vi_main(int argc, char **argv)
Eric Andersen3f980402001-04-04 17:31:15 +0000326{
Eric Andersend402edf2001-04-04 19:29:48 +0000327 int c;
Paul Fox8552aec2005-09-16 12:20:05 +0000328 RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN);
Eric Andersen3f980402001-04-04 17:31:15 +0000329
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000330#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000331 int i;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000332#endif /* CONFIG_FEATURE_VI_YANKMARK */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000333#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
334 my_pid = getpid();
335#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000336#ifdef CONFIG_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000337 (void) srand((long) my_pid);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000338#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000339
Eric Andersena68ea1c2006-01-30 22:48:39 +0000340 status_buffer = (Byte *)STATUS_BUFFER;
Paul Fox8552aec2005-09-16 12:20:05 +0000341 last_status_cksum = 0;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000342
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000343#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000344 vi_readonly = readonly = FALSE;
Eric Andersen3f980402001-04-04 17:31:15 +0000345 if (strncmp(argv[0], "view", 4) == 0) {
346 readonly = TRUE;
Eric Andersen822c3832001-05-07 17:37:43 +0000347 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000348 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000349#endif /* CONFIG_FEATURE_VI_READONLY */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000350 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000351#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000352 for (i = 0; i < 28; i++) {
353 reg[i] = 0;
354 } // init the yank regs
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000355#endif /* CONFIG_FEATURE_VI_YANKMARK */
Tim Riker86c76a92002-04-26 07:41:22 +0000356#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
Eric Andersen3f980402001-04-04 17:31:15 +0000357 modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000358#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000359
360 // 1- process $HOME/.exrc file
361 // 2- process EXINIT variable from environment
362 // 3- process command line args
363 while ((c = getopt(argc, argv, "hCR")) != -1) {
364 switch (c) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000365#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000366 case 'C':
367 crashme = 1;
368 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000369#endif /* CONFIG_FEATURE_VI_CRASHME */
370#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +0000371 case 'R': // Read-only flag
372 readonly = TRUE;
Eric Andersen90fb65f2004-03-31 11:12:51 +0000373 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000374 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000375#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen822c3832001-05-07 17:37:43 +0000376 //case 'r': // recover flag- ignore- we don't use tmp file
377 //case 'x': // encryption flag- ignore
378 //case 'c': // execute command first
379 //case 'h': // help -- just use default
Eric Andersen3f980402001-04-04 17:31:15 +0000380 default:
381 show_help();
Eric Andersendd8500b2001-07-02 18:06:14 +0000382 return 1;
Eric Andersen3f980402001-04-04 17:31:15 +0000383 }
384 }
385
386 // The argv array can be used by the ":next" and ":rewind" commands
387 // save optind.
388 fn_start = optind; // remember first file name for :next and :rew
389 save_argc = argc;
390
391 //----- This is the main file handling loop --------------
392 if (optind >= argc) {
393 editing = 1; // 0= exit, 1= one file, 2= multiple files
394 edit_file(0);
395 } else {
396 for (; optind < argc; optind++) {
397 editing = 1; // 0=exit, 1=one file, 2+ =many files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000398 free(cfn);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000399 cfn = (Byte *) bb_xstrdup(argv[optind]);
Eric Andersen3f980402001-04-04 17:31:15 +0000400 edit_file(cfn);
401 }
402 }
403 //-----------------------------------------------------------
404
405 return (0);
406}
407
Eric Andersen8efe9672003-09-15 08:33:45 +0000408#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
409//----- See what the window size currently is --------------------
410static inline void window_size_get(int fd)
411{
412 get_terminal_width_height(fd, &columns, &rows);
413}
414#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
415
Eric Andersen3f980402001-04-04 17:31:15 +0000416static void edit_file(Byte * fn)
417{
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000418 Byte c;
Eric Andersen1c0d3112001-04-16 15:46:44 +0000419 int cnt, size, ch;
Eric Andersen3f980402001-04-04 17:31:15 +0000420
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000421#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000422 int sig;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000423#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
424#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000425 static Byte *cur_line;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000426#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000427
428 rawmode();
429 rows = 24;
430 columns = 80;
Eric Andersen822c3832001-05-07 17:37:43 +0000431 ch= -1;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000432#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000433 window_size_get(0);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000434#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
Eric Andersen3f980402001-04-04 17:31:15 +0000435 new_screen(rows, columns); // get memory for virtual screen
436
437 cnt = file_size(fn); // file size
438 size = 2 * cnt; // 200% of file size
439 new_text(size); // get a text[] buffer
440 screenbegin = dot = end = text;
441 if (fn != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000442 ch= file_insert(fn, text, cnt);
443 }
444 if (ch < 1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000445 (void) char_insert(text, '\n'); // start empty buf with dummy line
446 }
Paul Fox8552aec2005-09-16 12:20:05 +0000447 file_modified = 0;
448 last_file_modified = -1;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000449#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000450 YDreg = 26; // default Yank/Delete reg
451 Ureg = 27; // hold orig line for "U" cmd
452 for (cnt = 0; cnt < 28; cnt++) {
453 mark[cnt] = 0;
454 } // init the marks
455 mark[26] = mark[27] = text; // init "previous context"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000456#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000457
Eric Andersen3f980402001-04-04 17:31:15 +0000458 last_forward_char = last_input_char = '\0';
459 crow = 0;
460 ccol = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000461
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000462#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000463 catch_sig(0);
464 core_sig(0);
Eric Andersen3f980402001-04-04 17:31:15 +0000465 signal(SIGWINCH, winch_sig);
466 signal(SIGTSTP, suspend_sig);
467 sig = setjmp(restart);
468 if (sig != 0) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000469 const char *msg = "";
470
Eric Andersen3f980402001-04-04 17:31:15 +0000471 if (sig == SIGWINCH)
472 msg = "(window resize)";
473 if (sig == SIGHUP)
474 msg = "(hangup)";
475 if (sig == SIGINT)
476 msg = "(interrupt)";
477 if (sig == SIGTERM)
478 msg = "(terminate)";
479 if (sig == SIGBUS)
480 msg = "(bus error)";
481 if (sig == SIGSEGV)
482 msg = "(I tried to touch invalid memory)";
483 if (sig == SIGALRM)
484 msg = "(alarm)";
485
486 psbs("-- caught signal %d %s--", sig, msg);
Eric Andersen1c0d3112001-04-16 15:46:44 +0000487 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000488 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000489#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +0000490
491 editing = 1;
492 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
493 cmdcnt = 0;
494 tabstop = 8;
495 offset = 0; // no horizontal offset
496 c = '\0';
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000497#ifdef CONFIG_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000498 free(last_modifying_cmd);
499 free(ioq_start);
Eric Andersen3f980402001-04-04 17:31:15 +0000500 ioq = ioq_start = last_modifying_cmd = 0;
501 adding2q = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000502#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen822c3832001-05-07 17:37:43 +0000503 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000504 show_status_line();
505
506 //------This is the main Vi cmd handling loop -----------------------
507 while (editing > 0) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000508#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000509 if (crashme > 0) {
510 if ((end - text) > 1) {
511 crash_dummy(); // generate a random command
512 } else {
513 crashme = 0;
514 dot =
515 string_insert(text, (Byte *) "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
516 refresh(FALSE);
517 }
518 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000519#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000520 last_input_char = c = get_one_char(); // get a cmd from user
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000521#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000522 // save a copy of the current line- for the 'U" command
523 if (begin_line(dot) != cur_line) {
524 cur_line = begin_line(dot);
525 text_yank(begin_line(dot), end_line(dot), Ureg);
526 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000527#endif /* CONFIG_FEATURE_VI_YANKMARK */
528#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000529 // These are commands that change text[].
530 // Remember the input for the "." command
531 if (!adding2q && ioq_start == 0
532 && strchr((char *) modifying_cmds, c) != NULL) {
533 start_new_cmd_q(c);
534 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000535#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000536 do_cmd(c); // execute the user command
537 //
538 // poll to see if there is input already waiting. if we are
539 // not able to display output fast enough to keep up, skip
540 // the display update until we catch up with input.
541 if (mysleep(0) == 0) {
542 // no input pending- so update output
543 refresh(FALSE);
544 show_status_line();
545 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000546#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000547 if (crashme > 0)
548 crash_test(); // test editor variables
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000549#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000550 }
551 //-------------------------------------------------------------------
552
Eric Andersen822c3832001-05-07 17:37:43 +0000553 place_cursor(rows, 0, FALSE); // go to bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +0000554 clear_to_eol(); // Erase to end of line
555 cookmode();
556}
557
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000558//----- The Colon commands -------------------------------------
559#ifdef CONFIG_FEATURE_VI_COLON
560static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present
561{
562 int st;
563 Byte *q;
564
565#ifdef CONFIG_FEATURE_VI_YANKMARK
566 Byte c;
567#endif /* CONFIG_FEATURE_VI_YANKMARK */
568#ifdef CONFIG_FEATURE_VI_SEARCH
569 Byte *pat, buf[BUFSIZ];
570#endif /* CONFIG_FEATURE_VI_SEARCH */
571
572 *addr = -1; // assume no addr
573 if (*p == '.') { // the current line
574 p++;
575 q = begin_line(dot);
576 *addr = count_lines(text, q);
577#ifdef CONFIG_FEATURE_VI_YANKMARK
578 } else if (*p == '\'') { // is this a mark addr
579 p++;
580 c = tolower(*p);
581 p++;
582 if (c >= 'a' && c <= 'z') {
583 // we have a mark
584 c = c - 'a';
585 q = mark[(int) c];
586 if (q != NULL) { // is mark valid
587 *addr = count_lines(text, q); // count lines
588 }
589 }
590#endif /* CONFIG_FEATURE_VI_YANKMARK */
591#ifdef CONFIG_FEATURE_VI_SEARCH
592 } else if (*p == '/') { // a search pattern
593 q = buf;
594 for (p++; *p; p++) {
595 if (*p == '/')
596 break;
597 *q++ = *p;
598 *q = '\0';
599 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000600 pat = (Byte *) bb_xstrdup((char *) buf); // save copy of pattern
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000601 if (*p == '/')
602 p++;
603 q = char_search(dot, pat, FORWARD, FULL);
604 if (q != NULL) {
605 *addr = count_lines(text, q);
606 }
607 free(pat);
608#endif /* CONFIG_FEATURE_VI_SEARCH */
609 } else if (*p == '$') { // the last line in file
610 p++;
611 q = begin_line(end - 1);
612 *addr = count_lines(text, q);
613 } else if (isdigit(*p)) { // specific line number
614 sscanf((char *) p, "%d%n", addr, &st);
615 p += st;
616 } else { // I don't reconise this
617 // unrecognised address- assume -1
618 *addr = -1;
619 }
620 return (p);
621}
622
623static Byte *get_address(Byte *p, int *b, int *e) // get two colon addrs, if present
624{
625 //----- get the address' i.e., 1,3 'a,'b -----
626 // get FIRST addr, if present
627 while (isblnk(*p))
628 p++; // skip over leading spaces
629 if (*p == '%') { // alias for 1,$
630 p++;
631 *b = 1;
632 *e = count_lines(text, end-1);
633 goto ga0;
634 }
635 p = get_one_address(p, b);
636 while (isblnk(*p))
637 p++;
Eric Andersenaff114c2004-04-14 17:51:38 +0000638 if (*p == ',') { // is there a address separator
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000639 p++;
640 while (isblnk(*p))
641 p++;
642 // get SECOND addr, if present
643 p = get_one_address(p, e);
644 }
645ga0:
646 while (isblnk(*p))
647 p++; // skip over trailing spaces
648 return (p);
649}
650
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000651#ifdef CONFIG_FEATURE_VI_SETOPTS
652static void setops(const Byte *args, const char *opname, int flg_no,
653 const char *short_opname, int opt)
654{
655 const char *a = (char *) args + flg_no;
656 int l = strlen(opname) - 1; /* opname have + ' ' */
657
658 if (strncasecmp(a, opname, l) == 0 ||
659 strncasecmp(a, short_opname, 2) == 0) {
660 if(flg_no)
661 vi_setops &= ~opt;
662 else
663 vi_setops |= opt;
664 }
665}
666#endif
667
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000668static void colon(Byte * buf)
669{
670 Byte c, *orig_buf, *buf1, *q, *r;
671 Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
672 int i, l, li, ch, st, b, e;
Eric Andersena9eb33d2004-08-19 19:15:06 +0000673 int useforce = FALSE, forced = FALSE;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000674 struct stat st_buf;
675
676 // :3154 // if (-e line 3154) goto it else stay put
677 // :4,33w! foo // write a portion of buffer to file "foo"
678 // :w // write all of buffer to current file
679 // :q // quit
680 // :q! // quit- dont care about modified file
681 // :'a,'z!sort -u // filter block through sort
682 // :'f // goto mark "f"
683 // :'fl // list literal the mark "f" line
684 // :.r bar // read file "bar" into buffer before dot
685 // :/123/,/abc/d // delete lines from "123" line to "abc" line
686 // :/xyz/ // goto the "xyz" line
687 // :s/find/replace/ // substitute pattern "find" with "replace"
688 // :!<cmd> // run <cmd> then return
689 //
Eric Andersen165e8cb2004-07-20 06:44:46 +0000690
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000691 if (strlen((char *) buf) <= 0)
692 goto vc1;
693 if (*buf == ':')
694 buf++; // move past the ':'
695
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000696 li = st = ch = i = 0;
697 b = e = -1;
698 q = text; // assume 1,$ for the range
699 r = end - 1;
700 li = count_lines(text, end - 1);
701 fn = cfn; // default to current file
702 memset(cmd, '\0', BUFSIZ); // clear cmd[]
703 memset(args, '\0', BUFSIZ); // clear args[]
704
705 // look for optional address(es) :. :1 :1,9 :'q,'a :%
706 buf = get_address(buf, &b, &e);
707
708 // remember orig command line
709 orig_buf = buf;
710
711 // get the COMMAND into cmd[]
712 buf1 = cmd;
713 while (*buf != '\0') {
714 if (isspace(*buf))
715 break;
716 *buf1++ = *buf++;
717 }
718 // get any ARGuments
719 while (isblnk(*buf))
720 buf++;
721 strcpy((char *) args, (char *) buf);
Eric Andersena68ea1c2006-01-30 22:48:39 +0000722 buf1 = (Byte*)last_char_is((char *)cmd, '!');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000723 if (buf1) {
724 useforce = TRUE;
725 *buf1 = '\0'; // get rid of !
726 }
727 if (b >= 0) {
728 // if there is only one addr, then the addr
729 // is the line number of the single line the
730 // user wants. So, reset the end
731 // pointer to point at end of the "b" line
732 q = find_line(b); // what line is #b
733 r = end_line(q);
734 li = 1;
735 }
736 if (e >= 0) {
737 // we were given two addrs. change the
738 // end pointer to the addr given by user.
739 r = find_line(e); // what line is #e
740 r = end_line(r);
741 li = e - b + 1;
742 }
743 // ------------ now look for the command ------------
744 i = strlen((char *) cmd);
745 if (i == 0) { // :123CR goto line #123
746 if (b >= 0) {
747 dot = find_line(b); // what line is #b
748 dot_skip_over_ws();
749 }
750 } else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd
751 // :!ls run the <cmd>
752 (void) alarm(0); // wait for input- no alarms
753 place_cursor(rows - 1, 0, FALSE); // go to Status line
754 clear_to_eol(); // clear the line
755 cookmode();
Eric Andersena68ea1c2006-01-30 22:48:39 +0000756 system((char*)(orig_buf+1)); // run the cmd
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000757 rawmode();
758 Hit_Return(); // let user see results
759 (void) alarm(3); // done waiting for input
760 } else if (strncmp((char *) cmd, "=", i) == 0) { // where is the address
761 if (b < 0) { // no addr given- use defaults
762 b = e = count_lines(text, dot);
763 }
764 psb("%d", b);
765 } else if (strncasecmp((char *) cmd, "delete", i) == 0) { // delete lines
766 if (b < 0) { // no addr given- use defaults
767 q = begin_line(dot); // assume .,. for the range
768 r = end_line(dot);
769 }
770 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
771 dot_skip_over_ws();
772 } else if (strncasecmp((char *) cmd, "edit", i) == 0) { // Edit a file
773 int sr;
774 sr= 0;
775 // don't edit, if the current file has been modified
776 if (file_modified && ! useforce) {
777 psbs("No write since last change (:edit! overrides)");
778 goto vc1;
779 }
Eric Andersena68ea1c2006-01-30 22:48:39 +0000780 if (strlen((char*)args) > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000781 // the user supplied a file name
782 fn= args;
Eric Andersena68ea1c2006-01-30 22:48:39 +0000783 } else if (cfn != 0 && strlen((char*)cfn) > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000784 // no user supplied name- use the current filename
785 fn= cfn;
786 goto vc5;
787 } else {
788 // no user file name, no current name- punt
789 psbs("No current filename");
790 goto vc1;
791 }
792
793 // see if file exists- if not, its just a new file request
794 if ((sr=stat((char*)fn, &st_buf)) < 0) {
795 // This is just a request for a new file creation.
796 // The file_insert below will fail but we get
797 // an empty buffer with a file name. Then the "write"
798 // command can do the create.
799 } else {
800 if ((st_buf.st_mode & (S_IFREG)) == 0) {
801 // This is not a regular file
802 psbs("\"%s\" is not a regular file", fn);
803 goto vc1;
804 }
805 if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
806 // dont have any read permissions
807 psbs("\"%s\" is not readable", fn);
808 goto vc1;
809 }
810 }
811
812 // There is a read-able regular file
813 // make this the current file
Manuel Novoa III cad53642003-03-19 09:13:01 +0000814 q = (Byte *) bb_xstrdup((char *) fn); // save the cfn
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000815 free(cfn); // free the old name
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000816 cfn = q; // remember new cfn
817
818 vc5:
819 // delete all the contents of text[]
820 new_text(2 * file_size(fn));
821 screenbegin = dot = end = text;
822
823 // insert new file
824 ch = file_insert(fn, text, file_size(fn));
825
826 if (ch < 1) {
827 // start empty buf with dummy line
828 (void) char_insert(text, '\n');
829 ch= 1;
830 }
Paul Fox8552aec2005-09-16 12:20:05 +0000831 file_modified = 0;
832 last_file_modified = -1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000833#ifdef CONFIG_FEATURE_VI_YANKMARK
834 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
835 free(reg[Ureg]); // free orig line reg- for 'U'
836 reg[Ureg]= 0;
837 }
838 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
839 free(reg[YDreg]); // free default yank/delete register
840 reg[YDreg]= 0;
841 }
842 for (li = 0; li < 28; li++) {
843 mark[li] = 0;
844 } // init the marks
845#endif /* CONFIG_FEATURE_VI_YANKMARK */
846 // how many lines in text[]?
847 li = count_lines(text, end - 1);
848 psb("\"%s\"%s"
849#ifdef CONFIG_FEATURE_VI_READONLY
850 "%s"
851#endif /* CONFIG_FEATURE_VI_READONLY */
852 " %dL, %dC", cfn,
853 (sr < 0 ? " [New file]" : ""),
854#ifdef CONFIG_FEATURE_VI_READONLY
855 ((vi_readonly || readonly) ? " [Read only]" : ""),
856#endif /* CONFIG_FEATURE_VI_READONLY */
857 li, ch);
858 } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this
859 if (b != -1 || e != -1) {
860 ni((Byte *) "No address allowed on this command");
861 goto vc1;
862 }
863 if (strlen((char *) args) > 0) {
864 // user wants a new filename
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000865 free(cfn);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000866 cfn = (Byte *) bb_xstrdup((char *) args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000867 } else {
868 // user wants file status info
Paul Fox8552aec2005-09-16 12:20:05 +0000869 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000870 }
871 } else if (strncasecmp((char *) cmd, "features", i) == 0) { // what features are available
872 // print out values of all features
873 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
874 clear_to_eol(); // clear the line
875 cookmode();
876 show_help();
877 rawmode();
878 Hit_Return();
879 } else if (strncasecmp((char *) cmd, "list", i) == 0) { // literal print line
880 if (b < 0) { // no addr given- use defaults
881 q = begin_line(dot); // assume .,. for the range
882 r = end_line(dot);
883 }
884 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
885 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000886 puts("\r");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000887 for (; q <= r; q++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000888 int c_is_no_print;
889
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000890 c = *q;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000891 c_is_no_print = c > 127 && !Isprint(c);
892 if (c_is_no_print) {
893 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000894 standout_start();
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000895 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000896 if (c == '\n') {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000897 write1("$\r");
898 } else if (c < ' ' || c == 127) {
899 putchar('^');
900 if(c == 127)
901 c = '?';
902 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000903 c += '@';
904 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000905 putchar(c);
906 if (c_is_no_print)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000907 standout_end();
908 }
909#ifdef CONFIG_FEATURE_VI_SET
910 vc2:
911#endif /* CONFIG_FEATURE_VI_SET */
912 Hit_Return();
913 } else if ((strncasecmp((char *) cmd, "quit", i) == 0) || // Quit
914 (strncasecmp((char *) cmd, "next", i) == 0)) { // edit next file
915 if (useforce) {
916 // force end of argv list
917 if (*cmd == 'q') {
918 optind = save_argc;
919 }
920 editing = 0;
921 goto vc1;
922 }
923 // don't exit if the file been modified
924 if (file_modified) {
925 psbs("No write since last change (:%s! overrides)",
926 (*cmd == 'q' ? "quit" : "next"));
927 goto vc1;
928 }
929 // are there other file to edit
930 if (*cmd == 'q' && optind < save_argc - 1) {
931 psbs("%d more file to edit", (save_argc - optind - 1));
932 goto vc1;
933 }
934 if (*cmd == 'n' && optind >= save_argc - 1) {
935 psbs("No more files to edit");
936 goto vc1;
937 }
938 editing = 0;
939 } else if (strncasecmp((char *) cmd, "read", i) == 0) { // read file into text[]
940 fn = args;
941 if (strlen((char *) fn) <= 0) {
942 psbs("No filename given");
943 goto vc1;
944 }
945 if (b < 0) { // no addr given- use defaults
946 q = begin_line(dot); // assume "dot"
947 }
948 // read after current line- unless user said ":0r foo"
949 if (b != 0)
950 q = next_line(q);
951#ifdef CONFIG_FEATURE_VI_READONLY
952 l= readonly; // remember current files' status
953#endif
954 ch = file_insert(fn, q, file_size(fn));
955#ifdef CONFIG_FEATURE_VI_READONLY
956 readonly= l;
957#endif
958 if (ch < 0)
959 goto vc1; // nothing was inserted
960 // how many lines in text[]?
961 li = count_lines(q, q + ch - 1);
962 psb("\"%s\""
963#ifdef CONFIG_FEATURE_VI_READONLY
964 "%s"
965#endif /* CONFIG_FEATURE_VI_READONLY */
966 " %dL, %dC", fn,
967#ifdef CONFIG_FEATURE_VI_READONLY
968 ((vi_readonly || readonly) ? " [Read only]" : ""),
969#endif /* CONFIG_FEATURE_VI_READONLY */
970 li, ch);
971 if (ch > 0) {
972 // if the insert is before "dot" then we need to update
973 if (q <= dot)
974 dot += ch;
Paul Fox8552aec2005-09-16 12:20:05 +0000975 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000976 }
977 } else if (strncasecmp((char *) cmd, "rewind", i) == 0) { // rewind cmd line args
978 if (file_modified && ! useforce) {
979 psbs("No write since last change (:rewind! overrides)");
980 } else {
981 // reset the filenames to edit
982 optind = fn_start - 1;
983 editing = 0;
984 }
985#ifdef CONFIG_FEATURE_VI_SET
986 } else if (strncasecmp((char *) cmd, "set", i) == 0) { // set or clear features
987 i = 0; // offset into args
988 if (strlen((char *) args) == 0) {
989 // print out values of all options
990 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
991 clear_to_eol(); // clear the line
992 printf("----------------------------------------\r\n");
993#ifdef CONFIG_FEATURE_VI_SETOPTS
994 if (!autoindent)
995 printf("no");
996 printf("autoindent ");
997 if (!err_method)
998 printf("no");
999 printf("flash ");
1000 if (!ignorecase)
1001 printf("no");
1002 printf("ignorecase ");
1003 if (!showmatch)
1004 printf("no");
1005 printf("showmatch ");
1006 printf("tabstop=%d ", tabstop);
1007#endif /* CONFIG_FEATURE_VI_SETOPTS */
1008 printf("\r\n");
1009 goto vc2;
1010 }
1011 if (strncasecmp((char *) args, "no", 2) == 0)
1012 i = 2; // ":set noautoindent"
1013#ifdef CONFIG_FEATURE_VI_SETOPTS
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001014 setops(args, "autoindent ", i, "ai", VI_AUTOINDENT);
1015 setops(args, "flash ", i, "fl", VI_ERR_METHOD);
1016 setops(args, "ignorecase ", i, "ic", VI_IGNORECASE);
1017 setops(args, "showmatch ", i, "ic", VI_SHOWMATCH);
1018 if (strncasecmp((char *) args + i, "tabstop=%d ", 7) == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001019 sscanf(strchr((char *) args + i, '='), "=%d", &ch);
1020 if (ch > 0 && ch < columns - 1)
1021 tabstop = ch;
1022 }
1023#endif /* CONFIG_FEATURE_VI_SETOPTS */
1024#endif /* CONFIG_FEATURE_VI_SET */
1025#ifdef CONFIG_FEATURE_VI_SEARCH
1026 } else if (strncasecmp((char *) cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
1027 Byte *ls, *F, *R;
1028 int gflag;
1029
1030 // F points to the "find" pattern
1031 // R points to the "replace" pattern
1032 // replace the cmd line delimiters "/" with NULLs
1033 gflag = 0; // global replace flag
1034 c = orig_buf[1]; // what is the delimiter
1035 F = orig_buf + 2; // start of "find"
1036 R = (Byte *) strchr((char *) F, c); // middle delimiter
1037 if (!R) goto colon_s_fail;
1038 *R++ = '\0'; // terminate "find"
1039 buf1 = (Byte *) strchr((char *) R, c);
1040 if (!buf1) goto colon_s_fail;
1041 *buf1++ = '\0'; // terminate "replace"
1042 if (*buf1 == 'g') { // :s/foo/bar/g
1043 buf1++;
1044 gflag++; // turn on gflag
1045 }
1046 q = begin_line(q);
1047 if (b < 0) { // maybe :s/foo/bar/
1048 q = begin_line(dot); // start with cur line
1049 b = count_lines(text, q); // cur line number
1050 }
1051 if (e < 0)
1052 e = b; // maybe :.s/foo/bar/
1053 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
1054 ls = q; // orig line start
1055 vc4:
1056 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
1057 if (buf1 != NULL) {
1058 // we found the "find" pattern- delete it
1059 (void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
1060 // inset the "replace" patern
1061 (void) string_insert(buf1, R); // insert the string
1062 // check for "global" :s/foo/bar/g
1063 if (gflag == 1) {
1064 if ((buf1 + strlen((char *) R)) < end_line(ls)) {
1065 q = buf1 + strlen((char *) R);
1066 goto vc4; // don't let q move past cur line
1067 }
1068 }
1069 }
1070 q = next_line(ls);
1071 }
1072#endif /* CONFIG_FEATURE_VI_SEARCH */
1073 } else if (strncasecmp((char *) cmd, "version", i) == 0) { // show software version
1074 psb("%s", vi_Version);
Paul Fox9360f422006-03-27 21:51:16 +00001075 } else if (strncasecmp((char *) cmd, "write", i) == 0 // write text to file
1076 || strncasecmp((char *) cmd, "wq", i) == 0
1077 || strncasecmp((char *) cmd, "wn", i) == 0
1078 || strncasecmp((char *) cmd, "x", i) == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001079 // is there a file name to write to?
1080 if (strlen((char *) args) > 0) {
1081 fn = args;
1082 }
1083#ifdef CONFIG_FEATURE_VI_READONLY
1084 if ((vi_readonly || readonly) && ! useforce) {
1085 psbs("\"%s\" File is read only", fn);
1086 goto vc3;
1087 }
1088#endif /* CONFIG_FEATURE_VI_READONLY */
1089 // how many lines in text[]?
1090 li = count_lines(q, r);
1091 ch = r - q + 1;
1092 // see if file exists- if not, its just a new file request
1093 if (useforce) {
1094 // if "fn" is not write-able, chmod u+w
1095 // sprintf(syscmd, "chmod u+w %s", fn);
1096 // system(syscmd);
1097 forced = TRUE;
1098 }
1099 l = file_write(fn, q, r);
1100 if (useforce && forced) {
1101 // chmod u-w
1102 // sprintf(syscmd, "chmod u-w %s", fn);
1103 // system(syscmd);
1104 forced = FALSE;
1105 }
Paul Fox61e45db2005-10-09 14:43:22 +00001106 if (l < 0) {
1107 if (l == -1)
1108 psbs("Write error: %s", strerror(errno));
1109 } else {
1110 psb("\"%s\" %dL, %dC", fn, li, l);
1111 if (q == text && r == end - 1 && l == ch) {
1112 file_modified = 0;
1113 last_file_modified = -1;
1114 }
Paul Fox9360f422006-03-27 21:51:16 +00001115 if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' ||
1116 cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N')
1117 && l == ch) {
Paul Fox61e45db2005-10-09 14:43:22 +00001118 editing = 0;
1119 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001120 }
1121#ifdef CONFIG_FEATURE_VI_READONLY
1122 vc3:;
1123#endif /* CONFIG_FEATURE_VI_READONLY */
1124#ifdef CONFIG_FEATURE_VI_YANKMARK
1125 } else if (strncasecmp((char *) cmd, "yank", i) == 0) { // yank lines
1126 if (b < 0) { // no addr given- use defaults
1127 q = begin_line(dot); // assume .,. for the range
1128 r = end_line(dot);
1129 }
1130 text_yank(q, r, YDreg);
1131 li = count_lines(q, r);
1132 psb("Yank %d lines (%d chars) into [%c]",
1133 li, strlen((char *) reg[YDreg]), what_reg());
1134#endif /* CONFIG_FEATURE_VI_YANKMARK */
1135 } else {
1136 // cmd unknown
1137 ni((Byte *) cmd);
1138 }
1139 vc1:
1140 dot = bound_dot(dot); // make sure "dot" is valid
1141 return;
1142#ifdef CONFIG_FEATURE_VI_SEARCH
1143colon_s_fail:
1144 psb(":s expression missing delimiters");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001145#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001146}
Paul Fox61e45db2005-10-09 14:43:22 +00001147
Paul Fox90372ed2005-10-09 14:26:26 +00001148#endif /* CONFIG_FEATURE_VI_COLON */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001149
1150static void Hit_Return(void)
1151{
1152 char c;
1153
1154 standout_start(); // start reverse video
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001155 write1("[Hit return to continue]");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001156 standout_end(); // end reverse video
1157 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
1158 ;
1159 redraw(TRUE); // force redraw all
1160}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001161
1162//----- Synchronize the cursor to Dot --------------------------
1163static void sync_cursor(Byte * d, int *row, int *col)
1164{
1165 Byte *beg_cur, *end_cur; // begin and end of "d" line
1166 Byte *beg_scr, *end_scr; // begin and end of screen
1167 Byte *tp;
1168 int cnt, ro, co;
1169
1170 beg_cur = begin_line(d); // first char of cur line
1171 end_cur = end_line(d); // last char of cur line
1172
1173 beg_scr = end_scr = screenbegin; // first char of screen
1174 end_scr = end_screen(); // last char of screen
1175
1176 if (beg_cur < screenbegin) {
1177 // "d" is before top line on screen
1178 // how many lines do we have to move
1179 cnt = count_lines(beg_cur, screenbegin);
1180 sc1:
1181 screenbegin = beg_cur;
1182 if (cnt > (rows - 1) / 2) {
1183 // we moved too many lines. put "dot" in middle of screen
1184 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1185 screenbegin = prev_line(screenbegin);
1186 }
1187 }
1188 } else if (beg_cur > end_scr) {
1189 // "d" is after bottom line on screen
1190 // how many lines do we have to move
1191 cnt = count_lines(end_scr, beg_cur);
1192 if (cnt > (rows - 1) / 2)
1193 goto sc1; // too many lines
1194 for (ro = 0; ro < cnt - 1; ro++) {
1195 // move screen begin the same amount
1196 screenbegin = next_line(screenbegin);
1197 // now, move the end of screen
1198 end_scr = next_line(end_scr);
1199 end_scr = end_line(end_scr);
1200 }
1201 }
1202 // "d" is on screen- find out which row
1203 tp = screenbegin;
1204 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1205 if (tp == beg_cur)
1206 break;
1207 tp = next_line(tp);
1208 }
1209
1210 // find out what col "d" is on
1211 co = 0;
1212 do { // drive "co" to correct column
1213 if (*tp == '\n' || *tp == '\0')
1214 break;
1215 if (*tp == '\t') {
1216 // 7 - (co % 8 )
1217 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001218 } else if (*tp < ' ' || *tp == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001219 co++; // display as ^X, use 2 columns
1220 }
1221 } while (tp++ < d && ++co);
1222
1223 // "co" is the column where "dot" is.
1224 // The screen has "columns" columns.
1225 // The currently displayed columns are 0+offset -- columns+ofset
1226 // |-------------------------------------------------------------|
1227 // ^ ^ ^
1228 // offset | |------- columns ----------------|
1229 //
1230 // If "co" is already in this range then we do not have to adjust offset
1231 // but, we do have to subtract the "offset" bias from "co".
1232 // If "co" is outside this range then we have to change "offset".
1233 // If the first char of a line is a tab the cursor will try to stay
1234 // in column 7, but we have to set offset to 0.
1235
1236 if (co < 0 + offset) {
1237 offset = co;
1238 }
1239 if (co >= columns + offset) {
1240 offset = co - columns + 1;
1241 }
1242 // if the first char of the line is a tab, and "dot" is sitting on it
1243 // force offset to 0.
1244 if (d == beg_cur && *d == '\t') {
1245 offset = 0;
1246 }
1247 co -= offset;
1248
1249 *row = ro;
1250 *col = co;
1251}
1252
1253//----- Text Movement Routines ---------------------------------
1254static Byte *begin_line(Byte * p) // return pointer to first char cur line
1255{
1256 while (p > text && p[-1] != '\n')
1257 p--; // go to cur line B-o-l
1258 return (p);
1259}
1260
1261static Byte *end_line(Byte * p) // return pointer to NL of cur line line
1262{
1263 while (p < end - 1 && *p != '\n')
1264 p++; // go to cur line E-o-l
1265 return (p);
1266}
1267
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001268static inline Byte *dollar_line(Byte * p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001269{
1270 while (p < end - 1 && *p != '\n')
1271 p++; // go to cur line E-o-l
1272 // Try to stay off of the Newline
1273 if (*p == '\n' && (p - begin_line(p)) > 0)
1274 p--;
1275 return (p);
1276}
1277
1278static Byte *prev_line(Byte * p) // return pointer first char prev line
1279{
1280 p = begin_line(p); // goto begining of cur line
1281 if (p[-1] == '\n' && p > text)
1282 p--; // step to prev line
1283 p = begin_line(p); // goto begining of prev line
1284 return (p);
1285}
1286
1287static Byte *next_line(Byte * p) // return pointer first char next line
1288{
1289 p = end_line(p);
1290 if (*p == '\n' && p < end - 1)
1291 p++; // step to next line
1292 return (p);
1293}
1294
1295//----- Text Information Routines ------------------------------
1296static Byte *end_screen(void)
1297{
1298 Byte *q;
1299 int cnt;
1300
1301 // find new bottom line
1302 q = screenbegin;
1303 for (cnt = 0; cnt < rows - 2; cnt++)
1304 q = next_line(q);
1305 q = end_line(q);
1306 return (q);
1307}
1308
1309static int count_lines(Byte * start, Byte * stop) // count line from start to stop
1310{
1311 Byte *q;
1312 int cnt;
1313
1314 if (stop < start) { // start and stop are backwards- reverse them
1315 q = start;
1316 start = stop;
1317 stop = q;
1318 }
1319 cnt = 0;
1320 stop = end_line(stop); // get to end of this line
1321 for (q = start; q <= stop && q <= end - 1; q++) {
1322 if (*q == '\n')
1323 cnt++;
1324 }
1325 return (cnt);
1326}
1327
1328static Byte *find_line(int li) // find begining of line #li
1329{
1330 Byte *q;
1331
1332 for (q = text; li > 1; li--) {
1333 q = next_line(q);
1334 }
1335 return (q);
1336}
1337
1338//----- Dot Movement Routines ----------------------------------
1339static void dot_left(void)
1340{
1341 if (dot > text && dot[-1] != '\n')
1342 dot--;
1343}
1344
1345static void dot_right(void)
1346{
1347 if (dot < end - 1 && *dot != '\n')
1348 dot++;
1349}
1350
1351static void dot_begin(void)
1352{
1353 dot = begin_line(dot); // return pointer to first char cur line
1354}
1355
1356static void dot_end(void)
1357{
1358 dot = end_line(dot); // return pointer to last char cur line
1359}
1360
1361static Byte *move_to_col(Byte * p, int l)
1362{
1363 int co;
1364
1365 p = begin_line(p);
1366 co = 0;
1367 do {
1368 if (*p == '\n' || *p == '\0')
1369 break;
1370 if (*p == '\t') {
1371 // 7 - (co % 8 )
1372 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001373 } else if (*p < ' ' || *p == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001374 co++; // display as ^X, use 2 columns
1375 }
1376 } while (++co <= l && p++ < end);
1377 return (p);
1378}
1379
1380static void dot_next(void)
1381{
1382 dot = next_line(dot);
1383}
1384
1385static void dot_prev(void)
1386{
1387 dot = prev_line(dot);
1388}
1389
1390static void dot_scroll(int cnt, int dir)
1391{
1392 Byte *q;
1393
1394 for (; cnt > 0; cnt--) {
1395 if (dir < 0) {
1396 // scroll Backwards
1397 // ctrl-Y scroll up one line
1398 screenbegin = prev_line(screenbegin);
1399 } else {
1400 // scroll Forwards
1401 // ctrl-E scroll down one line
1402 screenbegin = next_line(screenbegin);
1403 }
1404 }
1405 // make sure "dot" stays on the screen so we dont scroll off
1406 if (dot < screenbegin)
1407 dot = screenbegin;
1408 q = end_screen(); // find new bottom line
1409 if (dot > q)
1410 dot = begin_line(q); // is dot is below bottom line?
1411 dot_skip_over_ws();
1412}
1413
1414static void dot_skip_over_ws(void)
1415{
1416 // skip WS
1417 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1418 dot++;
1419}
1420
1421static void dot_delete(void) // delete the char at 'dot'
1422{
1423 (void) text_hole_delete(dot, dot);
1424}
1425
1426static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end"
1427{
1428 if (p >= end && end > text) {
1429 p = end - 1;
1430 indicate_error('1');
1431 }
1432 if (p < text) {
1433 p = text;
1434 indicate_error('2');
1435 }
1436 return (p);
1437}
1438
1439//----- Helper Utility Routines --------------------------------
1440
1441//----------------------------------------------------------------
1442//----- Char Routines --------------------------------------------
1443/* Chars that are part of a word-
1444 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1445 * Chars that are Not part of a word (stoppers)
1446 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1447 * Chars that are WhiteSpace
1448 * TAB NEWLINE VT FF RETURN SPACE
1449 * DO NOT COUNT NEWLINE AS WHITESPACE
1450 */
1451
1452static Byte *new_screen(int ro, int co)
1453{
1454 int li;
1455
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001456 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001457 screensize = ro * co + 8;
1458 screen = (Byte *) xmalloc(screensize);
1459 // initialize the new screen. assume this will be a empty file.
1460 screen_erase();
Eric Andersenaff114c2004-04-14 17:51:38 +00001461 // non-existent text[] lines start with a tilde (~).
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001462 for (li = 1; li < ro - 1; li++) {
1463 screen[(li * co) + 0] = '~';
1464 }
1465 return (screen);
1466}
1467
1468static Byte *new_text(int size)
1469{
1470 if (size < 10240)
1471 size = 10240; // have a minimum size for new files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001472 free(text);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001473 text = (Byte *) xmalloc(size + 8);
1474 memset(text, '\0', size); // clear new text[]
1475 //text += 4; // leave some room for "oops"
1476 textend = text + size - 1;
1477 //textend -= 4; // leave some root for "oops"
1478 return (text);
1479}
1480
1481#ifdef CONFIG_FEATURE_VI_SEARCH
1482static int mycmp(Byte * s1, Byte * s2, int len)
1483{
1484 int i;
1485
1486 i = strncmp((char *) s1, (char *) s2, len);
1487#ifdef CONFIG_FEATURE_VI_SETOPTS
1488 if (ignorecase) {
1489 i = strncasecmp((char *) s1, (char *) s2, len);
1490 }
1491#endif /* CONFIG_FEATURE_VI_SETOPTS */
1492 return (i);
1493}
1494
1495static Byte *char_search(Byte * p, Byte * pat, int dir, int range) // search for pattern starting at p
1496{
1497#ifndef REGEX_SEARCH
1498 Byte *start, *stop;
1499 int len;
1500
1501 len = strlen((char *) pat);
1502 if (dir == FORWARD) {
1503 stop = end - 1; // assume range is p - end-1
1504 if (range == LIMITED)
1505 stop = next_line(p); // range is to next line
1506 for (start = p; start < stop; start++) {
1507 if (mycmp(start, pat, len) == 0) {
1508 return (start);
1509 }
1510 }
1511 } else if (dir == BACK) {
1512 stop = text; // assume range is text - p
1513 if (range == LIMITED)
1514 stop = prev_line(p); // range is to prev line
1515 for (start = p - len; start >= stop; start--) {
1516 if (mycmp(start, pat, len) == 0) {
1517 return (start);
1518 }
1519 }
1520 }
1521 // pattern not found
1522 return (NULL);
1523#else /*REGEX_SEARCH */
1524 char *q;
1525 struct re_pattern_buffer preg;
1526 int i;
1527 int size, range;
1528
1529 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1530 preg.translate = 0;
1531 preg.fastmap = 0;
1532 preg.buffer = 0;
1533 preg.allocated = 0;
1534
1535 // assume a LIMITED forward search
1536 q = next_line(p);
1537 q = end_line(q);
1538 q = end - 1;
1539 if (dir == BACK) {
1540 q = prev_line(p);
1541 q = text;
1542 }
1543 // count the number of chars to search over, forward or backward
1544 size = q - p;
1545 if (size < 0)
1546 size = p - q;
1547 // RANGE could be negative if we are searching backwards
1548 range = q - p;
1549
1550 q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
1551 if (q != 0) {
1552 // The pattern was not compiled
1553 psbs("bad search pattern: \"%s\": %s", pat, q);
1554 i = 0; // return p if pattern not compiled
1555 goto cs1;
1556 }
1557
1558 q = p;
1559 if (range < 0) {
1560 q = p - size;
1561 if (q < text)
1562 q = text;
1563 }
1564 // search for the compiled pattern, preg, in p[]
1565 // range < 0- search backward
1566 // range > 0- search forward
1567 // 0 < start < size
1568 // re_search() < 0 not found or error
1569 // re_search() > 0 index of found pattern
1570 // struct pattern char int int int struct reg
1571 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1572 i = re_search(&preg, q, size, 0, range, 0);
1573 if (i == -1) {
1574 p = 0;
1575 i = 0; // return NULL if pattern not found
1576 }
1577 cs1:
1578 if (dir == FORWARD) {
1579 p = p + i;
1580 } else {
1581 p = p - i;
1582 }
1583 return (p);
1584#endif /*REGEX_SEARCH */
1585}
1586#endif /* CONFIG_FEATURE_VI_SEARCH */
1587
1588static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
1589{
1590 if (c == 22) { // Is this an ctrl-V?
1591 p = stupid_insert(p, '^'); // use ^ to indicate literal next
1592 p--; // backup onto ^
1593 refresh(FALSE); // show the ^
1594 c = get_one_char();
1595 *p = c;
1596 p++;
Paul Fox8552aec2005-09-16 12:20:05 +00001597 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001598 } else if (c == 27) { // Is this an ESC?
1599 cmd_mode = 0;
1600 cmdcnt = 0;
1601 end_cmd_q(); // stop adding to q
Paul Fox8552aec2005-09-16 12:20:05 +00001602 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001603 if ((p[-1] != '\n') && (dot>text)) {
1604 p--;
1605 }
Paul Foxd13b90b2005-07-18 22:17:25 +00001606 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001607 // 123456789
1608 if ((p[-1] != '\n') && (dot>text)) {
1609 p--;
1610 p = text_hole_delete(p, p); // shrink buffer 1 char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001611 }
1612 } else {
1613 // insert a char into text[]
1614 Byte *sp; // "save p"
1615
1616 if (c == 13)
1617 c = '\n'; // translate \r to \n
1618 sp = p; // remember addr of insert
1619 p = stupid_insert(p, c); // insert the char
1620#ifdef CONFIG_FEATURE_VI_SETOPTS
1621 if (showmatch && strchr(")]}", *sp) != NULL) {
1622 showmatching(sp);
1623 }
1624 if (autoindent && c == '\n') { // auto indent the new line
1625 Byte *q;
1626
1627 q = prev_line(p); // use prev line as templet
1628 for (; isblnk(*q); q++) {
1629 p = stupid_insert(p, *q); // insert the char
1630 }
1631 }
1632#endif /* CONFIG_FEATURE_VI_SETOPTS */
1633 }
1634 return (p);
1635}
1636
1637static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
1638{
1639 p = text_hole_make(p, 1);
1640 if (p != 0) {
1641 *p = c;
Paul Fox8552aec2005-09-16 12:20:05 +00001642 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001643 p++;
1644 }
1645 return (p);
1646}
1647
1648static Byte find_range(Byte ** start, Byte ** stop, Byte c)
1649{
1650 Byte *save_dot, *p, *q;
1651 int cnt;
1652
1653 save_dot = dot;
1654 p = q = dot;
1655
1656 if (strchr("cdy><", c)) {
1657 // these cmds operate on whole lines
1658 p = q = begin_line(p);
1659 for (cnt = 1; cnt < cmdcnt; cnt++) {
1660 q = next_line(q);
1661 }
1662 q = end_line(q);
1663 } else if (strchr("^%$0bBeEft", c)) {
1664 // These cmds operate on char positions
1665 do_cmd(c); // execute movement cmd
1666 q = dot;
1667 } else if (strchr("wW", c)) {
1668 do_cmd(c); // execute movement cmd
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001669 // if we are at the next word's first char
1670 // step back one char
1671 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00001672 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001673 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1674 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1675 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001676 if (dot > text && *dot == '\n')
1677 dot--; // stay off NL
1678 q = dot;
1679 } else if (strchr("H-k{", c)) {
1680 // these operate on multi-lines backwards
1681 q = end_line(dot); // find NL
1682 do_cmd(c); // execute movement cmd
1683 dot_begin();
1684 p = dot;
1685 } else if (strchr("L+j}\r\n", c)) {
1686 // these operate on multi-lines forwards
1687 p = begin_line(dot);
1688 do_cmd(c); // execute movement cmd
1689 dot_end(); // find NL
1690 q = dot;
1691 } else {
1692 c = 27; // error- return an ESC char
1693 //break;
1694 }
1695 *start = p;
1696 *stop = q;
1697 if (q < p) {
1698 *start = q;
1699 *stop = p;
1700 }
1701 dot = save_dot;
1702 return (c);
1703}
1704
1705static int st_test(Byte * p, int type, int dir, Byte * tested)
1706{
1707 Byte c, c0, ci;
1708 int test, inc;
1709
1710 inc = dir;
1711 c = c0 = p[0];
1712 ci = p[inc];
1713 test = 0;
1714
1715 if (type == S_BEFORE_WS) {
1716 c = ci;
1717 test = ((!isspace(c)) || c == '\n');
1718 }
1719 if (type == S_TO_WS) {
1720 c = c0;
1721 test = ((!isspace(c)) || c == '\n');
1722 }
1723 if (type == S_OVER_WS) {
1724 c = c0;
1725 test = ((isspace(c)));
1726 }
1727 if (type == S_END_PUNCT) {
1728 c = ci;
1729 test = ((ispunct(c)));
1730 }
1731 if (type == S_END_ALNUM) {
1732 c = ci;
1733 test = ((isalnum(c)) || c == '_');
1734 }
1735 *tested = c;
1736 return (test);
1737}
1738
1739static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
1740{
1741 Byte c;
1742
1743 while (st_test(p, type, dir, &c)) {
1744 // make sure we limit search to correct number of lines
1745 if (c == '\n' && --linecnt < 1)
1746 break;
1747 if (dir >= 0 && p >= end - 1)
1748 break;
1749 if (dir < 0 && p <= text)
1750 break;
1751 p += dir; // move to next char
1752 }
1753 return (p);
1754}
1755
1756// find matching char of pair () [] {}
1757static Byte *find_pair(Byte * p, Byte c)
1758{
1759 Byte match, *q;
1760 int dir, level;
1761
1762 match = ')';
1763 level = 1;
1764 dir = 1; // assume forward
1765 switch (c) {
1766 case '(':
1767 match = ')';
1768 break;
1769 case '[':
1770 match = ']';
1771 break;
1772 case '{':
1773 match = '}';
1774 break;
1775 case ')':
1776 match = '(';
1777 dir = -1;
1778 break;
1779 case ']':
1780 match = '[';
1781 dir = -1;
1782 break;
1783 case '}':
1784 match = '{';
1785 dir = -1;
1786 break;
1787 }
1788 for (q = p + dir; text <= q && q < end; q += dir) {
1789 // look for match, count levels of pairs (( ))
1790 if (*q == c)
1791 level++; // increase pair levels
1792 if (*q == match)
1793 level--; // reduce pair level
1794 if (level == 0)
1795 break; // found matching pair
1796 }
1797 if (level != 0)
1798 q = NULL; // indicate no match
1799 return (q);
1800}
1801
1802#ifdef CONFIG_FEATURE_VI_SETOPTS
1803// show the matching char of a pair, () [] {}
1804static void showmatching(Byte * p)
1805{
1806 Byte *q, *save_dot;
1807
1808 // we found half of a pair
1809 q = find_pair(p, *p); // get loc of matching char
1810 if (q == NULL) {
1811 indicate_error('3'); // no matching char
1812 } else {
1813 // "q" now points to matching pair
1814 save_dot = dot; // remember where we are
1815 dot = q; // go to new loc
1816 refresh(FALSE); // let the user see it
1817 (void) mysleep(40); // give user some time
1818 dot = save_dot; // go back to old loc
1819 refresh(FALSE);
1820 }
1821}
1822#endif /* CONFIG_FEATURE_VI_SETOPTS */
1823
1824// open a hole in text[]
1825static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
1826{
1827 Byte *src, *dest;
1828 int cnt;
1829
1830 if (size <= 0)
1831 goto thm0;
1832 src = p;
1833 dest = p + size;
1834 cnt = end - src; // the rest of buffer
1835 if (memmove(dest, src, cnt) != dest) {
1836 psbs("can't create room for new characters");
1837 }
1838 memset(p, ' ', size); // clear new hole
1839 end = end + size; // adjust the new END
Paul Fox8552aec2005-09-16 12:20:05 +00001840 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001841 thm0:
1842 return (p);
1843}
1844
1845// close a hole in text[]
1846static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
1847{
1848 Byte *src, *dest;
1849 int cnt, hole_size;
1850
1851 // move forwards, from beginning
1852 // assume p <= q
1853 src = q + 1;
1854 dest = p;
1855 if (q < p) { // they are backward- swap them
1856 src = p + 1;
1857 dest = q;
1858 }
1859 hole_size = q - p + 1;
1860 cnt = end - src;
1861 if (src < text || src > end)
1862 goto thd0;
1863 if (dest < text || dest >= end)
1864 goto thd0;
1865 if (src >= end)
1866 goto thd_atend; // just delete the end of the buffer
1867 if (memmove(dest, src, cnt) != dest) {
1868 psbs("can't delete the character");
1869 }
1870 thd_atend:
1871 end = end - hole_size; // adjust the new END
1872 if (dest >= end)
1873 dest = end - 1; // make sure dest in below end-1
1874 if (end <= text)
1875 dest = end = text; // keep pointers valid
Paul Fox8552aec2005-09-16 12:20:05 +00001876 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001877 thd0:
1878 return (dest);
1879}
1880
1881// copy text into register, then delete text.
1882// if dist <= 0, do not include, or go past, a NewLine
1883//
1884static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
1885{
1886 Byte *p;
1887
1888 // make sure start <= stop
1889 if (start > stop) {
1890 // they are backwards, reverse them
1891 p = start;
1892 start = stop;
1893 stop = p;
1894 }
1895 if (dist <= 0) {
1896 // we can not cross NL boundaries
1897 p = start;
1898 if (*p == '\n')
1899 return (p);
1900 // dont go past a NewLine
1901 for (; p + 1 <= stop; p++) {
1902 if (p[1] == '\n') {
1903 stop = p; // "stop" just before NewLine
1904 break;
1905 }
1906 }
1907 }
1908 p = start;
1909#ifdef CONFIG_FEATURE_VI_YANKMARK
1910 text_yank(start, stop, YDreg);
1911#endif /* CONFIG_FEATURE_VI_YANKMARK */
1912 if (yf == YANKDEL) {
1913 p = text_hole_delete(start, stop);
1914 } // delete lines
1915 return (p);
1916}
1917
1918static void show_help(void)
1919{
1920 puts("These features are available:"
1921#ifdef CONFIG_FEATURE_VI_SEARCH
1922 "\n\tPattern searches with / and ?"
1923#endif /* CONFIG_FEATURE_VI_SEARCH */
1924#ifdef CONFIG_FEATURE_VI_DOT_CMD
1925 "\n\tLast command repeat with \'.\'"
1926#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1927#ifdef CONFIG_FEATURE_VI_YANKMARK
1928 "\n\tLine marking with 'x"
1929 "\n\tNamed buffers with \"x"
1930#endif /* CONFIG_FEATURE_VI_YANKMARK */
1931#ifdef CONFIG_FEATURE_VI_READONLY
1932 "\n\tReadonly if vi is called as \"view\""
1933 "\n\tReadonly with -R command line arg"
1934#endif /* CONFIG_FEATURE_VI_READONLY */
1935#ifdef CONFIG_FEATURE_VI_SET
1936 "\n\tSome colon mode commands with \':\'"
1937#endif /* CONFIG_FEATURE_VI_SET */
1938#ifdef CONFIG_FEATURE_VI_SETOPTS
1939 "\n\tSettable options with \":set\""
1940#endif /* CONFIG_FEATURE_VI_SETOPTS */
1941#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
1942 "\n\tSignal catching- ^C"
1943 "\n\tJob suspend and resume with ^Z"
1944#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
1945#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
1946 "\n\tAdapt to window re-sizes"
1947#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
1948 );
1949}
1950
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001951static inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001952{
1953 Byte c, b[2];
1954
1955 b[1] = '\0';
1956 strcpy((char *) buf, ""); // init buf
1957 if (strlen((char *) s) <= 0)
1958 s = (Byte *) "(NULL)";
1959 for (; *s > '\0'; s++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001960 int c_is_no_print;
1961
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001962 c = *s;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001963 c_is_no_print = c > 127 && !Isprint(c);
1964 if (c_is_no_print) {
1965 strcat((char *) buf, SOn);
1966 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001967 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001968 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001969 strcat((char *) buf, "^");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001970 if(c == 127)
1971 c = '?';
1972 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001973 c += '@';
1974 }
1975 b[0] = c;
1976 strcat((char *) buf, (char *) b);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001977 if (c_is_no_print)
1978 strcat((char *) buf, SOs);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001979 if (*s == '\n') {
1980 strcat((char *) buf, "$");
1981 }
1982 }
1983}
1984
1985#ifdef CONFIG_FEATURE_VI_DOT_CMD
1986static void start_new_cmd_q(Byte c)
1987{
1988 // release old cmd
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001989 free(last_modifying_cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001990 // get buffer for new cmd
1991 last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
1992 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
1993 // if there is a current cmd count put it in the buffer first
1994 if (cmdcnt > 0)
Paul Foxd957b952005-11-28 18:07:53 +00001995 sprintf((char *) last_modifying_cmd, "%d%c", cmdcnt, c);
1996 else // just save char c onto queue
1997 last_modifying_cmd[0] = c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001998 adding2q = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001999}
2000
2001static void end_cmd_q(void)
2002{
2003#ifdef CONFIG_FEATURE_VI_YANKMARK
2004 YDreg = 26; // go back to default Yank/Delete reg
2005#endif /* CONFIG_FEATURE_VI_YANKMARK */
2006 adding2q = 0;
2007 return;
2008}
2009#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2010
Paul Foxb7b24d62006-04-05 14:17:24 +00002011#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_SEARCH) || defined(CONFIG_FEATURE_VI_CRASHME)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002012static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
2013{
2014 int cnt, i;
2015
2016 i = strlen((char *) s);
2017 p = text_hole_make(p, i);
2018 strncpy((char *) p, (char *) s, i);
2019 for (cnt = 0; *s != '\0'; s++) {
2020 if (*s == '\n')
2021 cnt++;
2022 }
2023#ifdef CONFIG_FEATURE_VI_YANKMARK
2024 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2025#endif /* CONFIG_FEATURE_VI_YANKMARK */
2026 return (p);
2027}
2028#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
2029
2030#ifdef CONFIG_FEATURE_VI_YANKMARK
2031static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register
2032{
2033 Byte *t;
2034 int cnt;
2035
2036 if (q < p) { // they are backwards- reverse them
2037 t = q;
2038 q = p;
2039 p = t;
2040 }
2041 cnt = q - p + 1;
2042 t = reg[dest];
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002043 free(t); // if already a yank register, free it
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002044 t = (Byte *) xmalloc(cnt + 1); // get a new register
2045 memset(t, '\0', cnt + 1); // clear new text[]
2046 strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer
2047 reg[dest] = t;
2048 return (p);
2049}
2050
2051static Byte what_reg(void)
2052{
2053 Byte c;
2054 int i;
2055
2056 i = 0;
2057 c = 'D'; // default to D-reg
2058 if (0 <= YDreg && YDreg <= 25)
2059 c = 'a' + (Byte) YDreg;
2060 if (YDreg == 26)
2061 c = 'D';
2062 if (YDreg == 27)
2063 c = 'U';
2064 return (c);
2065}
2066
2067static void check_context(Byte cmd)
2068{
2069 // A context is defined to be "modifying text"
2070 // Any modifying command establishes a new context.
2071
2072 if (dot < context_start || dot > context_end) {
2073 if (strchr((char *) modifying_cmds, cmd) != NULL) {
2074 // we are trying to modify text[]- make this the current context
2075 mark[27] = mark[26]; // move cur to prev
2076 mark[26] = dot; // move local to cur
2077 context_start = prev_line(prev_line(dot));
2078 context_end = next_line(next_line(dot));
2079 //loiter= start_loiter= now;
2080 }
2081 }
2082 return;
2083}
2084
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002085static inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002086{
2087 Byte *tmp;
2088
2089 // the current context is in mark[26]
2090 // the previous context is in mark[27]
2091 // only swap context if other context is valid
2092 if (text <= mark[27] && mark[27] <= end - 1) {
2093 tmp = mark[27];
2094 mark[27] = mark[26];
2095 mark[26] = tmp;
2096 p = mark[26]; // where we are going- previous context
2097 context_start = prev_line(prev_line(prev_line(p)));
2098 context_end = next_line(next_line(next_line(p)));
2099 }
2100 return (p);
2101}
2102#endif /* CONFIG_FEATURE_VI_YANKMARK */
2103
2104static int isblnk(Byte c) // is the char a blank or tab
2105{
2106 return (c == ' ' || c == '\t');
2107}
2108
2109//----- Set terminal attributes --------------------------------
2110static void rawmode(void)
2111{
2112 tcgetattr(0, &term_orig);
2113 term_vi = term_orig;
2114 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2115 term_vi.c_iflag &= (~IXON & ~ICRNL);
2116 term_vi.c_oflag &= (~ONLCR);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002117 term_vi.c_cc[VMIN] = 1;
2118 term_vi.c_cc[VTIME] = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002119 erase_char = term_vi.c_cc[VERASE];
2120 tcsetattr(0, TCSANOW, &term_vi);
2121}
2122
2123static void cookmode(void)
2124{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002125 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002126 tcsetattr(0, TCSANOW, &term_orig);
2127}
2128
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002129//----- Come here when we get a window resize signal ---------
2130#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002131static void winch_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002132{
2133 signal(SIGWINCH, winch_sig);
2134#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2135 window_size_get(0);
2136#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2137 new_screen(rows, columns); // get memory for virtual screen
2138 redraw(TRUE); // re-draw the screen
2139}
2140
2141//----- Come here when we get a continue signal -------------------
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002142static void cont_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002143{
2144 rawmode(); // terminal to "raw"
Paul Fox8552aec2005-09-16 12:20:05 +00002145 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002146 redraw(TRUE); // re-draw the screen
2147
2148 signal(SIGTSTP, suspend_sig);
2149 signal(SIGCONT, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002150 kill(my_pid, SIGCONT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002151}
2152
2153//----- Come here when we get a Suspend signal -------------------
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002154static void suspend_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002155{
2156 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2157 clear_to_eol(); // Erase to end of line
2158 cookmode(); // terminal to "cooked"
2159
2160 signal(SIGCONT, cont_sig);
2161 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002162 kill(my_pid, SIGTSTP);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002163}
2164
2165//----- Come here when we get a signal ---------------------------
2166static void catch_sig(int sig)
2167{
2168 signal(SIGHUP, catch_sig);
2169 signal(SIGINT, catch_sig);
2170 signal(SIGTERM, catch_sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002171 signal(SIGALRM, catch_sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002172 if(sig)
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002173 longjmp(restart, sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002174}
2175
2176//----- Come here when we get a core dump signal -----------------
2177static void core_sig(int sig)
2178{
2179 signal(SIGQUIT, core_sig);
2180 signal(SIGILL, core_sig);
2181 signal(SIGTRAP, core_sig);
2182 signal(SIGIOT, core_sig);
2183 signal(SIGABRT, core_sig);
2184 signal(SIGFPE, core_sig);
2185 signal(SIGBUS, core_sig);
2186 signal(SIGSEGV, core_sig);
2187#ifdef SIGSYS
2188 signal(SIGSYS, core_sig);
2189#endif
2190
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002191 if(sig) { // signaled
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002192 dot = bound_dot(dot); // make sure "dot" is valid
2193 longjmp(restart, sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002194 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002195}
2196#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2197
2198static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2199{
2200 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002201 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002202 FD_ZERO(&rfds);
2203 FD_SET(0, &rfds);
2204 tv.tv_sec = 0;
2205 tv.tv_usec = hund * 10000;
2206 select(1, &rfds, NULL, NULL, &tv);
2207 return (FD_ISSET(0, &rfds));
2208}
2209
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002210#define readbuffer bb_common_bufsiz1
2211
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002212static int readed_for_parse;
2213
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002214//----- IO Routines --------------------------------------------
2215static Byte readit(void) // read (maybe cursor) key from stdin
2216{
2217 Byte c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002218 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002219 struct esc_cmds {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002220 const char *seq;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002221 Byte val;
2222 };
2223
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002224 static const struct esc_cmds esccmds[] = {
2225 {"OA", (Byte) VI_K_UP}, // cursor key Up
2226 {"OB", (Byte) VI_K_DOWN}, // cursor key Down
2227 {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2228 {"OD", (Byte) VI_K_LEFT}, // cursor key Left
2229 {"OH", (Byte) VI_K_HOME}, // Cursor Key Home
2230 {"OF", (Byte) VI_K_END}, // Cursor Key End
2231 {"[A", (Byte) VI_K_UP}, // cursor key Up
2232 {"[B", (Byte) VI_K_DOWN}, // cursor key Down
2233 {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right
2234 {"[D", (Byte) VI_K_LEFT}, // cursor key Left
2235 {"[H", (Byte) VI_K_HOME}, // Cursor Key Home
2236 {"[F", (Byte) VI_K_END}, // Cursor Key End
2237 {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home
2238 {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2239 {"[4~", (Byte) VI_K_END}, // Cursor Key End
2240 {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2241 {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2242 {"OP", (Byte) VI_K_FUN1}, // Function Key F1
2243 {"OQ", (Byte) VI_K_FUN2}, // Function Key F2
2244 {"OR", (Byte) VI_K_FUN3}, // Function Key F3
2245 {"OS", (Byte) VI_K_FUN4}, // Function Key F4
2246 {"[15~", (Byte) VI_K_FUN5}, // Function Key F5
2247 {"[17~", (Byte) VI_K_FUN6}, // Function Key F6
2248 {"[18~", (Byte) VI_K_FUN7}, // Function Key F7
2249 {"[19~", (Byte) VI_K_FUN8}, // Function Key F8
2250 {"[20~", (Byte) VI_K_FUN9}, // Function Key F9
2251 {"[21~", (Byte) VI_K_FUN10}, // Function Key F10
2252 {"[23~", (Byte) VI_K_FUN11}, // Function Key F11
2253 {"[24~", (Byte) VI_K_FUN12}, // Function Key F12
2254 {"[11~", (Byte) VI_K_FUN1}, // Function Key F1
2255 {"[12~", (Byte) VI_K_FUN2}, // Function Key F2
2256 {"[13~", (Byte) VI_K_FUN3}, // Function Key F3
2257 {"[14~", (Byte) VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002258 };
2259
2260#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2261
2262 (void) alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002263 fflush(stdout);
2264 n = readed_for_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002265 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002266 if (n <= 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002267 ri0:
2268 // the Q is empty, wait for a typed char
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002269 n = read(0, readbuffer, BUFSIZ - 1);
2270 if (n < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002271 if (errno == EINTR)
2272 goto ri0; // interrupted sys call
2273 if (errno == EBADF)
2274 editing = 0;
2275 if (errno == EFAULT)
2276 editing = 0;
2277 if (errno == EINVAL)
2278 editing = 0;
2279 if (errno == EIO)
2280 editing = 0;
2281 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002282 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002283 if(n <= 0)
2284 return 0; // error
2285 if (readbuffer[0] == 27) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002286 // This is an ESC char. Is this Esc sequence?
2287 // Could be bare Esc key. See if there are any
2288 // more chars to read after the ESC. This would
2289 // be a Function or Cursor Key sequence.
2290 FD_ZERO(&rfds);
2291 FD_SET(0, &rfds);
2292 tv.tv_sec = 0;
2293 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2294
2295 // keep reading while there are input chars and room in buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002296 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002297 // read the rest of the ESC string
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002298 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2299 if (r > 0) {
2300 n += r;
2301 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002302 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002303 }
2304 readed_for_parse = n;
2305 }
2306 c = readbuffer[0];
2307 if(c == 27 && n > 1) {
2308 // Maybe cursor or function key?
2309 const struct esc_cmds *eindex;
2310
2311 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2312 int cnt = strlen(eindex->seq);
2313
2314 if(n <= cnt)
2315 continue;
2316 if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
2317 continue;
2318 // is a Cursor key- put derived value back into Q
2319 c = eindex->val;
2320 // for squeeze out the ESC sequence
2321 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002322 break;
2323 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002324 if(eindex == &esccmds[ESCCMDS_COUNT]) {
2325 /* defined ESC sequence not found, set only one ESC */
2326 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002327 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002328 } else {
2329 n = 1;
2330 }
2331 // remove key sequence from Q
2332 readed_for_parse -= n;
2333 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002334 (void) alarm(3); // we are done waiting for input, turn alarm ON
2335 return (c);
2336}
2337
2338//----- IO Routines --------------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002339static Byte get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002340{
2341 static Byte c;
2342
2343#ifdef CONFIG_FEATURE_VI_DOT_CMD
2344 // ! adding2q && ioq == 0 read()
2345 // ! adding2q && ioq != 0 *ioq
2346 // adding2q *last_modifying_cmd= read()
2347 if (!adding2q) {
2348 // we are not adding to the q.
2349 // but, we may be reading from a q
2350 if (ioq == 0) {
2351 // there is no current q, read from STDIN
2352 c = readit(); // get the users input
2353 } else {
2354 // there is a queue to get chars from first
2355 c = *ioq++;
2356 if (c == '\0') {
2357 // the end of the q, read from STDIN
2358 free(ioq_start);
2359 ioq_start = ioq = 0;
2360 c = readit(); // get the users input
2361 }
2362 }
2363 } else {
2364 // adding STDIN chars to q
2365 c = readit(); // get the users input
2366 if (last_modifying_cmd != 0) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002367 int len = strlen((char *) last_modifying_cmd);
2368 if (len + 1 >= BUFSIZ) {
2369 psbs("last_modifying_cmd overrun");
2370 } else {
2371 // add new char to q
2372 last_modifying_cmd[len] = c;
2373 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002374 }
2375 }
2376#else /* CONFIG_FEATURE_VI_DOT_CMD */
2377 c = readit(); // get the users input
2378#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2379 return (c); // return the char, where ever it came from
2380}
2381
2382static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2383{
2384 Byte buf[BUFSIZ];
2385 Byte c;
2386 int i;
2387 static Byte *obufp = NULL;
2388
2389 strcpy((char *) buf, (char *) prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002390 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002391 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2392 clear_to_eol(); // clear the line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002393 write1((char *) prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002394
2395 for (i = strlen((char *) buf); i < BUFSIZ;) {
2396 c = get_one_char(); // read user input
2397 if (c == '\n' || c == '\r' || c == 27)
2398 break; // is this end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002399 if (c == erase_char || c == 8 || c == 127) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002400 // user wants to erase prev char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002401 i--; // backup to prev char
2402 buf[i] = '\0'; // erase the char
2403 buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002404 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002405 if (i <= 0) { // user backs up before b-o-l, exit
2406 break;
2407 }
2408 } else {
2409 buf[i] = c; // save char in buffer
2410 buf[i + 1] = '\0'; // make sure buffer is null terminated
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002411 putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002412 i++;
2413 }
2414 }
2415 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002416 free(obufp);
Manuel Novoa III cad53642003-03-19 09:13:01 +00002417 obufp = (Byte *) bb_xstrdup((char *) buf);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002418 return (obufp);
2419}
2420
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002421static int file_size(const Byte * fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002422{
2423 struct stat st_buf;
2424 int cnt, sr;
2425
Eric Andersena68ea1c2006-01-30 22:48:39 +00002426 if (fn == 0 || strlen((char *)fn) <= 0)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002427 return (-1);
2428 cnt = -1;
2429 sr = stat((char *) fn, &st_buf); // see if file exists
2430 if (sr >= 0) {
2431 cnt = (int) st_buf.st_size;
2432 }
2433 return (cnt);
2434}
2435
2436static int file_insert(Byte * fn, Byte * p, int size)
2437{
2438 int fd, cnt;
2439
2440 cnt = -1;
2441#ifdef CONFIG_FEATURE_VI_READONLY
2442 readonly = FALSE;
2443#endif /* CONFIG_FEATURE_VI_READONLY */
2444 if (fn == 0 || strlen((char*) fn) <= 0) {
2445 psbs("No filename given");
2446 goto fi0;
2447 }
2448 if (size == 0) {
2449 // OK- this is just a no-op
2450 cnt = 0;
2451 goto fi0;
2452 }
2453 if (size < 0) {
2454 psbs("Trying to insert a negative number (%d) of characters", size);
2455 goto fi0;
2456 }
2457 if (p < text || p > end) {
2458 psbs("Trying to insert file outside of memory");
2459 goto fi0;
2460 }
2461
2462 // see if we can open the file
2463#ifdef CONFIG_FEATURE_VI_READONLY
2464 if (vi_readonly) goto fi1; // do not try write-mode
2465#endif
2466 fd = open((char *) fn, O_RDWR); // assume read & write
2467 if (fd < 0) {
2468 // could not open for writing- maybe file is read only
2469#ifdef CONFIG_FEATURE_VI_READONLY
2470 fi1:
2471#endif
2472 fd = open((char *) fn, O_RDONLY); // try read-only
2473 if (fd < 0) {
2474 psbs("\"%s\" %s", fn, "could not open file");
2475 goto fi0;
2476 }
2477#ifdef CONFIG_FEATURE_VI_READONLY
2478 // got the file- read-only
2479 readonly = TRUE;
2480#endif /* CONFIG_FEATURE_VI_READONLY */
2481 }
2482 p = text_hole_make(p, size);
2483 cnt = read(fd, p, size);
2484 close(fd);
2485 if (cnt < 0) {
2486 cnt = -1;
2487 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2488 psbs("could not read file \"%s\"", fn);
2489 } else if (cnt < size) {
2490 // There was a partial read, shrink unused space text[]
2491 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
2492 psbs("could not read all of file \"%s\"", fn);
2493 }
2494 if (cnt >= size)
Paul Fox8552aec2005-09-16 12:20:05 +00002495 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002496 fi0:
2497 return (cnt);
2498}
2499
2500static int file_write(Byte * fn, Byte * first, Byte * last)
2501{
2502 int fd, cnt, charcnt;
2503
2504 if (fn == 0) {
2505 psbs("No current filename");
Paul Fox61e45db2005-10-09 14:43:22 +00002506 return (-2);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002507 }
2508 charcnt = 0;
2509 // FIXIT- use the correct umask()
2510 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2511 if (fd < 0)
2512 return (-1);
2513 cnt = last - first + 1;
2514 charcnt = write(fd, first, cnt);
2515 if (charcnt == cnt) {
2516 // good write
2517 //file_modified= FALSE; // the file has not been modified
2518 } else {
2519 charcnt = 0;
2520 }
2521 close(fd);
2522 return (charcnt);
2523}
2524
2525//----- Terminal Drawing ---------------------------------------
2526// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002527// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002528// screen coordinates
2529// 0,0 ... 0,79
2530// 1,0 ... 1,79
2531// . ... .
2532// . ... .
2533// 22,0 ... 22,79
2534// 23,0 ... 23,79 status line
2535//
2536
2537//----- Move the cursor to row x col (count from 0, not 1) -------
2538static void place_cursor(int row, int col, int opti)
2539{
2540 char cm1[BUFSIZ];
2541 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002542#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2543 char cm2[BUFSIZ];
2544 Byte *screenp;
2545 // char cm3[BUFSIZ];
2546 int Rrow= last_row;
2547#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002548
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002549 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2550
2551 if (row < 0) row = 0;
2552 if (row >= rows) row = rows - 1;
2553 if (col < 0) col = 0;
2554 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002555
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002556 //----- 1. Try the standard terminal ESC sequence
2557 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2558 cm= cm1;
2559 if (! opti) goto pc0;
2560
2561#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2562 //----- find the minimum # of chars to move cursor -------------
2563 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2564 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002565
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002566 // move to the correct row
2567 while (row < Rrow) {
2568 // the cursor has to move up
2569 strcat(cm2, CMup);
2570 Rrow--;
2571 }
2572 while (row > Rrow) {
2573 // the cursor has to move down
2574 strcat(cm2, CMdown);
2575 Rrow++;
2576 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002577
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002578 // now move to the correct column
2579 strcat(cm2, "\r"); // start at col 0
2580 // just send out orignal source char to get to correct place
2581 screenp = &screen[row * columns]; // start of screen line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002582 strncat(cm2, (char* )screenp, col);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002583
2584 //----- 3. Try some other way of moving cursor
2585 //---------------------------------------------
2586
2587 // pick the shortest cursor motion to send out
2588 cm= cm1;
2589 if (strlen(cm2) < strlen(cm)) {
2590 cm= cm2;
2591 } /* else if (strlen(cm3) < strlen(cm)) {
2592 cm= cm3;
2593 } */
2594#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2595 pc0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002596 write1(cm); // move the cursor
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002597}
2598
2599//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002600static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002601{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002602 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002603}
2604
2605//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002606static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002607{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002608 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002609}
2610
2611//----- Start standout mode ------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002612static void standout_start(void) // send "start reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002613{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002614 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002615}
2616
2617//----- End standout mode --------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002618static void standout_end(void) // send "end reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002619{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002620 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002621}
2622
2623//----- Flash the screen --------------------------------------
2624static void flash(int h)
2625{
2626 standout_start(); // send "start reverse video" sequence
2627 redraw(TRUE);
2628 (void) mysleep(h);
2629 standout_end(); // send "end reverse video" sequence
2630 redraw(TRUE);
2631}
2632
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002633static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002634{
2635#ifdef CONFIG_FEATURE_VI_CRASHME
2636 if (crashme > 0)
2637 return; // generate a random command
2638#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002639 if (!err_method) {
2640 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002641 } else {
2642 flash(10);
2643 }
2644}
2645
2646//----- Screen[] Routines --------------------------------------
2647//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002648static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002649{
2650 memset(screen, ' ', screensize); // clear new screen
2651}
2652
Eric Andersena68ea1c2006-01-30 22:48:39 +00002653static int bufsum(unsigned char *buf, int count)
Paul Fox8552aec2005-09-16 12:20:05 +00002654{
2655 int sum = 0;
Eric Andersena68ea1c2006-01-30 22:48:39 +00002656 unsigned char *e = buf + count;
Paul Fox8552aec2005-09-16 12:20:05 +00002657 while (buf < e)
2658 sum += *buf++;
2659 return sum;
2660}
2661
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002662//----- Draw the status line at bottom of the screen -------------
2663static void show_status_line(void)
2664{
Paul Foxc3504852005-09-16 12:48:18 +00002665 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002666
Paul Fox8552aec2005-09-16 12:20:05 +00002667 // either we already have an error or status message, or we
2668 // create one.
2669 if (!have_status_msg) {
2670 cnt = format_edit_status();
2671 cksum = bufsum(status_buffer, cnt);
2672 }
2673 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
2674 last_status_cksum= cksum; // remember if we have seen this line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002675 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002676 write1((char*)status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002677 clear_to_eol();
Paul Fox8552aec2005-09-16 12:20:05 +00002678 if (have_status_msg) {
Eric Andersena68ea1c2006-01-30 22:48:39 +00002679 if (((int)strlen((char*)status_buffer) - (have_status_msg - 1)) >
Paul Fox8552aec2005-09-16 12:20:05 +00002680 (columns - 1) ) {
2681 have_status_msg = 0;
2682 Hit_Return();
2683 }
2684 have_status_msg = 0;
2685 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002686 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2687 }
Eric Andersena9eb33d2004-08-19 19:15:06 +00002688 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002689}
2690
2691//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00002692// format status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002693static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002694{
2695 va_list args;
2696
2697 va_start(args, format);
2698 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
Eric Andersen0ef24c62005-07-18 10:32:59 +00002699 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002700 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2701 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002702
2703 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002704
2705 return;
2706}
2707
Paul Fox8552aec2005-09-16 12:20:05 +00002708// format status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002709static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002710{
2711 va_list args;
2712
2713 va_start(args, format);
2714 vsprintf((char *) status_buffer, format, args);
2715 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002716
2717 have_status_msg = 1;
2718
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002719 return;
2720}
2721
2722static void ni(Byte * s) // display messages
2723{
2724 Byte buf[BUFSIZ];
2725
2726 print_literal(buf, s);
2727 psbs("\'%s\' is not implemented", buf);
2728}
2729
Paul Fox8552aec2005-09-16 12:20:05 +00002730static int format_edit_status(void) // show file status on status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002731{
Paul Fox8552aec2005-09-16 12:20:05 +00002732 int cur, percent, ret, trunc_at;
2733 static int tot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002734
Paul Fox8552aec2005-09-16 12:20:05 +00002735 // file_modified is now a counter rather than a flag. this
2736 // helps reduce the amount of line counting we need to do.
2737 // (this will cause a mis-reporting of modified status
2738 // once every MAXINT editing operations.)
2739
2740 // it would be nice to do a similar optimization here -- if
2741 // we haven't done a motion that could have changed which line
2742 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002743 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00002744
2745 // reduce counting -- the total lines can't have
2746 // changed if we haven't done any edits.
2747 if (file_modified != last_file_modified) {
2748 tot = cur + count_lines(dot, end - 1) - 1;
2749 last_file_modified = file_modified;
2750 }
2751
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002752 // current line percent
2753 // ------------- ~~ ----------
2754 // total lines 100
2755 if (tot > 0) {
2756 percent = (100 * cur) / tot;
2757 } else {
2758 cur = tot = 0;
2759 percent = 100;
2760 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00002761
Paul Fox8552aec2005-09-16 12:20:05 +00002762 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2763 columns : STATUS_BUFFER_LEN-1;
2764
2765 ret = snprintf((char *) status_buffer, trunc_at+1,
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002766#ifdef CONFIG_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002767 "%c %s%s%s %d/%d %d%%",
2768#else
2769 "%c %s%s %d/%d %d%%",
2770#endif
2771 (cmd_mode ? (cmd_mode == 2 ? 'R':'I'):'-'),
2772 (cfn != 0 ? (char *) cfn : "No file"),
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002773#ifdef CONFIG_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002774 ((vi_readonly || readonly) ? " [Read-only]" : ""),
2775#endif
2776 (file_modified ? " [modified]" : ""),
2777 cur, tot, percent);
2778
2779 if (ret >= 0 && ret < trunc_at)
2780 return ret; /* it all fit */
2781
2782 return trunc_at; /* had to truncate */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002783}
2784
2785//----- Force refresh of all Lines -----------------------------
2786static void redraw(int full_screen)
2787{
2788 place_cursor(0, 0, FALSE); // put cursor in correct place
2789 clear_to_eos(); // tel terminal to erase display
2790 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00002791 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002792 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00002793 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002794}
2795
2796//----- Format a text[] line into a buffer ---------------------
2797static void format_line(Byte *dest, Byte *src, int li)
2798{
2799 int co;
2800 Byte c;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002801
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002802 for (co= 0; co < MAX_SCR_COLS; co++) {
2803 c= ' '; // assume blank
2804 if (li > 0 && co == 0) {
2805 c = '~'; // not first line, assume Tilde
2806 }
2807 // are there chars in text[] and have we gone past the end
2808 if (text < end && src < end) {
2809 c = *src++;
2810 }
2811 if (c == '\n')
2812 break;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002813 if (c > 127 && !Isprint(c)) {
2814 c = '.';
2815 }
2816 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002817 if (c == '\t') {
2818 c = ' ';
2819 // co % 8 != 7
2820 for (; (co % tabstop) != (tabstop - 1); co++) {
2821 dest[co] = c;
2822 }
2823 } else {
2824 dest[co++] = '^';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002825 if(c == 127)
2826 c = '?';
2827 else
2828 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002829 }
2830 }
2831 // the co++ is done here so that the column will
2832 // not be overwritten when we blank-out the rest of line
2833 dest[co] = c;
2834 if (src >= end)
2835 break;
2836 }
2837}
2838
2839//----- Refresh the changed screen lines -----------------------
2840// Copy the source line from text[] into the buffer and note
2841// if the current screenline is different from the new buffer.
2842// If they differ then that line needs redrawing on the terminal.
2843//
2844static void refresh(int full_screen)
2845{
2846 static int old_offset;
2847 int li, changed;
2848 Byte buf[MAX_SCR_COLS];
2849 Byte *tp, *sp; // pointer into text[] and screen[]
2850#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2851 int last_li= -2; // last line that changed- for optimizing cursor movement
2852#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2853
2854#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2855 window_size_get(0);
2856#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2857 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2858 tp = screenbegin; // index into text[] of top line
2859
2860 // compare text[] to screen[] and mark screen[] lines that need updating
2861 for (li = 0; li < rows - 1; li++) {
2862 int cs, ce; // column start & end
2863 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2864 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2865 // format current text line into buf
2866 format_line(buf, tp, li);
2867
2868 // skip to the end of the current text[] line
2869 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2870
2871 // see if there are any changes between vitual screen and buf
2872 changed = FALSE; // assume no change
2873 cs= 0;
2874 ce= columns-1;
2875 sp = &screen[li * columns]; // start of screen line
2876 if (full_screen) {
2877 // force re-draw of every single column from 0 - columns-1
2878 goto re0;
2879 }
2880 // compare newly formatted buffer with virtual screen
2881 // look forward for first difference between buf and screen
2882 for ( ; cs <= ce; cs++) {
2883 if (buf[cs + offset] != sp[cs]) {
2884 changed = TRUE; // mark for redraw
2885 break;
2886 }
2887 }
2888
2889 // look backward for last difference between buf and screen
2890 for ( ; ce >= cs; ce--) {
2891 if (buf[ce + offset] != sp[ce]) {
2892 changed = TRUE; // mark for redraw
2893 break;
2894 }
2895 }
2896 // now, cs is index of first diff, and ce is index of last diff
2897
2898 // if horz offset has changed, force a redraw
2899 if (offset != old_offset) {
2900 re0:
2901 changed = TRUE;
2902 }
2903
2904 // make a sanity check of columns indexes
2905 if (cs < 0) cs= 0;
2906 if (ce > columns-1) ce= columns-1;
2907 if (cs > ce) { cs= 0; ce= columns-1; }
2908 // is there a change between vitual screen and buf
2909 if (changed) {
2910 // copy changed part of buffer to virtual screen
2911 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2912
2913 // move cursor to column of first change
2914 if (offset != old_offset) {
2915 // opti_cur_move is still too stupid
2916 // to handle offsets correctly
2917 place_cursor(li, cs, FALSE);
2918 } else {
2919#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2920 // if this just the next line
2921 // try to optimize cursor movement
2922 // otherwise, use standard ESC sequence
2923 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2924 last_li= li;
2925#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2926 place_cursor(li, cs, FALSE); // use standard ESC sequence
2927#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2928 }
2929
2930 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002931 {
2932 int nic = ce-cs+1;
Eric Andersena68ea1c2006-01-30 22:48:39 +00002933 char *out = (char*)sp+cs;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002934
2935 while(nic-- > 0) {
2936 putchar(*out);
2937 out++;
2938 }
2939 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002940#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2941 last_row = li;
2942#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2943 }
2944 }
2945
2946#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2947 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2948 last_row = crow;
2949#else
2950 place_cursor(crow, ccol, FALSE);
2951#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002952
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002953 if (offset != old_offset)
2954 old_offset = offset;
2955}
2956
Eric Andersen3f980402001-04-04 17:31:15 +00002957//---------------------------------------------------------------------
2958//----- the Ascii Chart -----------------------------------------------
2959//
2960// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2961// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2962// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2963// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2964// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2965// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2966// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2967// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2968// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2969// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2970// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2971// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2972// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2973// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2974// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2975// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2976//---------------------------------------------------------------------
2977
2978//----- Execute a Vi Command -----------------------------------
2979static void do_cmd(Byte c)
2980{
2981 Byte c1, *p, *q, *msg, buf[9], *save_dot;
2982 int cnt, i, j, dir, yf;
2983
2984 c1 = c; // quiet the compiler
2985 cnt = yf = dir = 0; // quiet the compiler
2986 p = q = save_dot = msg = buf; // quiet the compiler
2987 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00002988
Paul Fox8552aec2005-09-16 12:20:05 +00002989 show_status_line();
2990
Eric Andersenbff7a602001-11-17 07:15:43 +00002991 /* if this is a cursor key, skip these checks */
2992 switch (c) {
2993 case VI_K_UP:
2994 case VI_K_DOWN:
2995 case VI_K_LEFT:
2996 case VI_K_RIGHT:
2997 case VI_K_HOME:
2998 case VI_K_END:
2999 case VI_K_PAGEUP:
3000 case VI_K_PAGEDOWN:
3001 goto key_cmd_mode;
3002 }
3003
Eric Andersen3f980402001-04-04 17:31:15 +00003004 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003005 // flip-flop Insert/Replace mode
3006 if (c == VI_K_INSERT) goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00003007 // we are 'R'eplacing the current *dot with new char
3008 if (*dot == '\n') {
3009 // don't Replace past E-o-l
3010 cmd_mode = 1; // convert to insert
3011 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003012 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003013 if (c != 27)
3014 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3015 dot = char_insert(dot, c); // insert new char
3016 }
3017 goto dc1;
3018 }
3019 }
3020 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003021 // hitting "Insert" twice means "R" replace mode
3022 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00003023 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003024 if (1 <= c || Isprint(c)) {
3025 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00003026 }
3027 goto dc1;
3028 }
3029
Eric Andersenbff7a602001-11-17 07:15:43 +00003030key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00003031 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00003032 //case 0x01: // soh
3033 //case 0x09: // ht
3034 //case 0x0b: // vt
3035 //case 0x0e: // so
3036 //case 0x0f: // si
3037 //case 0x10: // dle
3038 //case 0x11: // dc1
3039 //case 0x13: // dc3
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003040#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00003041 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00003042 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003043 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003044#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen822c3832001-05-07 17:37:43 +00003045 //case 0x16: // syn
3046 //case 0x17: // etb
3047 //case 0x18: // can
3048 //case 0x1c: // fs
3049 //case 0x1d: // gs
3050 //case 0x1e: // rs
3051 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003052 //case '!': // !-
3053 //case '#': // #-
3054 //case '&': // &-
3055 //case '(': // (-
3056 //case ')': // )-
3057 //case '*': // *-
3058 //case ',': // ,-
3059 //case '=': // =-
3060 //case '@': // @-
3061 //case 'F': // F-
3062 //case 'K': // K-
3063 //case 'Q': // Q-
3064 //case 'S': // S-
3065 //case 'T': // T-
3066 //case 'V': // V-
3067 //case '[': // [-
3068 //case '\\': // \-
3069 //case ']': // ]-
3070 //case '_': // _-
3071 //case '`': // `-
3072 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003073 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003074 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00003075 default: // unrecognised command
3076 buf[0] = c;
3077 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003078 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00003079 buf[0] = '^';
3080 buf[1] = c + '@';
3081 buf[2] = '\0';
3082 }
3083 ni((Byte *) buf);
3084 end_cmd_q(); // stop adding to q
3085 case 0x00: // nul- ignore
3086 break;
3087 case 2: // ctrl-B scroll up full screen
3088 case VI_K_PAGEUP: // Cursor Key Page Up
3089 dot_scroll(rows - 2, -1);
3090 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003091#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003092 case 0x03: // ctrl-C interrupt
3093 longjmp(restart, 1);
3094 break;
3095 case 26: // ctrl-Z suspend
3096 suspend_sig(SIGTSTP);
3097 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003098#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +00003099 case 4: // ctrl-D scroll down half screen
3100 dot_scroll((rows - 2) / 2, 1);
3101 break;
3102 case 5: // ctrl-E scroll down one line
3103 dot_scroll(1, 1);
3104 break;
3105 case 6: // ctrl-F scroll down full screen
3106 case VI_K_PAGEDOWN: // Cursor Key Page Down
3107 dot_scroll(rows - 2, 1);
3108 break;
3109 case 7: // ctrl-G show current status
Paul Fox8552aec2005-09-16 12:20:05 +00003110 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003111 break;
3112 case 'h': // h- move left
3113 case VI_K_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003114 case 8: // ctrl-H- move left (This may be ERASE char)
3115 case 127: // DEL- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003116 if (cmdcnt-- > 1) {
3117 do_cmd(c);
3118 } // repeat cnt
3119 dot_left();
3120 break;
3121 case 10: // Newline ^J
3122 case 'j': // j- goto next line, same col
3123 case VI_K_DOWN: // cursor key Down
3124 if (cmdcnt-- > 1) {
3125 do_cmd(c);
3126 } // repeat cnt
3127 dot_next(); // go to next B-o-l
3128 dot = move_to_col(dot, ccol + offset); // try stay in same col
3129 break;
3130 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003131 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003132 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003133 clear_to_eos(); // tel terminal to erase display
3134 (void) mysleep(10);
3135 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003136 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003137 refresh(TRUE); // this will redraw the entire display
3138 break;
3139 case 13: // Carriage Return ^M
3140 case '+': // +- goto next line
3141 if (cmdcnt-- > 1) {
3142 do_cmd(c);
3143 } // repeat cnt
3144 dot_next();
3145 dot_skip_over_ws();
3146 break;
3147 case 21: // ctrl-U scroll up half screen
3148 dot_scroll((rows - 2) / 2, -1);
3149 break;
3150 case 25: // ctrl-Y scroll up one line
3151 dot_scroll(1, -1);
3152 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003153 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003154 if (cmd_mode == 0)
3155 indicate_error(c);
3156 cmd_mode = 0; // stop insrting
3157 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003158 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003159 break;
3160 case ' ': // move right
3161 case 'l': // move right
3162 case VI_K_RIGHT: // Cursor Key Right
3163 if (cmdcnt-- > 1) {
3164 do_cmd(c);
3165 } // repeat cnt
3166 dot_right();
3167 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003168#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003169 case '"': // "- name a register to use for Delete/Yank
3170 c1 = get_one_char();
3171 c1 = tolower(c1);
3172 if (islower(c1)) {
3173 YDreg = c1 - 'a';
3174 } else {
3175 indicate_error(c);
3176 }
3177 break;
3178 case '\'': // '- goto a specific mark
3179 c1 = get_one_char();
3180 c1 = tolower(c1);
3181 if (islower(c1)) {
3182 c1 = c1 - 'a';
3183 // get the b-o-l
3184 q = mark[(int) c1];
3185 if (text <= q && q < end) {
3186 dot = q;
3187 dot_begin(); // go to B-o-l
3188 dot_skip_over_ws();
3189 }
3190 } else if (c1 == '\'') { // goto previous context
3191 dot = swap_context(dot); // swap current and previous context
3192 dot_begin(); // go to B-o-l
3193 dot_skip_over_ws();
3194 } else {
3195 indicate_error(c);
3196 }
3197 break;
3198 case 'm': // m- Mark a line
3199 // this is really stupid. If there are any inserts or deletes
3200 // between text[0] and dot then this mark will not point to the
3201 // correct location! It could be off by many lines!
3202 // Well..., at least its quick and dirty.
3203 c1 = get_one_char();
3204 c1 = tolower(c1);
3205 if (islower(c1)) {
3206 c1 = c1 - 'a';
3207 // remember the line
3208 mark[(int) c1] = dot;
3209 } else {
3210 indicate_error(c);
3211 }
3212 break;
3213 case 'P': // P- Put register before
3214 case 'p': // p- put register after
3215 p = reg[YDreg];
3216 if (p == 0) {
3217 psbs("Nothing in register %c", what_reg());
3218 break;
3219 }
3220 // are we putting whole lines or strings
3221 if (strchr((char *) p, '\n') != NULL) {
3222 if (c == 'P') {
3223 dot_begin(); // putting lines- Put above
3224 }
3225 if (c == 'p') {
3226 // are we putting after very last line?
3227 if (end_line(dot) == (end - 1)) {
3228 dot = end; // force dot to end of text[]
3229 } else {
3230 dot_next(); // next line, then put before
3231 }
3232 }
3233 } else {
3234 if (c == 'p')
3235 dot_right(); // move to right, can move to NL
3236 }
3237 dot = string_insert(dot, p); // insert the string
3238 end_cmd_q(); // stop adding to q
3239 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003240 case 'U': // U- Undo; replace current line with original version
3241 if (reg[Ureg] != 0) {
3242 p = begin_line(dot);
3243 q = end_line(dot);
3244 p = text_hole_delete(p, q); // delete cur line
3245 p = string_insert(p, reg[Ureg]); // insert orig line
3246 dot = p;
3247 dot_skip_over_ws();
3248 }
3249 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003250#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003251 case '$': // $- goto end of line
3252 case VI_K_END: // Cursor Key End
3253 if (cmdcnt-- > 1) {
3254 do_cmd(c);
3255 } // repeat cnt
Glenn L McGrathee829062004-01-21 10:59:45 +00003256 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003257 break;
3258 case '%': // %- find matching char of pair () [] {}
3259 for (q = dot; q < end && *q != '\n'; q++) {
3260 if (strchr("()[]{}", *q) != NULL) {
3261 // we found half of a pair
3262 p = find_pair(q, *q);
3263 if (p == NULL) {
3264 indicate_error(c);
3265 } else {
3266 dot = p;
3267 }
3268 break;
3269 }
3270 }
3271 if (*q == '\n')
3272 indicate_error(c);
3273 break;
3274 case 'f': // f- forward to a user specified char
3275 last_forward_char = get_one_char(); // get the search char
3276 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003277 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003278 //
Paul Foxd13b90b2005-07-18 22:17:25 +00003279 //**** fall thru to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003280 case ';': // ;- look at rest of line for last forward char
3281 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003282 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003283 } // repeat cnt
Eric Andersen822c3832001-05-07 17:37:43 +00003284 if (last_forward_char == 0) break;
Eric Andersen3f980402001-04-04 17:31:15 +00003285 q = dot + 1;
3286 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3287 q++;
3288 }
3289 if (*q == last_forward_char)
3290 dot = q;
3291 break;
3292 case '-': // -- goto prev line
3293 if (cmdcnt-- > 1) {
3294 do_cmd(c);
3295 } // repeat cnt
3296 dot_prev();
3297 dot_skip_over_ws();
3298 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003299#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003300 case '.': // .- repeat the last modifying command
3301 // Stuff the last_modifying_cmd back into stdin
3302 // and let it be re-executed.
3303 if (last_modifying_cmd != 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +00003304 ioq = ioq_start = (Byte *) bb_xstrdup((char *) last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003305 }
3306 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003307#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3308#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003309 case '?': // /- search for a pattern
3310 case '/': // /- search for a pattern
3311 buf[0] = c;
3312 buf[1] = '\0';
3313 q = get_input_line(buf); // get input line- use "status line"
3314 if (strlen((char *) q) == 1)
3315 goto dc3; // if no pat re-use old pat
3316 if (strlen((char *) q) > 1) { // new pat- save it and find
3317 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003318 free(last_search_pattern);
Manuel Novoa III cad53642003-03-19 09:13:01 +00003319 last_search_pattern = (Byte *) bb_xstrdup((char *) q);
Eric Andersen3f980402001-04-04 17:31:15 +00003320 goto dc3; // now find the pattern
3321 }
3322 // user changed mind and erased the "/"- do nothing
3323 break;
3324 case 'N': // N- backward search for last pattern
3325 if (cmdcnt-- > 1) {
3326 do_cmd(c);
3327 } // repeat cnt
3328 dir = BACK; // assume BACKWARD search
3329 p = dot - 1;
3330 if (last_search_pattern[0] == '?') {
3331 dir = FORWARD;
3332 p = dot + 1;
3333 }
3334 goto dc4; // now search for pattern
3335 break;
3336 case 'n': // n- repeat search for last pattern
3337 // search rest of text[] starting at next char
3338 // if search fails return orignal "p" not the "p+1" address
3339 if (cmdcnt-- > 1) {
3340 do_cmd(c);
3341 } // repeat cnt
3342 dc3:
3343 if (last_search_pattern == 0) {
3344 msg = (Byte *) "No previous regular expression";
3345 goto dc2;
3346 }
3347 if (last_search_pattern[0] == '/') {
3348 dir = FORWARD; // assume FORWARD search
3349 p = dot + 1;
3350 }
3351 if (last_search_pattern[0] == '?') {
3352 dir = BACK;
3353 p = dot - 1;
3354 }
3355 dc4:
3356 q = char_search(p, last_search_pattern + 1, dir, FULL);
3357 if (q != NULL) {
3358 dot = q; // good search, update "dot"
3359 msg = (Byte *) "";
3360 goto dc2;
3361 }
3362 // no pattern found between "dot" and "end"- continue at top
3363 p = text;
3364 if (dir == BACK) {
3365 p = end - 1;
3366 }
3367 q = char_search(p, last_search_pattern + 1, dir, FULL);
3368 if (q != NULL) { // found something
3369 dot = q; // found new pattern- goto it
3370 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3371 if (dir == BACK) {
3372 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3373 }
3374 } else {
3375 msg = (Byte *) "Pattern not found";
3376 }
3377 dc2:
Paul Fox8552aec2005-09-16 12:20:05 +00003378 if (*msg) psbs("%s", msg);
Eric Andersen3f980402001-04-04 17:31:15 +00003379 break;
3380 case '{': // {- move backward paragraph
3381 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3382 if (q != NULL) { // found blank line
3383 dot = next_line(q); // move to next blank line
3384 }
3385 break;
3386 case '}': // }- move forward paragraph
3387 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3388 if (q != NULL) { // found blank line
3389 dot = next_line(q); // move to next blank line
3390 }
3391 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003392#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003393 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003394 case '1': // 1-
3395 case '2': // 2-
3396 case '3': // 3-
3397 case '4': // 4-
3398 case '5': // 5-
3399 case '6': // 6-
3400 case '7': // 7-
3401 case '8': // 8-
3402 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003403 if (c == '0' && cmdcnt < 1) {
3404 dot_begin(); // this was a standalone zero
3405 } else {
3406 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3407 }
3408 break;
3409 case ':': // :- the colon mode commands
Eric Andersen3f980402001-04-04 17:31:15 +00003410 p = get_input_line((Byte *) ":"); // get input line- use "status line"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003411#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003412 colon(p); // execute the command
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003413#else /* CONFIG_FEATURE_VI_COLON */
Eric Andersen822c3832001-05-07 17:37:43 +00003414 if (*p == ':')
3415 p++; // move past the ':'
3416 cnt = strlen((char *) p);
3417 if (cnt <= 0)
3418 break;
3419 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3420 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
Matt Kraai1f0c4362001-12-20 23:13:26 +00003421 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003422 psbs("No write since last change (:quit! overrides)");
3423 } else {
3424 editing = 0;
3425 }
Paul Fox9360f422006-03-27 21:51:16 +00003426 } else if (strncasecmp((char *) p, "write", cnt) == 0
3427 || strncasecmp((char *) p, "wq", cnt) == 0
3428 || strncasecmp((char *) p, "wn", cnt) == 0
3429 || strncasecmp((char *) p, "x", cnt) == 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003430 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003431 if (cnt < 0) {
3432 if (cnt == -1)
3433 psbs("Write error: %s", strerror(errno));
3434 } else {
3435 file_modified = 0;
3436 last_file_modified = -1;
3437 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
Paul Fox9360f422006-03-27 21:51:16 +00003438 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n' ||
3439 p[0] == 'X' || p[1] == 'Q' || p[1] == 'N') {
Paul Fox61e45db2005-10-09 14:43:22 +00003440 editing = 0;
3441 }
Eric Andersen3f980402001-04-04 17:31:15 +00003442 }
Eric Andersen822c3832001-05-07 17:37:43 +00003443 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
Paul Fox8552aec2005-09-16 12:20:05 +00003444 last_status_cksum = 0; // force status update
Eric Andersen822c3832001-05-07 17:37:43 +00003445 } else if (sscanf((char *) p, "%d", &j) > 0) {
3446 dot = find_line(j); // go to line # j
3447 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003448 } else { // unrecognised cmd
Eric Andersen822c3832001-05-07 17:37:43 +00003449 ni((Byte *) p);
Eric Andersen3f980402001-04-04 17:31:15 +00003450 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003451#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003452 break;
3453 case '<': // <- Left shift something
3454 case '>': // >- Right shift something
3455 cnt = count_lines(text, dot); // remember what line we are on
3456 c1 = get_one_char(); // get the type of thing to delete
3457 find_range(&p, &q, c1);
3458 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3459 p = begin_line(p);
3460 q = end_line(q);
3461 i = count_lines(p, q); // # of lines we are shifting
3462 for ( ; i > 0; i--, p = next_line(p)) {
3463 if (c == '<') {
3464 // shift left- remove tab or 8 spaces
3465 if (*p == '\t') {
3466 // shrink buffer 1 char
3467 (void) text_hole_delete(p, p);
3468 } else if (*p == ' ') {
3469 // we should be calculating columns, not just SPACE
3470 for (j = 0; *p == ' ' && j < tabstop; j++) {
3471 (void) text_hole_delete(p, p);
3472 }
3473 }
3474 } else if (c == '>') {
3475 // shift right -- add tab or 8 spaces
3476 (void) char_insert(p, '\t');
3477 }
3478 }
3479 dot = find_line(cnt); // what line were we on
3480 dot_skip_over_ws();
3481 end_cmd_q(); // stop adding to q
3482 break;
3483 case 'A': // A- append at e-o-l
3484 dot_end(); // go to e-o-l
3485 //**** fall thru to ... 'a'
3486 case 'a': // a- append after current char
3487 if (*dot != '\n')
3488 dot++;
3489 goto dc_i;
3490 break;
3491 case 'B': // B- back a blank-delimited Word
3492 case 'E': // E- end of a blank-delimited word
3493 case 'W': // W- forward a blank-delimited word
3494 if (cmdcnt-- > 1) {
3495 do_cmd(c);
3496 } // repeat cnt
3497 dir = FORWARD;
3498 if (c == 'B')
3499 dir = BACK;
3500 if (c == 'W' || isspace(dot[dir])) {
3501 dot = skip_thing(dot, 1, dir, S_TO_WS);
3502 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3503 }
3504 if (c != 'W')
3505 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3506 break;
3507 case 'C': // C- Change to e-o-l
3508 case 'D': // D- delete to e-o-l
3509 save_dot = dot;
3510 dot = dollar_line(dot); // move to before NL
3511 // copy text into a register and delete
3512 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3513 if (c == 'C')
3514 goto dc_i; // start inserting
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003515#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003516 if (c == 'D')
3517 end_cmd_q(); // stop adding to q
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003518#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003519 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003520 case 'G': // G- goto to a line number (default= E-O-F)
3521 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003522 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003523 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003524 }
3525 dot_skip_over_ws();
3526 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003527 case 'H': // H- goto top line on screen
3528 dot = screenbegin;
3529 if (cmdcnt > (rows - 1)) {
3530 cmdcnt = (rows - 1);
3531 }
3532 if (cmdcnt-- > 1) {
3533 do_cmd('+');
3534 } // repeat cnt
3535 dot_skip_over_ws();
3536 break;
3537 case 'I': // I- insert before first non-blank
3538 dot_begin(); // 0
3539 dot_skip_over_ws();
3540 //**** fall thru to ... 'i'
3541 case 'i': // i- insert before current char
3542 case VI_K_INSERT: // Cursor Key Insert
3543 dc_i:
3544 cmd_mode = 1; // start insrting
Eric Andersen3f980402001-04-04 17:31:15 +00003545 break;
3546 case 'J': // J- join current and next lines together
3547 if (cmdcnt-- > 2) {
3548 do_cmd(c);
3549 } // repeat cnt
3550 dot_end(); // move to NL
3551 if (dot < end - 1) { // make sure not last char in text[]
3552 *dot++ = ' '; // replace NL with space
Paul Fox8552aec2005-09-16 12:20:05 +00003553 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003554 while (isblnk(*dot)) { // delete leading WS
3555 dot_delete();
3556 }
3557 }
3558 end_cmd_q(); // stop adding to q
3559 break;
3560 case 'L': // L- goto bottom line on screen
3561 dot = end_screen();
3562 if (cmdcnt > (rows - 1)) {
3563 cmdcnt = (rows - 1);
3564 }
3565 if (cmdcnt-- > 1) {
3566 do_cmd('-');
3567 } // repeat cnt
3568 dot_begin();
3569 dot_skip_over_ws();
3570 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003571 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003572 dot = screenbegin;
3573 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3574 dot = next_line(dot);
3575 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003576 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003577 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003578 p = begin_line(dot);
3579 if (p[-1] == '\n') {
3580 dot_prev();
3581 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3582 dot_end();
3583 dot = char_insert(dot, '\n');
3584 } else {
3585 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003586 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003587 dot_prev(); // -
3588 }
3589 goto dc_i;
3590 break;
3591 case 'R': // R- continuous Replace char
Eric Andersen1c0d3112001-04-16 15:46:44 +00003592 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003593 cmd_mode = 2;
Eric Andersen3f980402001-04-04 17:31:15 +00003594 break;
3595 case 'X': // X- delete char before dot
3596 case 'x': // x- delete the current char
3597 case 's': // s- substitute the current char
3598 if (cmdcnt-- > 1) {
3599 do_cmd(c);
3600 } // repeat cnt
3601 dir = 0;
3602 if (c == 'X')
3603 dir = -1;
3604 if (dot[dir] != '\n') {
3605 if (c == 'X')
3606 dot--; // delete prev char
3607 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3608 }
3609 if (c == 's')
3610 goto dc_i; // start insrting
3611 end_cmd_q(); // stop adding to q
3612 break;
3613 case 'Z': // Z- if modified, {write}; exit
3614 // ZZ means to save file (if necessary), then exit
3615 c1 = get_one_char();
3616 if (c1 != 'Z') {
3617 indicate_error(c);
3618 break;
3619 }
Paul Foxf0305b72006-03-28 14:18:21 +00003620 if (file_modified) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003621#ifdef CONFIG_FEATURE_VI_READONLY
Paul Foxf0305b72006-03-28 14:18:21 +00003622 if (vi_readonly || readonly) {
3623 psbs("\"%s\" File is read only", cfn);
3624 break;
3625 }
3626#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00003627 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003628 if (cnt < 0) {
3629 if (cnt == -1)
3630 psbs("Write error: %s", strerror(errno));
3631 } else if (cnt == (end - 1 - text + 1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003632 editing = 0;
3633 }
3634 } else {
3635 editing = 0;
3636 }
3637 break;
3638 case '^': // ^- move to first non-blank on line
3639 dot_begin();
3640 dot_skip_over_ws();
3641 break;
3642 case 'b': // b- back a word
3643 case 'e': // e- end of word
3644 if (cmdcnt-- > 1) {
3645 do_cmd(c);
3646 } // repeat cnt
3647 dir = FORWARD;
3648 if (c == 'b')
3649 dir = BACK;
3650 if ((dot + dir) < text || (dot + dir) > end - 1)
3651 break;
3652 dot += dir;
3653 if (isspace(*dot)) {
3654 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3655 }
3656 if (isalnum(*dot) || *dot == '_') {
3657 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3658 } else if (ispunct(*dot)) {
3659 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3660 }
3661 break;
3662 case 'c': // c- change something
3663 case 'd': // d- delete something
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003664#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003665 case 'y': // y- yank something
3666 case 'Y': // Y- Yank a line
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003667#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003668 yf = YANKDEL; // assume either "c" or "d"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003669#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003670 if (c == 'y' || c == 'Y')
3671 yf = YANKONLY;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003672#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003673 c1 = 'y';
3674 if (c != 'Y')
3675 c1 = get_one_char(); // get the type of thing to delete
3676 find_range(&p, &q, c1);
3677 if (c1 == 27) { // ESC- user changed mind and wants out
3678 c = c1 = 27; // Escape- do nothing
3679 } else if (strchr("wW", c1)) {
3680 if (c == 'c') {
3681 // don't include trailing WS as part of word
3682 while (isblnk(*q)) {
3683 if (q <= text || q[-1] == '\n')
3684 break;
3685 q--;
3686 }
3687 }
3688 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003689 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003690 // single line copy text into a register and delete
3691 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003692 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003693 // multiple line copy text into a register and delete
3694 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003695 if (c == 'c') {
3696 dot = char_insert(dot, '\n');
3697 // on the last line of file don't move to prev line
3698 if (dot != (end-1)) {
3699 dot_prev();
3700 }
3701 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003702 dot_begin();
3703 dot_skip_over_ws();
3704 }
3705 } else {
3706 // could not recognize object
3707 c = c1 = 27; // error-
3708 indicate_error(c);
3709 }
3710 if (c1 != 27) {
3711 // if CHANGING, not deleting, start inserting after the delete
3712 if (c == 'c') {
3713 strcpy((char *) buf, "Change");
3714 goto dc_i; // start inserting
3715 }
3716 if (c == 'd') {
3717 strcpy((char *) buf, "Delete");
3718 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003719#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003720 if (c == 'y' || c == 'Y') {
3721 strcpy((char *) buf, "Yank");
3722 }
3723 p = reg[YDreg];
3724 q = p + strlen((char *) p);
3725 for (cnt = 0; p <= q; p++) {
3726 if (*p == '\n')
3727 cnt++;
3728 }
3729 psb("%s %d lines (%d chars) using [%c]",
3730 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003731#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003732 end_cmd_q(); // stop adding to q
3733 }
3734 break;
3735 case 'k': // k- goto prev line, same col
3736 case VI_K_UP: // cursor key Up
3737 if (cmdcnt-- > 1) {
3738 do_cmd(c);
3739 } // repeat cnt
3740 dot_prev();
3741 dot = move_to_col(dot, ccol + offset); // try stay in same col
3742 break;
3743 case 'r': // r- replace the current char with user input
3744 c1 = get_one_char(); // get the replacement char
3745 if (*dot != '\n') {
3746 *dot = c1;
Paul Fox8552aec2005-09-16 12:20:05 +00003747 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003748 }
3749 end_cmd_q(); // stop adding to q
3750 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003751 case 't': // t- move to char prior to next x
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00003752 last_forward_char = get_one_char();
3753 do_cmd(';');
3754 if (*dot == last_forward_char)
3755 dot_left();
3756 last_forward_char= 0;
Eric Andersen822c3832001-05-07 17:37:43 +00003757 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003758 case 'w': // w- forward a word
3759 if (cmdcnt-- > 1) {
3760 do_cmd(c);
3761 } // repeat cnt
3762 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3763 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3764 } else if (ispunct(*dot)) { // we are on PUNCT
3765 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3766 }
3767 if (dot < end - 1)
3768 dot++; // move over word
3769 if (isspace(*dot)) {
3770 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3771 }
3772 break;
3773 case 'z': // z-
3774 c1 = get_one_char(); // get the replacement char
3775 cnt = 0;
3776 if (c1 == '.')
3777 cnt = (rows - 2) / 2; // put dot at center
3778 if (c1 == '-')
3779 cnt = rows - 2; // put dot at bottom
3780 screenbegin = begin_line(dot); // start dot at top
3781 dot_scroll(cnt, -1);
3782 break;
3783 case '|': // |- move to column "cmdcnt"
3784 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3785 break;
3786 case '~': // ~- flip the case of letters a-z -> A-Z
3787 if (cmdcnt-- > 1) {
3788 do_cmd(c);
3789 } // repeat cnt
3790 if (islower(*dot)) {
3791 *dot = toupper(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003792 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003793 } else if (isupper(*dot)) {
3794 *dot = tolower(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003795 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003796 }
3797 dot_right();
3798 end_cmd_q(); // stop adding to q
3799 break;
3800 //----- The Cursor and Function Keys -----------------------------
3801 case VI_K_HOME: // Cursor Key Home
3802 dot_begin();
3803 break;
3804 // The Fn keys could point to do_macro which could translate them
3805 case VI_K_FUN1: // Function Key F1
3806 case VI_K_FUN2: // Function Key F2
3807 case VI_K_FUN3: // Function Key F3
3808 case VI_K_FUN4: // Function Key F4
3809 case VI_K_FUN5: // Function Key F5
3810 case VI_K_FUN6: // Function Key F6
3811 case VI_K_FUN7: // Function Key F7
3812 case VI_K_FUN8: // Function Key F8
3813 case VI_K_FUN9: // Function Key F9
3814 case VI_K_FUN10: // Function Key F10
3815 case VI_K_FUN11: // Function Key F11
3816 case VI_K_FUN12: // Function Key F12
3817 break;
3818 }
3819
3820 dc1:
3821 // if text[] just became empty, add back an empty line
3822 if (end == text) {
3823 (void) char_insert(text, '\n'); // start empty buf with dummy line
3824 dot = text;
3825 }
3826 // it is OK for dot to exactly equal to end, otherwise check dot validity
3827 if (dot != end) {
3828 dot = bound_dot(dot); // make sure "dot" is valid
3829 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003830#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003831 check_context(c); // update the current context
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003832#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003833
3834 if (!isdigit(c))
3835 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3836 cnt = dot - begin_line(dot);
3837 // Try to stay off of the Newline
3838 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3839 dot--;
3840}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003841
3842#ifdef CONFIG_FEATURE_VI_CRASHME
3843static int totalcmds = 0;
3844static int Mp = 85; // Movement command Probability
3845static int Np = 90; // Non-movement command Probability
3846static int Dp = 96; // Delete command Probability
3847static int Ip = 97; // Insert command Probability
3848static int Yp = 98; // Yank command Probability
3849static int Pp = 99; // Put command Probability
3850static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3851char chars[20] = "\t012345 abcdABCD-=.$";
3852char *words[20] = { "this", "is", "a", "test",
3853 "broadcast", "the", "emergency", "of",
3854 "system", "quick", "brown", "fox",
3855 "jumped", "over", "lazy", "dogs",
3856 "back", "January", "Febuary", "March"
3857};
3858char *lines[20] = {
3859 "You should have received a copy of the GNU General Public License\n",
3860 "char c, cm, *cmd, *cmd1;\n",
3861 "generate a command by percentages\n",
3862 "Numbers may be typed as a prefix to some commands.\n",
3863 "Quit, discarding changes!\n",
3864 "Forced write, if permission originally not valid.\n",
3865 "In general, any ex or ed command (such as substitute or delete).\n",
3866 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3867 "Please get w/ me and I will go over it with you.\n",
3868 "The following is a list of scheduled, committed changes.\n",
3869 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3870 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3871 "Any question about transactions please contact Sterling Huxley.\n",
3872 "I will try to get back to you by Friday, December 31.\n",
3873 "This Change will be implemented on Friday.\n",
3874 "Let me know if you have problems accessing this;\n",
3875 "Sterling Huxley recently added you to the access list.\n",
3876 "Would you like to go to lunch?\n",
3877 "The last command will be automatically run.\n",
3878 "This is too much english for a computer geek.\n",
3879};
3880char *multilines[20] = {
3881 "You should have received a copy of the GNU General Public License\n",
3882 "char c, cm, *cmd, *cmd1;\n",
3883 "generate a command by percentages\n",
3884 "Numbers may be typed as a prefix to some commands.\n",
3885 "Quit, discarding changes!\n",
3886 "Forced write, if permission originally not valid.\n",
3887 "In general, any ex or ed command (such as substitute or delete).\n",
3888 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3889 "Please get w/ me and I will go over it with you.\n",
3890 "The following is a list of scheduled, committed changes.\n",
3891 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3892 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3893 "Any question about transactions please contact Sterling Huxley.\n",
3894 "I will try to get back to you by Friday, December 31.\n",
3895 "This Change will be implemented on Friday.\n",
3896 "Let me know if you have problems accessing this;\n",
3897 "Sterling Huxley recently added you to the access list.\n",
3898 "Would you like to go to lunch?\n",
3899 "The last command will be automatically run.\n",
3900 "This is too much english for a computer geek.\n",
3901};
3902
3903// create a random command to execute
3904static void crash_dummy()
3905{
3906 static int sleeptime; // how long to pause between commands
3907 char c, cm, *cmd, *cmd1;
3908 int i, cnt, thing, rbi, startrbi, percent;
3909
3910 // "dot" movement commands
3911 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3912
3913 // is there already a command running?
3914 if (readed_for_parse > 0)
3915 goto cd1;
3916 cd0:
3917 startrbi = rbi = 0;
3918 sleeptime = 0; // how long to pause between commands
3919 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3920 // generate a command by percentages
3921 percent = (int) lrand48() % 100; // get a number from 0-99
3922 if (percent < Mp) { // Movement commands
3923 // available commands
3924 cmd = cmd1;
3925 M++;
3926 } else if (percent < Np) { // non-movement commands
3927 cmd = "mz<>\'\""; // available commands
3928 N++;
3929 } else if (percent < Dp) { // Delete commands
3930 cmd = "dx"; // available commands
3931 D++;
3932 } else if (percent < Ip) { // Inset commands
3933 cmd = "iIaAsrJ"; // available commands
3934 I++;
3935 } else if (percent < Yp) { // Yank commands
3936 cmd = "yY"; // available commands
3937 Y++;
3938 } else if (percent < Pp) { // Put commands
3939 cmd = "pP"; // available commands
3940 P++;
3941 } else {
3942 // We do not know how to handle this command, try again
3943 U++;
3944 goto cd0;
3945 }
3946 // randomly pick one of the available cmds from "cmd[]"
3947 i = (int) lrand48() % strlen(cmd);
3948 cm = cmd[i];
3949 if (strchr(":\024", cm))
3950 goto cd0; // dont allow colon or ctrl-T commands
3951 readbuffer[rbi++] = cm; // put cmd into input buffer
3952
3953 // now we have the command-
3954 // there are 1, 2, and multi char commands
3955 // find out which and generate the rest of command as necessary
3956 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3957 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3958 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3959 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3960 }
3961 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3962 c = cmd1[thing];
3963 readbuffer[rbi++] = c; // add movement to input buffer
3964 }
3965 if (strchr("iIaAsc", cm)) { // multi-char commands
3966 if (cm == 'c') {
3967 // change some thing
3968 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3969 c = cmd1[thing];
3970 readbuffer[rbi++] = c; // add movement to input buffer
3971 }
3972 thing = (int) lrand48() % 4; // what thing to insert
3973 cnt = (int) lrand48() % 10; // how many to insert
3974 for (i = 0; i < cnt; i++) {
3975 if (thing == 0) { // insert chars
3976 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3977 } else if (thing == 1) { // insert words
3978 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
3979 strcat((char *) readbuffer, " ");
3980 sleeptime = 0; // how fast to type
3981 } else if (thing == 2) { // insert lines
3982 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
3983 sleeptime = 0; // how fast to type
3984 } else { // insert multi-lines
3985 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
3986 sleeptime = 0; // how fast to type
3987 }
3988 }
3989 strcat((char *) readbuffer, "\033");
3990 }
3991 readed_for_parse = strlen(readbuffer);
3992 cd1:
3993 totalcmds++;
3994 if (sleeptime > 0)
3995 (void) mysleep(sleeptime); // sleep 1/100 sec
3996}
3997
3998// test to see if there are any errors
3999static void crash_test()
4000{
4001 static time_t oldtim;
4002 time_t tim;
Glenn L McGrath7127b582002-12-03 21:48:15 +00004003 char d[2], msg[BUFSIZ];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004004
4005 msg[0] = '\0';
4006 if (end < text) {
4007 strcat((char *) msg, "end<text ");
4008 }
4009 if (end > textend) {
4010 strcat((char *) msg, "end>textend ");
4011 }
4012 if (dot < text) {
4013 strcat((char *) msg, "dot<text ");
4014 }
4015 if (dot > end) {
4016 strcat((char *) msg, "dot>end ");
4017 }
4018 if (screenbegin < text) {
4019 strcat((char *) msg, "screenbegin<text ");
4020 }
4021 if (screenbegin > end - 1) {
4022 strcat((char *) msg, "screenbegin>end-1 ");
4023 }
4024
4025 if (strlen(msg) > 0) {
4026 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00004027 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004028 totalcmds, last_input_char, msg, SOs, SOn);
4029 fflush(stdout);
4030 while (read(0, d, 1) > 0) {
4031 if (d[0] == '\n' || d[0] == '\r')
4032 break;
4033 }
4034 alarm(3);
4035 }
4036 tim = (time_t) time((time_t *) 0);
4037 if (tim >= (oldtim + 3)) {
4038 sprintf((char *) status_buffer,
4039 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
4040 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
4041 oldtim = tim;
4042 }
4043 return;
4044}
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00004045#endif /* CONFIG_FEATURE_VI_CRASHME */