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