blob: df37affac9a821f8a64f2fbc1c3253958e5904f2 [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>
Bernhard Reutner-Fischera2a647d2006-05-19 12:30:00 +000056#include <strings.h>
Eric Andersen3f980402001-04-04 17:31:15 +000057#include <termios.h>
58#include <unistd.h>
59#include <sys/ioctl.h>
Eric Andersen3f980402001-04-04 17:31:15 +000060#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 */
Bernhard Reutner-Fischer1e23b6f2006-06-09 07:12:27 +0000305#if defined(CONFIG_FEATURE_VI_YANKMARK) || (defined(CONFIG_FEATURE_VI_COLON) && 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
Bernhard Reutner-Fischer1e23b6f2006-06-09 07:12:27 +00002011#if defined(CONFIG_FEATURE_VI_YANKMARK) || (defined(CONFIG_FEATURE_VI_COLON) && 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);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002182 signal(SIGABRT, core_sig);
2183 signal(SIGFPE, core_sig);
2184 signal(SIGBUS, core_sig);
2185 signal(SIGSEGV, core_sig);
2186#ifdef SIGSYS
2187 signal(SIGSYS, core_sig);
2188#endif
2189
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002190 if(sig) { // signaled
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002191 dot = bound_dot(dot); // make sure "dot" is valid
2192 longjmp(restart, sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002193 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002194}
2195#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2196
2197static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2198{
2199 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002200 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002201 FD_ZERO(&rfds);
2202 FD_SET(0, &rfds);
2203 tv.tv_sec = 0;
2204 tv.tv_usec = hund * 10000;
2205 select(1, &rfds, NULL, NULL, &tv);
2206 return (FD_ISSET(0, &rfds));
2207}
2208
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002209#define readbuffer bb_common_bufsiz1
2210
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002211static int readed_for_parse;
2212
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002213//----- IO Routines --------------------------------------------
2214static Byte readit(void) // read (maybe cursor) key from stdin
2215{
2216 Byte c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002217 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002218 struct esc_cmds {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002219 const char *seq;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002220 Byte val;
2221 };
2222
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002223 static const struct esc_cmds esccmds[] = {
2224 {"OA", (Byte) VI_K_UP}, // cursor key Up
2225 {"OB", (Byte) VI_K_DOWN}, // cursor key Down
2226 {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2227 {"OD", (Byte) VI_K_LEFT}, // cursor key Left
2228 {"OH", (Byte) VI_K_HOME}, // Cursor Key Home
2229 {"OF", (Byte) VI_K_END}, // Cursor Key End
2230 {"[A", (Byte) VI_K_UP}, // cursor key Up
2231 {"[B", (Byte) VI_K_DOWN}, // cursor key Down
2232 {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right
2233 {"[D", (Byte) VI_K_LEFT}, // cursor key Left
2234 {"[H", (Byte) VI_K_HOME}, // Cursor Key Home
2235 {"[F", (Byte) VI_K_END}, // Cursor Key End
2236 {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home
2237 {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2238 {"[4~", (Byte) VI_K_END}, // Cursor Key End
2239 {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2240 {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2241 {"OP", (Byte) VI_K_FUN1}, // Function Key F1
2242 {"OQ", (Byte) VI_K_FUN2}, // Function Key F2
2243 {"OR", (Byte) VI_K_FUN3}, // Function Key F3
2244 {"OS", (Byte) VI_K_FUN4}, // Function Key F4
2245 {"[15~", (Byte) VI_K_FUN5}, // Function Key F5
2246 {"[17~", (Byte) VI_K_FUN6}, // Function Key F6
2247 {"[18~", (Byte) VI_K_FUN7}, // Function Key F7
2248 {"[19~", (Byte) VI_K_FUN8}, // Function Key F8
2249 {"[20~", (Byte) VI_K_FUN9}, // Function Key F9
2250 {"[21~", (Byte) VI_K_FUN10}, // Function Key F10
2251 {"[23~", (Byte) VI_K_FUN11}, // Function Key F11
2252 {"[24~", (Byte) VI_K_FUN12}, // Function Key F12
2253 {"[11~", (Byte) VI_K_FUN1}, // Function Key F1
2254 {"[12~", (Byte) VI_K_FUN2}, // Function Key F2
2255 {"[13~", (Byte) VI_K_FUN3}, // Function Key F3
2256 {"[14~", (Byte) VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002257 };
2258
2259#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2260
2261 (void) alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002262 fflush(stdout);
2263 n = readed_for_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002264 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002265 if (n <= 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002266 ri0:
2267 // the Q is empty, wait for a typed char
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002268 n = read(0, readbuffer, BUFSIZ - 1);
2269 if (n < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002270 if (errno == EINTR)
2271 goto ri0; // interrupted sys call
2272 if (errno == EBADF)
2273 editing = 0;
2274 if (errno == EFAULT)
2275 editing = 0;
2276 if (errno == EINVAL)
2277 editing = 0;
2278 if (errno == EIO)
2279 editing = 0;
2280 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002281 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002282 if(n <= 0)
2283 return 0; // error
2284 if (readbuffer[0] == 27) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002285 // This is an ESC char. Is this Esc sequence?
2286 // Could be bare Esc key. See if there are any
2287 // more chars to read after the ESC. This would
2288 // be a Function or Cursor Key sequence.
2289 FD_ZERO(&rfds);
2290 FD_SET(0, &rfds);
2291 tv.tv_sec = 0;
2292 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2293
2294 // keep reading while there are input chars and room in buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002295 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002296 // read the rest of the ESC string
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002297 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2298 if (r > 0) {
2299 n += r;
2300 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002301 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002302 }
2303 readed_for_parse = n;
2304 }
2305 c = readbuffer[0];
2306 if(c == 27 && n > 1) {
2307 // Maybe cursor or function key?
2308 const struct esc_cmds *eindex;
2309
2310 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2311 int cnt = strlen(eindex->seq);
2312
2313 if(n <= cnt)
2314 continue;
2315 if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
2316 continue;
2317 // is a Cursor key- put derived value back into Q
2318 c = eindex->val;
2319 // for squeeze out the ESC sequence
2320 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002321 break;
2322 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002323 if(eindex == &esccmds[ESCCMDS_COUNT]) {
2324 /* defined ESC sequence not found, set only one ESC */
2325 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002326 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002327 } else {
2328 n = 1;
2329 }
2330 // remove key sequence from Q
2331 readed_for_parse -= n;
2332 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002333 (void) alarm(3); // we are done waiting for input, turn alarm ON
2334 return (c);
2335}
2336
2337//----- IO Routines --------------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002338static Byte get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002339{
2340 static Byte c;
2341
2342#ifdef CONFIG_FEATURE_VI_DOT_CMD
2343 // ! adding2q && ioq == 0 read()
2344 // ! adding2q && ioq != 0 *ioq
2345 // adding2q *last_modifying_cmd= read()
2346 if (!adding2q) {
2347 // we are not adding to the q.
2348 // but, we may be reading from a q
2349 if (ioq == 0) {
2350 // there is no current q, read from STDIN
2351 c = readit(); // get the users input
2352 } else {
2353 // there is a queue to get chars from first
2354 c = *ioq++;
2355 if (c == '\0') {
2356 // the end of the q, read from STDIN
2357 free(ioq_start);
2358 ioq_start = ioq = 0;
2359 c = readit(); // get the users input
2360 }
2361 }
2362 } else {
2363 // adding STDIN chars to q
2364 c = readit(); // get the users input
2365 if (last_modifying_cmd != 0) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002366 int len = strlen((char *) last_modifying_cmd);
2367 if (len + 1 >= BUFSIZ) {
2368 psbs("last_modifying_cmd overrun");
2369 } else {
2370 // add new char to q
2371 last_modifying_cmd[len] = c;
2372 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002373 }
2374 }
2375#else /* CONFIG_FEATURE_VI_DOT_CMD */
2376 c = readit(); // get the users input
2377#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2378 return (c); // return the char, where ever it came from
2379}
2380
2381static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2382{
2383 Byte buf[BUFSIZ];
2384 Byte c;
2385 int i;
2386 static Byte *obufp = NULL;
2387
2388 strcpy((char *) buf, (char *) prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002389 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002390 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2391 clear_to_eol(); // clear the line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002392 write1((char *) prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002393
2394 for (i = strlen((char *) buf); i < BUFSIZ;) {
2395 c = get_one_char(); // read user input
2396 if (c == '\n' || c == '\r' || c == 27)
2397 break; // is this end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002398 if (c == erase_char || c == 8 || c == 127) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002399 // user wants to erase prev char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002400 i--; // backup to prev char
2401 buf[i] = '\0'; // erase the char
2402 buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002403 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002404 if (i <= 0) { // user backs up before b-o-l, exit
2405 break;
2406 }
2407 } else {
2408 buf[i] = c; // save char in buffer
2409 buf[i + 1] = '\0'; // make sure buffer is null terminated
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002410 putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002411 i++;
2412 }
2413 }
2414 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002415 free(obufp);
Manuel Novoa III cad53642003-03-19 09:13:01 +00002416 obufp = (Byte *) bb_xstrdup((char *) buf);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002417 return (obufp);
2418}
2419
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002420static int file_size(const Byte * fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002421{
2422 struct stat st_buf;
2423 int cnt, sr;
2424
Eric Andersena68ea1c2006-01-30 22:48:39 +00002425 if (fn == 0 || strlen((char *)fn) <= 0)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002426 return (-1);
2427 cnt = -1;
2428 sr = stat((char *) fn, &st_buf); // see if file exists
2429 if (sr >= 0) {
2430 cnt = (int) st_buf.st_size;
2431 }
2432 return (cnt);
2433}
2434
2435static int file_insert(Byte * fn, Byte * p, int size)
2436{
2437 int fd, cnt;
2438
2439 cnt = -1;
2440#ifdef CONFIG_FEATURE_VI_READONLY
2441 readonly = FALSE;
2442#endif /* CONFIG_FEATURE_VI_READONLY */
2443 if (fn == 0 || strlen((char*) fn) <= 0) {
2444 psbs("No filename given");
2445 goto fi0;
2446 }
2447 if (size == 0) {
2448 // OK- this is just a no-op
2449 cnt = 0;
2450 goto fi0;
2451 }
2452 if (size < 0) {
2453 psbs("Trying to insert a negative number (%d) of characters", size);
2454 goto fi0;
2455 }
2456 if (p < text || p > end) {
2457 psbs("Trying to insert file outside of memory");
2458 goto fi0;
2459 }
2460
2461 // see if we can open the file
2462#ifdef CONFIG_FEATURE_VI_READONLY
2463 if (vi_readonly) goto fi1; // do not try write-mode
2464#endif
2465 fd = open((char *) fn, O_RDWR); // assume read & write
2466 if (fd < 0) {
2467 // could not open for writing- maybe file is read only
2468#ifdef CONFIG_FEATURE_VI_READONLY
2469 fi1:
2470#endif
2471 fd = open((char *) fn, O_RDONLY); // try read-only
2472 if (fd < 0) {
2473 psbs("\"%s\" %s", fn, "could not open file");
2474 goto fi0;
2475 }
2476#ifdef CONFIG_FEATURE_VI_READONLY
2477 // got the file- read-only
2478 readonly = TRUE;
2479#endif /* CONFIG_FEATURE_VI_READONLY */
2480 }
2481 p = text_hole_make(p, size);
2482 cnt = read(fd, p, size);
2483 close(fd);
2484 if (cnt < 0) {
2485 cnt = -1;
2486 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2487 psbs("could not read file \"%s\"", fn);
2488 } else if (cnt < size) {
2489 // There was a partial read, shrink unused space text[]
2490 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
2491 psbs("could not read all of file \"%s\"", fn);
2492 }
2493 if (cnt >= size)
Paul Fox8552aec2005-09-16 12:20:05 +00002494 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002495 fi0:
2496 return (cnt);
2497}
2498
2499static int file_write(Byte * fn, Byte * first, Byte * last)
2500{
2501 int fd, cnt, charcnt;
2502
2503 if (fn == 0) {
2504 psbs("No current filename");
Paul Fox61e45db2005-10-09 14:43:22 +00002505 return (-2);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002506 }
2507 charcnt = 0;
2508 // FIXIT- use the correct umask()
2509 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2510 if (fd < 0)
2511 return (-1);
2512 cnt = last - first + 1;
2513 charcnt = write(fd, first, cnt);
2514 if (charcnt == cnt) {
2515 // good write
2516 //file_modified= FALSE; // the file has not been modified
2517 } else {
2518 charcnt = 0;
2519 }
2520 close(fd);
2521 return (charcnt);
2522}
2523
2524//----- Terminal Drawing ---------------------------------------
2525// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002526// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002527// screen coordinates
2528// 0,0 ... 0,79
2529// 1,0 ... 1,79
2530// . ... .
2531// . ... .
2532// 22,0 ... 22,79
2533// 23,0 ... 23,79 status line
2534//
2535
2536//----- Move the cursor to row x col (count from 0, not 1) -------
2537static void place_cursor(int row, int col, int opti)
2538{
2539 char cm1[BUFSIZ];
2540 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002541#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2542 char cm2[BUFSIZ];
2543 Byte *screenp;
2544 // char cm3[BUFSIZ];
2545 int Rrow= last_row;
2546#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002547
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002548 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2549
2550 if (row < 0) row = 0;
2551 if (row >= rows) row = rows - 1;
2552 if (col < 0) col = 0;
2553 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002554
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002555 //----- 1. Try the standard terminal ESC sequence
2556 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2557 cm= cm1;
2558 if (! opti) goto pc0;
2559
2560#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2561 //----- find the minimum # of chars to move cursor -------------
2562 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2563 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002564
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002565 // move to the correct row
2566 while (row < Rrow) {
2567 // the cursor has to move up
2568 strcat(cm2, CMup);
2569 Rrow--;
2570 }
2571 while (row > Rrow) {
2572 // the cursor has to move down
2573 strcat(cm2, CMdown);
2574 Rrow++;
2575 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002576
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002577 // now move to the correct column
2578 strcat(cm2, "\r"); // start at col 0
2579 // just send out orignal source char to get to correct place
2580 screenp = &screen[row * columns]; // start of screen line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002581 strncat(cm2, (char* )screenp, col);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002582
2583 //----- 3. Try some other way of moving cursor
2584 //---------------------------------------------
2585
2586 // pick the shortest cursor motion to send out
2587 cm= cm1;
2588 if (strlen(cm2) < strlen(cm)) {
2589 cm= cm2;
2590 } /* else if (strlen(cm3) < strlen(cm)) {
2591 cm= cm3;
2592 } */
2593#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2594 pc0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002595 write1(cm); // move the cursor
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002596}
2597
2598//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002599static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002600{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002601 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002602}
2603
2604//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002605static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002606{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002607 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002608}
2609
2610//----- Start standout mode ------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002611static void standout_start(void) // send "start reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002612{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002613 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002614}
2615
2616//----- End standout mode --------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002617static void standout_end(void) // send "end reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002618{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002619 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002620}
2621
2622//----- Flash the screen --------------------------------------
2623static void flash(int h)
2624{
2625 standout_start(); // send "start reverse video" sequence
2626 redraw(TRUE);
2627 (void) mysleep(h);
2628 standout_end(); // send "end reverse video" sequence
2629 redraw(TRUE);
2630}
2631
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002632static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002633{
2634#ifdef CONFIG_FEATURE_VI_CRASHME
2635 if (crashme > 0)
2636 return; // generate a random command
2637#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002638 if (!err_method) {
2639 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002640 } else {
2641 flash(10);
2642 }
2643}
2644
2645//----- Screen[] Routines --------------------------------------
2646//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002647static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002648{
2649 memset(screen, ' ', screensize); // clear new screen
2650}
2651
Eric Andersena68ea1c2006-01-30 22:48:39 +00002652static int bufsum(unsigned char *buf, int count)
Paul Fox8552aec2005-09-16 12:20:05 +00002653{
2654 int sum = 0;
Eric Andersena68ea1c2006-01-30 22:48:39 +00002655 unsigned char *e = buf + count;
Paul Fox8552aec2005-09-16 12:20:05 +00002656 while (buf < e)
2657 sum += *buf++;
2658 return sum;
2659}
2660
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002661//----- Draw the status line at bottom of the screen -------------
2662static void show_status_line(void)
2663{
Paul Foxc3504852005-09-16 12:48:18 +00002664 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002665
Paul Fox8552aec2005-09-16 12:20:05 +00002666 // either we already have an error or status message, or we
2667 // create one.
2668 if (!have_status_msg) {
2669 cnt = format_edit_status();
2670 cksum = bufsum(status_buffer, cnt);
2671 }
2672 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
2673 last_status_cksum= cksum; // remember if we have seen this line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002674 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002675 write1((char*)status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002676 clear_to_eol();
Paul Fox8552aec2005-09-16 12:20:05 +00002677 if (have_status_msg) {
Eric Andersena68ea1c2006-01-30 22:48:39 +00002678 if (((int)strlen((char*)status_buffer) - (have_status_msg - 1)) >
Paul Fox8552aec2005-09-16 12:20:05 +00002679 (columns - 1) ) {
2680 have_status_msg = 0;
2681 Hit_Return();
2682 }
2683 have_status_msg = 0;
2684 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002685 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2686 }
Eric Andersena9eb33d2004-08-19 19:15:06 +00002687 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002688}
2689
2690//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00002691// format status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002692static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002693{
2694 va_list args;
2695
2696 va_start(args, format);
2697 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
Eric Andersen0ef24c62005-07-18 10:32:59 +00002698 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002699 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2700 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002701
2702 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002703
2704 return;
2705}
2706
Paul Fox8552aec2005-09-16 12:20:05 +00002707// format status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002708static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002709{
2710 va_list args;
2711
2712 va_start(args, format);
2713 vsprintf((char *) status_buffer, format, args);
2714 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002715
2716 have_status_msg = 1;
2717
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002718 return;
2719}
2720
2721static void ni(Byte * s) // display messages
2722{
2723 Byte buf[BUFSIZ];
2724
2725 print_literal(buf, s);
2726 psbs("\'%s\' is not implemented", buf);
2727}
2728
Paul Fox8552aec2005-09-16 12:20:05 +00002729static int format_edit_status(void) // show file status on status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002730{
Paul Fox8552aec2005-09-16 12:20:05 +00002731 int cur, percent, ret, trunc_at;
2732 static int tot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002733
Paul Fox8552aec2005-09-16 12:20:05 +00002734 // file_modified is now a counter rather than a flag. this
2735 // helps reduce the amount of line counting we need to do.
2736 // (this will cause a mis-reporting of modified status
2737 // once every MAXINT editing operations.)
2738
2739 // it would be nice to do a similar optimization here -- if
2740 // we haven't done a motion that could have changed which line
2741 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002742 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00002743
2744 // reduce counting -- the total lines can't have
2745 // changed if we haven't done any edits.
2746 if (file_modified != last_file_modified) {
2747 tot = cur + count_lines(dot, end - 1) - 1;
2748 last_file_modified = file_modified;
2749 }
2750
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002751 // current line percent
2752 // ------------- ~~ ----------
2753 // total lines 100
2754 if (tot > 0) {
2755 percent = (100 * cur) / tot;
2756 } else {
2757 cur = tot = 0;
2758 percent = 100;
2759 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00002760
Paul Fox8552aec2005-09-16 12:20:05 +00002761 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2762 columns : STATUS_BUFFER_LEN-1;
2763
2764 ret = snprintf((char *) status_buffer, trunc_at+1,
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002765#ifdef CONFIG_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002766 "%c %s%s%s %d/%d %d%%",
2767#else
2768 "%c %s%s %d/%d %d%%",
2769#endif
2770 (cmd_mode ? (cmd_mode == 2 ? 'R':'I'):'-'),
2771 (cfn != 0 ? (char *) cfn : "No file"),
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002772#ifdef CONFIG_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002773 ((vi_readonly || readonly) ? " [Read-only]" : ""),
2774#endif
2775 (file_modified ? " [modified]" : ""),
2776 cur, tot, percent);
2777
2778 if (ret >= 0 && ret < trunc_at)
2779 return ret; /* it all fit */
2780
2781 return trunc_at; /* had to truncate */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002782}
2783
2784//----- Force refresh of all Lines -----------------------------
2785static void redraw(int full_screen)
2786{
2787 place_cursor(0, 0, FALSE); // put cursor in correct place
2788 clear_to_eos(); // tel terminal to erase display
2789 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00002790 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002791 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00002792 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002793}
2794
2795//----- Format a text[] line into a buffer ---------------------
2796static void format_line(Byte *dest, Byte *src, int li)
2797{
2798 int co;
2799 Byte c;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002800
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002801 for (co= 0; co < MAX_SCR_COLS; co++) {
2802 c= ' '; // assume blank
2803 if (li > 0 && co == 0) {
2804 c = '~'; // not first line, assume Tilde
2805 }
2806 // are there chars in text[] and have we gone past the end
2807 if (text < end && src < end) {
2808 c = *src++;
2809 }
2810 if (c == '\n')
2811 break;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002812 if (c > 127 && !Isprint(c)) {
2813 c = '.';
2814 }
2815 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002816 if (c == '\t') {
2817 c = ' ';
2818 // co % 8 != 7
2819 for (; (co % tabstop) != (tabstop - 1); co++) {
2820 dest[co] = c;
2821 }
2822 } else {
2823 dest[co++] = '^';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002824 if(c == 127)
2825 c = '?';
2826 else
2827 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002828 }
2829 }
2830 // the co++ is done here so that the column will
2831 // not be overwritten when we blank-out the rest of line
2832 dest[co] = c;
2833 if (src >= end)
2834 break;
2835 }
2836}
2837
2838//----- Refresh the changed screen lines -----------------------
2839// Copy the source line from text[] into the buffer and note
2840// if the current screenline is different from the new buffer.
2841// If they differ then that line needs redrawing on the terminal.
2842//
2843static void refresh(int full_screen)
2844{
2845 static int old_offset;
2846 int li, changed;
2847 Byte buf[MAX_SCR_COLS];
2848 Byte *tp, *sp; // pointer into text[] and screen[]
2849#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2850 int last_li= -2; // last line that changed- for optimizing cursor movement
2851#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2852
2853#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2854 window_size_get(0);
2855#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2856 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2857 tp = screenbegin; // index into text[] of top line
2858
2859 // compare text[] to screen[] and mark screen[] lines that need updating
2860 for (li = 0; li < rows - 1; li++) {
2861 int cs, ce; // column start & end
2862 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2863 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2864 // format current text line into buf
2865 format_line(buf, tp, li);
2866
2867 // skip to the end of the current text[] line
2868 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2869
2870 // see if there are any changes between vitual screen and buf
2871 changed = FALSE; // assume no change
2872 cs= 0;
2873 ce= columns-1;
2874 sp = &screen[li * columns]; // start of screen line
2875 if (full_screen) {
2876 // force re-draw of every single column from 0 - columns-1
2877 goto re0;
2878 }
2879 // compare newly formatted buffer with virtual screen
2880 // look forward for first difference between buf and screen
2881 for ( ; cs <= ce; cs++) {
2882 if (buf[cs + offset] != sp[cs]) {
2883 changed = TRUE; // mark for redraw
2884 break;
2885 }
2886 }
2887
2888 // look backward for last difference between buf and screen
2889 for ( ; ce >= cs; ce--) {
2890 if (buf[ce + offset] != sp[ce]) {
2891 changed = TRUE; // mark for redraw
2892 break;
2893 }
2894 }
2895 // now, cs is index of first diff, and ce is index of last diff
2896
2897 // if horz offset has changed, force a redraw
2898 if (offset != old_offset) {
2899 re0:
2900 changed = TRUE;
2901 }
2902
2903 // make a sanity check of columns indexes
2904 if (cs < 0) cs= 0;
2905 if (ce > columns-1) ce= columns-1;
2906 if (cs > ce) { cs= 0; ce= columns-1; }
2907 // is there a change between vitual screen and buf
2908 if (changed) {
2909 // copy changed part of buffer to virtual screen
2910 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2911
2912 // move cursor to column of first change
2913 if (offset != old_offset) {
2914 // opti_cur_move is still too stupid
2915 // to handle offsets correctly
2916 place_cursor(li, cs, FALSE);
2917 } else {
2918#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2919 // if this just the next line
2920 // try to optimize cursor movement
2921 // otherwise, use standard ESC sequence
2922 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2923 last_li= li;
2924#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2925 place_cursor(li, cs, FALSE); // use standard ESC sequence
2926#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2927 }
2928
2929 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002930 {
2931 int nic = ce-cs+1;
Eric Andersena68ea1c2006-01-30 22:48:39 +00002932 char *out = (char*)sp+cs;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002933
2934 while(nic-- > 0) {
2935 putchar(*out);
2936 out++;
2937 }
2938 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002939#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2940 last_row = li;
2941#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2942 }
2943 }
2944
2945#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2946 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2947 last_row = crow;
2948#else
2949 place_cursor(crow, ccol, FALSE);
2950#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002951
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002952 if (offset != old_offset)
2953 old_offset = offset;
2954}
2955
Eric Andersen3f980402001-04-04 17:31:15 +00002956//---------------------------------------------------------------------
2957//----- the Ascii Chart -----------------------------------------------
2958//
2959// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2960// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2961// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2962// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2963// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2964// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2965// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2966// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2967// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2968// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2969// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2970// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2971// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2972// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2973// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2974// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2975//---------------------------------------------------------------------
2976
2977//----- Execute a Vi Command -----------------------------------
2978static void do_cmd(Byte c)
2979{
2980 Byte c1, *p, *q, *msg, buf[9], *save_dot;
2981 int cnt, i, j, dir, yf;
2982
2983 c1 = c; // quiet the compiler
2984 cnt = yf = dir = 0; // quiet the compiler
2985 p = q = save_dot = msg = buf; // quiet the compiler
2986 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00002987
Paul Fox8552aec2005-09-16 12:20:05 +00002988 show_status_line();
2989
Eric Andersenbff7a602001-11-17 07:15:43 +00002990 /* if this is a cursor key, skip these checks */
2991 switch (c) {
2992 case VI_K_UP:
2993 case VI_K_DOWN:
2994 case VI_K_LEFT:
2995 case VI_K_RIGHT:
2996 case VI_K_HOME:
2997 case VI_K_END:
2998 case VI_K_PAGEUP:
2999 case VI_K_PAGEDOWN:
3000 goto key_cmd_mode;
3001 }
3002
Eric Andersen3f980402001-04-04 17:31:15 +00003003 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003004 // flip-flop Insert/Replace mode
3005 if (c == VI_K_INSERT) goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00003006 // we are 'R'eplacing the current *dot with new char
3007 if (*dot == '\n') {
3008 // don't Replace past E-o-l
3009 cmd_mode = 1; // convert to insert
3010 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003011 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003012 if (c != 27)
3013 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3014 dot = char_insert(dot, c); // insert new char
3015 }
3016 goto dc1;
3017 }
3018 }
3019 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003020 // hitting "Insert" twice means "R" replace mode
3021 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00003022 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003023 if (1 <= c || Isprint(c)) {
3024 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00003025 }
3026 goto dc1;
3027 }
3028
Eric Andersenbff7a602001-11-17 07:15:43 +00003029key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00003030 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00003031 //case 0x01: // soh
3032 //case 0x09: // ht
3033 //case 0x0b: // vt
3034 //case 0x0e: // so
3035 //case 0x0f: // si
3036 //case 0x10: // dle
3037 //case 0x11: // dc1
3038 //case 0x13: // dc3
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003039#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00003040 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00003041 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003042 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003043#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen822c3832001-05-07 17:37:43 +00003044 //case 0x16: // syn
3045 //case 0x17: // etb
3046 //case 0x18: // can
3047 //case 0x1c: // fs
3048 //case 0x1d: // gs
3049 //case 0x1e: // rs
3050 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003051 //case '!': // !-
3052 //case '#': // #-
3053 //case '&': // &-
3054 //case '(': // (-
3055 //case ')': // )-
3056 //case '*': // *-
3057 //case ',': // ,-
3058 //case '=': // =-
3059 //case '@': // @-
3060 //case 'F': // F-
3061 //case 'K': // K-
3062 //case 'Q': // Q-
3063 //case 'S': // S-
3064 //case 'T': // T-
3065 //case 'V': // V-
3066 //case '[': // [-
3067 //case '\\': // \-
3068 //case ']': // ]-
3069 //case '_': // _-
3070 //case '`': // `-
3071 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003072 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003073 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00003074 default: // unrecognised command
3075 buf[0] = c;
3076 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003077 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00003078 buf[0] = '^';
3079 buf[1] = c + '@';
3080 buf[2] = '\0';
3081 }
3082 ni((Byte *) buf);
3083 end_cmd_q(); // stop adding to q
3084 case 0x00: // nul- ignore
3085 break;
3086 case 2: // ctrl-B scroll up full screen
3087 case VI_K_PAGEUP: // Cursor Key Page Up
3088 dot_scroll(rows - 2, -1);
3089 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003090#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003091 case 0x03: // ctrl-C interrupt
3092 longjmp(restart, 1);
3093 break;
3094 case 26: // ctrl-Z suspend
3095 suspend_sig(SIGTSTP);
3096 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003097#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +00003098 case 4: // ctrl-D scroll down half screen
3099 dot_scroll((rows - 2) / 2, 1);
3100 break;
3101 case 5: // ctrl-E scroll down one line
3102 dot_scroll(1, 1);
3103 break;
3104 case 6: // ctrl-F scroll down full screen
3105 case VI_K_PAGEDOWN: // Cursor Key Page Down
3106 dot_scroll(rows - 2, 1);
3107 break;
3108 case 7: // ctrl-G show current status
Paul Fox8552aec2005-09-16 12:20:05 +00003109 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003110 break;
3111 case 'h': // h- move left
3112 case VI_K_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003113 case 8: // ctrl-H- move left (This may be ERASE char)
3114 case 127: // DEL- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003115 if (cmdcnt-- > 1) {
3116 do_cmd(c);
3117 } // repeat cnt
3118 dot_left();
3119 break;
3120 case 10: // Newline ^J
3121 case 'j': // j- goto next line, same col
3122 case VI_K_DOWN: // cursor key Down
3123 if (cmdcnt-- > 1) {
3124 do_cmd(c);
3125 } // repeat cnt
3126 dot_next(); // go to next B-o-l
3127 dot = move_to_col(dot, ccol + offset); // try stay in same col
3128 break;
3129 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003130 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003131 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003132 clear_to_eos(); // tel terminal to erase display
3133 (void) mysleep(10);
3134 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003135 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003136 refresh(TRUE); // this will redraw the entire display
3137 break;
3138 case 13: // Carriage Return ^M
3139 case '+': // +- goto next line
3140 if (cmdcnt-- > 1) {
3141 do_cmd(c);
3142 } // repeat cnt
3143 dot_next();
3144 dot_skip_over_ws();
3145 break;
3146 case 21: // ctrl-U scroll up half screen
3147 dot_scroll((rows - 2) / 2, -1);
3148 break;
3149 case 25: // ctrl-Y scroll up one line
3150 dot_scroll(1, -1);
3151 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003152 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003153 if (cmd_mode == 0)
3154 indicate_error(c);
3155 cmd_mode = 0; // stop insrting
3156 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003157 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003158 break;
3159 case ' ': // move right
3160 case 'l': // move right
3161 case VI_K_RIGHT: // Cursor Key Right
3162 if (cmdcnt-- > 1) {
3163 do_cmd(c);
3164 } // repeat cnt
3165 dot_right();
3166 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003167#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003168 case '"': // "- name a register to use for Delete/Yank
3169 c1 = get_one_char();
3170 c1 = tolower(c1);
3171 if (islower(c1)) {
3172 YDreg = c1 - 'a';
3173 } else {
3174 indicate_error(c);
3175 }
3176 break;
3177 case '\'': // '- goto a specific mark
3178 c1 = get_one_char();
3179 c1 = tolower(c1);
3180 if (islower(c1)) {
3181 c1 = c1 - 'a';
3182 // get the b-o-l
3183 q = mark[(int) c1];
3184 if (text <= q && q < end) {
3185 dot = q;
3186 dot_begin(); // go to B-o-l
3187 dot_skip_over_ws();
3188 }
3189 } else if (c1 == '\'') { // goto previous context
3190 dot = swap_context(dot); // swap current and previous context
3191 dot_begin(); // go to B-o-l
3192 dot_skip_over_ws();
3193 } else {
3194 indicate_error(c);
3195 }
3196 break;
3197 case 'm': // m- Mark a line
3198 // this is really stupid. If there are any inserts or deletes
3199 // between text[0] and dot then this mark will not point to the
3200 // correct location! It could be off by many lines!
3201 // Well..., at least its quick and dirty.
3202 c1 = get_one_char();
3203 c1 = tolower(c1);
3204 if (islower(c1)) {
3205 c1 = c1 - 'a';
3206 // remember the line
3207 mark[(int) c1] = dot;
3208 } else {
3209 indicate_error(c);
3210 }
3211 break;
3212 case 'P': // P- Put register before
3213 case 'p': // p- put register after
3214 p = reg[YDreg];
3215 if (p == 0) {
3216 psbs("Nothing in register %c", what_reg());
3217 break;
3218 }
3219 // are we putting whole lines or strings
3220 if (strchr((char *) p, '\n') != NULL) {
3221 if (c == 'P') {
3222 dot_begin(); // putting lines- Put above
3223 }
3224 if (c == 'p') {
3225 // are we putting after very last line?
3226 if (end_line(dot) == (end - 1)) {
3227 dot = end; // force dot to end of text[]
3228 } else {
3229 dot_next(); // next line, then put before
3230 }
3231 }
3232 } else {
3233 if (c == 'p')
3234 dot_right(); // move to right, can move to NL
3235 }
3236 dot = string_insert(dot, p); // insert the string
3237 end_cmd_q(); // stop adding to q
3238 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003239 case 'U': // U- Undo; replace current line with original version
3240 if (reg[Ureg] != 0) {
3241 p = begin_line(dot);
3242 q = end_line(dot);
3243 p = text_hole_delete(p, q); // delete cur line
3244 p = string_insert(p, reg[Ureg]); // insert orig line
3245 dot = p;
3246 dot_skip_over_ws();
3247 }
3248 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003249#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003250 case '$': // $- goto end of line
3251 case VI_K_END: // Cursor Key End
3252 if (cmdcnt-- > 1) {
3253 do_cmd(c);
3254 } // repeat cnt
Glenn L McGrathee829062004-01-21 10:59:45 +00003255 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003256 break;
3257 case '%': // %- find matching char of pair () [] {}
3258 for (q = dot; q < end && *q != '\n'; q++) {
3259 if (strchr("()[]{}", *q) != NULL) {
3260 // we found half of a pair
3261 p = find_pair(q, *q);
3262 if (p == NULL) {
3263 indicate_error(c);
3264 } else {
3265 dot = p;
3266 }
3267 break;
3268 }
3269 }
3270 if (*q == '\n')
3271 indicate_error(c);
3272 break;
3273 case 'f': // f- forward to a user specified char
3274 last_forward_char = get_one_char(); // get the search char
3275 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003276 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003277 //
Paul Foxd13b90b2005-07-18 22:17:25 +00003278 //**** fall thru to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003279 case ';': // ;- look at rest of line for last forward char
3280 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003281 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003282 } // repeat cnt
Eric Andersen822c3832001-05-07 17:37:43 +00003283 if (last_forward_char == 0) break;
Eric Andersen3f980402001-04-04 17:31:15 +00003284 q = dot + 1;
3285 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3286 q++;
3287 }
3288 if (*q == last_forward_char)
3289 dot = q;
3290 break;
3291 case '-': // -- goto prev line
3292 if (cmdcnt-- > 1) {
3293 do_cmd(c);
3294 } // repeat cnt
3295 dot_prev();
3296 dot_skip_over_ws();
3297 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003298#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003299 case '.': // .- repeat the last modifying command
3300 // Stuff the last_modifying_cmd back into stdin
3301 // and let it be re-executed.
3302 if (last_modifying_cmd != 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +00003303 ioq = ioq_start = (Byte *) bb_xstrdup((char *) last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003304 }
3305 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003306#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3307#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003308 case '?': // /- search for a pattern
3309 case '/': // /- search for a pattern
3310 buf[0] = c;
3311 buf[1] = '\0';
3312 q = get_input_line(buf); // get input line- use "status line"
3313 if (strlen((char *) q) == 1)
3314 goto dc3; // if no pat re-use old pat
3315 if (strlen((char *) q) > 1) { // new pat- save it and find
3316 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003317 free(last_search_pattern);
Manuel Novoa III cad53642003-03-19 09:13:01 +00003318 last_search_pattern = (Byte *) bb_xstrdup((char *) q);
Eric Andersen3f980402001-04-04 17:31:15 +00003319 goto dc3; // now find the pattern
3320 }
3321 // user changed mind and erased the "/"- do nothing
3322 break;
3323 case 'N': // N- backward search for last pattern
3324 if (cmdcnt-- > 1) {
3325 do_cmd(c);
3326 } // repeat cnt
3327 dir = BACK; // assume BACKWARD search
3328 p = dot - 1;
3329 if (last_search_pattern[0] == '?') {
3330 dir = FORWARD;
3331 p = dot + 1;
3332 }
3333 goto dc4; // now search for pattern
3334 break;
3335 case 'n': // n- repeat search for last pattern
3336 // search rest of text[] starting at next char
3337 // if search fails return orignal "p" not the "p+1" address
3338 if (cmdcnt-- > 1) {
3339 do_cmd(c);
3340 } // repeat cnt
3341 dc3:
3342 if (last_search_pattern == 0) {
3343 msg = (Byte *) "No previous regular expression";
3344 goto dc2;
3345 }
3346 if (last_search_pattern[0] == '/') {
3347 dir = FORWARD; // assume FORWARD search
3348 p = dot + 1;
3349 }
3350 if (last_search_pattern[0] == '?') {
3351 dir = BACK;
3352 p = dot - 1;
3353 }
3354 dc4:
3355 q = char_search(p, last_search_pattern + 1, dir, FULL);
3356 if (q != NULL) {
3357 dot = q; // good search, update "dot"
3358 msg = (Byte *) "";
3359 goto dc2;
3360 }
3361 // no pattern found between "dot" and "end"- continue at top
3362 p = text;
3363 if (dir == BACK) {
3364 p = end - 1;
3365 }
3366 q = char_search(p, last_search_pattern + 1, dir, FULL);
3367 if (q != NULL) { // found something
3368 dot = q; // found new pattern- goto it
3369 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3370 if (dir == BACK) {
3371 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3372 }
3373 } else {
3374 msg = (Byte *) "Pattern not found";
3375 }
3376 dc2:
Paul Fox8552aec2005-09-16 12:20:05 +00003377 if (*msg) psbs("%s", msg);
Eric Andersen3f980402001-04-04 17:31:15 +00003378 break;
3379 case '{': // {- move backward paragraph
3380 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3381 if (q != NULL) { // found blank line
3382 dot = next_line(q); // move to next blank line
3383 }
3384 break;
3385 case '}': // }- move forward paragraph
3386 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3387 if (q != NULL) { // found blank line
3388 dot = next_line(q); // move to next blank line
3389 }
3390 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003391#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003392 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003393 case '1': // 1-
3394 case '2': // 2-
3395 case '3': // 3-
3396 case '4': // 4-
3397 case '5': // 5-
3398 case '6': // 6-
3399 case '7': // 7-
3400 case '8': // 8-
3401 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003402 if (c == '0' && cmdcnt < 1) {
3403 dot_begin(); // this was a standalone zero
3404 } else {
3405 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3406 }
3407 break;
3408 case ':': // :- the colon mode commands
Eric Andersen3f980402001-04-04 17:31:15 +00003409 p = get_input_line((Byte *) ":"); // get input line- use "status line"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003410#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003411 colon(p); // execute the command
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003412#else /* CONFIG_FEATURE_VI_COLON */
Eric Andersen822c3832001-05-07 17:37:43 +00003413 if (*p == ':')
3414 p++; // move past the ':'
3415 cnt = strlen((char *) p);
3416 if (cnt <= 0)
3417 break;
3418 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3419 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
Matt Kraai1f0c4362001-12-20 23:13:26 +00003420 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003421 psbs("No write since last change (:quit! overrides)");
3422 } else {
3423 editing = 0;
3424 }
Paul Fox9360f422006-03-27 21:51:16 +00003425 } else if (strncasecmp((char *) p, "write", cnt) == 0
3426 || strncasecmp((char *) p, "wq", cnt) == 0
3427 || strncasecmp((char *) p, "wn", cnt) == 0
3428 || strncasecmp((char *) p, "x", cnt) == 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003429 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003430 if (cnt < 0) {
3431 if (cnt == -1)
3432 psbs("Write error: %s", strerror(errno));
3433 } else {
3434 file_modified = 0;
3435 last_file_modified = -1;
3436 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
Paul Fox9360f422006-03-27 21:51:16 +00003437 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n' ||
3438 p[0] == 'X' || p[1] == 'Q' || p[1] == 'N') {
Paul Fox61e45db2005-10-09 14:43:22 +00003439 editing = 0;
3440 }
Eric Andersen3f980402001-04-04 17:31:15 +00003441 }
Eric Andersen822c3832001-05-07 17:37:43 +00003442 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
Paul Fox8552aec2005-09-16 12:20:05 +00003443 last_status_cksum = 0; // force status update
Eric Andersen822c3832001-05-07 17:37:43 +00003444 } else if (sscanf((char *) p, "%d", &j) > 0) {
3445 dot = find_line(j); // go to line # j
3446 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003447 } else { // unrecognised cmd
Eric Andersen822c3832001-05-07 17:37:43 +00003448 ni((Byte *) p);
Eric Andersen3f980402001-04-04 17:31:15 +00003449 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003450#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003451 break;
3452 case '<': // <- Left shift something
3453 case '>': // >- Right shift something
3454 cnt = count_lines(text, dot); // remember what line we are on
3455 c1 = get_one_char(); // get the type of thing to delete
3456 find_range(&p, &q, c1);
3457 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3458 p = begin_line(p);
3459 q = end_line(q);
3460 i = count_lines(p, q); // # of lines we are shifting
3461 for ( ; i > 0; i--, p = next_line(p)) {
3462 if (c == '<') {
3463 // shift left- remove tab or 8 spaces
3464 if (*p == '\t') {
3465 // shrink buffer 1 char
3466 (void) text_hole_delete(p, p);
3467 } else if (*p == ' ') {
3468 // we should be calculating columns, not just SPACE
3469 for (j = 0; *p == ' ' && j < tabstop; j++) {
3470 (void) text_hole_delete(p, p);
3471 }
3472 }
3473 } else if (c == '>') {
3474 // shift right -- add tab or 8 spaces
3475 (void) char_insert(p, '\t');
3476 }
3477 }
3478 dot = find_line(cnt); // what line were we on
3479 dot_skip_over_ws();
3480 end_cmd_q(); // stop adding to q
3481 break;
3482 case 'A': // A- append at e-o-l
3483 dot_end(); // go to e-o-l
3484 //**** fall thru to ... 'a'
3485 case 'a': // a- append after current char
3486 if (*dot != '\n')
3487 dot++;
3488 goto dc_i;
3489 break;
3490 case 'B': // B- back a blank-delimited Word
3491 case 'E': // E- end of a blank-delimited word
3492 case 'W': // W- forward a blank-delimited word
3493 if (cmdcnt-- > 1) {
3494 do_cmd(c);
3495 } // repeat cnt
3496 dir = FORWARD;
3497 if (c == 'B')
3498 dir = BACK;
3499 if (c == 'W' || isspace(dot[dir])) {
3500 dot = skip_thing(dot, 1, dir, S_TO_WS);
3501 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3502 }
3503 if (c != 'W')
3504 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3505 break;
3506 case 'C': // C- Change to e-o-l
3507 case 'D': // D- delete to e-o-l
3508 save_dot = dot;
3509 dot = dollar_line(dot); // move to before NL
3510 // copy text into a register and delete
3511 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3512 if (c == 'C')
3513 goto dc_i; // start inserting
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003514#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003515 if (c == 'D')
3516 end_cmd_q(); // stop adding to q
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003517#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003518 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003519 case 'G': // G- goto to a line number (default= E-O-F)
3520 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003521 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003522 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003523 }
3524 dot_skip_over_ws();
3525 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003526 case 'H': // H- goto top line on screen
3527 dot = screenbegin;
3528 if (cmdcnt > (rows - 1)) {
3529 cmdcnt = (rows - 1);
3530 }
3531 if (cmdcnt-- > 1) {
3532 do_cmd('+');
3533 } // repeat cnt
3534 dot_skip_over_ws();
3535 break;
3536 case 'I': // I- insert before first non-blank
3537 dot_begin(); // 0
3538 dot_skip_over_ws();
3539 //**** fall thru to ... 'i'
3540 case 'i': // i- insert before current char
3541 case VI_K_INSERT: // Cursor Key Insert
3542 dc_i:
3543 cmd_mode = 1; // start insrting
Eric Andersen3f980402001-04-04 17:31:15 +00003544 break;
3545 case 'J': // J- join current and next lines together
3546 if (cmdcnt-- > 2) {
3547 do_cmd(c);
3548 } // repeat cnt
3549 dot_end(); // move to NL
3550 if (dot < end - 1) { // make sure not last char in text[]
3551 *dot++ = ' '; // replace NL with space
Paul Fox8552aec2005-09-16 12:20:05 +00003552 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003553 while (isblnk(*dot)) { // delete leading WS
3554 dot_delete();
3555 }
3556 }
3557 end_cmd_q(); // stop adding to q
3558 break;
3559 case 'L': // L- goto bottom line on screen
3560 dot = end_screen();
3561 if (cmdcnt > (rows - 1)) {
3562 cmdcnt = (rows - 1);
3563 }
3564 if (cmdcnt-- > 1) {
3565 do_cmd('-');
3566 } // repeat cnt
3567 dot_begin();
3568 dot_skip_over_ws();
3569 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003570 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003571 dot = screenbegin;
3572 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3573 dot = next_line(dot);
3574 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003575 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003576 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003577 p = begin_line(dot);
3578 if (p[-1] == '\n') {
3579 dot_prev();
3580 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3581 dot_end();
3582 dot = char_insert(dot, '\n');
3583 } else {
3584 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003585 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003586 dot_prev(); // -
3587 }
3588 goto dc_i;
3589 break;
3590 case 'R': // R- continuous Replace char
Eric Andersen1c0d3112001-04-16 15:46:44 +00003591 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003592 cmd_mode = 2;
Eric Andersen3f980402001-04-04 17:31:15 +00003593 break;
3594 case 'X': // X- delete char before dot
3595 case 'x': // x- delete the current char
3596 case 's': // s- substitute the current char
3597 if (cmdcnt-- > 1) {
3598 do_cmd(c);
3599 } // repeat cnt
3600 dir = 0;
3601 if (c == 'X')
3602 dir = -1;
3603 if (dot[dir] != '\n') {
3604 if (c == 'X')
3605 dot--; // delete prev char
3606 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3607 }
3608 if (c == 's')
3609 goto dc_i; // start insrting
3610 end_cmd_q(); // stop adding to q
3611 break;
3612 case 'Z': // Z- if modified, {write}; exit
3613 // ZZ means to save file (if necessary), then exit
3614 c1 = get_one_char();
3615 if (c1 != 'Z') {
3616 indicate_error(c);
3617 break;
3618 }
Paul Foxf0305b72006-03-28 14:18:21 +00003619 if (file_modified) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003620#ifdef CONFIG_FEATURE_VI_READONLY
Paul Foxf0305b72006-03-28 14:18:21 +00003621 if (vi_readonly || readonly) {
3622 psbs("\"%s\" File is read only", cfn);
3623 break;
3624 }
3625#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00003626 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003627 if (cnt < 0) {
3628 if (cnt == -1)
3629 psbs("Write error: %s", strerror(errno));
3630 } else if (cnt == (end - 1 - text + 1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003631 editing = 0;
3632 }
3633 } else {
3634 editing = 0;
3635 }
3636 break;
3637 case '^': // ^- move to first non-blank on line
3638 dot_begin();
3639 dot_skip_over_ws();
3640 break;
3641 case 'b': // b- back a word
3642 case 'e': // e- end of word
3643 if (cmdcnt-- > 1) {
3644 do_cmd(c);
3645 } // repeat cnt
3646 dir = FORWARD;
3647 if (c == 'b')
3648 dir = BACK;
3649 if ((dot + dir) < text || (dot + dir) > end - 1)
3650 break;
3651 dot += dir;
3652 if (isspace(*dot)) {
3653 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3654 }
3655 if (isalnum(*dot) || *dot == '_') {
3656 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3657 } else if (ispunct(*dot)) {
3658 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3659 }
3660 break;
3661 case 'c': // c- change something
3662 case 'd': // d- delete something
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003663#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003664 case 'y': // y- yank something
3665 case 'Y': // Y- Yank a line
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003666#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003667 yf = YANKDEL; // assume either "c" or "d"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003668#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003669 if (c == 'y' || c == 'Y')
3670 yf = YANKONLY;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003671#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003672 c1 = 'y';
3673 if (c != 'Y')
3674 c1 = get_one_char(); // get the type of thing to delete
3675 find_range(&p, &q, c1);
3676 if (c1 == 27) { // ESC- user changed mind and wants out
3677 c = c1 = 27; // Escape- do nothing
3678 } else if (strchr("wW", c1)) {
3679 if (c == 'c') {
3680 // don't include trailing WS as part of word
3681 while (isblnk(*q)) {
3682 if (q <= text || q[-1] == '\n')
3683 break;
3684 q--;
3685 }
3686 }
3687 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003688 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003689 // single line copy text into a register and delete
3690 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003691 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003692 // multiple line copy text into a register and delete
3693 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003694 if (c == 'c') {
3695 dot = char_insert(dot, '\n');
3696 // on the last line of file don't move to prev line
3697 if (dot != (end-1)) {
3698 dot_prev();
3699 }
3700 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003701 dot_begin();
3702 dot_skip_over_ws();
3703 }
3704 } else {
3705 // could not recognize object
3706 c = c1 = 27; // error-
3707 indicate_error(c);
3708 }
3709 if (c1 != 27) {
3710 // if CHANGING, not deleting, start inserting after the delete
3711 if (c == 'c') {
3712 strcpy((char *) buf, "Change");
3713 goto dc_i; // start inserting
3714 }
3715 if (c == 'd') {
3716 strcpy((char *) buf, "Delete");
3717 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003718#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003719 if (c == 'y' || c == 'Y') {
3720 strcpy((char *) buf, "Yank");
3721 }
3722 p = reg[YDreg];
3723 q = p + strlen((char *) p);
3724 for (cnt = 0; p <= q; p++) {
3725 if (*p == '\n')
3726 cnt++;
3727 }
3728 psb("%s %d lines (%d chars) using [%c]",
3729 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003730#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003731 end_cmd_q(); // stop adding to q
3732 }
3733 break;
3734 case 'k': // k- goto prev line, same col
3735 case VI_K_UP: // cursor key Up
3736 if (cmdcnt-- > 1) {
3737 do_cmd(c);
3738 } // repeat cnt
3739 dot_prev();
3740 dot = move_to_col(dot, ccol + offset); // try stay in same col
3741 break;
3742 case 'r': // r- replace the current char with user input
3743 c1 = get_one_char(); // get the replacement char
3744 if (*dot != '\n') {
3745 *dot = c1;
Paul Fox8552aec2005-09-16 12:20:05 +00003746 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003747 }
3748 end_cmd_q(); // stop adding to q
3749 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003750 case 't': // t- move to char prior to next x
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00003751 last_forward_char = get_one_char();
3752 do_cmd(';');
3753 if (*dot == last_forward_char)
3754 dot_left();
3755 last_forward_char= 0;
Eric Andersen822c3832001-05-07 17:37:43 +00003756 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003757 case 'w': // w- forward a word
3758 if (cmdcnt-- > 1) {
3759 do_cmd(c);
3760 } // repeat cnt
3761 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3762 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3763 } else if (ispunct(*dot)) { // we are on PUNCT
3764 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3765 }
3766 if (dot < end - 1)
3767 dot++; // move over word
3768 if (isspace(*dot)) {
3769 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3770 }
3771 break;
3772 case 'z': // z-
3773 c1 = get_one_char(); // get the replacement char
3774 cnt = 0;
3775 if (c1 == '.')
3776 cnt = (rows - 2) / 2; // put dot at center
3777 if (c1 == '-')
3778 cnt = rows - 2; // put dot at bottom
3779 screenbegin = begin_line(dot); // start dot at top
3780 dot_scroll(cnt, -1);
3781 break;
3782 case '|': // |- move to column "cmdcnt"
3783 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3784 break;
3785 case '~': // ~- flip the case of letters a-z -> A-Z
3786 if (cmdcnt-- > 1) {
3787 do_cmd(c);
3788 } // repeat cnt
3789 if (islower(*dot)) {
3790 *dot = toupper(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003791 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003792 } else if (isupper(*dot)) {
3793 *dot = tolower(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003794 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003795 }
3796 dot_right();
3797 end_cmd_q(); // stop adding to q
3798 break;
3799 //----- The Cursor and Function Keys -----------------------------
3800 case VI_K_HOME: // Cursor Key Home
3801 dot_begin();
3802 break;
3803 // The Fn keys could point to do_macro which could translate them
3804 case VI_K_FUN1: // Function Key F1
3805 case VI_K_FUN2: // Function Key F2
3806 case VI_K_FUN3: // Function Key F3
3807 case VI_K_FUN4: // Function Key F4
3808 case VI_K_FUN5: // Function Key F5
3809 case VI_K_FUN6: // Function Key F6
3810 case VI_K_FUN7: // Function Key F7
3811 case VI_K_FUN8: // Function Key F8
3812 case VI_K_FUN9: // Function Key F9
3813 case VI_K_FUN10: // Function Key F10
3814 case VI_K_FUN11: // Function Key F11
3815 case VI_K_FUN12: // Function Key F12
3816 break;
3817 }
3818
3819 dc1:
3820 // if text[] just became empty, add back an empty line
3821 if (end == text) {
3822 (void) char_insert(text, '\n'); // start empty buf with dummy line
3823 dot = text;
3824 }
3825 // it is OK for dot to exactly equal to end, otherwise check dot validity
3826 if (dot != end) {
3827 dot = bound_dot(dot); // make sure "dot" is valid
3828 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003829#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003830 check_context(c); // update the current context
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003831#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003832
3833 if (!isdigit(c))
3834 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3835 cnt = dot - begin_line(dot);
3836 // Try to stay off of the Newline
3837 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3838 dot--;
3839}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003840
3841#ifdef CONFIG_FEATURE_VI_CRASHME
3842static int totalcmds = 0;
3843static int Mp = 85; // Movement command Probability
3844static int Np = 90; // Non-movement command Probability
3845static int Dp = 96; // Delete command Probability
3846static int Ip = 97; // Insert command Probability
3847static int Yp = 98; // Yank command Probability
3848static int Pp = 99; // Put command Probability
3849static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3850char chars[20] = "\t012345 abcdABCD-=.$";
3851char *words[20] = { "this", "is", "a", "test",
3852 "broadcast", "the", "emergency", "of",
3853 "system", "quick", "brown", "fox",
3854 "jumped", "over", "lazy", "dogs",
3855 "back", "January", "Febuary", "March"
3856};
3857char *lines[20] = {
3858 "You should have received a copy of the GNU General Public License\n",
3859 "char c, cm, *cmd, *cmd1;\n",
3860 "generate a command by percentages\n",
3861 "Numbers may be typed as a prefix to some commands.\n",
3862 "Quit, discarding changes!\n",
3863 "Forced write, if permission originally not valid.\n",
3864 "In general, any ex or ed command (such as substitute or delete).\n",
3865 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3866 "Please get w/ me and I will go over it with you.\n",
3867 "The following is a list of scheduled, committed changes.\n",
3868 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3869 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3870 "Any question about transactions please contact Sterling Huxley.\n",
3871 "I will try to get back to you by Friday, December 31.\n",
3872 "This Change will be implemented on Friday.\n",
3873 "Let me know if you have problems accessing this;\n",
3874 "Sterling Huxley recently added you to the access list.\n",
3875 "Would you like to go to lunch?\n",
3876 "The last command will be automatically run.\n",
3877 "This is too much english for a computer geek.\n",
3878};
3879char *multilines[20] = {
3880 "You should have received a copy of the GNU General Public License\n",
3881 "char c, cm, *cmd, *cmd1;\n",
3882 "generate a command by percentages\n",
3883 "Numbers may be typed as a prefix to some commands.\n",
3884 "Quit, discarding changes!\n",
3885 "Forced write, if permission originally not valid.\n",
3886 "In general, any ex or ed command (such as substitute or delete).\n",
3887 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3888 "Please get w/ me and I will go over it with you.\n",
3889 "The following is a list of scheduled, committed changes.\n",
3890 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3891 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3892 "Any question about transactions please contact Sterling Huxley.\n",
3893 "I will try to get back to you by Friday, December 31.\n",
3894 "This Change will be implemented on Friday.\n",
3895 "Let me know if you have problems accessing this;\n",
3896 "Sterling Huxley recently added you to the access list.\n",
3897 "Would you like to go to lunch?\n",
3898 "The last command will be automatically run.\n",
3899 "This is too much english for a computer geek.\n",
3900};
3901
3902// create a random command to execute
3903static void crash_dummy()
3904{
3905 static int sleeptime; // how long to pause between commands
3906 char c, cm, *cmd, *cmd1;
3907 int i, cnt, thing, rbi, startrbi, percent;
3908
3909 // "dot" movement commands
3910 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3911
3912 // is there already a command running?
3913 if (readed_for_parse > 0)
3914 goto cd1;
3915 cd0:
3916 startrbi = rbi = 0;
3917 sleeptime = 0; // how long to pause between commands
3918 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3919 // generate a command by percentages
3920 percent = (int) lrand48() % 100; // get a number from 0-99
3921 if (percent < Mp) { // Movement commands
3922 // available commands
3923 cmd = cmd1;
3924 M++;
3925 } else if (percent < Np) { // non-movement commands
3926 cmd = "mz<>\'\""; // available commands
3927 N++;
3928 } else if (percent < Dp) { // Delete commands
3929 cmd = "dx"; // available commands
3930 D++;
3931 } else if (percent < Ip) { // Inset commands
3932 cmd = "iIaAsrJ"; // available commands
3933 I++;
3934 } else if (percent < Yp) { // Yank commands
3935 cmd = "yY"; // available commands
3936 Y++;
3937 } else if (percent < Pp) { // Put commands
3938 cmd = "pP"; // available commands
3939 P++;
3940 } else {
3941 // We do not know how to handle this command, try again
3942 U++;
3943 goto cd0;
3944 }
3945 // randomly pick one of the available cmds from "cmd[]"
3946 i = (int) lrand48() % strlen(cmd);
3947 cm = cmd[i];
3948 if (strchr(":\024", cm))
3949 goto cd0; // dont allow colon or ctrl-T commands
3950 readbuffer[rbi++] = cm; // put cmd into input buffer
3951
3952 // now we have the command-
3953 // there are 1, 2, and multi char commands
3954 // find out which and generate the rest of command as necessary
3955 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3956 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3957 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3958 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3959 }
3960 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3961 c = cmd1[thing];
3962 readbuffer[rbi++] = c; // add movement to input buffer
3963 }
3964 if (strchr("iIaAsc", cm)) { // multi-char commands
3965 if (cm == 'c') {
3966 // change some thing
3967 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3968 c = cmd1[thing];
3969 readbuffer[rbi++] = c; // add movement to input buffer
3970 }
3971 thing = (int) lrand48() % 4; // what thing to insert
3972 cnt = (int) lrand48() % 10; // how many to insert
3973 for (i = 0; i < cnt; i++) {
3974 if (thing == 0) { // insert chars
3975 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3976 } else if (thing == 1) { // insert words
3977 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
3978 strcat((char *) readbuffer, " ");
3979 sleeptime = 0; // how fast to type
3980 } else if (thing == 2) { // insert lines
3981 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
3982 sleeptime = 0; // how fast to type
3983 } else { // insert multi-lines
3984 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
3985 sleeptime = 0; // how fast to type
3986 }
3987 }
3988 strcat((char *) readbuffer, "\033");
3989 }
3990 readed_for_parse = strlen(readbuffer);
3991 cd1:
3992 totalcmds++;
3993 if (sleeptime > 0)
3994 (void) mysleep(sleeptime); // sleep 1/100 sec
3995}
3996
3997// test to see if there are any errors
3998static void crash_test()
3999{
4000 static time_t oldtim;
4001 time_t tim;
Glenn L McGrath7127b582002-12-03 21:48:15 +00004002 char d[2], msg[BUFSIZ];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004003
4004 msg[0] = '\0';
4005 if (end < text) {
4006 strcat((char *) msg, "end<text ");
4007 }
4008 if (end > textend) {
4009 strcat((char *) msg, "end>textend ");
4010 }
4011 if (dot < text) {
4012 strcat((char *) msg, "dot<text ");
4013 }
4014 if (dot > end) {
4015 strcat((char *) msg, "dot>end ");
4016 }
4017 if (screenbegin < text) {
4018 strcat((char *) msg, "screenbegin<text ");
4019 }
4020 if (screenbegin > end - 1) {
4021 strcat((char *) msg, "screenbegin>end-1 ");
4022 }
4023
4024 if (strlen(msg) > 0) {
4025 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00004026 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004027 totalcmds, last_input_char, msg, SOs, SOn);
4028 fflush(stdout);
4029 while (read(0, d, 1) > 0) {
4030 if (d[0] == '\n' || d[0] == '\r')
4031 break;
4032 }
4033 alarm(3);
4034 }
4035 tim = (time_t) time((time_t *) 0);
4036 if (tim >= (oldtim + 3)) {
4037 sprintf((char *) status_buffer,
4038 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
4039 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
4040 oldtim = tim;
4041 }
4042 return;
4043}
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00004044#endif /* CONFIG_FEATURE_VI_CRASHME */