blob: 46181cf60fe31df0dee96df3397830c3da420f8d [file] [log] [blame]
Rob Landleye5e1a102006-06-21 01:15:36 +00001/* vi: set sw=4 ts=4: */
Eric Andersen3f980402001-04-04 17:31:15 +00002/*
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/*
Eric Andersen3f980402001-04-04 17:31:15 +000010 * Things To Do:
11 * EXINIT
Eric Andersen1c0d3112001-04-16 15:46:44 +000012 * $HOME/.exrc and ./.exrc
Eric Andersen3f980402001-04-04 17:31:15 +000013 * add magic to search /foo.*bar
14 * add :help command
15 * :map macros
Eric Andersen3f980402001-04-04 17:31:15 +000016 * if mark[] values were line numbers rather than pointers
17 * it would be easier to change the mark when add/delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +000018 * More intelligence in refresh()
19 * ":r !cmd" and "!cmd" to filter text through an external command
20 * A true "undo" facility
21 * An "ex" line oriented mode- maybe using "cmdedit"
Eric Andersen3f980402001-04-04 17:31:15 +000022 */
23
Rob Landley2d6af162006-06-21 00:52:31 +000024#include "busybox.h"
Eric Andersen3f980402001-04-04 17:31:15 +000025
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +000026#define ENABLE_FEATURE_VI_CRASHME 0
27
28#if ENABLE_LOCALE_SUPPORT
Glenn L McGrath09adaca2002-12-02 21:18:10 +000029#define Isprint(c) isprint((c))
30#else
31#define Isprint(c) ( (c) >= ' ' && (c) != 127 && (c) != ((unsigned char)'\233') )
32#endif
33
Eric Andersen1c0d3112001-04-16 15:46:44 +000034#define MAX_SCR_COLS BUFSIZ
Eric Andersen3f980402001-04-04 17:31:15 +000035
36// Misc. non-Ascii keys that report an escape sequence
37#define VI_K_UP 128 // cursor key Up
38#define VI_K_DOWN 129 // cursor key Down
39#define VI_K_RIGHT 130 // Cursor Key Right
40#define VI_K_LEFT 131 // cursor key Left
41#define VI_K_HOME 132 // Cursor Key Home
42#define VI_K_END 133 // Cursor Key End
43#define VI_K_INSERT 134 // Cursor Key Insert
44#define VI_K_PAGEUP 135 // Cursor Key Page Up
45#define VI_K_PAGEDOWN 136 // Cursor Key Page Down
46#define VI_K_FUN1 137 // Function Key F1
47#define VI_K_FUN2 138 // Function Key F2
48#define VI_K_FUN3 139 // Function Key F3
49#define VI_K_FUN4 140 // Function Key F4
50#define VI_K_FUN5 141 // Function Key F5
51#define VI_K_FUN6 142 // Function Key F6
52#define VI_K_FUN7 143 // Function Key F7
53#define VI_K_FUN8 144 // Function Key F8
54#define VI_K_FUN9 145 // Function Key F9
55#define VI_K_FUN10 146 // Function Key F10
56#define VI_K_FUN11 147 // Function Key F11
57#define VI_K_FUN12 148 // Function Key F12
58
Glenn L McGrath09adaca2002-12-02 21:18:10 +000059/* vt102 typical ESC sequence */
60/* terminal standout start/normal ESC sequence */
61static const char SOs[] = "\033[7m";
62static const char SOn[] = "\033[0m";
63/* terminal bell sequence */
64static const char bell[] = "\007";
65/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
66static const char Ceol[] = "\033[0K";
67static const char Ceos [] = "\033[0J";
68/* Cursor motion arbitrary destination ESC sequence */
69static const char CMrc[] = "\033[%d;%dH";
70/* Cursor motion up and down ESC sequence */
71static const char CMup[] = "\033[A";
72static const char CMdown[] = "\n";
73
74
Rob Landleybc68cd12006-03-10 19:22:06 +000075enum {
76 YANKONLY = FALSE,
77 YANKDEL = TRUE,
78 FORWARD = 1, // code depends on "1" for array index
79 BACK = -1, // code depends on "-1" for array index
80 LIMITED = 0, // how much of text[] in char_search
81 FULL = 1, // how much of text[] in char_search
Eric Andersen3f980402001-04-04 17:31:15 +000082
Rob Landleybc68cd12006-03-10 19:22:06 +000083 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
84 S_TO_WS = 2, // used in skip_thing() for moving "dot"
85 S_OVER_WS = 3, // used in skip_thing() for moving "dot"
86 S_END_PUNCT = 4, // used in skip_thing() for moving "dot"
87 S_END_ALNUM = 5 // used in skip_thing() for moving "dot"
88};
Eric Andersen3f980402001-04-04 17:31:15 +000089
90typedef unsigned char Byte;
91
Glenn L McGrath09adaca2002-12-02 21:18:10 +000092static int vi_setops;
93#define VI_AUTOINDENT 1
94#define VI_SHOWMATCH 2
95#define VI_IGNORECASE 4
96#define VI_ERR_METHOD 8
97#define autoindent (vi_setops & VI_AUTOINDENT)
98#define showmatch (vi_setops & VI_SHOWMATCH )
99#define ignorecase (vi_setops & VI_IGNORECASE)
100/* indicate error with beep or flash */
101#define err_method (vi_setops & VI_ERR_METHOD)
102
Eric Andersen3f980402001-04-04 17:31:15 +0000103
104static int editing; // >0 while we are editing a file
Paul Fox8552aec2005-09-16 12:20:05 +0000105static int cmd_mode; // 0=command 1=insert 2=replace
Eric Andersen3f980402001-04-04 17:31:15 +0000106static int file_modified; // buffer contents changed
Paul Fox8552aec2005-09-16 12:20:05 +0000107static int last_file_modified = -1;
Eric Andersen3f980402001-04-04 17:31:15 +0000108static int fn_start; // index of first cmd line file name
109static int save_argc; // how many file names on cmd line
110static int cmdcnt; // repetition count
111static fd_set rfds; // use select() for small sleeps
112static struct timeval tv; // use select() for small sleeps
Eric Andersen3f980402001-04-04 17:31:15 +0000113static int rows, columns; // the terminal screen is this size
114static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset
Eric Andersen3f980402001-04-04 17:31:15 +0000115static Byte *status_buffer; // mesages to the user
Paul Fox8552aec2005-09-16 12:20:05 +0000116#define STATUS_BUFFER_LEN 200
117static int have_status_msg; // is default edit status needed?
118static int last_status_cksum; // hash of current status line
Eric Andersen3f980402001-04-04 17:31:15 +0000119static Byte *cfn; // previous, current, and next file name
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +0000120static Byte *text, *end; // pointers to the user data in memory
Eric Andersen3f980402001-04-04 17:31:15 +0000121static Byte *screen; // pointer to the virtual screen buffer
122static int screensize; // and its size
123static Byte *screenbegin; // index into text[], of top line on the screen
124static Byte *dot; // where all the action takes place
125static int tabstop;
126static struct termios term_orig, term_vi; // remember what the cooked mode was
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000127static Byte erase_char; // the users erase character
128static Byte last_input_char; // last char read from user
129static Byte last_forward_char; // last char searched for with 'f'
Eric Andersen3f980402001-04-04 17:31:15 +0000130
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000131#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +0000132static int last_row; // where the cursor was last moved to
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000133#endif
134#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000135static jmp_buf restart; // catch_sig()
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000136#endif
137#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000138static int my_pid;
139#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000140#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000141static int adding2q; // are we currently adding user input to q
142static Byte *last_modifying_cmd; // last modifying cmd for "."
143static Byte *ioq, *ioq_start; // pointer to string for get_one_char to "read"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000144#endif
145#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000146static Byte *modifying_cmds; // cmds that modify text[]
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000147#endif
148#if ENABLE_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000149static int vi_readonly, readonly;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000150#endif
151#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000152static Byte *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
153static int YDreg, Ureg; // default delete register and orig line for "U"
154static Byte *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
155static Byte *context_start, *context_end;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000156#endif
157#if ENABLE_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000158static Byte *last_search_pattern; // last pattern from a '/' or '?' search
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000159#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000160
161
162static void edit_file(Byte *); // edit one file
163static void do_cmd(Byte); // execute a command
164static void sync_cursor(Byte *, int *, int *); // synchronize the screen cursor to dot
165static Byte *begin_line(Byte *); // return pointer to cur line B-o-l
166static Byte *end_line(Byte *); // return pointer to cur line E-o-l
Eric Andersen3f980402001-04-04 17:31:15 +0000167static Byte *prev_line(Byte *); // return pointer to prev line B-o-l
168static Byte *next_line(Byte *); // return pointer to next line B-o-l
169static Byte *end_screen(void); // get pointer to last char on screen
170static int count_lines(Byte *, Byte *); // count line from start to stop
171static Byte *find_line(int); // find begining of line #li
172static Byte *move_to_col(Byte *, int); // move "p" to column l
173static int isblnk(Byte); // is the char a blank or tab
174static void dot_left(void); // move dot left- dont leave line
175static void dot_right(void); // move dot right- dont leave line
176static void dot_begin(void); // move dot to B-o-l
177static void dot_end(void); // move dot to E-o-l
178static void dot_next(void); // move dot to next line B-o-l
179static void dot_prev(void); // move dot to prev line B-o-l
180static void dot_scroll(int, int); // move the screen up or down
181static void dot_skip_over_ws(void); // move dot pat WS
182static void dot_delete(void); // delete the char at 'dot'
183static Byte *bound_dot(Byte *); // make sure text[0] <= P < "end"
184static Byte *new_screen(int, int); // malloc virtual screen memory
185static Byte *new_text(int); // malloc memory for text[] buffer
186static Byte *char_insert(Byte *, Byte); // insert the char c at 'p'
187static Byte *stupid_insert(Byte *, Byte); // stupidly insert the char c at 'p'
188static Byte find_range(Byte **, Byte **, Byte); // return pointers for an object
189static int st_test(Byte *, int, int, Byte *); // helper for skip_thing()
190static Byte *skip_thing(Byte *, int, int, int); // skip some object
191static Byte *find_pair(Byte *, Byte); // find matching pair () [] {}
192static Byte *text_hole_delete(Byte *, Byte *); // at "p", delete a 'size' byte hole
193static Byte *text_hole_make(Byte *, int); // at "p", make a 'size' byte hole
194static Byte *yank_delete(Byte *, Byte *, int, int); // yank text[] into register then delete
195static void show_help(void); // display some help info
Eric Andersen3f980402001-04-04 17:31:15 +0000196static void rawmode(void); // set "raw" mode on tty
197static void cookmode(void); // return to "cooked" mode on tty
198static int mysleep(int); // sleep for 'h' 1/100 seconds
199static Byte readit(void); // read (maybe cursor) key from stdin
200static Byte get_one_char(void); // read 1 char from stdin
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000201static int file_size(const Byte *); // what is the byte size of "fn"
Eric Andersen3f980402001-04-04 17:31:15 +0000202static int file_insert(Byte *, Byte *, int);
203static int file_write(Byte *, Byte *, Byte *);
Eric Andersen822c3832001-05-07 17:37:43 +0000204static void place_cursor(int, int, int);
Eric Andersenbff7a602001-11-17 07:15:43 +0000205static void screen_erase(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000206static void clear_to_eol(void);
207static void clear_to_eos(void);
208static void standout_start(void); // send "start reverse video" sequence
209static void standout_end(void); // send "end reverse video" sequence
210static void flash(int); // flash the terminal screen
Eric Andersen3f980402001-04-04 17:31:15 +0000211static void show_status_line(void); // put a message on the bottom line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000212static void psb(const char *, ...); // Print Status Buf
213static void psbs(const char *, ...); // Print Status Buf in standout mode
Eric Andersen3f980402001-04-04 17:31:15 +0000214static void ni(Byte *); // display messages
Paul Fox8552aec2005-09-16 12:20:05 +0000215static int format_edit_status(void); // format file status on status line
Eric Andersen3f980402001-04-04 17:31:15 +0000216static void redraw(int); // force a full screen refresh
Eric Andersen822c3832001-05-07 17:37:43 +0000217static void format_line(Byte*, Byte*, int);
Eric Andersen3f980402001-04-04 17:31:15 +0000218static void refresh(int); // update the terminal from screen[]
219
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000220static void Indicate_Error(void); // use flash or beep to indicate error
221#define indicate_error(c) Indicate_Error()
Paul Fox90372ed2005-10-09 14:26:26 +0000222static void Hit_Return(void);
223
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000224#if ENABLE_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000225static Byte *char_search(Byte *, Byte *, int, int); // search for pattern starting at p
226static int mycmp(Byte *, Byte *, int); // string cmp based in "ignorecase"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000227#endif
228#if ENABLE_FEATURE_VI_COLON
Eric Andersen1c0d3112001-04-16 15:46:44 +0000229static Byte *get_one_address(Byte *, int *); // get colon addr, if present
230static Byte *get_address(Byte *, int *, int *); // get two colon addrs, if present
Eric Andersen3f980402001-04-04 17:31:15 +0000231static void colon(Byte *); // execute the "colon" mode cmds
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000232#endif
233#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000234static void winch_sig(int); // catch window size changes
235static void suspend_sig(int); // catch ctrl-Z
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000236static void catch_sig(int); // catch ctrl-C and alarm time-outs
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000237#endif
238#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000239static void start_new_cmd_q(Byte); // new queue for command
Eric Andersenbff7a602001-11-17 07:15:43 +0000240static void end_cmd_q(void); // stop saving input chars
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000241#else
242#define end_cmd_q() ((void)0)
243#endif
244#if ENABLE_FEATURE_VI_SETOPTS
Eric Andersen3f980402001-04-04 17:31:15 +0000245static void showmatching(Byte *); // show the matching pair () [] {}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000246#endif
247#if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000248static Byte *string_insert(Byte *, Byte *); // insert the string at 'p'
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000249#endif
250#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000251static Byte *text_yank(Byte *, Byte *, int); // save copy of "p" into a register
252static Byte what_reg(void); // what is letter of current YDreg
253static void check_context(Byte); // remember context for '' command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000254#endif
255#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000256static void crash_dummy();
257static void crash_test();
258static int crashme = 0;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000259#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000260
261
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000262static void write1(const char *out)
263{
264 fputs(out, stdout);
265}
266
Rob Landleydfba7412006-03-06 20:47:33 +0000267int vi_main(int argc, char **argv)
Eric Andersen3f980402001-04-04 17:31:15 +0000268{
Eric Andersend402edf2001-04-04 19:29:48 +0000269 int c;
Paul Fox8552aec2005-09-16 12:20:05 +0000270 RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN);
Eric Andersen3f980402001-04-04 17:31:15 +0000271
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000272#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000273 int i;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000274#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000275#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
276 my_pid = getpid();
277#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000278#if ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000279 (void) srand((long) my_pid);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000280#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000281
Eric Andersena68ea1c2006-01-30 22:48:39 +0000282 status_buffer = (Byte *)STATUS_BUFFER;
Paul Fox8552aec2005-09-16 12:20:05 +0000283 last_status_cksum = 0;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000284
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000285#if ENABLE_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000286 vi_readonly = readonly = FALSE;
Eric Andersen3f980402001-04-04 17:31:15 +0000287 if (strncmp(argv[0], "view", 4) == 0) {
288 readonly = TRUE;
Eric Andersen822c3832001-05-07 17:37:43 +0000289 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000290 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000291#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000292 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000293#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000294 for (i = 0; i < 28; i++) {
295 reg[i] = 0;
296 } // init the yank regs
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000297#endif
298#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000299 modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000300#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000301
302 // 1- process $HOME/.exrc file
303 // 2- process EXINIT variable from environment
304 // 3- process command line args
305 while ((c = getopt(argc, argv, "hCR")) != -1) {
306 switch (c) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000307#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000308 case 'C':
309 crashme = 1;
310 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000311#endif
312#if ENABLE_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +0000313 case 'R': // Read-only flag
314 readonly = TRUE;
Eric Andersen90fb65f2004-03-31 11:12:51 +0000315 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000316 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000317#endif
Eric Andersen822c3832001-05-07 17:37:43 +0000318 //case 'r': // recover flag- ignore- we don't use tmp file
319 //case 'x': // encryption flag- ignore
320 //case 'c': // execute command first
321 //case 'h': // help -- just use default
Eric Andersen3f980402001-04-04 17:31:15 +0000322 default:
323 show_help();
Eric Andersendd8500b2001-07-02 18:06:14 +0000324 return 1;
Eric Andersen3f980402001-04-04 17:31:15 +0000325 }
326 }
327
328 // The argv array can be used by the ":next" and ":rewind" commands
329 // save optind.
330 fn_start = optind; // remember first file name for :next and :rew
331 save_argc = argc;
332
333 //----- This is the main file handling loop --------------
334 if (optind >= argc) {
335 editing = 1; // 0= exit, 1= one file, 2= multiple files
336 edit_file(0);
337 } else {
338 for (; optind < argc; optind++) {
339 editing = 1; // 0=exit, 1=one file, 2+ =many files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000340 free(cfn);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000341 cfn = (Byte *) xstrdup(argv[optind]);
Eric Andersen3f980402001-04-04 17:31:15 +0000342 edit_file(cfn);
343 }
344 }
345 //-----------------------------------------------------------
346
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000347 return 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000348}
349
350static void edit_file(Byte * fn)
351{
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000352 Byte c;
Eric Andersen1c0d3112001-04-16 15:46:44 +0000353 int cnt, size, ch;
Eric Andersen3f980402001-04-04 17:31:15 +0000354
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000355#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000356 int sig;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000357#endif
358#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000359 static Byte *cur_line;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000360#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000361
362 rawmode();
363 rows = 24;
364 columns = 80;
Eric Andersen822c3832001-05-07 17:37:43 +0000365 ch= -1;
Rob Landleye5e1a102006-06-21 01:15:36 +0000366 if (ENABLE_FEATURE_VI_WIN_RESIZE)
367 get_terminal_width_height(0, &columns, &rows);
Eric Andersen3f980402001-04-04 17:31:15 +0000368 new_screen(rows, columns); // get memory for virtual screen
369
370 cnt = file_size(fn); // file size
371 size = 2 * cnt; // 200% of file size
372 new_text(size); // get a text[] buffer
373 screenbegin = dot = end = text;
374 if (fn != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000375 ch= file_insert(fn, text, cnt);
376 }
377 if (ch < 1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000378 (void) char_insert(text, '\n'); // start empty buf with dummy line
379 }
Paul Fox8552aec2005-09-16 12:20:05 +0000380 file_modified = 0;
381 last_file_modified = -1;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000382#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000383 YDreg = 26; // default Yank/Delete reg
384 Ureg = 27; // hold orig line for "U" cmd
385 for (cnt = 0; cnt < 28; cnt++) {
386 mark[cnt] = 0;
387 } // init the marks
388 mark[26] = mark[27] = text; // init "previous context"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000389#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000390
Eric Andersen3f980402001-04-04 17:31:15 +0000391 last_forward_char = last_input_char = '\0';
392 crow = 0;
393 ccol = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000394
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000395#if ENABLE_FEATURE_VI_USE_SIGNALS
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000396 catch_sig(0);
Eric Andersen3f980402001-04-04 17:31:15 +0000397 signal(SIGWINCH, winch_sig);
398 signal(SIGTSTP, suspend_sig);
399 sig = setjmp(restart);
400 if (sig != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000401 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000402 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000403#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000404
405 editing = 1;
406 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
407 cmdcnt = 0;
408 tabstop = 8;
409 offset = 0; // no horizontal offset
410 c = '\0';
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000411#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000412 free(last_modifying_cmd);
413 free(ioq_start);
Eric Andersen3f980402001-04-04 17:31:15 +0000414 ioq = ioq_start = last_modifying_cmd = 0;
415 adding2q = 0;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000416#endif
Eric Andersen822c3832001-05-07 17:37:43 +0000417 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000418 show_status_line();
419
420 //------This is the main Vi cmd handling loop -----------------------
421 while (editing > 0) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000422#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000423 if (crashme > 0) {
424 if ((end - text) > 1) {
425 crash_dummy(); // generate a random command
426 } else {
427 crashme = 0;
428 dot =
429 string_insert(text, (Byte *) "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
430 refresh(FALSE);
431 }
432 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000433#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000434 last_input_char = c = get_one_char(); // get a cmd from user
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000435#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000436 // save a copy of the current line- for the 'U" command
437 if (begin_line(dot) != cur_line) {
438 cur_line = begin_line(dot);
439 text_yank(begin_line(dot), end_line(dot), Ureg);
440 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000441#endif
442#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000443 // These are commands that change text[].
444 // Remember the input for the "." command
445 if (!adding2q && ioq_start == 0
446 && strchr((char *) modifying_cmds, c) != NULL) {
447 start_new_cmd_q(c);
448 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000449#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000450 do_cmd(c); // execute the user command
451 //
452 // poll to see if there is input already waiting. if we are
453 // not able to display output fast enough to keep up, skip
454 // the display update until we catch up with input.
455 if (mysleep(0) == 0) {
456 // no input pending- so update output
457 refresh(FALSE);
458 show_status_line();
459 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000460#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000461 if (crashme > 0)
462 crash_test(); // test editor variables
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000463#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000464 }
465 //-------------------------------------------------------------------
466
Eric Andersen822c3832001-05-07 17:37:43 +0000467 place_cursor(rows, 0, FALSE); // go to bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +0000468 clear_to_eol(); // Erase to end of line
469 cookmode();
470}
471
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000472//----- The Colon commands -------------------------------------
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000473#if ENABLE_FEATURE_VI_COLON
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000474static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present
475{
476 int st;
477 Byte *q;
478
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000479#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000480 Byte c;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000481#endif
482#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000483 Byte *pat, buf[BUFSIZ];
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000484#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000485
486 *addr = -1; // assume no addr
487 if (*p == '.') { // the current line
488 p++;
489 q = begin_line(dot);
490 *addr = count_lines(text, q);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000491#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000492 } else if (*p == '\'') { // is this a mark addr
493 p++;
494 c = tolower(*p);
495 p++;
496 if (c >= 'a' && c <= 'z') {
497 // we have a mark
498 c = c - 'a';
499 q = mark[(int) c];
500 if (q != NULL) { // is mark valid
501 *addr = count_lines(text, q); // count lines
502 }
503 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000504#endif
505#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000506 } else if (*p == '/') { // a search pattern
507 q = buf;
508 for (p++; *p; p++) {
509 if (*p == '/')
510 break;
511 *q++ = *p;
512 *q = '\0';
513 }
Rob Landleyd921b2e2006-08-03 15:41:12 +0000514 pat = (Byte *) xstrdup((char *) buf); // save copy of pattern
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000515 if (*p == '/')
516 p++;
517 q = char_search(dot, pat, FORWARD, FULL);
518 if (q != NULL) {
519 *addr = count_lines(text, q);
520 }
521 free(pat);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000522#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000523 } else if (*p == '$') { // the last line in file
524 p++;
525 q = begin_line(end - 1);
526 *addr = count_lines(text, q);
527 } else if (isdigit(*p)) { // specific line number
528 sscanf((char *) p, "%d%n", addr, &st);
529 p += st;
530 } else { // I don't reconise this
531 // unrecognised address- assume -1
532 *addr = -1;
533 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000534 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000535}
536
537static Byte *get_address(Byte *p, int *b, int *e) // get two colon addrs, if present
538{
539 //----- get the address' i.e., 1,3 'a,'b -----
540 // get FIRST addr, if present
541 while (isblnk(*p))
542 p++; // skip over leading spaces
543 if (*p == '%') { // alias for 1,$
544 p++;
545 *b = 1;
546 *e = count_lines(text, end-1);
547 goto ga0;
548 }
549 p = get_one_address(p, b);
550 while (isblnk(*p))
551 p++;
Eric Andersenaff114c2004-04-14 17:51:38 +0000552 if (*p == ',') { // is there a address separator
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000553 p++;
554 while (isblnk(*p))
555 p++;
556 // get SECOND addr, if present
557 p = get_one_address(p, e);
558 }
559ga0:
560 while (isblnk(*p))
561 p++; // skip over trailing spaces
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000562 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000563}
564
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000565#if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000566static void setops(const Byte *args, const char *opname, int flg_no,
567 const char *short_opname, int opt)
568{
569 const char *a = (char *) args + flg_no;
570 int l = strlen(opname) - 1; /* opname have + ' ' */
571
572 if (strncasecmp(a, opname, l) == 0 ||
573 strncasecmp(a, short_opname, 2) == 0) {
574 if(flg_no)
575 vi_setops &= ~opt;
576 else
577 vi_setops |= opt;
578 }
579}
580#endif
581
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000582static void colon(Byte * buf)
583{
584 Byte c, *orig_buf, *buf1, *q, *r;
585 Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +0000586 int i, l, li, ch, b, e;
Eric Andersena9eb33d2004-08-19 19:15:06 +0000587 int useforce = FALSE, forced = FALSE;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000588 struct stat st_buf;
589
590 // :3154 // if (-e line 3154) goto it else stay put
591 // :4,33w! foo // write a portion of buffer to file "foo"
592 // :w // write all of buffer to current file
593 // :q // quit
594 // :q! // quit- dont care about modified file
595 // :'a,'z!sort -u // filter block through sort
596 // :'f // goto mark "f"
597 // :'fl // list literal the mark "f" line
598 // :.r bar // read file "bar" into buffer before dot
599 // :/123/,/abc/d // delete lines from "123" line to "abc" line
600 // :/xyz/ // goto the "xyz" line
601 // :s/find/replace/ // substitute pattern "find" with "replace"
602 // :!<cmd> // run <cmd> then return
603 //
Eric Andersen165e8cb2004-07-20 06:44:46 +0000604
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000605 if (strlen((char *) buf) <= 0)
606 goto vc1;
607 if (*buf == ':')
608 buf++; // move past the ':'
609
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +0000610 li = ch = i = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000611 b = e = -1;
612 q = text; // assume 1,$ for the range
613 r = end - 1;
614 li = count_lines(text, end - 1);
615 fn = cfn; // default to current file
616 memset(cmd, '\0', BUFSIZ); // clear cmd[]
617 memset(args, '\0', BUFSIZ); // clear args[]
618
619 // look for optional address(es) :. :1 :1,9 :'q,'a :%
620 buf = get_address(buf, &b, &e);
621
622 // remember orig command line
623 orig_buf = buf;
624
625 // get the COMMAND into cmd[]
626 buf1 = cmd;
627 while (*buf != '\0') {
628 if (isspace(*buf))
629 break;
630 *buf1++ = *buf++;
631 }
632 // get any ARGuments
633 while (isblnk(*buf))
634 buf++;
635 strcpy((char *) args, (char *) buf);
Eric Andersena68ea1c2006-01-30 22:48:39 +0000636 buf1 = (Byte*)last_char_is((char *)cmd, '!');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000637 if (buf1) {
638 useforce = TRUE;
639 *buf1 = '\0'; // get rid of !
640 }
641 if (b >= 0) {
642 // if there is only one addr, then the addr
643 // is the line number of the single line the
644 // user wants. So, reset the end
645 // pointer to point at end of the "b" line
646 q = find_line(b); // what line is #b
647 r = end_line(q);
648 li = 1;
649 }
650 if (e >= 0) {
651 // we were given two addrs. change the
652 // end pointer to the addr given by user.
653 r = find_line(e); // what line is #e
654 r = end_line(r);
655 li = e - b + 1;
656 }
657 // ------------ now look for the command ------------
658 i = strlen((char *) cmd);
659 if (i == 0) { // :123CR goto line #123
660 if (b >= 0) {
661 dot = find_line(b); // what line is #b
662 dot_skip_over_ws();
663 }
Denis Vlasenko249fabf2006-12-19 00:29:22 +0000664 }
665#if ENABLE_FEATURE_ALLOW_EXEC
666 else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000667 // :!ls run the <cmd>
668 (void) alarm(0); // wait for input- no alarms
669 place_cursor(rows - 1, 0, FALSE); // go to Status line
670 clear_to_eol(); // clear the line
671 cookmode();
Eric Andersena68ea1c2006-01-30 22:48:39 +0000672 system((char*)(orig_buf+1)); // run the cmd
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000673 rawmode();
674 Hit_Return(); // let user see results
675 (void) alarm(3); // done waiting for input
Denis Vlasenko249fabf2006-12-19 00:29:22 +0000676 }
677#endif
678 else if (strncmp((char *) cmd, "=", i) == 0) { // where is the address
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000679 if (b < 0) { // no addr given- use defaults
680 b = e = count_lines(text, dot);
681 }
682 psb("%d", b);
683 } else if (strncasecmp((char *) cmd, "delete", i) == 0) { // delete lines
684 if (b < 0) { // no addr given- use defaults
685 q = begin_line(dot); // assume .,. for the range
686 r = end_line(dot);
687 }
688 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
689 dot_skip_over_ws();
690 } else if (strncasecmp((char *) cmd, "edit", i) == 0) { // Edit a file
691 int sr;
692 sr= 0;
693 // don't edit, if the current file has been modified
694 if (file_modified && ! useforce) {
695 psbs("No write since last change (:edit! overrides)");
696 goto vc1;
697 }
Eric Andersena68ea1c2006-01-30 22:48:39 +0000698 if (strlen((char*)args) > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000699 // the user supplied a file name
700 fn= args;
Eric Andersena68ea1c2006-01-30 22:48:39 +0000701 } else if (cfn != 0 && strlen((char*)cfn) > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000702 // no user supplied name- use the current filename
703 fn= cfn;
704 goto vc5;
705 } else {
706 // no user file name, no current name- punt
707 psbs("No current filename");
708 goto vc1;
709 }
710
711 // see if file exists- if not, its just a new file request
712 if ((sr=stat((char*)fn, &st_buf)) < 0) {
713 // This is just a request for a new file creation.
714 // The file_insert below will fail but we get
715 // an empty buffer with a file name. Then the "write"
716 // command can do the create.
717 } else {
718 if ((st_buf.st_mode & (S_IFREG)) == 0) {
719 // This is not a regular file
720 psbs("\"%s\" is not a regular file", fn);
721 goto vc1;
722 }
723 if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
724 // dont have any read permissions
725 psbs("\"%s\" is not readable", fn);
726 goto vc1;
727 }
728 }
729
730 // There is a read-able regular file
731 // make this the current file
Rob Landleyd921b2e2006-08-03 15:41:12 +0000732 q = (Byte *) xstrdup((char *) fn); // save the cfn
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000733 free(cfn); // free the old name
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000734 cfn = q; // remember new cfn
735
736 vc5:
737 // delete all the contents of text[]
738 new_text(2 * file_size(fn));
739 screenbegin = dot = end = text;
740
741 // insert new file
742 ch = file_insert(fn, text, file_size(fn));
743
744 if (ch < 1) {
745 // start empty buf with dummy line
746 (void) char_insert(text, '\n');
747 ch= 1;
748 }
Paul Fox8552aec2005-09-16 12:20:05 +0000749 file_modified = 0;
750 last_file_modified = -1;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000751#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000752 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
753 free(reg[Ureg]); // free orig line reg- for 'U'
754 reg[Ureg]= 0;
755 }
756 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
757 free(reg[YDreg]); // free default yank/delete register
758 reg[YDreg]= 0;
759 }
760 for (li = 0; li < 28; li++) {
761 mark[li] = 0;
762 } // init the marks
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000763#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000764 // how many lines in text[]?
765 li = count_lines(text, end - 1);
766 psb("\"%s\"%s"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000767#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000768 "%s"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000769#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000770 " %dL, %dC", cfn,
771 (sr < 0 ? " [New file]" : ""),
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000772#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000773 ((vi_readonly || readonly) ? " [Read only]" : ""),
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000774#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000775 li, ch);
776 } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this
777 if (b != -1 || e != -1) {
778 ni((Byte *) "No address allowed on this command");
779 goto vc1;
780 }
781 if (strlen((char *) args) > 0) {
782 // user wants a new filename
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000783 free(cfn);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000784 cfn = (Byte *) xstrdup((char *) args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000785 } else {
786 // user wants file status info
Paul Fox8552aec2005-09-16 12:20:05 +0000787 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000788 }
789 } else if (strncasecmp((char *) cmd, "features", i) == 0) { // what features are available
790 // print out values of all features
791 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
792 clear_to_eol(); // clear the line
793 cookmode();
794 show_help();
795 rawmode();
796 Hit_Return();
797 } else if (strncasecmp((char *) cmd, "list", i) == 0) { // literal print line
798 if (b < 0) { // no addr given- use defaults
799 q = begin_line(dot); // assume .,. for the range
800 r = end_line(dot);
801 }
802 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
803 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000804 puts("\r");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000805 for (; q <= r; q++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000806 int c_is_no_print;
807
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000808 c = *q;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000809 c_is_no_print = c > 127 && !Isprint(c);
810 if (c_is_no_print) {
811 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000812 standout_start();
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000813 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000814 if (c == '\n') {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000815 write1("$\r");
816 } else if (c < ' ' || c == 127) {
817 putchar('^');
818 if(c == 127)
819 c = '?';
820 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000821 c += '@';
822 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000823 putchar(c);
824 if (c_is_no_print)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000825 standout_end();
826 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000827#if ENABLE_FEATURE_VI_SET
828 vc2:
829#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000830 Hit_Return();
831 } else if ((strncasecmp((char *) cmd, "quit", i) == 0) || // Quit
832 (strncasecmp((char *) cmd, "next", i) == 0)) { // edit next file
833 if (useforce) {
834 // force end of argv list
835 if (*cmd == 'q') {
836 optind = save_argc;
837 }
838 editing = 0;
839 goto vc1;
840 }
841 // don't exit if the file been modified
842 if (file_modified) {
843 psbs("No write since last change (:%s! overrides)",
844 (*cmd == 'q' ? "quit" : "next"));
845 goto vc1;
846 }
847 // are there other file to edit
848 if (*cmd == 'q' && optind < save_argc - 1) {
849 psbs("%d more file to edit", (save_argc - optind - 1));
850 goto vc1;
851 }
852 if (*cmd == 'n' && optind >= save_argc - 1) {
853 psbs("No more files to edit");
854 goto vc1;
855 }
856 editing = 0;
857 } else if (strncasecmp((char *) cmd, "read", i) == 0) { // read file into text[]
858 fn = args;
859 if (strlen((char *) fn) <= 0) {
860 psbs("No filename given");
861 goto vc1;
862 }
863 if (b < 0) { // no addr given- use defaults
864 q = begin_line(dot); // assume "dot"
865 }
866 // read after current line- unless user said ":0r foo"
867 if (b != 0)
868 q = next_line(q);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000869#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000870 l= readonly; // remember current files' status
871#endif
872 ch = file_insert(fn, q, file_size(fn));
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000873#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000874 readonly= l;
875#endif
876 if (ch < 0)
877 goto vc1; // nothing was inserted
878 // how many lines in text[]?
879 li = count_lines(q, q + ch - 1);
880 psb("\"%s\""
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000881#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000882 "%s"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000883#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000884 " %dL, %dC", fn,
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000885#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000886 ((vi_readonly || readonly) ? " [Read only]" : ""),
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000887#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000888 li, ch);
889 if (ch > 0) {
890 // if the insert is before "dot" then we need to update
891 if (q <= dot)
892 dot += ch;
Paul Fox8552aec2005-09-16 12:20:05 +0000893 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000894 }
895 } else if (strncasecmp((char *) cmd, "rewind", i) == 0) { // rewind cmd line args
896 if (file_modified && ! useforce) {
897 psbs("No write since last change (:rewind! overrides)");
898 } else {
899 // reset the filenames to edit
900 optind = fn_start - 1;
901 editing = 0;
902 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000903#if ENABLE_FEATURE_VI_SET
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000904 } else if (strncasecmp((char *) cmd, "set", i) == 0) { // set or clear features
905 i = 0; // offset into args
906 if (strlen((char *) args) == 0) {
907 // print out values of all options
908 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
909 clear_to_eol(); // clear the line
910 printf("----------------------------------------\r\n");
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000911#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000912 if (!autoindent)
913 printf("no");
914 printf("autoindent ");
915 if (!err_method)
916 printf("no");
917 printf("flash ");
918 if (!ignorecase)
919 printf("no");
920 printf("ignorecase ");
921 if (!showmatch)
922 printf("no");
923 printf("showmatch ");
924 printf("tabstop=%d ", tabstop);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000925#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000926 printf("\r\n");
927 goto vc2;
928 }
929 if (strncasecmp((char *) args, "no", 2) == 0)
930 i = 2; // ":set noautoindent"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000931#if ENABLE_FEATURE_VI_SETOPTS
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000932 setops(args, "autoindent ", i, "ai", VI_AUTOINDENT);
933 setops(args, "flash ", i, "fl", VI_ERR_METHOD);
934 setops(args, "ignorecase ", i, "ic", VI_IGNORECASE);
935 setops(args, "showmatch ", i, "ic", VI_SHOWMATCH);
936 if (strncasecmp((char *) args + i, "tabstop=%d ", 7) == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000937 sscanf(strchr((char *) args + i, '='), "=%d", &ch);
938 if (ch > 0 && ch < columns - 1)
939 tabstop = ch;
940 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000941#endif /* FEATURE_VI_SETOPTS */
942#endif /* FEATURE_VI_SET */
943#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000944 } else if (strncasecmp((char *) cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
945 Byte *ls, *F, *R;
946 int gflag;
947
948 // F points to the "find" pattern
949 // R points to the "replace" pattern
950 // replace the cmd line delimiters "/" with NULLs
951 gflag = 0; // global replace flag
952 c = orig_buf[1]; // what is the delimiter
953 F = orig_buf + 2; // start of "find"
954 R = (Byte *) strchr((char *) F, c); // middle delimiter
955 if (!R) goto colon_s_fail;
956 *R++ = '\0'; // terminate "find"
957 buf1 = (Byte *) strchr((char *) R, c);
958 if (!buf1) goto colon_s_fail;
959 *buf1++ = '\0'; // terminate "replace"
960 if (*buf1 == 'g') { // :s/foo/bar/g
961 buf1++;
962 gflag++; // turn on gflag
963 }
964 q = begin_line(q);
965 if (b < 0) { // maybe :s/foo/bar/
966 q = begin_line(dot); // start with cur line
967 b = count_lines(text, q); // cur line number
968 }
969 if (e < 0)
970 e = b; // maybe :.s/foo/bar/
971 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
972 ls = q; // orig line start
973 vc4:
974 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
975 if (buf1 != NULL) {
976 // we found the "find" pattern- delete it
977 (void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
978 // inset the "replace" patern
979 (void) string_insert(buf1, R); // insert the string
980 // check for "global" :s/foo/bar/g
981 if (gflag == 1) {
982 if ((buf1 + strlen((char *) R)) < end_line(ls)) {
983 q = buf1 + strlen((char *) R);
984 goto vc4; // don't let q move past cur line
985 }
986 }
987 }
988 q = next_line(ls);
989 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000990#endif /* FEATURE_VI_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000991 } else if (strncasecmp((char *) cmd, "version", i) == 0) { // show software version
Rob Landleyd921b2e2006-08-03 15:41:12 +0000992 psb("%s", BB_VER " " BB_BT);
Paul Fox9360f422006-03-27 21:51:16 +0000993 } else if (strncasecmp((char *) cmd, "write", i) == 0 // write text to file
994 || strncasecmp((char *) cmd, "wq", i) == 0
995 || strncasecmp((char *) cmd, "wn", i) == 0
996 || strncasecmp((char *) cmd, "x", i) == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000997 // is there a file name to write to?
998 if (strlen((char *) args) > 0) {
999 fn = args;
1000 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001001#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001002 if ((vi_readonly || readonly) && ! useforce) {
1003 psbs("\"%s\" File is read only", fn);
1004 goto vc3;
1005 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001006#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001007 // how many lines in text[]?
1008 li = count_lines(q, r);
1009 ch = r - q + 1;
1010 // see if file exists- if not, its just a new file request
1011 if (useforce) {
1012 // if "fn" is not write-able, chmod u+w
1013 // sprintf(syscmd, "chmod u+w %s", fn);
1014 // system(syscmd);
1015 forced = TRUE;
1016 }
1017 l = file_write(fn, q, r);
1018 if (useforce && forced) {
1019 // chmod u-w
1020 // sprintf(syscmd, "chmod u-w %s", fn);
1021 // system(syscmd);
1022 forced = FALSE;
1023 }
Paul Fox61e45db2005-10-09 14:43:22 +00001024 if (l < 0) {
1025 if (l == -1)
1026 psbs("Write error: %s", strerror(errno));
1027 } else {
1028 psb("\"%s\" %dL, %dC", fn, li, l);
1029 if (q == text && r == end - 1 && l == ch) {
1030 file_modified = 0;
1031 last_file_modified = -1;
1032 }
Paul Fox9360f422006-03-27 21:51:16 +00001033 if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' ||
1034 cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N')
1035 && l == ch) {
Paul Fox61e45db2005-10-09 14:43:22 +00001036 editing = 0;
1037 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001038 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001039#if ENABLE_FEATURE_VI_READONLY
1040 vc3:;
1041#endif
1042#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001043 } else if (strncasecmp((char *) cmd, "yank", i) == 0) { // yank lines
1044 if (b < 0) { // no addr given- use defaults
1045 q = begin_line(dot); // assume .,. for the range
1046 r = end_line(dot);
1047 }
1048 text_yank(q, r, YDreg);
1049 li = count_lines(q, r);
1050 psb("Yank %d lines (%d chars) into [%c]",
1051 li, strlen((char *) reg[YDreg]), what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001052#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001053 } else {
1054 // cmd unknown
1055 ni((Byte *) cmd);
1056 }
1057 vc1:
1058 dot = bound_dot(dot); // make sure "dot" is valid
1059 return;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001060#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001061colon_s_fail:
1062 psb(":s expression missing delimiters");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001063#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001064}
Paul Fox61e45db2005-10-09 14:43:22 +00001065
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001066#endif /* FEATURE_VI_COLON */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001067
1068static void Hit_Return(void)
1069{
1070 char c;
1071
1072 standout_start(); // start reverse video
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001073 write1("[Hit return to continue]");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001074 standout_end(); // end reverse video
1075 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
1076 ;
1077 redraw(TRUE); // force redraw all
1078}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001079
1080//----- Synchronize the cursor to Dot --------------------------
1081static void sync_cursor(Byte * d, int *row, int *col)
1082{
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +00001083 Byte *beg_cur; // begin and end of "d" line
1084 Byte *end_scr; // begin and end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001085 Byte *tp;
1086 int cnt, ro, co;
1087
1088 beg_cur = begin_line(d); // first char of cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001089
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001090 end_scr = end_screen(); // last char of screen
1091
1092 if (beg_cur < screenbegin) {
1093 // "d" is before top line on screen
1094 // how many lines do we have to move
1095 cnt = count_lines(beg_cur, screenbegin);
1096 sc1:
1097 screenbegin = beg_cur;
1098 if (cnt > (rows - 1) / 2) {
1099 // we moved too many lines. put "dot" in middle of screen
1100 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1101 screenbegin = prev_line(screenbegin);
1102 }
1103 }
1104 } else if (beg_cur > end_scr) {
1105 // "d" is after bottom line on screen
1106 // how many lines do we have to move
1107 cnt = count_lines(end_scr, beg_cur);
1108 if (cnt > (rows - 1) / 2)
1109 goto sc1; // too many lines
1110 for (ro = 0; ro < cnt - 1; ro++) {
1111 // move screen begin the same amount
1112 screenbegin = next_line(screenbegin);
1113 // now, move the end of screen
1114 end_scr = next_line(end_scr);
1115 end_scr = end_line(end_scr);
1116 }
1117 }
1118 // "d" is on screen- find out which row
1119 tp = screenbegin;
1120 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1121 if (tp == beg_cur)
1122 break;
1123 tp = next_line(tp);
1124 }
1125
1126 // find out what col "d" is on
1127 co = 0;
1128 do { // drive "co" to correct column
1129 if (*tp == '\n' || *tp == '\0')
1130 break;
1131 if (*tp == '\t') {
1132 // 7 - (co % 8 )
1133 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001134 } else if (*tp < ' ' || *tp == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001135 co++; // display as ^X, use 2 columns
1136 }
1137 } while (tp++ < d && ++co);
1138
1139 // "co" is the column where "dot" is.
1140 // The screen has "columns" columns.
1141 // The currently displayed columns are 0+offset -- columns+ofset
1142 // |-------------------------------------------------------------|
1143 // ^ ^ ^
1144 // offset | |------- columns ----------------|
1145 //
1146 // If "co" is already in this range then we do not have to adjust offset
1147 // but, we do have to subtract the "offset" bias from "co".
1148 // If "co" is outside this range then we have to change "offset".
1149 // If the first char of a line is a tab the cursor will try to stay
1150 // in column 7, but we have to set offset to 0.
1151
1152 if (co < 0 + offset) {
1153 offset = co;
1154 }
1155 if (co >= columns + offset) {
1156 offset = co - columns + 1;
1157 }
1158 // if the first char of the line is a tab, and "dot" is sitting on it
1159 // force offset to 0.
1160 if (d == beg_cur && *d == '\t') {
1161 offset = 0;
1162 }
1163 co -= offset;
1164
1165 *row = ro;
1166 *col = co;
1167}
1168
1169//----- Text Movement Routines ---------------------------------
1170static Byte *begin_line(Byte * p) // return pointer to first char cur line
1171{
1172 while (p > text && p[-1] != '\n')
1173 p--; // go to cur line B-o-l
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001174 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001175}
1176
1177static Byte *end_line(Byte * p) // return pointer to NL of cur line line
1178{
1179 while (p < end - 1 && *p != '\n')
1180 p++; // go to cur line E-o-l
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001181 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001182}
1183
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001184static inline Byte *dollar_line(Byte * p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001185{
1186 while (p < end - 1 && *p != '\n')
1187 p++; // go to cur line E-o-l
1188 // Try to stay off of the Newline
1189 if (*p == '\n' && (p - begin_line(p)) > 0)
1190 p--;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001191 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001192}
1193
1194static Byte *prev_line(Byte * p) // return pointer first char prev line
1195{
1196 p = begin_line(p); // goto begining of cur line
1197 if (p[-1] == '\n' && p > text)
1198 p--; // step to prev line
1199 p = begin_line(p); // goto begining of prev line
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001200 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001201}
1202
1203static Byte *next_line(Byte * p) // return pointer first char next line
1204{
1205 p = end_line(p);
1206 if (*p == '\n' && p < end - 1)
1207 p++; // step to next line
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001208 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001209}
1210
1211//----- Text Information Routines ------------------------------
1212static Byte *end_screen(void)
1213{
1214 Byte *q;
1215 int cnt;
1216
1217 // find new bottom line
1218 q = screenbegin;
1219 for (cnt = 0; cnt < rows - 2; cnt++)
1220 q = next_line(q);
1221 q = end_line(q);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001222 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001223}
1224
1225static int count_lines(Byte * start, Byte * stop) // count line from start to stop
1226{
1227 Byte *q;
1228 int cnt;
1229
1230 if (stop < start) { // start and stop are backwards- reverse them
1231 q = start;
1232 start = stop;
1233 stop = q;
1234 }
1235 cnt = 0;
1236 stop = end_line(stop); // get to end of this line
1237 for (q = start; q <= stop && q <= end - 1; q++) {
1238 if (*q == '\n')
1239 cnt++;
1240 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001241 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001242}
1243
1244static Byte *find_line(int li) // find begining of line #li
1245{
1246 Byte *q;
1247
1248 for (q = text; li > 1; li--) {
1249 q = next_line(q);
1250 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001251 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001252}
1253
1254//----- Dot Movement Routines ----------------------------------
1255static void dot_left(void)
1256{
1257 if (dot > text && dot[-1] != '\n')
1258 dot--;
1259}
1260
1261static void dot_right(void)
1262{
1263 if (dot < end - 1 && *dot != '\n')
1264 dot++;
1265}
1266
1267static void dot_begin(void)
1268{
1269 dot = begin_line(dot); // return pointer to first char cur line
1270}
1271
1272static void dot_end(void)
1273{
1274 dot = end_line(dot); // return pointer to last char cur line
1275}
1276
1277static Byte *move_to_col(Byte * p, int l)
1278{
1279 int co;
1280
1281 p = begin_line(p);
1282 co = 0;
1283 do {
1284 if (*p == '\n' || *p == '\0')
1285 break;
1286 if (*p == '\t') {
1287 // 7 - (co % 8 )
1288 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001289 } else if (*p < ' ' || *p == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001290 co++; // display as ^X, use 2 columns
1291 }
1292 } while (++co <= l && p++ < end);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001293 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001294}
1295
1296static void dot_next(void)
1297{
1298 dot = next_line(dot);
1299}
1300
1301static void dot_prev(void)
1302{
1303 dot = prev_line(dot);
1304}
1305
1306static void dot_scroll(int cnt, int dir)
1307{
1308 Byte *q;
1309
1310 for (; cnt > 0; cnt--) {
1311 if (dir < 0) {
1312 // scroll Backwards
1313 // ctrl-Y scroll up one line
1314 screenbegin = prev_line(screenbegin);
1315 } else {
1316 // scroll Forwards
1317 // ctrl-E scroll down one line
1318 screenbegin = next_line(screenbegin);
1319 }
1320 }
1321 // make sure "dot" stays on the screen so we dont scroll off
1322 if (dot < screenbegin)
1323 dot = screenbegin;
1324 q = end_screen(); // find new bottom line
1325 if (dot > q)
1326 dot = begin_line(q); // is dot is below bottom line?
1327 dot_skip_over_ws();
1328}
1329
1330static void dot_skip_over_ws(void)
1331{
1332 // skip WS
1333 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1334 dot++;
1335}
1336
1337static void dot_delete(void) // delete the char at 'dot'
1338{
1339 (void) text_hole_delete(dot, dot);
1340}
1341
1342static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end"
1343{
1344 if (p >= end && end > text) {
1345 p = end - 1;
1346 indicate_error('1');
1347 }
1348 if (p < text) {
1349 p = text;
1350 indicate_error('2');
1351 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001352 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001353}
1354
1355//----- Helper Utility Routines --------------------------------
1356
1357//----------------------------------------------------------------
1358//----- Char Routines --------------------------------------------
1359/* Chars that are part of a word-
1360 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1361 * Chars that are Not part of a word (stoppers)
1362 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1363 * Chars that are WhiteSpace
1364 * TAB NEWLINE VT FF RETURN SPACE
1365 * DO NOT COUNT NEWLINE AS WHITESPACE
1366 */
1367
1368static Byte *new_screen(int ro, int co)
1369{
1370 int li;
1371
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001372 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001373 screensize = ro * co + 8;
Denis Vlasenkob95636c2006-12-19 23:36:04 +00001374 screen = xmalloc(screensize);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001375 // initialize the new screen. assume this will be a empty file.
1376 screen_erase();
Eric Andersenaff114c2004-04-14 17:51:38 +00001377 // non-existent text[] lines start with a tilde (~).
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001378 for (li = 1; li < ro - 1; li++) {
1379 screen[(li * co) + 0] = '~';
1380 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001381 return screen;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001382}
1383
1384static Byte *new_text(int size)
1385{
1386 if (size < 10240)
1387 size = 10240; // have a minimum size for new files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001388 free(text);
Denis Vlasenkob95636c2006-12-19 23:36:04 +00001389 text = xmalloc(size + 8);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001390 memset(text, '\0', size); // clear new text[]
1391 //text += 4; // leave some room for "oops"
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001392 return text;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001393}
1394
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001395#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001396static int mycmp(Byte * s1, Byte * s2, int len)
1397{
1398 int i;
1399
1400 i = strncmp((char *) s1, (char *) s2, len);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001401#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001402 if (ignorecase) {
1403 i = strncasecmp((char *) s1, (char *) s2, len);
1404 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001405#endif
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001406 return i;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001407}
1408
1409static Byte *char_search(Byte * p, Byte * pat, int dir, int range) // search for pattern starting at p
1410{
1411#ifndef REGEX_SEARCH
1412 Byte *start, *stop;
1413 int len;
1414
1415 len = strlen((char *) pat);
1416 if (dir == FORWARD) {
1417 stop = end - 1; // assume range is p - end-1
1418 if (range == LIMITED)
1419 stop = next_line(p); // range is to next line
1420 for (start = p; start < stop; start++) {
1421 if (mycmp(start, pat, len) == 0) {
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001422 return start;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001423 }
1424 }
1425 } else if (dir == BACK) {
1426 stop = text; // assume range is text - p
1427 if (range == LIMITED)
1428 stop = prev_line(p); // range is to prev line
1429 for (start = p - len; start >= stop; start--) {
1430 if (mycmp(start, pat, len) == 0) {
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001431 return start;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001432 }
1433 }
1434 }
1435 // pattern not found
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001436 return NULL;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001437#else /*REGEX_SEARCH */
1438 char *q;
1439 struct re_pattern_buffer preg;
1440 int i;
1441 int size, range;
1442
1443 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1444 preg.translate = 0;
1445 preg.fastmap = 0;
1446 preg.buffer = 0;
1447 preg.allocated = 0;
1448
1449 // assume a LIMITED forward search
1450 q = next_line(p);
1451 q = end_line(q);
1452 q = end - 1;
1453 if (dir == BACK) {
1454 q = prev_line(p);
1455 q = text;
1456 }
1457 // count the number of chars to search over, forward or backward
1458 size = q - p;
1459 if (size < 0)
1460 size = p - q;
1461 // RANGE could be negative if we are searching backwards
1462 range = q - p;
1463
1464 q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
1465 if (q != 0) {
1466 // The pattern was not compiled
1467 psbs("bad search pattern: \"%s\": %s", pat, q);
1468 i = 0; // return p if pattern not compiled
1469 goto cs1;
1470 }
1471
1472 q = p;
1473 if (range < 0) {
1474 q = p - size;
1475 if (q < text)
1476 q = text;
1477 }
1478 // search for the compiled pattern, preg, in p[]
1479 // range < 0- search backward
1480 // range > 0- search forward
1481 // 0 < start < size
1482 // re_search() < 0 not found or error
1483 // re_search() > 0 index of found pattern
1484 // struct pattern char int int int struct reg
1485 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1486 i = re_search(&preg, q, size, 0, range, 0);
1487 if (i == -1) {
1488 p = 0;
1489 i = 0; // return NULL if pattern not found
1490 }
1491 cs1:
1492 if (dir == FORWARD) {
1493 p = p + i;
1494 } else {
1495 p = p - i;
1496 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001497 return p;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001498#endif /* REGEX_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001499}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001500#endif /* FEATURE_VI_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001501
1502static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
1503{
1504 if (c == 22) { // Is this an ctrl-V?
1505 p = stupid_insert(p, '^'); // use ^ to indicate literal next
1506 p--; // backup onto ^
1507 refresh(FALSE); // show the ^
1508 c = get_one_char();
1509 *p = c;
1510 p++;
Paul Fox8552aec2005-09-16 12:20:05 +00001511 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001512 } else if (c == 27) { // Is this an ESC?
1513 cmd_mode = 0;
1514 cmdcnt = 0;
1515 end_cmd_q(); // stop adding to q
Paul Fox8552aec2005-09-16 12:20:05 +00001516 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001517 if ((p[-1] != '\n') && (dot>text)) {
1518 p--;
1519 }
Paul Foxd13b90b2005-07-18 22:17:25 +00001520 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001521 // 123456789
1522 if ((p[-1] != '\n') && (dot>text)) {
1523 p--;
1524 p = text_hole_delete(p, p); // shrink buffer 1 char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001525 }
1526 } else {
1527 // insert a char into text[]
1528 Byte *sp; // "save p"
1529
1530 if (c == 13)
1531 c = '\n'; // translate \r to \n
1532 sp = p; // remember addr of insert
1533 p = stupid_insert(p, c); // insert the char
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001534#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001535 if (showmatch && strchr(")]}", *sp) != NULL) {
1536 showmatching(sp);
1537 }
1538 if (autoindent && c == '\n') { // auto indent the new line
1539 Byte *q;
1540
1541 q = prev_line(p); // use prev line as templet
1542 for (; isblnk(*q); q++) {
1543 p = stupid_insert(p, *q); // insert the char
1544 }
1545 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001546#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001547 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001548 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001549}
1550
1551static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
1552{
1553 p = text_hole_make(p, 1);
1554 if (p != 0) {
1555 *p = c;
Paul Fox8552aec2005-09-16 12:20:05 +00001556 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001557 p++;
1558 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001559 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001560}
1561
1562static Byte find_range(Byte ** start, Byte ** stop, Byte c)
1563{
1564 Byte *save_dot, *p, *q;
1565 int cnt;
1566
1567 save_dot = dot;
1568 p = q = dot;
1569
1570 if (strchr("cdy><", c)) {
1571 // these cmds operate on whole lines
1572 p = q = begin_line(p);
1573 for (cnt = 1; cnt < cmdcnt; cnt++) {
1574 q = next_line(q);
1575 }
1576 q = end_line(q);
1577 } else if (strchr("^%$0bBeEft", c)) {
1578 // These cmds operate on char positions
1579 do_cmd(c); // execute movement cmd
1580 q = dot;
1581 } else if (strchr("wW", c)) {
1582 do_cmd(c); // execute movement cmd
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001583 // if we are at the next word's first char
1584 // step back one char
1585 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00001586 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001587 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1588 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1589 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001590 if (dot > text && *dot == '\n')
1591 dot--; // stay off NL
1592 q = dot;
1593 } else if (strchr("H-k{", c)) {
1594 // these operate on multi-lines backwards
1595 q = end_line(dot); // find NL
1596 do_cmd(c); // execute movement cmd
1597 dot_begin();
1598 p = dot;
1599 } else if (strchr("L+j}\r\n", c)) {
1600 // these operate on multi-lines forwards
1601 p = begin_line(dot);
1602 do_cmd(c); // execute movement cmd
1603 dot_end(); // find NL
1604 q = dot;
1605 } else {
1606 c = 27; // error- return an ESC char
1607 //break;
1608 }
1609 *start = p;
1610 *stop = q;
1611 if (q < p) {
1612 *start = q;
1613 *stop = p;
1614 }
1615 dot = save_dot;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001616 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001617}
1618
1619static int st_test(Byte * p, int type, int dir, Byte * tested)
1620{
1621 Byte c, c0, ci;
1622 int test, inc;
1623
1624 inc = dir;
1625 c = c0 = p[0];
1626 ci = p[inc];
1627 test = 0;
1628
1629 if (type == S_BEFORE_WS) {
1630 c = ci;
1631 test = ((!isspace(c)) || c == '\n');
1632 }
1633 if (type == S_TO_WS) {
1634 c = c0;
1635 test = ((!isspace(c)) || c == '\n');
1636 }
1637 if (type == S_OVER_WS) {
1638 c = c0;
1639 test = ((isspace(c)));
1640 }
1641 if (type == S_END_PUNCT) {
1642 c = ci;
1643 test = ((ispunct(c)));
1644 }
1645 if (type == S_END_ALNUM) {
1646 c = ci;
1647 test = ((isalnum(c)) || c == '_');
1648 }
1649 *tested = c;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001650 return test;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001651}
1652
1653static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
1654{
1655 Byte c;
1656
1657 while (st_test(p, type, dir, &c)) {
1658 // make sure we limit search to correct number of lines
1659 if (c == '\n' && --linecnt < 1)
1660 break;
1661 if (dir >= 0 && p >= end - 1)
1662 break;
1663 if (dir < 0 && p <= text)
1664 break;
1665 p += dir; // move to next char
1666 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001667 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001668}
1669
1670// find matching char of pair () [] {}
1671static Byte *find_pair(Byte * p, Byte c)
1672{
1673 Byte match, *q;
1674 int dir, level;
1675
1676 match = ')';
1677 level = 1;
1678 dir = 1; // assume forward
1679 switch (c) {
1680 case '(':
1681 match = ')';
1682 break;
1683 case '[':
1684 match = ']';
1685 break;
1686 case '{':
1687 match = '}';
1688 break;
1689 case ')':
1690 match = '(';
1691 dir = -1;
1692 break;
1693 case ']':
1694 match = '[';
1695 dir = -1;
1696 break;
1697 case '}':
1698 match = '{';
1699 dir = -1;
1700 break;
1701 }
1702 for (q = p + dir; text <= q && q < end; q += dir) {
1703 // look for match, count levels of pairs (( ))
1704 if (*q == c)
1705 level++; // increase pair levels
1706 if (*q == match)
1707 level--; // reduce pair level
1708 if (level == 0)
1709 break; // found matching pair
1710 }
1711 if (level != 0)
1712 q = NULL; // indicate no match
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001713 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001714}
1715
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001716#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001717// show the matching char of a pair, () [] {}
1718static void showmatching(Byte * p)
1719{
1720 Byte *q, *save_dot;
1721
1722 // we found half of a pair
1723 q = find_pair(p, *p); // get loc of matching char
1724 if (q == NULL) {
1725 indicate_error('3'); // no matching char
1726 } else {
1727 // "q" now points to matching pair
1728 save_dot = dot; // remember where we are
1729 dot = q; // go to new loc
1730 refresh(FALSE); // let the user see it
1731 (void) mysleep(40); // give user some time
1732 dot = save_dot; // go back to old loc
1733 refresh(FALSE);
1734 }
1735}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001736#endif /* FEATURE_VI_SETOPTS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001737
1738// open a hole in text[]
1739static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
1740{
1741 Byte *src, *dest;
1742 int cnt;
1743
1744 if (size <= 0)
1745 goto thm0;
1746 src = p;
1747 dest = p + size;
1748 cnt = end - src; // the rest of buffer
1749 if (memmove(dest, src, cnt) != dest) {
1750 psbs("can't create room for new characters");
1751 }
1752 memset(p, ' ', size); // clear new hole
1753 end = end + size; // adjust the new END
Paul Fox8552aec2005-09-16 12:20:05 +00001754 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001755 thm0:
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001756 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001757}
1758
1759// close a hole in text[]
1760static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
1761{
1762 Byte *src, *dest;
1763 int cnt, hole_size;
1764
1765 // move forwards, from beginning
1766 // assume p <= q
1767 src = q + 1;
1768 dest = p;
1769 if (q < p) { // they are backward- swap them
1770 src = p + 1;
1771 dest = q;
1772 }
1773 hole_size = q - p + 1;
1774 cnt = end - src;
1775 if (src < text || src > end)
1776 goto thd0;
1777 if (dest < text || dest >= end)
1778 goto thd0;
1779 if (src >= end)
1780 goto thd_atend; // just delete the end of the buffer
1781 if (memmove(dest, src, cnt) != dest) {
1782 psbs("can't delete the character");
1783 }
1784 thd_atend:
1785 end = end - hole_size; // adjust the new END
1786 if (dest >= end)
1787 dest = end - 1; // make sure dest in below end-1
1788 if (end <= text)
1789 dest = end = text; // keep pointers valid
Paul Fox8552aec2005-09-16 12:20:05 +00001790 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001791 thd0:
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001792 return dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001793}
1794
1795// copy text into register, then delete text.
1796// if dist <= 0, do not include, or go past, a NewLine
1797//
1798static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
1799{
1800 Byte *p;
1801
1802 // make sure start <= stop
1803 if (start > stop) {
1804 // they are backwards, reverse them
1805 p = start;
1806 start = stop;
1807 stop = p;
1808 }
1809 if (dist <= 0) {
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +00001810 // we cannot cross NL boundaries
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001811 p = start;
1812 if (*p == '\n')
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001813 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001814 // dont go past a NewLine
1815 for (; p + 1 <= stop; p++) {
1816 if (p[1] == '\n') {
1817 stop = p; // "stop" just before NewLine
1818 break;
1819 }
1820 }
1821 }
1822 p = start;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001823#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001824 text_yank(start, stop, YDreg);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001825#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001826 if (yf == YANKDEL) {
1827 p = text_hole_delete(start, stop);
1828 } // delete lines
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001829 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001830}
1831
1832static void show_help(void)
1833{
1834 puts("These features are available:"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001835#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001836 "\n\tPattern searches with / and ?"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001837#endif
1838#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001839 "\n\tLast command repeat with \'.\'"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001840#endif
1841#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001842 "\n\tLine marking with 'x"
1843 "\n\tNamed buffers with \"x"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001844#endif
1845#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001846 "\n\tReadonly if vi is called as \"view\""
1847 "\n\tReadonly with -R command line arg"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001848#endif
1849#if ENABLE_FEATURE_VI_SET
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001850 "\n\tSome colon mode commands with \':\'"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001851#endif
1852#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001853 "\n\tSettable options with \":set\""
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001854#endif
1855#if ENABLE_FEATURE_VI_USE_SIGNALS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001856 "\n\tSignal catching- ^C"
1857 "\n\tJob suspend and resume with ^Z"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001858#endif
1859#if ENABLE_FEATURE_VI_WIN_RESIZE
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001860 "\n\tAdapt to window re-sizes"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001861#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001862 );
1863}
1864
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001865static inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001866{
1867 Byte c, b[2];
1868
1869 b[1] = '\0';
1870 strcpy((char *) buf, ""); // init buf
1871 if (strlen((char *) s) <= 0)
1872 s = (Byte *) "(NULL)";
1873 for (; *s > '\0'; s++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001874 int c_is_no_print;
1875
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001876 c = *s;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001877 c_is_no_print = c > 127 && !Isprint(c);
1878 if (c_is_no_print) {
1879 strcat((char *) buf, SOn);
1880 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001881 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001882 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001883 strcat((char *) buf, "^");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001884 if(c == 127)
1885 c = '?';
1886 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001887 c += '@';
1888 }
1889 b[0] = c;
1890 strcat((char *) buf, (char *) b);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001891 if (c_is_no_print)
1892 strcat((char *) buf, SOs);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001893 if (*s == '\n') {
1894 strcat((char *) buf, "$");
1895 }
1896 }
1897}
1898
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001899#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001900static void start_new_cmd_q(Byte c)
1901{
1902 // release old cmd
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001903 free(last_modifying_cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001904 // get buffer for new cmd
Denis Vlasenkob95636c2006-12-19 23:36:04 +00001905 last_modifying_cmd = xmalloc(BUFSIZ);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001906 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
1907 // if there is a current cmd count put it in the buffer first
1908 if (cmdcnt > 0)
Paul Foxd957b952005-11-28 18:07:53 +00001909 sprintf((char *) last_modifying_cmd, "%d%c", cmdcnt, c);
1910 else // just save char c onto queue
1911 last_modifying_cmd[0] = c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001912 adding2q = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001913}
1914
1915static void end_cmd_q(void)
1916{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001917#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001918 YDreg = 26; // go back to default Yank/Delete reg
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001919#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001920 adding2q = 0;
1921 return;
1922}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001923#endif /* FEATURE_VI_DOT_CMD */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001924
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001925#if ENABLE_FEATURE_VI_YANKMARK \
1926 || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
1927 || ENABLE_FEATURE_VI_CRASHME
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001928static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
1929{
1930 int cnt, i;
1931
1932 i = strlen((char *) s);
1933 p = text_hole_make(p, i);
1934 strncpy((char *) p, (char *) s, i);
1935 for (cnt = 0; *s != '\0'; s++) {
1936 if (*s == '\n')
1937 cnt++;
1938 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001939#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001940 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001941#endif
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001942 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001943}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001944#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001945
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001946#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001947static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register
1948{
1949 Byte *t;
1950 int cnt;
1951
1952 if (q < p) { // they are backwards- reverse them
1953 t = q;
1954 q = p;
1955 p = t;
1956 }
1957 cnt = q - p + 1;
1958 t = reg[dest];
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001959 free(t); // if already a yank register, free it
Denis Vlasenkob95636c2006-12-19 23:36:04 +00001960 t = xmalloc(cnt + 1); // get a new register
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001961 memset(t, '\0', cnt + 1); // clear new text[]
1962 strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer
1963 reg[dest] = t;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001964 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001965}
1966
1967static Byte what_reg(void)
1968{
1969 Byte c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001970
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001971 c = 'D'; // default to D-reg
1972 if (0 <= YDreg && YDreg <= 25)
1973 c = 'a' + (Byte) YDreg;
1974 if (YDreg == 26)
1975 c = 'D';
1976 if (YDreg == 27)
1977 c = 'U';
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001978 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001979}
1980
1981static void check_context(Byte cmd)
1982{
1983 // A context is defined to be "modifying text"
1984 // Any modifying command establishes a new context.
1985
1986 if (dot < context_start || dot > context_end) {
1987 if (strchr((char *) modifying_cmds, cmd) != NULL) {
1988 // we are trying to modify text[]- make this the current context
1989 mark[27] = mark[26]; // move cur to prev
1990 mark[26] = dot; // move local to cur
1991 context_start = prev_line(prev_line(dot));
1992 context_end = next_line(next_line(dot));
1993 //loiter= start_loiter= now;
1994 }
1995 }
1996 return;
1997}
1998
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001999static inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002000{
2001 Byte *tmp;
2002
2003 // the current context is in mark[26]
2004 // the previous context is in mark[27]
2005 // only swap context if other context is valid
2006 if (text <= mark[27] && mark[27] <= end - 1) {
2007 tmp = mark[27];
2008 mark[27] = mark[26];
2009 mark[26] = tmp;
2010 p = mark[26]; // where we are going- previous context
2011 context_start = prev_line(prev_line(prev_line(p)));
2012 context_end = next_line(next_line(next_line(p)));
2013 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002014 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002015}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002016#endif /* FEATURE_VI_YANKMARK */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002017
2018static int isblnk(Byte c) // is the char a blank or tab
2019{
2020 return (c == ' ' || c == '\t');
2021}
2022
2023//----- Set terminal attributes --------------------------------
2024static void rawmode(void)
2025{
2026 tcgetattr(0, &term_orig);
2027 term_vi = term_orig;
2028 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2029 term_vi.c_iflag &= (~IXON & ~ICRNL);
2030 term_vi.c_oflag &= (~ONLCR);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002031 term_vi.c_cc[VMIN] = 1;
2032 term_vi.c_cc[VTIME] = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002033 erase_char = term_vi.c_cc[VERASE];
2034 tcsetattr(0, TCSANOW, &term_vi);
2035}
2036
2037static void cookmode(void)
2038{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002039 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002040 tcsetattr(0, TCSANOW, &term_orig);
2041}
2042
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002043//----- Come here when we get a window resize signal ---------
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002044#if ENABLE_FEATURE_VI_USE_SIGNALS
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002045static void winch_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002046{
2047 signal(SIGWINCH, winch_sig);
Rob Landleye5e1a102006-06-21 01:15:36 +00002048 if (ENABLE_FEATURE_VI_WIN_RESIZE)
Denis Vlasenko621204b2006-10-27 09:03:24 +00002049 get_terminal_width_height(0, &columns, &rows);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002050 new_screen(rows, columns); // get memory for virtual screen
2051 redraw(TRUE); // re-draw the screen
2052}
2053
2054//----- Come here when we get a continue signal -------------------
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002055static void cont_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002056{
2057 rawmode(); // terminal to "raw"
Paul Fox8552aec2005-09-16 12:20:05 +00002058 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002059 redraw(TRUE); // re-draw the screen
2060
2061 signal(SIGTSTP, suspend_sig);
2062 signal(SIGCONT, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002063 kill(my_pid, SIGCONT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002064}
2065
2066//----- Come here when we get a Suspend signal -------------------
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002067static void suspend_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002068{
2069 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2070 clear_to_eol(); // Erase to end of line
2071 cookmode(); // terminal to "cooked"
2072
2073 signal(SIGCONT, cont_sig);
2074 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002075 kill(my_pid, SIGTSTP);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002076}
2077
2078//----- Come here when we get a signal ---------------------------
2079static void catch_sig(int sig)
2080{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002081 signal(SIGINT, catch_sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002082 if(sig)
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002083 longjmp(restart, sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002084}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002085#endif /* FEATURE_VI_USE_SIGNALS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002086
2087static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2088{
2089 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002090 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002091 FD_ZERO(&rfds);
2092 FD_SET(0, &rfds);
2093 tv.tv_sec = 0;
2094 tv.tv_usec = hund * 10000;
2095 select(1, &rfds, NULL, NULL, &tv);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002096 return FD_ISSET(0, &rfds);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002097}
2098
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002099#define readbuffer bb_common_bufsiz1
2100
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002101static int readed_for_parse;
2102
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002103//----- IO Routines --------------------------------------------
2104static Byte readit(void) // read (maybe cursor) key from stdin
2105{
2106 Byte c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002107 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002108 struct esc_cmds {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002109 const char *seq;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002110 Byte val;
2111 };
2112
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002113 static const struct esc_cmds esccmds[] = {
2114 {"OA", (Byte) VI_K_UP}, // cursor key Up
2115 {"OB", (Byte) VI_K_DOWN}, // cursor key Down
2116 {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2117 {"OD", (Byte) VI_K_LEFT}, // cursor key Left
2118 {"OH", (Byte) VI_K_HOME}, // Cursor Key Home
2119 {"OF", (Byte) VI_K_END}, // Cursor Key End
2120 {"[A", (Byte) VI_K_UP}, // cursor key Up
2121 {"[B", (Byte) VI_K_DOWN}, // cursor key Down
2122 {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right
2123 {"[D", (Byte) VI_K_LEFT}, // cursor key Left
2124 {"[H", (Byte) VI_K_HOME}, // Cursor Key Home
2125 {"[F", (Byte) VI_K_END}, // Cursor Key End
2126 {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home
2127 {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2128 {"[4~", (Byte) VI_K_END}, // Cursor Key End
2129 {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2130 {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2131 {"OP", (Byte) VI_K_FUN1}, // Function Key F1
2132 {"OQ", (Byte) VI_K_FUN2}, // Function Key F2
2133 {"OR", (Byte) VI_K_FUN3}, // Function Key F3
2134 {"OS", (Byte) VI_K_FUN4}, // Function Key F4
2135 {"[15~", (Byte) VI_K_FUN5}, // Function Key F5
2136 {"[17~", (Byte) VI_K_FUN6}, // Function Key F6
2137 {"[18~", (Byte) VI_K_FUN7}, // Function Key F7
2138 {"[19~", (Byte) VI_K_FUN8}, // Function Key F8
2139 {"[20~", (Byte) VI_K_FUN9}, // Function Key F9
2140 {"[21~", (Byte) VI_K_FUN10}, // Function Key F10
2141 {"[23~", (Byte) VI_K_FUN11}, // Function Key F11
2142 {"[24~", (Byte) VI_K_FUN12}, // Function Key F12
2143 {"[11~", (Byte) VI_K_FUN1}, // Function Key F1
2144 {"[12~", (Byte) VI_K_FUN2}, // Function Key F2
2145 {"[13~", (Byte) VI_K_FUN3}, // Function Key F3
2146 {"[14~", (Byte) VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002147 };
2148
2149#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2150
2151 (void) alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002152 fflush(stdout);
2153 n = readed_for_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002154 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002155 if (n <= 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002156 ri0:
2157 // the Q is empty, wait for a typed char
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002158 n = read(0, readbuffer, BUFSIZ - 1);
2159 if (n < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002160 if (errno == EINTR)
2161 goto ri0; // interrupted sys call
2162 if (errno == EBADF)
2163 editing = 0;
2164 if (errno == EFAULT)
2165 editing = 0;
2166 if (errno == EINVAL)
2167 editing = 0;
2168 if (errno == EIO)
2169 editing = 0;
2170 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002171 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002172 if(n <= 0)
2173 return 0; // error
2174 if (readbuffer[0] == 27) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002175 // This is an ESC char. Is this Esc sequence?
2176 // Could be bare Esc key. See if there are any
2177 // more chars to read after the ESC. This would
2178 // be a Function or Cursor Key sequence.
2179 FD_ZERO(&rfds);
2180 FD_SET(0, &rfds);
2181 tv.tv_sec = 0;
2182 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2183
2184 // keep reading while there are input chars and room in buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002185 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002186 // read the rest of the ESC string
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002187 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2188 if (r > 0) {
2189 n += r;
2190 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002191 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002192 }
2193 readed_for_parse = n;
2194 }
2195 c = readbuffer[0];
2196 if(c == 27 && n > 1) {
2197 // Maybe cursor or function key?
2198 const struct esc_cmds *eindex;
2199
2200 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2201 int cnt = strlen(eindex->seq);
2202
2203 if(n <= cnt)
2204 continue;
2205 if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
2206 continue;
2207 // is a Cursor key- put derived value back into Q
2208 c = eindex->val;
2209 // for squeeze out the ESC sequence
2210 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002211 break;
2212 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002213 if(eindex == &esccmds[ESCCMDS_COUNT]) {
2214 /* defined ESC sequence not found, set only one ESC */
2215 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002216 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002217 } else {
2218 n = 1;
2219 }
2220 // remove key sequence from Q
2221 readed_for_parse -= n;
2222 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002223 (void) alarm(3); // we are done waiting for input, turn alarm ON
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002224 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002225}
2226
2227//----- IO Routines --------------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002228static Byte get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002229{
2230 static Byte c;
2231
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002232#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002233 // ! adding2q && ioq == 0 read()
2234 // ! adding2q && ioq != 0 *ioq
2235 // adding2q *last_modifying_cmd= read()
2236 if (!adding2q) {
2237 // we are not adding to the q.
2238 // but, we may be reading from a q
2239 if (ioq == 0) {
2240 // there is no current q, read from STDIN
2241 c = readit(); // get the users input
2242 } else {
2243 // there is a queue to get chars from first
2244 c = *ioq++;
2245 if (c == '\0') {
2246 // the end of the q, read from STDIN
2247 free(ioq_start);
2248 ioq_start = ioq = 0;
2249 c = readit(); // get the users input
2250 }
2251 }
2252 } else {
2253 // adding STDIN chars to q
2254 c = readit(); // get the users input
2255 if (last_modifying_cmd != 0) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002256 int len = strlen((char *) last_modifying_cmd);
2257 if (len + 1 >= BUFSIZ) {
2258 psbs("last_modifying_cmd overrun");
2259 } else {
2260 // add new char to q
2261 last_modifying_cmd[len] = c;
2262 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002263 }
2264 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002265#else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002266 c = readit(); // get the users input
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002267#endif /* FEATURE_VI_DOT_CMD */
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002268 return c; // return the char, where ever it came from
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002269}
2270
2271static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2272{
2273 Byte buf[BUFSIZ];
2274 Byte c;
2275 int i;
2276 static Byte *obufp = NULL;
2277
2278 strcpy((char *) buf, (char *) prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002279 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002280 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2281 clear_to_eol(); // clear the line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002282 write1((char *) prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002283
2284 for (i = strlen((char *) buf); i < BUFSIZ;) {
2285 c = get_one_char(); // read user input
2286 if (c == '\n' || c == '\r' || c == 27)
2287 break; // is this end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002288 if (c == erase_char || c == 8 || c == 127) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002289 // user wants to erase prev char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002290 i--; // backup to prev char
2291 buf[i] = '\0'; // erase the char
2292 buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002293 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002294 if (i <= 0) { // user backs up before b-o-l, exit
2295 break;
2296 }
2297 } else {
2298 buf[i] = c; // save char in buffer
2299 buf[i + 1] = '\0'; // make sure buffer is null terminated
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002300 putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002301 i++;
2302 }
2303 }
2304 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002305 free(obufp);
Rob Landleyd921b2e2006-08-03 15:41:12 +00002306 obufp = (Byte *) xstrdup((char *) buf);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002307 return obufp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002308}
2309
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002310static int file_size(const Byte * fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002311{
2312 struct stat st_buf;
2313 int cnt, sr;
2314
Eric Andersena68ea1c2006-01-30 22:48:39 +00002315 if (fn == 0 || strlen((char *)fn) <= 0)
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002316 return -1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002317 cnt = -1;
2318 sr = stat((char *) fn, &st_buf); // see if file exists
2319 if (sr >= 0) {
2320 cnt = (int) st_buf.st_size;
2321 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002322 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002323}
2324
2325static int file_insert(Byte * fn, Byte * p, int size)
2326{
2327 int fd, cnt;
2328
2329 cnt = -1;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002330#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002331 readonly = FALSE;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002332#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002333 if (fn == 0 || strlen((char*) fn) <= 0) {
2334 psbs("No filename given");
2335 goto fi0;
2336 }
2337 if (size == 0) {
2338 // OK- this is just a no-op
2339 cnt = 0;
2340 goto fi0;
2341 }
2342 if (size < 0) {
2343 psbs("Trying to insert a negative number (%d) of characters", size);
2344 goto fi0;
2345 }
2346 if (p < text || p > end) {
2347 psbs("Trying to insert file outside of memory");
2348 goto fi0;
2349 }
2350
2351 // see if we can open the file
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002352#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002353 if (vi_readonly) goto fi1; // do not try write-mode
2354#endif
2355 fd = open((char *) fn, O_RDWR); // assume read & write
2356 if (fd < 0) {
2357 // could not open for writing- maybe file is read only
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002358#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002359 fi1:
2360#endif
2361 fd = open((char *) fn, O_RDONLY); // try read-only
2362 if (fd < 0) {
Denis Vlasenkoea620772006-10-14 02:23:43 +00002363 psbs("\"%s\" %s", fn, "cannot open file");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002364 goto fi0;
2365 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002366#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002367 // got the file- read-only
2368 readonly = TRUE;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002369#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002370 }
2371 p = text_hole_make(p, size);
2372 cnt = read(fd, p, size);
2373 close(fd);
2374 if (cnt < 0) {
2375 cnt = -1;
2376 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
Denis Vlasenkoea620772006-10-14 02:23:43 +00002377 psbs("cannot read file \"%s\"", fn);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002378 } else if (cnt < size) {
2379 // There was a partial read, shrink unused space text[]
2380 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
Denis Vlasenkoea620772006-10-14 02:23:43 +00002381 psbs("cannot read all of file \"%s\"", fn);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002382 }
2383 if (cnt >= size)
Paul Fox8552aec2005-09-16 12:20:05 +00002384 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002385 fi0:
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002386 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002387}
2388
2389static int file_write(Byte * fn, Byte * first, Byte * last)
2390{
2391 int fd, cnt, charcnt;
2392
2393 if (fn == 0) {
2394 psbs("No current filename");
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002395 return -2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002396 }
2397 charcnt = 0;
2398 // FIXIT- use the correct umask()
2399 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2400 if (fd < 0)
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002401 return -1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002402 cnt = last - first + 1;
2403 charcnt = write(fd, first, cnt);
2404 if (charcnt == cnt) {
2405 // good write
2406 //file_modified= FALSE; // the file has not been modified
2407 } else {
2408 charcnt = 0;
2409 }
2410 close(fd);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002411 return charcnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002412}
2413
2414//----- Terminal Drawing ---------------------------------------
2415// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002416// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002417// screen coordinates
2418// 0,0 ... 0,79
2419// 1,0 ... 1,79
2420// . ... .
2421// . ... .
2422// 22,0 ... 22,79
2423// 23,0 ... 23,79 status line
2424//
2425
2426//----- Move the cursor to row x col (count from 0, not 1) -------
2427static void place_cursor(int row, int col, int opti)
2428{
2429 char cm1[BUFSIZ];
2430 char *cm;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002431#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002432 char cm2[BUFSIZ];
2433 Byte *screenp;
2434 // char cm3[BUFSIZ];
2435 int Rrow= last_row;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002436#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002437
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002438 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2439
2440 if (row < 0) row = 0;
2441 if (row >= rows) row = rows - 1;
2442 if (col < 0) col = 0;
2443 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002444
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002445 //----- 1. Try the standard terminal ESC sequence
2446 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2447 cm= cm1;
2448 if (! opti) goto pc0;
2449
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002450#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002451 //----- find the minimum # of chars to move cursor -------------
2452 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2453 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002454
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002455 // move to the correct row
2456 while (row < Rrow) {
2457 // the cursor has to move up
2458 strcat(cm2, CMup);
2459 Rrow--;
2460 }
2461 while (row > Rrow) {
2462 // the cursor has to move down
2463 strcat(cm2, CMdown);
2464 Rrow++;
2465 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002466
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002467 // now move to the correct column
2468 strcat(cm2, "\r"); // start at col 0
2469 // just send out orignal source char to get to correct place
2470 screenp = &screen[row * columns]; // start of screen line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002471 strncat(cm2, (char* )screenp, col);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002472
2473 //----- 3. Try some other way of moving cursor
2474 //---------------------------------------------
2475
2476 // pick the shortest cursor motion to send out
2477 cm= cm1;
2478 if (strlen(cm2) < strlen(cm)) {
2479 cm= cm2;
2480 } /* else if (strlen(cm3) < strlen(cm)) {
2481 cm= cm3;
2482 } */
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002483#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
2484 pc0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002485 write1(cm); // move the cursor
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002486}
2487
2488//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002489static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002490{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002491 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002492}
2493
2494//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002495static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002496{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002497 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002498}
2499
2500//----- Start standout mode ------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002501static void standout_start(void) // send "start reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002502{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002503 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002504}
2505
2506//----- End standout mode --------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002507static void standout_end(void) // send "end reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002508{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002509 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002510}
2511
2512//----- Flash the screen --------------------------------------
2513static void flash(int h)
2514{
2515 standout_start(); // send "start reverse video" sequence
2516 redraw(TRUE);
2517 (void) mysleep(h);
2518 standout_end(); // send "end reverse video" sequence
2519 redraw(TRUE);
2520}
2521
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002522static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002523{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002524#if ENABLE_FEATURE_VI_CRASHME
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002525 if (crashme > 0)
2526 return; // generate a random command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002527#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002528 if (!err_method) {
2529 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002530 } else {
2531 flash(10);
2532 }
2533}
2534
2535//----- Screen[] Routines --------------------------------------
2536//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002537static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002538{
2539 memset(screen, ' ', screensize); // clear new screen
2540}
2541
Eric Andersena68ea1c2006-01-30 22:48:39 +00002542static int bufsum(unsigned char *buf, int count)
Paul Fox8552aec2005-09-16 12:20:05 +00002543{
2544 int sum = 0;
Eric Andersena68ea1c2006-01-30 22:48:39 +00002545 unsigned char *e = buf + count;
Paul Fox8552aec2005-09-16 12:20:05 +00002546 while (buf < e)
2547 sum += *buf++;
2548 return sum;
2549}
2550
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002551//----- Draw the status line at bottom of the screen -------------
2552static void show_status_line(void)
2553{
Paul Foxc3504852005-09-16 12:48:18 +00002554 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002555
Paul Fox8552aec2005-09-16 12:20:05 +00002556 // either we already have an error or status message, or we
2557 // create one.
2558 if (!have_status_msg) {
2559 cnt = format_edit_status();
2560 cksum = bufsum(status_buffer, cnt);
2561 }
2562 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
2563 last_status_cksum= cksum; // remember if we have seen this line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002564 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002565 write1((char*)status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002566 clear_to_eol();
Paul Fox8552aec2005-09-16 12:20:05 +00002567 if (have_status_msg) {
Eric Andersena68ea1c2006-01-30 22:48:39 +00002568 if (((int)strlen((char*)status_buffer) - (have_status_msg - 1)) >
Paul Fox8552aec2005-09-16 12:20:05 +00002569 (columns - 1) ) {
2570 have_status_msg = 0;
2571 Hit_Return();
2572 }
2573 have_status_msg = 0;
2574 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002575 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2576 }
Eric Andersena9eb33d2004-08-19 19:15:06 +00002577 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002578}
2579
2580//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00002581// format status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002582static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002583{
2584 va_list args;
2585
2586 va_start(args, format);
2587 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
Eric Andersen0ef24c62005-07-18 10:32:59 +00002588 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002589 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2590 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002591
2592 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002593
2594 return;
2595}
2596
Paul Fox8552aec2005-09-16 12:20:05 +00002597// format status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002598static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002599{
2600 va_list args;
2601
2602 va_start(args, format);
2603 vsprintf((char *) status_buffer, format, args);
2604 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002605
2606 have_status_msg = 1;
2607
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002608 return;
2609}
2610
2611static void ni(Byte * s) // display messages
2612{
2613 Byte buf[BUFSIZ];
2614
2615 print_literal(buf, s);
2616 psbs("\'%s\' is not implemented", buf);
2617}
2618
Paul Fox8552aec2005-09-16 12:20:05 +00002619static int format_edit_status(void) // show file status on status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002620{
Paul Fox8552aec2005-09-16 12:20:05 +00002621 int cur, percent, ret, trunc_at;
2622 static int tot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002623
Paul Fox8552aec2005-09-16 12:20:05 +00002624 // file_modified is now a counter rather than a flag. this
2625 // helps reduce the amount of line counting we need to do.
2626 // (this will cause a mis-reporting of modified status
2627 // once every MAXINT editing operations.)
2628
2629 // it would be nice to do a similar optimization here -- if
2630 // we haven't done a motion that could have changed which line
2631 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002632 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00002633
2634 // reduce counting -- the total lines can't have
2635 // changed if we haven't done any edits.
2636 if (file_modified != last_file_modified) {
2637 tot = cur + count_lines(dot, end - 1) - 1;
2638 last_file_modified = file_modified;
2639 }
2640
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002641 // current line percent
2642 // ------------- ~~ ----------
2643 // total lines 100
2644 if (tot > 0) {
2645 percent = (100 * cur) / tot;
2646 } else {
2647 cur = tot = 0;
2648 percent = 100;
2649 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00002650
Paul Fox8552aec2005-09-16 12:20:05 +00002651 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2652 columns : STATUS_BUFFER_LEN-1;
2653
2654 ret = snprintf((char *) status_buffer, trunc_at+1,
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002655#if ENABLE_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002656 "%c %s%s%s %d/%d %d%%",
2657#else
2658 "%c %s%s %d/%d %d%%",
2659#endif
2660 (cmd_mode ? (cmd_mode == 2 ? 'R':'I'):'-'),
2661 (cfn != 0 ? (char *) cfn : "No file"),
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002662#if ENABLE_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002663 ((vi_readonly || readonly) ? " [Read-only]" : ""),
2664#endif
2665 (file_modified ? " [modified]" : ""),
2666 cur, tot, percent);
2667
2668 if (ret >= 0 && ret < trunc_at)
2669 return ret; /* it all fit */
2670
2671 return trunc_at; /* had to truncate */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002672}
2673
2674//----- Force refresh of all Lines -----------------------------
2675static void redraw(int full_screen)
2676{
2677 place_cursor(0, 0, FALSE); // put cursor in correct place
2678 clear_to_eos(); // tel terminal to erase display
2679 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00002680 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002681 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00002682 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002683}
2684
2685//----- Format a text[] line into a buffer ---------------------
2686static void format_line(Byte *dest, Byte *src, int li)
2687{
2688 int co;
2689 Byte c;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002690
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002691 for (co= 0; co < MAX_SCR_COLS; co++) {
2692 c= ' '; // assume blank
2693 if (li > 0 && co == 0) {
2694 c = '~'; // not first line, assume Tilde
2695 }
2696 // are there chars in text[] and have we gone past the end
2697 if (text < end && src < end) {
2698 c = *src++;
2699 }
2700 if (c == '\n')
2701 break;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002702 if (c > 127 && !Isprint(c)) {
2703 c = '.';
2704 }
2705 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002706 if (c == '\t') {
2707 c = ' ';
2708 // co % 8 != 7
2709 for (; (co % tabstop) != (tabstop - 1); co++) {
2710 dest[co] = c;
2711 }
2712 } else {
2713 dest[co++] = '^';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002714 if(c == 127)
2715 c = '?';
2716 else
2717 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002718 }
2719 }
2720 // the co++ is done here so that the column will
2721 // not be overwritten when we blank-out the rest of line
2722 dest[co] = c;
2723 if (src >= end)
2724 break;
2725 }
2726}
2727
2728//----- Refresh the changed screen lines -----------------------
2729// Copy the source line from text[] into the buffer and note
2730// if the current screenline is different from the new buffer.
2731// If they differ then that line needs redrawing on the terminal.
2732//
2733static void refresh(int full_screen)
2734{
2735 static int old_offset;
2736 int li, changed;
2737 Byte buf[MAX_SCR_COLS];
2738 Byte *tp, *sp; // pointer into text[] and screen[]
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002739#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002740 int last_li= -2; // last line that changed- for optimizing cursor movement
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002741#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002742
Rob Landleye5e1a102006-06-21 01:15:36 +00002743 if (ENABLE_FEATURE_VI_WIN_RESIZE)
2744 get_terminal_width_height(0, &columns, &rows);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002745 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2746 tp = screenbegin; // index into text[] of top line
2747
2748 // compare text[] to screen[] and mark screen[] lines that need updating
2749 for (li = 0; li < rows - 1; li++) {
2750 int cs, ce; // column start & end
2751 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2752 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2753 // format current text line into buf
2754 format_line(buf, tp, li);
2755
2756 // skip to the end of the current text[] line
2757 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2758
2759 // see if there are any changes between vitual screen and buf
2760 changed = FALSE; // assume no change
2761 cs= 0;
2762 ce= columns-1;
2763 sp = &screen[li * columns]; // start of screen line
2764 if (full_screen) {
2765 // force re-draw of every single column from 0 - columns-1
2766 goto re0;
2767 }
2768 // compare newly formatted buffer with virtual screen
2769 // look forward for first difference between buf and screen
2770 for ( ; cs <= ce; cs++) {
2771 if (buf[cs + offset] != sp[cs]) {
2772 changed = TRUE; // mark for redraw
2773 break;
2774 }
2775 }
2776
2777 // look backward for last difference between buf and screen
2778 for ( ; ce >= cs; ce--) {
2779 if (buf[ce + offset] != sp[ce]) {
2780 changed = TRUE; // mark for redraw
2781 break;
2782 }
2783 }
2784 // now, cs is index of first diff, and ce is index of last diff
2785
2786 // if horz offset has changed, force a redraw
2787 if (offset != old_offset) {
2788 re0:
2789 changed = TRUE;
2790 }
2791
2792 // make a sanity check of columns indexes
2793 if (cs < 0) cs= 0;
2794 if (ce > columns-1) ce= columns-1;
2795 if (cs > ce) { cs= 0; ce= columns-1; }
2796 // is there a change between vitual screen and buf
2797 if (changed) {
2798 // copy changed part of buffer to virtual screen
2799 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2800
2801 // move cursor to column of first change
2802 if (offset != old_offset) {
2803 // opti_cur_move is still too stupid
2804 // to handle offsets correctly
2805 place_cursor(li, cs, FALSE);
2806 } else {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002807#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002808 // if this just the next line
2809 // try to optimize cursor movement
2810 // otherwise, use standard ESC sequence
2811 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2812 last_li= li;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002813#else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002814 place_cursor(li, cs, FALSE); // use standard ESC sequence
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002815#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002816 }
2817
2818 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002819 {
2820 int nic = ce-cs+1;
Eric Andersena68ea1c2006-01-30 22:48:39 +00002821 char *out = (char*)sp+cs;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002822
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00002823 while (nic-- > 0) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002824 putchar(*out);
2825 out++;
2826 }
2827 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002828#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002829 last_row = li;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002830#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002831 }
2832 }
2833
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002834#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002835 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2836 last_row = crow;
2837#else
2838 place_cursor(crow, ccol, FALSE);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002839#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002840
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002841 if (offset != old_offset)
2842 old_offset = offset;
2843}
2844
Eric Andersen3f980402001-04-04 17:31:15 +00002845//---------------------------------------------------------------------
2846//----- the Ascii Chart -----------------------------------------------
2847//
2848// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2849// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2850// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2851// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2852// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2853// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2854// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2855// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2856// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2857// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2858// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2859// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2860// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2861// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2862// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2863// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2864//---------------------------------------------------------------------
2865
2866//----- Execute a Vi Command -----------------------------------
2867static void do_cmd(Byte c)
2868{
2869 Byte c1, *p, *q, *msg, buf[9], *save_dot;
2870 int cnt, i, j, dir, yf;
2871
2872 c1 = c; // quiet the compiler
2873 cnt = yf = dir = 0; // quiet the compiler
2874 p = q = save_dot = msg = buf; // quiet the compiler
2875 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00002876
Paul Fox8552aec2005-09-16 12:20:05 +00002877 show_status_line();
2878
Eric Andersenbff7a602001-11-17 07:15:43 +00002879 /* if this is a cursor key, skip these checks */
2880 switch (c) {
2881 case VI_K_UP:
2882 case VI_K_DOWN:
2883 case VI_K_LEFT:
2884 case VI_K_RIGHT:
2885 case VI_K_HOME:
2886 case VI_K_END:
2887 case VI_K_PAGEUP:
2888 case VI_K_PAGEDOWN:
2889 goto key_cmd_mode;
2890 }
2891
Eric Andersen3f980402001-04-04 17:31:15 +00002892 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002893 // flip-flop Insert/Replace mode
2894 if (c == VI_K_INSERT) goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00002895 // we are 'R'eplacing the current *dot with new char
2896 if (*dot == '\n') {
2897 // don't Replace past E-o-l
2898 cmd_mode = 1; // convert to insert
2899 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002900 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00002901 if (c != 27)
2902 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
2903 dot = char_insert(dot, c); // insert new char
2904 }
2905 goto dc1;
2906 }
2907 }
2908 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00002909 // hitting "Insert" twice means "R" replace mode
2910 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00002911 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002912 if (1 <= c || Isprint(c)) {
2913 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00002914 }
2915 goto dc1;
2916 }
2917
Eric Andersenbff7a602001-11-17 07:15:43 +00002918key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00002919 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00002920 //case 0x01: // soh
2921 //case 0x09: // ht
2922 //case 0x0b: // vt
2923 //case 0x0e: // so
2924 //case 0x0f: // si
2925 //case 0x10: // dle
2926 //case 0x11: // dc1
2927 //case 0x13: // dc3
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002928#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00002929 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00002930 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00002931 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002932#endif
Eric Andersen822c3832001-05-07 17:37:43 +00002933 //case 0x16: // syn
2934 //case 0x17: // etb
2935 //case 0x18: // can
2936 //case 0x1c: // fs
2937 //case 0x1d: // gs
2938 //case 0x1e: // rs
2939 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002940 //case '!': // !-
2941 //case '#': // #-
2942 //case '&': // &-
2943 //case '(': // (-
2944 //case ')': // )-
2945 //case '*': // *-
2946 //case ',': // ,-
2947 //case '=': // =-
2948 //case '@': // @-
2949 //case 'F': // F-
2950 //case 'K': // K-
2951 //case 'Q': // Q-
2952 //case 'S': // S-
2953 //case 'T': // T-
2954 //case 'V': // V-
2955 //case '[': // [-
2956 //case '\\': // \-
2957 //case ']': // ]-
2958 //case '_': // _-
2959 //case '`': // `-
2960 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00002961 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002962 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00002963 default: // unrecognised command
2964 buf[0] = c;
2965 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002966 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00002967 buf[0] = '^';
2968 buf[1] = c + '@';
2969 buf[2] = '\0';
2970 }
2971 ni((Byte *) buf);
2972 end_cmd_q(); // stop adding to q
2973 case 0x00: // nul- ignore
2974 break;
2975 case 2: // ctrl-B scroll up full screen
2976 case VI_K_PAGEUP: // Cursor Key Page Up
2977 dot_scroll(rows - 2, -1);
2978 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002979#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00002980 case 0x03: // ctrl-C interrupt
2981 longjmp(restart, 1);
2982 break;
2983 case 26: // ctrl-Z suspend
2984 suspend_sig(SIGTSTP);
2985 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002986#endif
Eric Andersen3f980402001-04-04 17:31:15 +00002987 case 4: // ctrl-D scroll down half screen
2988 dot_scroll((rows - 2) / 2, 1);
2989 break;
2990 case 5: // ctrl-E scroll down one line
2991 dot_scroll(1, 1);
2992 break;
2993 case 6: // ctrl-F scroll down full screen
2994 case VI_K_PAGEDOWN: // Cursor Key Page Down
2995 dot_scroll(rows - 2, 1);
2996 break;
2997 case 7: // ctrl-G show current status
Paul Fox8552aec2005-09-16 12:20:05 +00002998 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00002999 break;
3000 case 'h': // h- move left
3001 case VI_K_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003002 case 8: // ctrl-H- move left (This may be ERASE char)
3003 case 127: // DEL- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003004 if (cmdcnt-- > 1) {
3005 do_cmd(c);
3006 } // repeat cnt
3007 dot_left();
3008 break;
3009 case 10: // Newline ^J
3010 case 'j': // j- goto next line, same col
3011 case VI_K_DOWN: // cursor key Down
3012 if (cmdcnt-- > 1) {
3013 do_cmd(c);
3014 } // repeat cnt
3015 dot_next(); // go to next B-o-l
3016 dot = move_to_col(dot, ccol + offset); // try stay in same col
3017 break;
3018 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003019 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003020 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003021 clear_to_eos(); // tel terminal to erase display
3022 (void) mysleep(10);
3023 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003024 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003025 refresh(TRUE); // this will redraw the entire display
3026 break;
3027 case 13: // Carriage Return ^M
3028 case '+': // +- goto next line
3029 if (cmdcnt-- > 1) {
3030 do_cmd(c);
3031 } // repeat cnt
3032 dot_next();
3033 dot_skip_over_ws();
3034 break;
3035 case 21: // ctrl-U scroll up half screen
3036 dot_scroll((rows - 2) / 2, -1);
3037 break;
3038 case 25: // ctrl-Y scroll up one line
3039 dot_scroll(1, -1);
3040 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003041 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003042 if (cmd_mode == 0)
3043 indicate_error(c);
3044 cmd_mode = 0; // stop insrting
3045 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003046 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003047 break;
3048 case ' ': // move right
3049 case 'l': // move right
3050 case VI_K_RIGHT: // Cursor Key Right
3051 if (cmdcnt-- > 1) {
3052 do_cmd(c);
3053 } // repeat cnt
3054 dot_right();
3055 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003056#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003057 case '"': // "- name a register to use for Delete/Yank
3058 c1 = get_one_char();
3059 c1 = tolower(c1);
3060 if (islower(c1)) {
3061 YDreg = c1 - 'a';
3062 } else {
3063 indicate_error(c);
3064 }
3065 break;
3066 case '\'': // '- goto a specific mark
3067 c1 = get_one_char();
3068 c1 = tolower(c1);
3069 if (islower(c1)) {
3070 c1 = c1 - 'a';
3071 // get the b-o-l
3072 q = mark[(int) c1];
3073 if (text <= q && q < end) {
3074 dot = q;
3075 dot_begin(); // go to B-o-l
3076 dot_skip_over_ws();
3077 }
3078 } else if (c1 == '\'') { // goto previous context
3079 dot = swap_context(dot); // swap current and previous context
3080 dot_begin(); // go to B-o-l
3081 dot_skip_over_ws();
3082 } else {
3083 indicate_error(c);
3084 }
3085 break;
3086 case 'm': // m- Mark a line
3087 // this is really stupid. If there are any inserts or deletes
3088 // between text[0] and dot then this mark will not point to the
3089 // correct location! It could be off by many lines!
3090 // Well..., at least its quick and dirty.
3091 c1 = get_one_char();
3092 c1 = tolower(c1);
3093 if (islower(c1)) {
3094 c1 = c1 - 'a';
3095 // remember the line
3096 mark[(int) c1] = dot;
3097 } else {
3098 indicate_error(c);
3099 }
3100 break;
3101 case 'P': // P- Put register before
3102 case 'p': // p- put register after
3103 p = reg[YDreg];
3104 if (p == 0) {
3105 psbs("Nothing in register %c", what_reg());
3106 break;
3107 }
3108 // are we putting whole lines or strings
3109 if (strchr((char *) p, '\n') != NULL) {
3110 if (c == 'P') {
3111 dot_begin(); // putting lines- Put above
3112 }
3113 if (c == 'p') {
3114 // are we putting after very last line?
3115 if (end_line(dot) == (end - 1)) {
3116 dot = end; // force dot to end of text[]
3117 } else {
3118 dot_next(); // next line, then put before
3119 }
3120 }
3121 } else {
3122 if (c == 'p')
3123 dot_right(); // move to right, can move to NL
3124 }
3125 dot = string_insert(dot, p); // insert the string
3126 end_cmd_q(); // stop adding to q
3127 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003128 case 'U': // U- Undo; replace current line with original version
3129 if (reg[Ureg] != 0) {
3130 p = begin_line(dot);
3131 q = end_line(dot);
3132 p = text_hole_delete(p, q); // delete cur line
3133 p = string_insert(p, reg[Ureg]); // insert orig line
3134 dot = p;
3135 dot_skip_over_ws();
3136 }
3137 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003138#endif /* FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003139 case '$': // $- goto end of line
3140 case VI_K_END: // Cursor Key End
3141 if (cmdcnt-- > 1) {
3142 do_cmd(c);
3143 } // repeat cnt
Glenn L McGrathee829062004-01-21 10:59:45 +00003144 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003145 break;
3146 case '%': // %- find matching char of pair () [] {}
3147 for (q = dot; q < end && *q != '\n'; q++) {
3148 if (strchr("()[]{}", *q) != NULL) {
3149 // we found half of a pair
3150 p = find_pair(q, *q);
3151 if (p == NULL) {
3152 indicate_error(c);
3153 } else {
3154 dot = p;
3155 }
3156 break;
3157 }
3158 }
3159 if (*q == '\n')
3160 indicate_error(c);
3161 break;
3162 case 'f': // f- forward to a user specified char
3163 last_forward_char = get_one_char(); // get the search char
3164 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003165 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003166 //
Paul Foxd13b90b2005-07-18 22:17:25 +00003167 //**** fall thru to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003168 case ';': // ;- look at rest of line for last forward char
3169 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003170 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003171 } // repeat cnt
Eric Andersen822c3832001-05-07 17:37:43 +00003172 if (last_forward_char == 0) break;
Eric Andersen3f980402001-04-04 17:31:15 +00003173 q = dot + 1;
3174 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3175 q++;
3176 }
3177 if (*q == last_forward_char)
3178 dot = q;
3179 break;
3180 case '-': // -- goto prev line
3181 if (cmdcnt-- > 1) {
3182 do_cmd(c);
3183 } // repeat cnt
3184 dot_prev();
3185 dot_skip_over_ws();
3186 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003187#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003188 case '.': // .- repeat the last modifying command
3189 // Stuff the last_modifying_cmd back into stdin
3190 // and let it be re-executed.
3191 if (last_modifying_cmd != 0) {
Rob Landleyd921b2e2006-08-03 15:41:12 +00003192 ioq = ioq_start = (Byte *) xstrdup((char *) last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003193 }
3194 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003195#endif
3196#if ENABLE_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003197 case '?': // /- search for a pattern
3198 case '/': // /- search for a pattern
3199 buf[0] = c;
3200 buf[1] = '\0';
3201 q = get_input_line(buf); // get input line- use "status line"
3202 if (strlen((char *) q) == 1)
3203 goto dc3; // if no pat re-use old pat
3204 if (strlen((char *) q) > 1) { // new pat- save it and find
3205 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003206 free(last_search_pattern);
Rob Landleyd921b2e2006-08-03 15:41:12 +00003207 last_search_pattern = (Byte *) xstrdup((char *) q);
Eric Andersen3f980402001-04-04 17:31:15 +00003208 goto dc3; // now find the pattern
3209 }
3210 // user changed mind and erased the "/"- do nothing
3211 break;
3212 case 'N': // N- backward search for last pattern
3213 if (cmdcnt-- > 1) {
3214 do_cmd(c);
3215 } // repeat cnt
3216 dir = BACK; // assume BACKWARD search
3217 p = dot - 1;
3218 if (last_search_pattern[0] == '?') {
3219 dir = FORWARD;
3220 p = dot + 1;
3221 }
3222 goto dc4; // now search for pattern
3223 break;
3224 case 'n': // n- repeat search for last pattern
3225 // search rest of text[] starting at next char
3226 // if search fails return orignal "p" not the "p+1" address
3227 if (cmdcnt-- > 1) {
3228 do_cmd(c);
3229 } // repeat cnt
3230 dc3:
3231 if (last_search_pattern == 0) {
3232 msg = (Byte *) "No previous regular expression";
3233 goto dc2;
3234 }
3235 if (last_search_pattern[0] == '/') {
3236 dir = FORWARD; // assume FORWARD search
3237 p = dot + 1;
3238 }
3239 if (last_search_pattern[0] == '?') {
3240 dir = BACK;
3241 p = dot - 1;
3242 }
3243 dc4:
3244 q = char_search(p, last_search_pattern + 1, dir, FULL);
3245 if (q != NULL) {
3246 dot = q; // good search, update "dot"
3247 msg = (Byte *) "";
3248 goto dc2;
3249 }
3250 // no pattern found between "dot" and "end"- continue at top
3251 p = text;
3252 if (dir == BACK) {
3253 p = end - 1;
3254 }
3255 q = char_search(p, last_search_pattern + 1, dir, FULL);
3256 if (q != NULL) { // found something
3257 dot = q; // found new pattern- goto it
3258 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3259 if (dir == BACK) {
3260 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3261 }
3262 } else {
3263 msg = (Byte *) "Pattern not found";
3264 }
3265 dc2:
Paul Fox8552aec2005-09-16 12:20:05 +00003266 if (*msg) psbs("%s", msg);
Eric Andersen3f980402001-04-04 17:31:15 +00003267 break;
3268 case '{': // {- move backward paragraph
3269 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3270 if (q != NULL) { // found blank line
3271 dot = next_line(q); // move to next blank line
3272 }
3273 break;
3274 case '}': // }- move forward paragraph
3275 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3276 if (q != NULL) { // found blank line
3277 dot = next_line(q); // move to next blank line
3278 }
3279 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003280#endif /* FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003281 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003282 case '1': // 1-
3283 case '2': // 2-
3284 case '3': // 3-
3285 case '4': // 4-
3286 case '5': // 5-
3287 case '6': // 6-
3288 case '7': // 7-
3289 case '8': // 8-
3290 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003291 if (c == '0' && cmdcnt < 1) {
3292 dot_begin(); // this was a standalone zero
3293 } else {
3294 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3295 }
3296 break;
3297 case ':': // :- the colon mode commands
Eric Andersen3f980402001-04-04 17:31:15 +00003298 p = get_input_line((Byte *) ":"); // get input line- use "status line"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003299#if ENABLE_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003300 colon(p); // execute the command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003301#else
Eric Andersen822c3832001-05-07 17:37:43 +00003302 if (*p == ':')
3303 p++; // move past the ':'
3304 cnt = strlen((char *) p);
3305 if (cnt <= 0)
3306 break;
3307 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3308 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
Matt Kraai1f0c4362001-12-20 23:13:26 +00003309 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003310 psbs("No write since last change (:quit! overrides)");
3311 } else {
3312 editing = 0;
3313 }
Paul Fox9360f422006-03-27 21:51:16 +00003314 } else if (strncasecmp((char *) p, "write", cnt) == 0
3315 || strncasecmp((char *) p, "wq", cnt) == 0
3316 || strncasecmp((char *) p, "wn", cnt) == 0
3317 || strncasecmp((char *) p, "x", cnt) == 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003318 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003319 if (cnt < 0) {
3320 if (cnt == -1)
3321 psbs("Write error: %s", strerror(errno));
3322 } else {
3323 file_modified = 0;
3324 last_file_modified = -1;
3325 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
Paul Fox9360f422006-03-27 21:51:16 +00003326 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n' ||
3327 p[0] == 'X' || p[1] == 'Q' || p[1] == 'N') {
Paul Fox61e45db2005-10-09 14:43:22 +00003328 editing = 0;
3329 }
Eric Andersen3f980402001-04-04 17:31:15 +00003330 }
Eric Andersen822c3832001-05-07 17:37:43 +00003331 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
Paul Fox8552aec2005-09-16 12:20:05 +00003332 last_status_cksum = 0; // force status update
Eric Andersen822c3832001-05-07 17:37:43 +00003333 } else if (sscanf((char *) p, "%d", &j) > 0) {
3334 dot = find_line(j); // go to line # j
3335 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003336 } else { // unrecognised cmd
Eric Andersen822c3832001-05-07 17:37:43 +00003337 ni((Byte *) p);
Eric Andersen3f980402001-04-04 17:31:15 +00003338 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003339#endif /* !FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003340 break;
3341 case '<': // <- Left shift something
3342 case '>': // >- Right shift something
3343 cnt = count_lines(text, dot); // remember what line we are on
3344 c1 = get_one_char(); // get the type of thing to delete
3345 find_range(&p, &q, c1);
3346 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3347 p = begin_line(p);
3348 q = end_line(q);
3349 i = count_lines(p, q); // # of lines we are shifting
3350 for ( ; i > 0; i--, p = next_line(p)) {
3351 if (c == '<') {
3352 // shift left- remove tab or 8 spaces
3353 if (*p == '\t') {
3354 // shrink buffer 1 char
3355 (void) text_hole_delete(p, p);
3356 } else if (*p == ' ') {
3357 // we should be calculating columns, not just SPACE
3358 for (j = 0; *p == ' ' && j < tabstop; j++) {
3359 (void) text_hole_delete(p, p);
3360 }
3361 }
3362 } else if (c == '>') {
3363 // shift right -- add tab or 8 spaces
3364 (void) char_insert(p, '\t');
3365 }
3366 }
3367 dot = find_line(cnt); // what line were we on
3368 dot_skip_over_ws();
3369 end_cmd_q(); // stop adding to q
3370 break;
3371 case 'A': // A- append at e-o-l
3372 dot_end(); // go to e-o-l
3373 //**** fall thru to ... 'a'
3374 case 'a': // a- append after current char
3375 if (*dot != '\n')
3376 dot++;
3377 goto dc_i;
3378 break;
3379 case 'B': // B- back a blank-delimited Word
3380 case 'E': // E- end of a blank-delimited word
3381 case 'W': // W- forward a blank-delimited word
3382 if (cmdcnt-- > 1) {
3383 do_cmd(c);
3384 } // repeat cnt
3385 dir = FORWARD;
3386 if (c == 'B')
3387 dir = BACK;
3388 if (c == 'W' || isspace(dot[dir])) {
3389 dot = skip_thing(dot, 1, dir, S_TO_WS);
3390 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3391 }
3392 if (c != 'W')
3393 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3394 break;
3395 case 'C': // C- Change to e-o-l
3396 case 'D': // D- delete to e-o-l
3397 save_dot = dot;
3398 dot = dollar_line(dot); // move to before NL
3399 // copy text into a register and delete
3400 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3401 if (c == 'C')
3402 goto dc_i; // start inserting
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003403#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003404 if (c == 'D')
3405 end_cmd_q(); // stop adding to q
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003406#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003407 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003408 case 'G': // G- goto to a line number (default= E-O-F)
3409 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003410 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003411 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003412 }
3413 dot_skip_over_ws();
3414 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003415 case 'H': // H- goto top line on screen
3416 dot = screenbegin;
3417 if (cmdcnt > (rows - 1)) {
3418 cmdcnt = (rows - 1);
3419 }
3420 if (cmdcnt-- > 1) {
3421 do_cmd('+');
3422 } // repeat cnt
3423 dot_skip_over_ws();
3424 break;
3425 case 'I': // I- insert before first non-blank
3426 dot_begin(); // 0
3427 dot_skip_over_ws();
3428 //**** fall thru to ... 'i'
3429 case 'i': // i- insert before current char
3430 case VI_K_INSERT: // Cursor Key Insert
3431 dc_i:
3432 cmd_mode = 1; // start insrting
Eric Andersen3f980402001-04-04 17:31:15 +00003433 break;
3434 case 'J': // J- join current and next lines together
3435 if (cmdcnt-- > 2) {
3436 do_cmd(c);
3437 } // repeat cnt
3438 dot_end(); // move to NL
3439 if (dot < end - 1) { // make sure not last char in text[]
3440 *dot++ = ' '; // replace NL with space
Paul Fox8552aec2005-09-16 12:20:05 +00003441 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003442 while (isblnk(*dot)) { // delete leading WS
3443 dot_delete();
3444 }
3445 }
3446 end_cmd_q(); // stop adding to q
3447 break;
3448 case 'L': // L- goto bottom line on screen
3449 dot = end_screen();
3450 if (cmdcnt > (rows - 1)) {
3451 cmdcnt = (rows - 1);
3452 }
3453 if (cmdcnt-- > 1) {
3454 do_cmd('-');
3455 } // repeat cnt
3456 dot_begin();
3457 dot_skip_over_ws();
3458 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003459 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003460 dot = screenbegin;
3461 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3462 dot = next_line(dot);
3463 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003464 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003465 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003466 p = begin_line(dot);
3467 if (p[-1] == '\n') {
3468 dot_prev();
3469 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3470 dot_end();
3471 dot = char_insert(dot, '\n');
3472 } else {
3473 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003474 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003475 dot_prev(); // -
3476 }
3477 goto dc_i;
3478 break;
3479 case 'R': // R- continuous Replace char
Eric Andersen1c0d3112001-04-16 15:46:44 +00003480 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003481 cmd_mode = 2;
Eric Andersen3f980402001-04-04 17:31:15 +00003482 break;
3483 case 'X': // X- delete char before dot
3484 case 'x': // x- delete the current char
3485 case 's': // s- substitute the current char
3486 if (cmdcnt-- > 1) {
3487 do_cmd(c);
3488 } // repeat cnt
3489 dir = 0;
3490 if (c == 'X')
3491 dir = -1;
3492 if (dot[dir] != '\n') {
3493 if (c == 'X')
3494 dot--; // delete prev char
3495 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3496 }
3497 if (c == 's')
3498 goto dc_i; // start insrting
3499 end_cmd_q(); // stop adding to q
3500 break;
3501 case 'Z': // Z- if modified, {write}; exit
3502 // ZZ means to save file (if necessary), then exit
3503 c1 = get_one_char();
3504 if (c1 != 'Z') {
3505 indicate_error(c);
3506 break;
3507 }
Paul Foxf0305b72006-03-28 14:18:21 +00003508 if (file_modified) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003509#if ENABLE_FEATURE_VI_READONLY
Paul Foxf0305b72006-03-28 14:18:21 +00003510 if (vi_readonly || readonly) {
Denis Vlasenko92758142006-10-03 19:56:34 +00003511 psbs("\"%s\" File is read only", cfn);
3512 break;
Paul Foxf0305b72006-03-28 14:18:21 +00003513 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003514#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003515 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003516 if (cnt < 0) {
3517 if (cnt == -1)
3518 psbs("Write error: %s", strerror(errno));
3519 } else if (cnt == (end - 1 - text + 1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003520 editing = 0;
3521 }
3522 } else {
3523 editing = 0;
3524 }
3525 break;
3526 case '^': // ^- move to first non-blank on line
3527 dot_begin();
3528 dot_skip_over_ws();
3529 break;
3530 case 'b': // b- back a word
3531 case 'e': // e- end of word
3532 if (cmdcnt-- > 1) {
3533 do_cmd(c);
3534 } // repeat cnt
3535 dir = FORWARD;
3536 if (c == 'b')
3537 dir = BACK;
3538 if ((dot + dir) < text || (dot + dir) > end - 1)
3539 break;
3540 dot += dir;
3541 if (isspace(*dot)) {
3542 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3543 }
3544 if (isalnum(*dot) || *dot == '_') {
3545 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3546 } else if (ispunct(*dot)) {
3547 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3548 }
3549 break;
3550 case 'c': // c- change something
3551 case 'd': // d- delete something
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003552#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003553 case 'y': // y- yank something
3554 case 'Y': // Y- Yank a line
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003555#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003556 yf = YANKDEL; // assume either "c" or "d"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003557#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003558 if (c == 'y' || c == 'Y')
3559 yf = YANKONLY;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003560#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003561 c1 = 'y';
3562 if (c != 'Y')
3563 c1 = get_one_char(); // get the type of thing to delete
3564 find_range(&p, &q, c1);
3565 if (c1 == 27) { // ESC- user changed mind and wants out
3566 c = c1 = 27; // Escape- do nothing
3567 } else if (strchr("wW", c1)) {
3568 if (c == 'c') {
3569 // don't include trailing WS as part of word
3570 while (isblnk(*q)) {
3571 if (q <= text || q[-1] == '\n')
3572 break;
3573 q--;
3574 }
3575 }
3576 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003577 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003578 // single line copy text into a register and delete
3579 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003580 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003581 // multiple line copy text into a register and delete
3582 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003583 if (c == 'c') {
3584 dot = char_insert(dot, '\n');
3585 // on the last line of file don't move to prev line
3586 if (dot != (end-1)) {
3587 dot_prev();
3588 }
3589 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003590 dot_begin();
3591 dot_skip_over_ws();
3592 }
3593 } else {
3594 // could not recognize object
3595 c = c1 = 27; // error-
3596 indicate_error(c);
3597 }
3598 if (c1 != 27) {
3599 // if CHANGING, not deleting, start inserting after the delete
3600 if (c == 'c') {
3601 strcpy((char *) buf, "Change");
3602 goto dc_i; // start inserting
3603 }
3604 if (c == 'd') {
3605 strcpy((char *) buf, "Delete");
3606 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003607#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003608 if (c == 'y' || c == 'Y') {
3609 strcpy((char *) buf, "Yank");
3610 }
3611 p = reg[YDreg];
3612 q = p + strlen((char *) p);
3613 for (cnt = 0; p <= q; p++) {
3614 if (*p == '\n')
3615 cnt++;
3616 }
3617 psb("%s %d lines (%d chars) using [%c]",
3618 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003619#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003620 end_cmd_q(); // stop adding to q
3621 }
3622 break;
3623 case 'k': // k- goto prev line, same col
3624 case VI_K_UP: // cursor key Up
3625 if (cmdcnt-- > 1) {
3626 do_cmd(c);
3627 } // repeat cnt
3628 dot_prev();
3629 dot = move_to_col(dot, ccol + offset); // try stay in same col
3630 break;
3631 case 'r': // r- replace the current char with user input
3632 c1 = get_one_char(); // get the replacement char
3633 if (*dot != '\n') {
3634 *dot = c1;
Paul Fox8552aec2005-09-16 12:20:05 +00003635 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003636 }
3637 end_cmd_q(); // stop adding to q
3638 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003639 case 't': // t- move to char prior to next x
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00003640 last_forward_char = get_one_char();
3641 do_cmd(';');
3642 if (*dot == last_forward_char)
3643 dot_left();
3644 last_forward_char= 0;
Eric Andersen822c3832001-05-07 17:37:43 +00003645 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003646 case 'w': // w- forward a word
3647 if (cmdcnt-- > 1) {
3648 do_cmd(c);
3649 } // repeat cnt
3650 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3651 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3652 } else if (ispunct(*dot)) { // we are on PUNCT
3653 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3654 }
3655 if (dot < end - 1)
3656 dot++; // move over word
3657 if (isspace(*dot)) {
3658 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3659 }
3660 break;
3661 case 'z': // z-
3662 c1 = get_one_char(); // get the replacement char
3663 cnt = 0;
3664 if (c1 == '.')
3665 cnt = (rows - 2) / 2; // put dot at center
3666 if (c1 == '-')
3667 cnt = rows - 2; // put dot at bottom
3668 screenbegin = begin_line(dot); // start dot at top
3669 dot_scroll(cnt, -1);
3670 break;
3671 case '|': // |- move to column "cmdcnt"
3672 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3673 break;
3674 case '~': // ~- flip the case of letters a-z -> A-Z
3675 if (cmdcnt-- > 1) {
3676 do_cmd(c);
3677 } // repeat cnt
3678 if (islower(*dot)) {
3679 *dot = toupper(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003680 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003681 } else if (isupper(*dot)) {
3682 *dot = tolower(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003683 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003684 }
3685 dot_right();
3686 end_cmd_q(); // stop adding to q
3687 break;
3688 //----- The Cursor and Function Keys -----------------------------
3689 case VI_K_HOME: // Cursor Key Home
3690 dot_begin();
3691 break;
3692 // The Fn keys could point to do_macro which could translate them
3693 case VI_K_FUN1: // Function Key F1
3694 case VI_K_FUN2: // Function Key F2
3695 case VI_K_FUN3: // Function Key F3
3696 case VI_K_FUN4: // Function Key F4
3697 case VI_K_FUN5: // Function Key F5
3698 case VI_K_FUN6: // Function Key F6
3699 case VI_K_FUN7: // Function Key F7
3700 case VI_K_FUN8: // Function Key F8
3701 case VI_K_FUN9: // Function Key F9
3702 case VI_K_FUN10: // Function Key F10
3703 case VI_K_FUN11: // Function Key F11
3704 case VI_K_FUN12: // Function Key F12
3705 break;
3706 }
3707
3708 dc1:
3709 // if text[] just became empty, add back an empty line
3710 if (end == text) {
3711 (void) char_insert(text, '\n'); // start empty buf with dummy line
3712 dot = text;
3713 }
3714 // it is OK for dot to exactly equal to end, otherwise check dot validity
3715 if (dot != end) {
3716 dot = bound_dot(dot); // make sure "dot" is valid
3717 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003718#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003719 check_context(c); // update the current context
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003720#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003721
3722 if (!isdigit(c))
3723 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3724 cnt = dot - begin_line(dot);
3725 // Try to stay off of the Newline
3726 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3727 dot--;
3728}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003729
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003730#if ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003731static int totalcmds = 0;
3732static int Mp = 85; // Movement command Probability
3733static int Np = 90; // Non-movement command Probability
3734static int Dp = 96; // Delete command Probability
3735static int Ip = 97; // Insert command Probability
3736static int Yp = 98; // Yank command Probability
3737static int Pp = 99; // Put command Probability
3738static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3739char chars[20] = "\t012345 abcdABCD-=.$";
3740char *words[20] = { "this", "is", "a", "test",
3741 "broadcast", "the", "emergency", "of",
3742 "system", "quick", "brown", "fox",
3743 "jumped", "over", "lazy", "dogs",
3744 "back", "January", "Febuary", "March"
3745};
3746char *lines[20] = {
3747 "You should have received a copy of the GNU General Public License\n",
3748 "char c, cm, *cmd, *cmd1;\n",
3749 "generate a command by percentages\n",
3750 "Numbers may be typed as a prefix to some commands.\n",
3751 "Quit, discarding changes!\n",
3752 "Forced write, if permission originally not valid.\n",
3753 "In general, any ex or ed command (such as substitute or delete).\n",
3754 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3755 "Please get w/ me and I will go over it with you.\n",
3756 "The following is a list of scheduled, committed changes.\n",
3757 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3758 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3759 "Any question about transactions please contact Sterling Huxley.\n",
3760 "I will try to get back to you by Friday, December 31.\n",
3761 "This Change will be implemented on Friday.\n",
3762 "Let me know if you have problems accessing this;\n",
3763 "Sterling Huxley recently added you to the access list.\n",
3764 "Would you like to go to lunch?\n",
3765 "The last command will be automatically run.\n",
3766 "This is too much english for a computer geek.\n",
3767};
3768char *multilines[20] = {
3769 "You should have received a copy of the GNU General Public License\n",
3770 "char c, cm, *cmd, *cmd1;\n",
3771 "generate a command by percentages\n",
3772 "Numbers may be typed as a prefix to some commands.\n",
3773 "Quit, discarding changes!\n",
3774 "Forced write, if permission originally not valid.\n",
3775 "In general, any ex or ed command (such as substitute or delete).\n",
3776 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3777 "Please get w/ me and I will go over it with you.\n",
3778 "The following is a list of scheduled, committed changes.\n",
3779 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3780 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3781 "Any question about transactions please contact Sterling Huxley.\n",
3782 "I will try to get back to you by Friday, December 31.\n",
3783 "This Change will be implemented on Friday.\n",
3784 "Let me know if you have problems accessing this;\n",
3785 "Sterling Huxley recently added you to the access list.\n",
3786 "Would you like to go to lunch?\n",
3787 "The last command will be automatically run.\n",
3788 "This is too much english for a computer geek.\n",
3789};
3790
3791// create a random command to execute
3792static void crash_dummy()
3793{
3794 static int sleeptime; // how long to pause between commands
3795 char c, cm, *cmd, *cmd1;
3796 int i, cnt, thing, rbi, startrbi, percent;
3797
3798 // "dot" movement commands
3799 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3800
3801 // is there already a command running?
3802 if (readed_for_parse > 0)
3803 goto cd1;
3804 cd0:
3805 startrbi = rbi = 0;
3806 sleeptime = 0; // how long to pause between commands
3807 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3808 // generate a command by percentages
3809 percent = (int) lrand48() % 100; // get a number from 0-99
3810 if (percent < Mp) { // Movement commands
3811 // available commands
3812 cmd = cmd1;
3813 M++;
3814 } else if (percent < Np) { // non-movement commands
3815 cmd = "mz<>\'\""; // available commands
3816 N++;
3817 } else if (percent < Dp) { // Delete commands
3818 cmd = "dx"; // available commands
3819 D++;
3820 } else if (percent < Ip) { // Inset commands
3821 cmd = "iIaAsrJ"; // available commands
3822 I++;
3823 } else if (percent < Yp) { // Yank commands
3824 cmd = "yY"; // available commands
3825 Y++;
3826 } else if (percent < Pp) { // Put commands
3827 cmd = "pP"; // available commands
3828 P++;
3829 } else {
3830 // We do not know how to handle this command, try again
3831 U++;
3832 goto cd0;
3833 }
3834 // randomly pick one of the available cmds from "cmd[]"
3835 i = (int) lrand48() % strlen(cmd);
3836 cm = cmd[i];
3837 if (strchr(":\024", cm))
3838 goto cd0; // dont allow colon or ctrl-T commands
3839 readbuffer[rbi++] = cm; // put cmd into input buffer
3840
3841 // now we have the command-
3842 // there are 1, 2, and multi char commands
3843 // find out which and generate the rest of command as necessary
3844 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3845 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3846 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3847 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3848 }
3849 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3850 c = cmd1[thing];
3851 readbuffer[rbi++] = c; // add movement to input buffer
3852 }
3853 if (strchr("iIaAsc", cm)) { // multi-char commands
3854 if (cm == 'c') {
3855 // change some thing
3856 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3857 c = cmd1[thing];
3858 readbuffer[rbi++] = c; // add movement to input buffer
3859 }
3860 thing = (int) lrand48() % 4; // what thing to insert
3861 cnt = (int) lrand48() % 10; // how many to insert
3862 for (i = 0; i < cnt; i++) {
3863 if (thing == 0) { // insert chars
3864 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3865 } else if (thing == 1) { // insert words
3866 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
3867 strcat((char *) readbuffer, " ");
3868 sleeptime = 0; // how fast to type
3869 } else if (thing == 2) { // insert lines
3870 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
3871 sleeptime = 0; // how fast to type
3872 } else { // insert multi-lines
3873 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
3874 sleeptime = 0; // how fast to type
3875 }
3876 }
3877 strcat((char *) readbuffer, "\033");
3878 }
3879 readed_for_parse = strlen(readbuffer);
3880 cd1:
3881 totalcmds++;
3882 if (sleeptime > 0)
3883 (void) mysleep(sleeptime); // sleep 1/100 sec
3884}
3885
3886// test to see if there are any errors
3887static void crash_test()
3888{
3889 static time_t oldtim;
3890 time_t tim;
Glenn L McGrath7127b582002-12-03 21:48:15 +00003891 char d[2], msg[BUFSIZ];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003892
3893 msg[0] = '\0';
3894 if (end < text) {
3895 strcat((char *) msg, "end<text ");
3896 }
3897 if (end > textend) {
3898 strcat((char *) msg, "end>textend ");
3899 }
3900 if (dot < text) {
3901 strcat((char *) msg, "dot<text ");
3902 }
3903 if (dot > end) {
3904 strcat((char *) msg, "dot>end ");
3905 }
3906 if (screenbegin < text) {
3907 strcat((char *) msg, "screenbegin<text ");
3908 }
3909 if (screenbegin > end - 1) {
3910 strcat((char *) msg, "screenbegin>end-1 ");
3911 }
3912
3913 if (strlen(msg) > 0) {
3914 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00003915 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003916 totalcmds, last_input_char, msg, SOs, SOn);
3917 fflush(stdout);
3918 while (read(0, d, 1) > 0) {
3919 if (d[0] == '\n' || d[0] == '\r')
3920 break;
3921 }
3922 alarm(3);
3923 }
3924 tim = (time_t) time((time_t *) 0);
3925 if (tim >= (oldtim + 3)) {
3926 sprintf((char *) status_buffer,
3927 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
3928 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
3929 oldtim = tim;
3930 }
3931 return;
3932}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003933#endif