blob: e01ee11659bbc43483d201e576dd3dcfed931302 [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 Andersen8efe9672003-09-15 08:33:45 +000022 "$Id: vi.c,v 1.29 2003/09/15 08:33:36 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 Andersenbdfd0d72001-10-24 05:00:29 +000028 * gcc -Wall -Os -s -DSTANDALONE -DCONFIG_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 Andersenbdfd0d72001-10-24 05:00:29 +000051#define CONFIG_FEATURE_VI_COLON // 4288
52#define CONFIG_FEATURE_VI_YANKMARK // 1408
53#define CONFIG_FEATURE_VI_SEARCH // 1088
54#define CONFIG_FEATURE_VI_USE_SIGNALS // 1056
55#define CONFIG_FEATURE_VI_DOT_CMD // 576
56#define CONFIG_FEATURE_VI_READONLY // 128
57#define CONFIG_FEATURE_VI_SETOPTS // 576
58#define CONFIG_FEATURE_VI_SET // 224
59#define CONFIG_FEATURE_VI_WIN_RESIZE // 256 WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +000060// 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 Andersenbdfd0d72001-10-24 05:00:29 +000065//#define CONFIG_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
Glenn L McGrath09adaca2002-12-02 21:18:10 +000090#ifdef CONFIG_LOCALE_SUPPORT
91#define Isprint(c) isprint((c))
92#else
93#define Isprint(c) ( (c) >= ' ' && (c) != 127 && (c) != ((unsigned char)'\233') )
94#endif
95
Eric Andersen3f980402001-04-04 17:31:15 +000096#ifndef TRUE
97#define TRUE ((int)1)
98#define FALSE ((int)0)
99#endif /* TRUE */
Eric Andersen1c0d3112001-04-16 15:46:44 +0000100#define MAX_SCR_COLS BUFSIZ
Eric Andersen3f980402001-04-04 17:31:15 +0000101
102// Misc. non-Ascii keys that report an escape sequence
103#define VI_K_UP 128 // cursor key Up
104#define VI_K_DOWN 129 // cursor key Down
105#define VI_K_RIGHT 130 // Cursor Key Right
106#define VI_K_LEFT 131 // cursor key Left
107#define VI_K_HOME 132 // Cursor Key Home
108#define VI_K_END 133 // Cursor Key End
109#define VI_K_INSERT 134 // Cursor Key Insert
110#define VI_K_PAGEUP 135 // Cursor Key Page Up
111#define VI_K_PAGEDOWN 136 // Cursor Key Page Down
112#define VI_K_FUN1 137 // Function Key F1
113#define VI_K_FUN2 138 // Function Key F2
114#define VI_K_FUN3 139 // Function Key F3
115#define VI_K_FUN4 140 // Function Key F4
116#define VI_K_FUN5 141 // Function Key F5
117#define VI_K_FUN6 142 // Function Key F6
118#define VI_K_FUN7 143 // Function Key F7
119#define VI_K_FUN8 144 // Function Key F8
120#define VI_K_FUN9 145 // Function Key F9
121#define VI_K_FUN10 146 // Function Key F10
122#define VI_K_FUN11 147 // Function Key F11
123#define VI_K_FUN12 148 // Function Key F12
124
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000125/* vt102 typical ESC sequence */
126/* terminal standout start/normal ESC sequence */
127static const char SOs[] = "\033[7m";
128static const char SOn[] = "\033[0m";
129/* terminal bell sequence */
130static const char bell[] = "\007";
131/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
132static const char Ceol[] = "\033[0K";
133static const char Ceos [] = "\033[0J";
134/* Cursor motion arbitrary destination ESC sequence */
135static const char CMrc[] = "\033[%d;%dH";
136/* Cursor motion up and down ESC sequence */
137static const char CMup[] = "\033[A";
138static const char CMdown[] = "\n";
139
140
Eric Andersen3f980402001-04-04 17:31:15 +0000141static const int YANKONLY = FALSE;
142static const int YANKDEL = TRUE;
143static const int FORWARD = 1; // code depends on "1" for array index
144static const int BACK = -1; // code depends on "-1" for array index
145static const int LIMITED = 0; // how much of text[] in char_search
146static const int FULL = 1; // how much of text[] in char_search
147
148static const int S_BEFORE_WS = 1; // used in skip_thing() for moving "dot"
149static const int S_TO_WS = 2; // used in skip_thing() for moving "dot"
150static const int S_OVER_WS = 3; // used in skip_thing() for moving "dot"
151static const int S_END_PUNCT = 4; // used in skip_thing() for moving "dot"
152static const int S_END_ALNUM = 5; // used in skip_thing() for moving "dot"
153
154typedef unsigned char Byte;
155
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000156static int vi_setops;
157#define VI_AUTOINDENT 1
158#define VI_SHOWMATCH 2
159#define VI_IGNORECASE 4
160#define VI_ERR_METHOD 8
161#define autoindent (vi_setops & VI_AUTOINDENT)
162#define showmatch (vi_setops & VI_SHOWMATCH )
163#define ignorecase (vi_setops & VI_IGNORECASE)
164/* indicate error with beep or flash */
165#define err_method (vi_setops & VI_ERR_METHOD)
166
Eric Andersen3f980402001-04-04 17:31:15 +0000167
168static int editing; // >0 while we are editing a file
169static int cmd_mode; // 0=command 1=insert
170static int file_modified; // buffer contents changed
Eric Andersen3f980402001-04-04 17:31:15 +0000171static int fn_start; // index of first cmd line file name
172static int save_argc; // how many file names on cmd line
173static int cmdcnt; // repetition count
174static fd_set rfds; // use select() for small sleeps
175static struct timeval tv; // use select() for small sleeps
Eric Andersen3f980402001-04-04 17:31:15 +0000176static int rows, columns; // the terminal screen is this size
177static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset
Eric Andersen3f980402001-04-04 17:31:15 +0000178static Byte *status_buffer; // mesages to the user
Eric Andersen3f980402001-04-04 17:31:15 +0000179static Byte *cfn; // previous, current, and next file name
180static Byte *text, *end, *textend; // pointers to the user data in memory
181static Byte *screen; // pointer to the virtual screen buffer
182static int screensize; // and its size
183static Byte *screenbegin; // index into text[], of top line on the screen
184static Byte *dot; // where all the action takes place
185static int tabstop;
186static struct termios term_orig, term_vi; // remember what the cooked mode was
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000187static Byte erase_char; // the users erase character
188static Byte last_input_char; // last char read from user
189static Byte last_forward_char; // last char searched for with 'f'
Eric Andersen3f980402001-04-04 17:31:15 +0000190
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000191#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +0000192static int last_row; // where the cursor was last moved to
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000193#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
194#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000195static jmp_buf restart; // catch_sig()
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000196#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000197#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
198static int my_pid;
199#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000200#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000201static int adding2q; // are we currently adding user input to q
202static Byte *last_modifying_cmd; // last modifying cmd for "."
203static Byte *ioq, *ioq_start; // pointer to string for get_one_char to "read"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000204#endif /* CONFIG_FEATURE_VI_DOT_CMD */
205#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
Eric Andersen3f980402001-04-04 17:31:15 +0000206static Byte *modifying_cmds; // cmds that modify text[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000207#endif /* CONFIG_FEATURE_VI_DOT_CMD || CONFIG_FEATURE_VI_YANKMARK */
208#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000209static int vi_readonly, readonly;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000210#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000211#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000212static Byte *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
213static int YDreg, Ureg; // default delete register and orig line for "U"
214static Byte *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
215static Byte *context_start, *context_end;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000216#endif /* CONFIG_FEATURE_VI_YANKMARK */
217#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000218static Byte *last_search_pattern; // last pattern from a '/' or '?' search
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000219#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +0000220
221
222static void edit_file(Byte *); // edit one file
223static void do_cmd(Byte); // execute a command
224static void sync_cursor(Byte *, int *, int *); // synchronize the screen cursor to dot
225static Byte *begin_line(Byte *); // return pointer to cur line B-o-l
226static Byte *end_line(Byte *); // return pointer to cur line E-o-l
Eric Andersen3f980402001-04-04 17:31:15 +0000227static Byte *prev_line(Byte *); // return pointer to prev line B-o-l
228static Byte *next_line(Byte *); // return pointer to next line B-o-l
229static Byte *end_screen(void); // get pointer to last char on screen
230static int count_lines(Byte *, Byte *); // count line from start to stop
231static Byte *find_line(int); // find begining of line #li
232static Byte *move_to_col(Byte *, int); // move "p" to column l
233static int isblnk(Byte); // is the char a blank or tab
234static void dot_left(void); // move dot left- dont leave line
235static void dot_right(void); // move dot right- dont leave line
236static void dot_begin(void); // move dot to B-o-l
237static void dot_end(void); // move dot to E-o-l
238static void dot_next(void); // move dot to next line B-o-l
239static void dot_prev(void); // move dot to prev line B-o-l
240static void dot_scroll(int, int); // move the screen up or down
241static void dot_skip_over_ws(void); // move dot pat WS
242static void dot_delete(void); // delete the char at 'dot'
243static Byte *bound_dot(Byte *); // make sure text[0] <= P < "end"
244static Byte *new_screen(int, int); // malloc virtual screen memory
245static Byte *new_text(int); // malloc memory for text[] buffer
246static Byte *char_insert(Byte *, Byte); // insert the char c at 'p'
247static Byte *stupid_insert(Byte *, Byte); // stupidly insert the char c at 'p'
248static Byte find_range(Byte **, Byte **, Byte); // return pointers for an object
249static int st_test(Byte *, int, int, Byte *); // helper for skip_thing()
250static Byte *skip_thing(Byte *, int, int, int); // skip some object
251static Byte *find_pair(Byte *, Byte); // find matching pair () [] {}
252static Byte *text_hole_delete(Byte *, Byte *); // at "p", delete a 'size' byte hole
253static Byte *text_hole_make(Byte *, int); // at "p", make a 'size' byte hole
254static Byte *yank_delete(Byte *, Byte *, int, int); // yank text[] into register then delete
255static void show_help(void); // display some help info
Eric Andersen3f980402001-04-04 17:31:15 +0000256static void rawmode(void); // set "raw" mode on tty
257static void cookmode(void); // return to "cooked" mode on tty
258static int mysleep(int); // sleep for 'h' 1/100 seconds
259static Byte readit(void); // read (maybe cursor) key from stdin
260static Byte get_one_char(void); // read 1 char from stdin
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000261static int file_size(const Byte *); // what is the byte size of "fn"
Eric Andersen3f980402001-04-04 17:31:15 +0000262static int file_insert(Byte *, Byte *, int);
263static int file_write(Byte *, Byte *, Byte *);
Eric Andersen822c3832001-05-07 17:37:43 +0000264static void place_cursor(int, int, int);
Eric Andersenbff7a602001-11-17 07:15:43 +0000265static void screen_erase(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000266static void clear_to_eol(void);
267static void clear_to_eos(void);
268static void standout_start(void); // send "start reverse video" sequence
269static void standout_end(void); // send "end reverse video" sequence
270static void flash(int); // flash the terminal screen
Eric Andersen3f980402001-04-04 17:31:15 +0000271static void show_status_line(void); // put a message on the bottom line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000272static void psb(const char *, ...); // Print Status Buf
273static void psbs(const char *, ...); // Print Status Buf in standout mode
Eric Andersen3f980402001-04-04 17:31:15 +0000274static void ni(Byte *); // display messages
275static void edit_status(void); // show file status on status line
276static void redraw(int); // force a full screen refresh
Eric Andersen822c3832001-05-07 17:37:43 +0000277static void format_line(Byte*, Byte*, int);
Eric Andersen3f980402001-04-04 17:31:15 +0000278static void refresh(int); // update the terminal from screen[]
279
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000280static void Indicate_Error(void); // use flash or beep to indicate error
281#define indicate_error(c) Indicate_Error()
282
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000283#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000284static Byte *char_search(Byte *, Byte *, int, int); // search for pattern starting at p
285static int mycmp(Byte *, Byte *, int); // string cmp based in "ignorecase"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000286#endif /* CONFIG_FEATURE_VI_SEARCH */
287#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +0000288static void Hit_Return(void);
Eric Andersen1c0d3112001-04-16 15:46:44 +0000289static Byte *get_one_address(Byte *, int *); // get colon addr, if present
290static Byte *get_address(Byte *, int *, int *); // get two colon addrs, if present
Eric Andersen3f980402001-04-04 17:31:15 +0000291static void colon(Byte *); // execute the "colon" mode cmds
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000292#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000293#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000294static void winch_sig(int); // catch window size changes
295static void suspend_sig(int); // catch ctrl-Z
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000296static void catch_sig(int); // catch ctrl-C and alarm time-outs
Eric Andersen3f980402001-04-04 17:31:15 +0000297static void core_sig(int); // catch a core dump signal
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000298#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
299#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000300static void start_new_cmd_q(Byte); // new queue for command
Eric Andersenbff7a602001-11-17 07:15:43 +0000301static void end_cmd_q(void); // stop saving input chars
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000302#else /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000303#define end_cmd_q()
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000304#endif /* CONFIG_FEATURE_VI_DOT_CMD */
305#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000306static void window_size_get(int); // find out what size the window is
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000307#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
308#ifdef CONFIG_FEATURE_VI_SETOPTS
Eric Andersen3f980402001-04-04 17:31:15 +0000309static void showmatching(Byte *); // show the matching pair () [] {}
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000310#endif /* CONFIG_FEATURE_VI_SETOPTS */
311#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
Eric Andersen3f980402001-04-04 17:31:15 +0000312static Byte *string_insert(Byte *, Byte *); // insert the string at 'p'
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000313#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
314#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000315static Byte *text_yank(Byte *, Byte *, int); // save copy of "p" into a register
316static Byte what_reg(void); // what is letter of current YDreg
317static void check_context(Byte); // remember context for '' command
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000318#endif /* CONFIG_FEATURE_VI_YANKMARK */
319#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000320static void crash_dummy();
321static void crash_test();
322static int crashme = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000323#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000324
325
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000326static void write1(const char *out)
327{
328 fputs(out, stdout);
329}
330
Eric Andersen3f980402001-04-04 17:31:15 +0000331extern int vi_main(int argc, char **argv)
Eric Andersen3f980402001-04-04 17:31:15 +0000332{
Eric Andersend402edf2001-04-04 19:29:48 +0000333 int c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000334 RESERVE_CONFIG_BUFFER(STATUS_BUFFER, 200);
Eric Andersen3f980402001-04-04 17:31:15 +0000335
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000336#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000337 int i;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000338#endif /* CONFIG_FEATURE_VI_YANKMARK */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000339#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
340 my_pid = getpid();
341#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000342#ifdef CONFIG_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000343 (void) srand((long) my_pid);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000344#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000345
346 status_buffer = STATUS_BUFFER;
347
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000348#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000349 vi_readonly = readonly = FALSE;
Eric Andersen3f980402001-04-04 17:31:15 +0000350 if (strncmp(argv[0], "view", 4) == 0) {
351 readonly = TRUE;
Eric Andersen822c3832001-05-07 17:37:43 +0000352 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000353 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000354#endif /* CONFIG_FEATURE_VI_READONLY */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000355 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000356#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000357 for (i = 0; i < 28; i++) {
358 reg[i] = 0;
359 } // init the yank regs
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000360#endif /* CONFIG_FEATURE_VI_YANKMARK */
Tim Riker86c76a92002-04-26 07:41:22 +0000361#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
Eric Andersen3f980402001-04-04 17:31:15 +0000362 modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000363#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000364
365 // 1- process $HOME/.exrc file
366 // 2- process EXINIT variable from environment
367 // 3- process command line args
368 while ((c = getopt(argc, argv, "hCR")) != -1) {
369 switch (c) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000370#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000371 case 'C':
372 crashme = 1;
373 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000374#endif /* CONFIG_FEATURE_VI_CRASHME */
375#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +0000376 case 'R': // Read-only flag
377 readonly = TRUE;
378 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000379#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen822c3832001-05-07 17:37:43 +0000380 //case 'r': // recover flag- ignore- we don't use tmp file
381 //case 'x': // encryption flag- ignore
382 //case 'c': // execute command first
383 //case 'h': // help -- just use default
Eric Andersen3f980402001-04-04 17:31:15 +0000384 default:
385 show_help();
Eric Andersendd8500b2001-07-02 18:06:14 +0000386 return 1;
Eric Andersen3f980402001-04-04 17:31:15 +0000387 }
388 }
389
390 // The argv array can be used by the ":next" and ":rewind" commands
391 // save optind.
392 fn_start = optind; // remember first file name for :next and :rew
393 save_argc = argc;
394
395 //----- This is the main file handling loop --------------
396 if (optind >= argc) {
397 editing = 1; // 0= exit, 1= one file, 2= multiple files
398 edit_file(0);
399 } else {
400 for (; optind < argc; optind++) {
401 editing = 1; // 0=exit, 1=one file, 2+ =many files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000402 free(cfn);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000403 cfn = (Byte *) bb_xstrdup(argv[optind]);
Eric Andersen3f980402001-04-04 17:31:15 +0000404 edit_file(cfn);
405 }
406 }
407 //-----------------------------------------------------------
408
409 return (0);
410}
411
Eric Andersen8efe9672003-09-15 08:33:45 +0000412#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
413//----- See what the window size currently is --------------------
414static inline void window_size_get(int fd)
415{
416 get_terminal_width_height(fd, &columns, &rows);
417}
418#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
419
Eric Andersen3f980402001-04-04 17:31:15 +0000420static void edit_file(Byte * fn)
421{
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000422 Byte c;
Eric Andersen1c0d3112001-04-16 15:46:44 +0000423 int cnt, size, ch;
Eric Andersen3f980402001-04-04 17:31:15 +0000424
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000425#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000426 int sig;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000427#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
428#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000429 static Byte *cur_line;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000430#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000431
432 rawmode();
433 rows = 24;
434 columns = 80;
Eric Andersen822c3832001-05-07 17:37:43 +0000435 ch= -1;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000436#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000437 window_size_get(0);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000438#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
Eric Andersen3f980402001-04-04 17:31:15 +0000439 new_screen(rows, columns); // get memory for virtual screen
440
441 cnt = file_size(fn); // file size
442 size = 2 * cnt; // 200% of file size
443 new_text(size); // get a text[] buffer
444 screenbegin = dot = end = text;
445 if (fn != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000446 ch= file_insert(fn, text, cnt);
447 }
448 if (ch < 1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000449 (void) char_insert(text, '\n'); // start empty buf with dummy line
450 }
451 file_modified = FALSE;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000452#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000453 YDreg = 26; // default Yank/Delete reg
454 Ureg = 27; // hold orig line for "U" cmd
455 for (cnt = 0; cnt < 28; cnt++) {
456 mark[cnt] = 0;
457 } // init the marks
458 mark[26] = mark[27] = text; // init "previous context"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000459#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000460
Eric Andersen3f980402001-04-04 17:31:15 +0000461 last_forward_char = last_input_char = '\0';
462 crow = 0;
463 ccol = 0;
464 edit_status();
465
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000466#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000467 catch_sig(0);
468 core_sig(0);
Eric Andersen3f980402001-04-04 17:31:15 +0000469 signal(SIGWINCH, winch_sig);
470 signal(SIGTSTP, suspend_sig);
471 sig = setjmp(restart);
472 if (sig != 0) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000473 const char *msg = "";
474
Eric Andersen3f980402001-04-04 17:31:15 +0000475 if (sig == SIGWINCH)
476 msg = "(window resize)";
477 if (sig == SIGHUP)
478 msg = "(hangup)";
479 if (sig == SIGINT)
480 msg = "(interrupt)";
481 if (sig == SIGTERM)
482 msg = "(terminate)";
483 if (sig == SIGBUS)
484 msg = "(bus error)";
485 if (sig == SIGSEGV)
486 msg = "(I tried to touch invalid memory)";
487 if (sig == SIGALRM)
488 msg = "(alarm)";
489
490 psbs("-- caught signal %d %s--", sig, msg);
Eric Andersen1c0d3112001-04-16 15:46:44 +0000491 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000492 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000493#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +0000494
495 editing = 1;
496 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
497 cmdcnt = 0;
498 tabstop = 8;
499 offset = 0; // no horizontal offset
500 c = '\0';
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000501#ifdef CONFIG_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000502 free(last_modifying_cmd);
503 free(ioq_start);
Eric Andersen3f980402001-04-04 17:31:15 +0000504 ioq = ioq_start = last_modifying_cmd = 0;
505 adding2q = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000506#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen822c3832001-05-07 17:37:43 +0000507 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000508 show_status_line();
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000509 fflush(stdout);
Eric Andersen3f980402001-04-04 17:31:15 +0000510
511 //------This is the main Vi cmd handling loop -----------------------
512 while (editing > 0) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000513#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000514 if (crashme > 0) {
515 if ((end - text) > 1) {
516 crash_dummy(); // generate a random command
517 } else {
518 crashme = 0;
519 dot =
520 string_insert(text, (Byte *) "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
521 refresh(FALSE);
522 }
523 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000524#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000525 last_input_char = c = get_one_char(); // get a cmd from user
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000526#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000527 // save a copy of the current line- for the 'U" command
528 if (begin_line(dot) != cur_line) {
529 cur_line = begin_line(dot);
530 text_yank(begin_line(dot), end_line(dot), Ureg);
531 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000532#endif /* CONFIG_FEATURE_VI_YANKMARK */
533#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000534 // These are commands that change text[].
535 // Remember the input for the "." command
536 if (!adding2q && ioq_start == 0
537 && strchr((char *) modifying_cmds, c) != NULL) {
538 start_new_cmd_q(c);
539 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000540#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000541 do_cmd(c); // execute the user command
542 //
543 // poll to see if there is input already waiting. if we are
544 // not able to display output fast enough to keep up, skip
545 // the display update until we catch up with input.
546 if (mysleep(0) == 0) {
547 // no input pending- so update output
548 refresh(FALSE);
549 show_status_line();
550 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000551#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000552 if (crashme > 0)
553 crash_test(); // test editor variables
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000554#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000555 }
556 //-------------------------------------------------------------------
557
Eric Andersen822c3832001-05-07 17:37:43 +0000558 place_cursor(rows, 0, FALSE); // go to bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +0000559 clear_to_eol(); // Erase to end of line
560 cookmode();
561}
562
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000563//----- The Colon commands -------------------------------------
564#ifdef CONFIG_FEATURE_VI_COLON
565static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present
566{
567 int st;
568 Byte *q;
569
570#ifdef CONFIG_FEATURE_VI_YANKMARK
571 Byte c;
572#endif /* CONFIG_FEATURE_VI_YANKMARK */
573#ifdef CONFIG_FEATURE_VI_SEARCH
574 Byte *pat, buf[BUFSIZ];
575#endif /* CONFIG_FEATURE_VI_SEARCH */
576
577 *addr = -1; // assume no addr
578 if (*p == '.') { // the current line
579 p++;
580 q = begin_line(dot);
581 *addr = count_lines(text, q);
582#ifdef CONFIG_FEATURE_VI_YANKMARK
583 } else if (*p == '\'') { // is this a mark addr
584 p++;
585 c = tolower(*p);
586 p++;
587 if (c >= 'a' && c <= 'z') {
588 // we have a mark
589 c = c - 'a';
590 q = mark[(int) c];
591 if (q != NULL) { // is mark valid
592 *addr = count_lines(text, q); // count lines
593 }
594 }
595#endif /* CONFIG_FEATURE_VI_YANKMARK */
596#ifdef CONFIG_FEATURE_VI_SEARCH
597 } else if (*p == '/') { // a search pattern
598 q = buf;
599 for (p++; *p; p++) {
600 if (*p == '/')
601 break;
602 *q++ = *p;
603 *q = '\0';
604 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000605 pat = (Byte *) bb_xstrdup((char *) buf); // save copy of pattern
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000606 if (*p == '/')
607 p++;
608 q = char_search(dot, pat, FORWARD, FULL);
609 if (q != NULL) {
610 *addr = count_lines(text, q);
611 }
612 free(pat);
613#endif /* CONFIG_FEATURE_VI_SEARCH */
614 } else if (*p == '$') { // the last line in file
615 p++;
616 q = begin_line(end - 1);
617 *addr = count_lines(text, q);
618 } else if (isdigit(*p)) { // specific line number
619 sscanf((char *) p, "%d%n", addr, &st);
620 p += st;
621 } else { // I don't reconise this
622 // unrecognised address- assume -1
623 *addr = -1;
624 }
625 return (p);
626}
627
628static Byte *get_address(Byte *p, int *b, int *e) // get two colon addrs, if present
629{
630 //----- get the address' i.e., 1,3 'a,'b -----
631 // get FIRST addr, if present
632 while (isblnk(*p))
633 p++; // skip over leading spaces
634 if (*p == '%') { // alias for 1,$
635 p++;
636 *b = 1;
637 *e = count_lines(text, end-1);
638 goto ga0;
639 }
640 p = get_one_address(p, b);
641 while (isblnk(*p))
642 p++;
643 if (*p == ',') { // is there a address seperator
644 p++;
645 while (isblnk(*p))
646 p++;
647 // get SECOND addr, if present
648 p = get_one_address(p, e);
649 }
650ga0:
651 while (isblnk(*p))
652 p++; // skip over trailing spaces
653 return (p);
654}
655
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000656#ifdef CONFIG_FEATURE_VI_SETOPTS
657static void setops(const Byte *args, const char *opname, int flg_no,
658 const char *short_opname, int opt)
659{
660 const char *a = (char *) args + flg_no;
661 int l = strlen(opname) - 1; /* opname have + ' ' */
662
663 if (strncasecmp(a, opname, l) == 0 ||
664 strncasecmp(a, short_opname, 2) == 0) {
665 if(flg_no)
666 vi_setops &= ~opt;
667 else
668 vi_setops |= opt;
669 }
670}
671#endif
672
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000673static void colon(Byte * buf)
674{
675 Byte c, *orig_buf, *buf1, *q, *r;
676 Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
677 int i, l, li, ch, st, b, e;
678 int useforce, forced;
679 struct stat st_buf;
680
681 // :3154 // if (-e line 3154) goto it else stay put
682 // :4,33w! foo // write a portion of buffer to file "foo"
683 // :w // write all of buffer to current file
684 // :q // quit
685 // :q! // quit- dont care about modified file
686 // :'a,'z!sort -u // filter block through sort
687 // :'f // goto mark "f"
688 // :'fl // list literal the mark "f" line
689 // :.r bar // read file "bar" into buffer before dot
690 // :/123/,/abc/d // delete lines from "123" line to "abc" line
691 // :/xyz/ // goto the "xyz" line
692 // :s/find/replace/ // substitute pattern "find" with "replace"
693 // :!<cmd> // run <cmd> then return
694 //
695 if (strlen((char *) buf) <= 0)
696 goto vc1;
697 if (*buf == ':')
698 buf++; // move past the ':'
699
700 forced = useforce = FALSE;
701 li = st = ch = i = 0;
702 b = e = -1;
703 q = text; // assume 1,$ for the range
704 r = end - 1;
705 li = count_lines(text, end - 1);
706 fn = cfn; // default to current file
707 memset(cmd, '\0', BUFSIZ); // clear cmd[]
708 memset(args, '\0', BUFSIZ); // clear args[]
709
710 // look for optional address(es) :. :1 :1,9 :'q,'a :%
711 buf = get_address(buf, &b, &e);
712
713 // remember orig command line
714 orig_buf = buf;
715
716 // get the COMMAND into cmd[]
717 buf1 = cmd;
718 while (*buf != '\0') {
719 if (isspace(*buf))
720 break;
721 *buf1++ = *buf++;
722 }
723 // get any ARGuments
724 while (isblnk(*buf))
725 buf++;
726 strcpy((char *) args, (char *) buf);
727 buf1 = last_char_is((char *)cmd, '!');
728 if (buf1) {
729 useforce = TRUE;
730 *buf1 = '\0'; // get rid of !
731 }
732 if (b >= 0) {
733 // if there is only one addr, then the addr
734 // is the line number of the single line the
735 // user wants. So, reset the end
736 // pointer to point at end of the "b" line
737 q = find_line(b); // what line is #b
738 r = end_line(q);
739 li = 1;
740 }
741 if (e >= 0) {
742 // we were given two addrs. change the
743 // end pointer to the addr given by user.
744 r = find_line(e); // what line is #e
745 r = end_line(r);
746 li = e - b + 1;
747 }
748 // ------------ now look for the command ------------
749 i = strlen((char *) cmd);
750 if (i == 0) { // :123CR goto line #123
751 if (b >= 0) {
752 dot = find_line(b); // what line is #b
753 dot_skip_over_ws();
754 }
755 } else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd
756 // :!ls run the <cmd>
757 (void) alarm(0); // wait for input- no alarms
758 place_cursor(rows - 1, 0, FALSE); // go to Status line
759 clear_to_eol(); // clear the line
760 cookmode();
761 system(orig_buf+1); // run the cmd
762 rawmode();
763 Hit_Return(); // let user see results
764 (void) alarm(3); // done waiting for input
765 } else if (strncmp((char *) cmd, "=", i) == 0) { // where is the address
766 if (b < 0) { // no addr given- use defaults
767 b = e = count_lines(text, dot);
768 }
769 psb("%d", b);
770 } else if (strncasecmp((char *) cmd, "delete", i) == 0) { // delete lines
771 if (b < 0) { // no addr given- use defaults
772 q = begin_line(dot); // assume .,. for the range
773 r = end_line(dot);
774 }
775 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
776 dot_skip_over_ws();
777 } else if (strncasecmp((char *) cmd, "edit", i) == 0) { // Edit a file
778 int sr;
779 sr= 0;
780 // don't edit, if the current file has been modified
781 if (file_modified && ! useforce) {
782 psbs("No write since last change (:edit! overrides)");
783 goto vc1;
784 }
785 if (strlen(args) > 0) {
786 // the user supplied a file name
787 fn= args;
788 } else if (cfn != 0 && strlen(cfn) > 0) {
789 // no user supplied name- use the current filename
790 fn= cfn;
791 goto vc5;
792 } else {
793 // no user file name, no current name- punt
794 psbs("No current filename");
795 goto vc1;
796 }
797
798 // see if file exists- if not, its just a new file request
799 if ((sr=stat((char*)fn, &st_buf)) < 0) {
800 // This is just a request for a new file creation.
801 // The file_insert below will fail but we get
802 // an empty buffer with a file name. Then the "write"
803 // command can do the create.
804 } else {
805 if ((st_buf.st_mode & (S_IFREG)) == 0) {
806 // This is not a regular file
807 psbs("\"%s\" is not a regular file", fn);
808 goto vc1;
809 }
810 if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
811 // dont have any read permissions
812 psbs("\"%s\" is not readable", fn);
813 goto vc1;
814 }
815 }
816
817 // There is a read-able regular file
818 // make this the current file
Manuel Novoa III cad53642003-03-19 09:13:01 +0000819 q = (Byte *) bb_xstrdup((char *) fn); // save the cfn
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000820 free(cfn); // free the old name
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000821 cfn = q; // remember new cfn
822
823 vc5:
824 // delete all the contents of text[]
825 new_text(2 * file_size(fn));
826 screenbegin = dot = end = text;
827
828 // insert new file
829 ch = file_insert(fn, text, file_size(fn));
830
831 if (ch < 1) {
832 // start empty buf with dummy line
833 (void) char_insert(text, '\n');
834 ch= 1;
835 }
836 file_modified = FALSE;
837#ifdef CONFIG_FEATURE_VI_YANKMARK
838 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
839 free(reg[Ureg]); // free orig line reg- for 'U'
840 reg[Ureg]= 0;
841 }
842 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
843 free(reg[YDreg]); // free default yank/delete register
844 reg[YDreg]= 0;
845 }
846 for (li = 0; li < 28; li++) {
847 mark[li] = 0;
848 } // init the marks
849#endif /* CONFIG_FEATURE_VI_YANKMARK */
850 // how many lines in text[]?
851 li = count_lines(text, end - 1);
852 psb("\"%s\"%s"
853#ifdef CONFIG_FEATURE_VI_READONLY
854 "%s"
855#endif /* CONFIG_FEATURE_VI_READONLY */
856 " %dL, %dC", cfn,
857 (sr < 0 ? " [New file]" : ""),
858#ifdef CONFIG_FEATURE_VI_READONLY
859 ((vi_readonly || readonly) ? " [Read only]" : ""),
860#endif /* CONFIG_FEATURE_VI_READONLY */
861 li, ch);
862 } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this
863 if (b != -1 || e != -1) {
864 ni((Byte *) "No address allowed on this command");
865 goto vc1;
866 }
867 if (strlen((char *) args) > 0) {
868 // user wants a new filename
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000869 free(cfn);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000870 cfn = (Byte *) bb_xstrdup((char *) args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000871 } else {
872 // user wants file status info
873 edit_status();
874 }
875 } else if (strncasecmp((char *) cmd, "features", i) == 0) { // what features are available
876 // print out values of all features
877 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
878 clear_to_eol(); // clear the line
879 cookmode();
880 show_help();
881 rawmode();
882 Hit_Return();
883 } else if (strncasecmp((char *) cmd, "list", i) == 0) { // literal print line
884 if (b < 0) { // no addr given- use defaults
885 q = begin_line(dot); // assume .,. for the range
886 r = end_line(dot);
887 }
888 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
889 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000890 puts("\r");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000891 for (; q <= r; q++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000892 int c_is_no_print;
893
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000894 c = *q;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000895 c_is_no_print = c > 127 && !Isprint(c);
896 if (c_is_no_print) {
897 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000898 standout_start();
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000899 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000900 if (c == '\n') {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000901 write1("$\r");
902 } else if (c < ' ' || c == 127) {
903 putchar('^');
904 if(c == 127)
905 c = '?';
906 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000907 c += '@';
908 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000909 putchar(c);
910 if (c_is_no_print)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000911 standout_end();
912 }
913#ifdef CONFIG_FEATURE_VI_SET
914 vc2:
915#endif /* CONFIG_FEATURE_VI_SET */
916 Hit_Return();
917 } else if ((strncasecmp((char *) cmd, "quit", i) == 0) || // Quit
918 (strncasecmp((char *) cmd, "next", i) == 0)) { // edit next file
919 if (useforce) {
920 // force end of argv list
921 if (*cmd == 'q') {
922 optind = save_argc;
923 }
924 editing = 0;
925 goto vc1;
926 }
927 // don't exit if the file been modified
928 if (file_modified) {
929 psbs("No write since last change (:%s! overrides)",
930 (*cmd == 'q' ? "quit" : "next"));
931 goto vc1;
932 }
933 // are there other file to edit
934 if (*cmd == 'q' && optind < save_argc - 1) {
935 psbs("%d more file to edit", (save_argc - optind - 1));
936 goto vc1;
937 }
938 if (*cmd == 'n' && optind >= save_argc - 1) {
939 psbs("No more files to edit");
940 goto vc1;
941 }
942 editing = 0;
943 } else if (strncasecmp((char *) cmd, "read", i) == 0) { // read file into text[]
944 fn = args;
945 if (strlen((char *) fn) <= 0) {
946 psbs("No filename given");
947 goto vc1;
948 }
949 if (b < 0) { // no addr given- use defaults
950 q = begin_line(dot); // assume "dot"
951 }
952 // read after current line- unless user said ":0r foo"
953 if (b != 0)
954 q = next_line(q);
955#ifdef CONFIG_FEATURE_VI_READONLY
956 l= readonly; // remember current files' status
957#endif
958 ch = file_insert(fn, q, file_size(fn));
959#ifdef CONFIG_FEATURE_VI_READONLY
960 readonly= l;
961#endif
962 if (ch < 0)
963 goto vc1; // nothing was inserted
964 // how many lines in text[]?
965 li = count_lines(q, q + ch - 1);
966 psb("\"%s\""
967#ifdef CONFIG_FEATURE_VI_READONLY
968 "%s"
969#endif /* CONFIG_FEATURE_VI_READONLY */
970 " %dL, %dC", fn,
971#ifdef CONFIG_FEATURE_VI_READONLY
972 ((vi_readonly || readonly) ? " [Read only]" : ""),
973#endif /* CONFIG_FEATURE_VI_READONLY */
974 li, ch);
975 if (ch > 0) {
976 // if the insert is before "dot" then we need to update
977 if (q <= dot)
978 dot += ch;
979 file_modified = TRUE;
980 }
981 } else if (strncasecmp((char *) cmd, "rewind", i) == 0) { // rewind cmd line args
982 if (file_modified && ! useforce) {
983 psbs("No write since last change (:rewind! overrides)");
984 } else {
985 // reset the filenames to edit
986 optind = fn_start - 1;
987 editing = 0;
988 }
989#ifdef CONFIG_FEATURE_VI_SET
990 } else if (strncasecmp((char *) cmd, "set", i) == 0) { // set or clear features
991 i = 0; // offset into args
992 if (strlen((char *) args) == 0) {
993 // print out values of all options
994 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
995 clear_to_eol(); // clear the line
996 printf("----------------------------------------\r\n");
997#ifdef CONFIG_FEATURE_VI_SETOPTS
998 if (!autoindent)
999 printf("no");
1000 printf("autoindent ");
1001 if (!err_method)
1002 printf("no");
1003 printf("flash ");
1004 if (!ignorecase)
1005 printf("no");
1006 printf("ignorecase ");
1007 if (!showmatch)
1008 printf("no");
1009 printf("showmatch ");
1010 printf("tabstop=%d ", tabstop);
1011#endif /* CONFIG_FEATURE_VI_SETOPTS */
1012 printf("\r\n");
1013 goto vc2;
1014 }
1015 if (strncasecmp((char *) args, "no", 2) == 0)
1016 i = 2; // ":set noautoindent"
1017#ifdef CONFIG_FEATURE_VI_SETOPTS
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001018 setops(args, "autoindent ", i, "ai", VI_AUTOINDENT);
1019 setops(args, "flash ", i, "fl", VI_ERR_METHOD);
1020 setops(args, "ignorecase ", i, "ic", VI_IGNORECASE);
1021 setops(args, "showmatch ", i, "ic", VI_SHOWMATCH);
1022 if (strncasecmp((char *) args + i, "tabstop=%d ", 7) == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001023 sscanf(strchr((char *) args + i, '='), "=%d", &ch);
1024 if (ch > 0 && ch < columns - 1)
1025 tabstop = ch;
1026 }
1027#endif /* CONFIG_FEATURE_VI_SETOPTS */
1028#endif /* CONFIG_FEATURE_VI_SET */
1029#ifdef CONFIG_FEATURE_VI_SEARCH
1030 } else if (strncasecmp((char *) cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
1031 Byte *ls, *F, *R;
1032 int gflag;
1033
1034 // F points to the "find" pattern
1035 // R points to the "replace" pattern
1036 // replace the cmd line delimiters "/" with NULLs
1037 gflag = 0; // global replace flag
1038 c = orig_buf[1]; // what is the delimiter
1039 F = orig_buf + 2; // start of "find"
1040 R = (Byte *) strchr((char *) F, c); // middle delimiter
1041 if (!R) goto colon_s_fail;
1042 *R++ = '\0'; // terminate "find"
1043 buf1 = (Byte *) strchr((char *) R, c);
1044 if (!buf1) goto colon_s_fail;
1045 *buf1++ = '\0'; // terminate "replace"
1046 if (*buf1 == 'g') { // :s/foo/bar/g
1047 buf1++;
1048 gflag++; // turn on gflag
1049 }
1050 q = begin_line(q);
1051 if (b < 0) { // maybe :s/foo/bar/
1052 q = begin_line(dot); // start with cur line
1053 b = count_lines(text, q); // cur line number
1054 }
1055 if (e < 0)
1056 e = b; // maybe :.s/foo/bar/
1057 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
1058 ls = q; // orig line start
1059 vc4:
1060 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
1061 if (buf1 != NULL) {
1062 // we found the "find" pattern- delete it
1063 (void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
1064 // inset the "replace" patern
1065 (void) string_insert(buf1, R); // insert the string
1066 // check for "global" :s/foo/bar/g
1067 if (gflag == 1) {
1068 if ((buf1 + strlen((char *) R)) < end_line(ls)) {
1069 q = buf1 + strlen((char *) R);
1070 goto vc4; // don't let q move past cur line
1071 }
1072 }
1073 }
1074 q = next_line(ls);
1075 }
1076#endif /* CONFIG_FEATURE_VI_SEARCH */
1077 } else if (strncasecmp((char *) cmd, "version", i) == 0) { // show software version
1078 psb("%s", vi_Version);
1079 } else if ((strncasecmp((char *) cmd, "write", i) == 0) || // write text to file
1080 (strncasecmp((char *) cmd, "wq", i) == 0) ||
1081 (strncasecmp((char *) cmd, "x", i) == 0)) {
1082 // is there a file name to write to?
1083 if (strlen((char *) args) > 0) {
1084 fn = args;
1085 }
1086#ifdef CONFIG_FEATURE_VI_READONLY
1087 if ((vi_readonly || readonly) && ! useforce) {
1088 psbs("\"%s\" File is read only", fn);
1089 goto vc3;
1090 }
1091#endif /* CONFIG_FEATURE_VI_READONLY */
1092 // how many lines in text[]?
1093 li = count_lines(q, r);
1094 ch = r - q + 1;
1095 // see if file exists- if not, its just a new file request
1096 if (useforce) {
1097 // if "fn" is not write-able, chmod u+w
1098 // sprintf(syscmd, "chmod u+w %s", fn);
1099 // system(syscmd);
1100 forced = TRUE;
1101 }
1102 l = file_write(fn, q, r);
1103 if (useforce && forced) {
1104 // chmod u-w
1105 // sprintf(syscmd, "chmod u-w %s", fn);
1106 // system(syscmd);
1107 forced = FALSE;
1108 }
1109 psb("\"%s\" %dL, %dC", fn, li, l);
1110 if (q == text && r == end - 1 && l == ch)
1111 file_modified = FALSE;
1112 if ((cmd[0] == 'x' || cmd[1] == 'q') && l == ch) {
1113 editing = 0;
1114 }
1115#ifdef CONFIG_FEATURE_VI_READONLY
1116 vc3:;
1117#endif /* CONFIG_FEATURE_VI_READONLY */
1118#ifdef CONFIG_FEATURE_VI_YANKMARK
1119 } else if (strncasecmp((char *) cmd, "yank", i) == 0) { // yank lines
1120 if (b < 0) { // no addr given- use defaults
1121 q = begin_line(dot); // assume .,. for the range
1122 r = end_line(dot);
1123 }
1124 text_yank(q, r, YDreg);
1125 li = count_lines(q, r);
1126 psb("Yank %d lines (%d chars) into [%c]",
1127 li, strlen((char *) reg[YDreg]), what_reg());
1128#endif /* CONFIG_FEATURE_VI_YANKMARK */
1129 } else {
1130 // cmd unknown
1131 ni((Byte *) cmd);
1132 }
1133 vc1:
1134 dot = bound_dot(dot); // make sure "dot" is valid
1135 return;
1136#ifdef CONFIG_FEATURE_VI_SEARCH
1137colon_s_fail:
1138 psb(":s expression missing delimiters");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001139#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001140}
1141
1142static void Hit_Return(void)
1143{
1144 char c;
1145
1146 standout_start(); // start reverse video
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001147 write1("[Hit return to continue]");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001148 standout_end(); // end reverse video
1149 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
1150 ;
1151 redraw(TRUE); // force redraw all
1152}
1153#endif /* CONFIG_FEATURE_VI_COLON */
1154
1155//----- Synchronize the cursor to Dot --------------------------
1156static void sync_cursor(Byte * d, int *row, int *col)
1157{
1158 Byte *beg_cur, *end_cur; // begin and end of "d" line
1159 Byte *beg_scr, *end_scr; // begin and end of screen
1160 Byte *tp;
1161 int cnt, ro, co;
1162
1163 beg_cur = begin_line(d); // first char of cur line
1164 end_cur = end_line(d); // last char of cur line
1165
1166 beg_scr = end_scr = screenbegin; // first char of screen
1167 end_scr = end_screen(); // last char of screen
1168
1169 if (beg_cur < screenbegin) {
1170 // "d" is before top line on screen
1171 // how many lines do we have to move
1172 cnt = count_lines(beg_cur, screenbegin);
1173 sc1:
1174 screenbegin = beg_cur;
1175 if (cnt > (rows - 1) / 2) {
1176 // we moved too many lines. put "dot" in middle of screen
1177 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1178 screenbegin = prev_line(screenbegin);
1179 }
1180 }
1181 } else if (beg_cur > end_scr) {
1182 // "d" is after bottom line on screen
1183 // how many lines do we have to move
1184 cnt = count_lines(end_scr, beg_cur);
1185 if (cnt > (rows - 1) / 2)
1186 goto sc1; // too many lines
1187 for (ro = 0; ro < cnt - 1; ro++) {
1188 // move screen begin the same amount
1189 screenbegin = next_line(screenbegin);
1190 // now, move the end of screen
1191 end_scr = next_line(end_scr);
1192 end_scr = end_line(end_scr);
1193 }
1194 }
1195 // "d" is on screen- find out which row
1196 tp = screenbegin;
1197 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1198 if (tp == beg_cur)
1199 break;
1200 tp = next_line(tp);
1201 }
1202
1203 // find out what col "d" is on
1204 co = 0;
1205 do { // drive "co" to correct column
1206 if (*tp == '\n' || *tp == '\0')
1207 break;
1208 if (*tp == '\t') {
1209 // 7 - (co % 8 )
1210 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001211 } else if (*tp < ' ' || *tp == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001212 co++; // display as ^X, use 2 columns
1213 }
1214 } while (tp++ < d && ++co);
1215
1216 // "co" is the column where "dot" is.
1217 // The screen has "columns" columns.
1218 // The currently displayed columns are 0+offset -- columns+ofset
1219 // |-------------------------------------------------------------|
1220 // ^ ^ ^
1221 // offset | |------- columns ----------------|
1222 //
1223 // If "co" is already in this range then we do not have to adjust offset
1224 // but, we do have to subtract the "offset" bias from "co".
1225 // If "co" is outside this range then we have to change "offset".
1226 // If the first char of a line is a tab the cursor will try to stay
1227 // in column 7, but we have to set offset to 0.
1228
1229 if (co < 0 + offset) {
1230 offset = co;
1231 }
1232 if (co >= columns + offset) {
1233 offset = co - columns + 1;
1234 }
1235 // if the first char of the line is a tab, and "dot" is sitting on it
1236 // force offset to 0.
1237 if (d == beg_cur && *d == '\t') {
1238 offset = 0;
1239 }
1240 co -= offset;
1241
1242 *row = ro;
1243 *col = co;
1244}
1245
1246//----- Text Movement Routines ---------------------------------
1247static Byte *begin_line(Byte * p) // return pointer to first char cur line
1248{
1249 while (p > text && p[-1] != '\n')
1250 p--; // go to cur line B-o-l
1251 return (p);
1252}
1253
1254static Byte *end_line(Byte * p) // return pointer to NL of cur line line
1255{
1256 while (p < end - 1 && *p != '\n')
1257 p++; // go to cur line E-o-l
1258 return (p);
1259}
1260
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001261static inline Byte *dollar_line(Byte * p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001262{
1263 while (p < end - 1 && *p != '\n')
1264 p++; // go to cur line E-o-l
1265 // Try to stay off of the Newline
1266 if (*p == '\n' && (p - begin_line(p)) > 0)
1267 p--;
1268 return (p);
1269}
1270
1271static Byte *prev_line(Byte * p) // return pointer first char prev line
1272{
1273 p = begin_line(p); // goto begining of cur line
1274 if (p[-1] == '\n' && p > text)
1275 p--; // step to prev line
1276 p = begin_line(p); // goto begining of prev line
1277 return (p);
1278}
1279
1280static Byte *next_line(Byte * p) // return pointer first char next line
1281{
1282 p = end_line(p);
1283 if (*p == '\n' && p < end - 1)
1284 p++; // step to next line
1285 return (p);
1286}
1287
1288//----- Text Information Routines ------------------------------
1289static Byte *end_screen(void)
1290{
1291 Byte *q;
1292 int cnt;
1293
1294 // find new bottom line
1295 q = screenbegin;
1296 for (cnt = 0; cnt < rows - 2; cnt++)
1297 q = next_line(q);
1298 q = end_line(q);
1299 return (q);
1300}
1301
1302static int count_lines(Byte * start, Byte * stop) // count line from start to stop
1303{
1304 Byte *q;
1305 int cnt;
1306
1307 if (stop < start) { // start and stop are backwards- reverse them
1308 q = start;
1309 start = stop;
1310 stop = q;
1311 }
1312 cnt = 0;
1313 stop = end_line(stop); // get to end of this line
1314 for (q = start; q <= stop && q <= end - 1; q++) {
1315 if (*q == '\n')
1316 cnt++;
1317 }
1318 return (cnt);
1319}
1320
1321static Byte *find_line(int li) // find begining of line #li
1322{
1323 Byte *q;
1324
1325 for (q = text; li > 1; li--) {
1326 q = next_line(q);
1327 }
1328 return (q);
1329}
1330
1331//----- Dot Movement Routines ----------------------------------
1332static void dot_left(void)
1333{
1334 if (dot > text && dot[-1] != '\n')
1335 dot--;
1336}
1337
1338static void dot_right(void)
1339{
1340 if (dot < end - 1 && *dot != '\n')
1341 dot++;
1342}
1343
1344static void dot_begin(void)
1345{
1346 dot = begin_line(dot); // return pointer to first char cur line
1347}
1348
1349static void dot_end(void)
1350{
1351 dot = end_line(dot); // return pointer to last char cur line
1352}
1353
1354static Byte *move_to_col(Byte * p, int l)
1355{
1356 int co;
1357
1358 p = begin_line(p);
1359 co = 0;
1360 do {
1361 if (*p == '\n' || *p == '\0')
1362 break;
1363 if (*p == '\t') {
1364 // 7 - (co % 8 )
1365 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001366 } else if (*p < ' ' || *p == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001367 co++; // display as ^X, use 2 columns
1368 }
1369 } while (++co <= l && p++ < end);
1370 return (p);
1371}
1372
1373static void dot_next(void)
1374{
1375 dot = next_line(dot);
1376}
1377
1378static void dot_prev(void)
1379{
1380 dot = prev_line(dot);
1381}
1382
1383static void dot_scroll(int cnt, int dir)
1384{
1385 Byte *q;
1386
1387 for (; cnt > 0; cnt--) {
1388 if (dir < 0) {
1389 // scroll Backwards
1390 // ctrl-Y scroll up one line
1391 screenbegin = prev_line(screenbegin);
1392 } else {
1393 // scroll Forwards
1394 // ctrl-E scroll down one line
1395 screenbegin = next_line(screenbegin);
1396 }
1397 }
1398 // make sure "dot" stays on the screen so we dont scroll off
1399 if (dot < screenbegin)
1400 dot = screenbegin;
1401 q = end_screen(); // find new bottom line
1402 if (dot > q)
1403 dot = begin_line(q); // is dot is below bottom line?
1404 dot_skip_over_ws();
1405}
1406
1407static void dot_skip_over_ws(void)
1408{
1409 // skip WS
1410 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1411 dot++;
1412}
1413
1414static void dot_delete(void) // delete the char at 'dot'
1415{
1416 (void) text_hole_delete(dot, dot);
1417}
1418
1419static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end"
1420{
1421 if (p >= end && end > text) {
1422 p = end - 1;
1423 indicate_error('1');
1424 }
1425 if (p < text) {
1426 p = text;
1427 indicate_error('2');
1428 }
1429 return (p);
1430}
1431
1432//----- Helper Utility Routines --------------------------------
1433
1434//----------------------------------------------------------------
1435//----- Char Routines --------------------------------------------
1436/* Chars that are part of a word-
1437 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1438 * Chars that are Not part of a word (stoppers)
1439 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1440 * Chars that are WhiteSpace
1441 * TAB NEWLINE VT FF RETURN SPACE
1442 * DO NOT COUNT NEWLINE AS WHITESPACE
1443 */
1444
1445static Byte *new_screen(int ro, int co)
1446{
1447 int li;
1448
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001449 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001450 screensize = ro * co + 8;
1451 screen = (Byte *) xmalloc(screensize);
1452 // initialize the new screen. assume this will be a empty file.
1453 screen_erase();
1454 // non-existant text[] lines start with a tilde (~).
1455 for (li = 1; li < ro - 1; li++) {
1456 screen[(li * co) + 0] = '~';
1457 }
1458 return (screen);
1459}
1460
1461static Byte *new_text(int size)
1462{
1463 if (size < 10240)
1464 size = 10240; // have a minimum size for new files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001465 free(text);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001466 text = (Byte *) xmalloc(size + 8);
1467 memset(text, '\0', size); // clear new text[]
1468 //text += 4; // leave some room for "oops"
1469 textend = text + size - 1;
1470 //textend -= 4; // leave some root for "oops"
1471 return (text);
1472}
1473
1474#ifdef CONFIG_FEATURE_VI_SEARCH
1475static int mycmp(Byte * s1, Byte * s2, int len)
1476{
1477 int i;
1478
1479 i = strncmp((char *) s1, (char *) s2, len);
1480#ifdef CONFIG_FEATURE_VI_SETOPTS
1481 if (ignorecase) {
1482 i = strncasecmp((char *) s1, (char *) s2, len);
1483 }
1484#endif /* CONFIG_FEATURE_VI_SETOPTS */
1485 return (i);
1486}
1487
1488static Byte *char_search(Byte * p, Byte * pat, int dir, int range) // search for pattern starting at p
1489{
1490#ifndef REGEX_SEARCH
1491 Byte *start, *stop;
1492 int len;
1493
1494 len = strlen((char *) pat);
1495 if (dir == FORWARD) {
1496 stop = end - 1; // assume range is p - end-1
1497 if (range == LIMITED)
1498 stop = next_line(p); // range is to next line
1499 for (start = p; start < stop; start++) {
1500 if (mycmp(start, pat, len) == 0) {
1501 return (start);
1502 }
1503 }
1504 } else if (dir == BACK) {
1505 stop = text; // assume range is text - p
1506 if (range == LIMITED)
1507 stop = prev_line(p); // range is to prev line
1508 for (start = p - len; start >= stop; start--) {
1509 if (mycmp(start, pat, len) == 0) {
1510 return (start);
1511 }
1512 }
1513 }
1514 // pattern not found
1515 return (NULL);
1516#else /*REGEX_SEARCH */
1517 char *q;
1518 struct re_pattern_buffer preg;
1519 int i;
1520 int size, range;
1521
1522 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1523 preg.translate = 0;
1524 preg.fastmap = 0;
1525 preg.buffer = 0;
1526 preg.allocated = 0;
1527
1528 // assume a LIMITED forward search
1529 q = next_line(p);
1530 q = end_line(q);
1531 q = end - 1;
1532 if (dir == BACK) {
1533 q = prev_line(p);
1534 q = text;
1535 }
1536 // count the number of chars to search over, forward or backward
1537 size = q - p;
1538 if (size < 0)
1539 size = p - q;
1540 // RANGE could be negative if we are searching backwards
1541 range = q - p;
1542
1543 q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
1544 if (q != 0) {
1545 // The pattern was not compiled
1546 psbs("bad search pattern: \"%s\": %s", pat, q);
1547 i = 0; // return p if pattern not compiled
1548 goto cs1;
1549 }
1550
1551 q = p;
1552 if (range < 0) {
1553 q = p - size;
1554 if (q < text)
1555 q = text;
1556 }
1557 // search for the compiled pattern, preg, in p[]
1558 // range < 0- search backward
1559 // range > 0- search forward
1560 // 0 < start < size
1561 // re_search() < 0 not found or error
1562 // re_search() > 0 index of found pattern
1563 // struct pattern char int int int struct reg
1564 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1565 i = re_search(&preg, q, size, 0, range, 0);
1566 if (i == -1) {
1567 p = 0;
1568 i = 0; // return NULL if pattern not found
1569 }
1570 cs1:
1571 if (dir == FORWARD) {
1572 p = p + i;
1573 } else {
1574 p = p - i;
1575 }
1576 return (p);
1577#endif /*REGEX_SEARCH */
1578}
1579#endif /* CONFIG_FEATURE_VI_SEARCH */
1580
1581static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
1582{
1583 if (c == 22) { // Is this an ctrl-V?
1584 p = stupid_insert(p, '^'); // use ^ to indicate literal next
1585 p--; // backup onto ^
1586 refresh(FALSE); // show the ^
1587 c = get_one_char();
1588 *p = c;
1589 p++;
1590 file_modified = TRUE; // has the file been modified
1591 } else if (c == 27) { // Is this an ESC?
1592 cmd_mode = 0;
1593 cmdcnt = 0;
1594 end_cmd_q(); // stop adding to q
1595 strcpy((char *) status_buffer, " "); // clear the status buffer
1596 if ((p[-1] != '\n') && (dot>text)) {
1597 p--;
1598 }
1599 } else if (c == erase_char) { // Is this a BS
1600 // 123456789
1601 if ((p[-1] != '\n') && (dot>text)) {
1602 p--;
1603 p = text_hole_delete(p, p); // shrink buffer 1 char
1604#ifdef CONFIG_FEATURE_VI_DOT_CMD
1605 // also rmove char from last_modifying_cmd
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001606 if (last_modifying_cmd != 0 && strlen((char *) last_modifying_cmd) > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001607 Byte *q;
1608
1609 q = last_modifying_cmd;
1610 q[strlen((char *) q) - 1] = '\0'; // erase BS
1611 q[strlen((char *) q) - 1] = '\0'; // erase prev char
1612 }
1613#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1614 }
1615 } else {
1616 // insert a char into text[]
1617 Byte *sp; // "save p"
1618
1619 if (c == 13)
1620 c = '\n'; // translate \r to \n
1621 sp = p; // remember addr of insert
1622 p = stupid_insert(p, c); // insert the char
1623#ifdef CONFIG_FEATURE_VI_SETOPTS
1624 if (showmatch && strchr(")]}", *sp) != NULL) {
1625 showmatching(sp);
1626 }
1627 if (autoindent && c == '\n') { // auto indent the new line
1628 Byte *q;
1629
1630 q = prev_line(p); // use prev line as templet
1631 for (; isblnk(*q); q++) {
1632 p = stupid_insert(p, *q); // insert the char
1633 }
1634 }
1635#endif /* CONFIG_FEATURE_VI_SETOPTS */
1636 }
1637 return (p);
1638}
1639
1640static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
1641{
1642 p = text_hole_make(p, 1);
1643 if (p != 0) {
1644 *p = c;
1645 file_modified = TRUE; // has the file been modified
1646 p++;
1647 }
1648 return (p);
1649}
1650
1651static Byte find_range(Byte ** start, Byte ** stop, Byte c)
1652{
1653 Byte *save_dot, *p, *q;
1654 int cnt;
1655
1656 save_dot = dot;
1657 p = q = dot;
1658
1659 if (strchr("cdy><", c)) {
1660 // these cmds operate on whole lines
1661 p = q = begin_line(p);
1662 for (cnt = 1; cnt < cmdcnt; cnt++) {
1663 q = next_line(q);
1664 }
1665 q = end_line(q);
1666 } else if (strchr("^%$0bBeEft", c)) {
1667 // These cmds operate on char positions
1668 do_cmd(c); // execute movement cmd
1669 q = dot;
1670 } else if (strchr("wW", c)) {
1671 do_cmd(c); // execute movement cmd
1672 if (dot > text)
1673 dot--; // move back off of next word
1674 if (dot > text && *dot == '\n')
1675 dot--; // stay off NL
1676 q = dot;
1677 } else if (strchr("H-k{", c)) {
1678 // these operate on multi-lines backwards
1679 q = end_line(dot); // find NL
1680 do_cmd(c); // execute movement cmd
1681 dot_begin();
1682 p = dot;
1683 } else if (strchr("L+j}\r\n", c)) {
1684 // these operate on multi-lines forwards
1685 p = begin_line(dot);
1686 do_cmd(c); // execute movement cmd
1687 dot_end(); // find NL
1688 q = dot;
1689 } else {
1690 c = 27; // error- return an ESC char
1691 //break;
1692 }
1693 *start = p;
1694 *stop = q;
1695 if (q < p) {
1696 *start = q;
1697 *stop = p;
1698 }
1699 dot = save_dot;
1700 return (c);
1701}
1702
1703static int st_test(Byte * p, int type, int dir, Byte * tested)
1704{
1705 Byte c, c0, ci;
1706 int test, inc;
1707
1708 inc = dir;
1709 c = c0 = p[0];
1710 ci = p[inc];
1711 test = 0;
1712
1713 if (type == S_BEFORE_WS) {
1714 c = ci;
1715 test = ((!isspace(c)) || c == '\n');
1716 }
1717 if (type == S_TO_WS) {
1718 c = c0;
1719 test = ((!isspace(c)) || c == '\n');
1720 }
1721 if (type == S_OVER_WS) {
1722 c = c0;
1723 test = ((isspace(c)));
1724 }
1725 if (type == S_END_PUNCT) {
1726 c = ci;
1727 test = ((ispunct(c)));
1728 }
1729 if (type == S_END_ALNUM) {
1730 c = ci;
1731 test = ((isalnum(c)) || c == '_');
1732 }
1733 *tested = c;
1734 return (test);
1735}
1736
1737static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
1738{
1739 Byte c;
1740
1741 while (st_test(p, type, dir, &c)) {
1742 // make sure we limit search to correct number of lines
1743 if (c == '\n' && --linecnt < 1)
1744 break;
1745 if (dir >= 0 && p >= end - 1)
1746 break;
1747 if (dir < 0 && p <= text)
1748 break;
1749 p += dir; // move to next char
1750 }
1751 return (p);
1752}
1753
1754// find matching char of pair () [] {}
1755static Byte *find_pair(Byte * p, Byte c)
1756{
1757 Byte match, *q;
1758 int dir, level;
1759
1760 match = ')';
1761 level = 1;
1762 dir = 1; // assume forward
1763 switch (c) {
1764 case '(':
1765 match = ')';
1766 break;
1767 case '[':
1768 match = ']';
1769 break;
1770 case '{':
1771 match = '}';
1772 break;
1773 case ')':
1774 match = '(';
1775 dir = -1;
1776 break;
1777 case ']':
1778 match = '[';
1779 dir = -1;
1780 break;
1781 case '}':
1782 match = '{';
1783 dir = -1;
1784 break;
1785 }
1786 for (q = p + dir; text <= q && q < end; q += dir) {
1787 // look for match, count levels of pairs (( ))
1788 if (*q == c)
1789 level++; // increase pair levels
1790 if (*q == match)
1791 level--; // reduce pair level
1792 if (level == 0)
1793 break; // found matching pair
1794 }
1795 if (level != 0)
1796 q = NULL; // indicate no match
1797 return (q);
1798}
1799
1800#ifdef CONFIG_FEATURE_VI_SETOPTS
1801// show the matching char of a pair, () [] {}
1802static void showmatching(Byte * p)
1803{
1804 Byte *q, *save_dot;
1805
1806 // we found half of a pair
1807 q = find_pair(p, *p); // get loc of matching char
1808 if (q == NULL) {
1809 indicate_error('3'); // no matching char
1810 } else {
1811 // "q" now points to matching pair
1812 save_dot = dot; // remember where we are
1813 dot = q; // go to new loc
1814 refresh(FALSE); // let the user see it
1815 (void) mysleep(40); // give user some time
1816 dot = save_dot; // go back to old loc
1817 refresh(FALSE);
1818 }
1819}
1820#endif /* CONFIG_FEATURE_VI_SETOPTS */
1821
1822// open a hole in text[]
1823static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
1824{
1825 Byte *src, *dest;
1826 int cnt;
1827
1828 if (size <= 0)
1829 goto thm0;
1830 src = p;
1831 dest = p + size;
1832 cnt = end - src; // the rest of buffer
1833 if (memmove(dest, src, cnt) != dest) {
1834 psbs("can't create room for new characters");
1835 }
1836 memset(p, ' ', size); // clear new hole
1837 end = end + size; // adjust the new END
1838 file_modified = TRUE; // has the file been modified
1839 thm0:
1840 return (p);
1841}
1842
1843// close a hole in text[]
1844static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
1845{
1846 Byte *src, *dest;
1847 int cnt, hole_size;
1848
1849 // move forwards, from beginning
1850 // assume p <= q
1851 src = q + 1;
1852 dest = p;
1853 if (q < p) { // they are backward- swap them
1854 src = p + 1;
1855 dest = q;
1856 }
1857 hole_size = q - p + 1;
1858 cnt = end - src;
1859 if (src < text || src > end)
1860 goto thd0;
1861 if (dest < text || dest >= end)
1862 goto thd0;
1863 if (src >= end)
1864 goto thd_atend; // just delete the end of the buffer
1865 if (memmove(dest, src, cnt) != dest) {
1866 psbs("can't delete the character");
1867 }
1868 thd_atend:
1869 end = end - hole_size; // adjust the new END
1870 if (dest >= end)
1871 dest = end - 1; // make sure dest in below end-1
1872 if (end <= text)
1873 dest = end = text; // keep pointers valid
1874 file_modified = TRUE; // has the file been modified
1875 thd0:
1876 return (dest);
1877}
1878
1879// copy text into register, then delete text.
1880// if dist <= 0, do not include, or go past, a NewLine
1881//
1882static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
1883{
1884 Byte *p;
1885
1886 // make sure start <= stop
1887 if (start > stop) {
1888 // they are backwards, reverse them
1889 p = start;
1890 start = stop;
1891 stop = p;
1892 }
1893 if (dist <= 0) {
1894 // we can not cross NL boundaries
1895 p = start;
1896 if (*p == '\n')
1897 return (p);
1898 // dont go past a NewLine
1899 for (; p + 1 <= stop; p++) {
1900 if (p[1] == '\n') {
1901 stop = p; // "stop" just before NewLine
1902 break;
1903 }
1904 }
1905 }
1906 p = start;
1907#ifdef CONFIG_FEATURE_VI_YANKMARK
1908 text_yank(start, stop, YDreg);
1909#endif /* CONFIG_FEATURE_VI_YANKMARK */
1910 if (yf == YANKDEL) {
1911 p = text_hole_delete(start, stop);
1912 } // delete lines
1913 return (p);
1914}
1915
1916static void show_help(void)
1917{
1918 puts("These features are available:"
1919#ifdef CONFIG_FEATURE_VI_SEARCH
1920 "\n\tPattern searches with / and ?"
1921#endif /* CONFIG_FEATURE_VI_SEARCH */
1922#ifdef CONFIG_FEATURE_VI_DOT_CMD
1923 "\n\tLast command repeat with \'.\'"
1924#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1925#ifdef CONFIG_FEATURE_VI_YANKMARK
1926 "\n\tLine marking with 'x"
1927 "\n\tNamed buffers with \"x"
1928#endif /* CONFIG_FEATURE_VI_YANKMARK */
1929#ifdef CONFIG_FEATURE_VI_READONLY
1930 "\n\tReadonly if vi is called as \"view\""
1931 "\n\tReadonly with -R command line arg"
1932#endif /* CONFIG_FEATURE_VI_READONLY */
1933#ifdef CONFIG_FEATURE_VI_SET
1934 "\n\tSome colon mode commands with \':\'"
1935#endif /* CONFIG_FEATURE_VI_SET */
1936#ifdef CONFIG_FEATURE_VI_SETOPTS
1937 "\n\tSettable options with \":set\""
1938#endif /* CONFIG_FEATURE_VI_SETOPTS */
1939#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
1940 "\n\tSignal catching- ^C"
1941 "\n\tJob suspend and resume with ^Z"
1942#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
1943#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
1944 "\n\tAdapt to window re-sizes"
1945#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
1946 );
1947}
1948
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001949static inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001950{
1951 Byte c, b[2];
1952
1953 b[1] = '\0';
1954 strcpy((char *) buf, ""); // init buf
1955 if (strlen((char *) s) <= 0)
1956 s = (Byte *) "(NULL)";
1957 for (; *s > '\0'; s++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001958 int c_is_no_print;
1959
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001960 c = *s;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001961 c_is_no_print = c > 127 && !Isprint(c);
1962 if (c_is_no_print) {
1963 strcat((char *) buf, SOn);
1964 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001965 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001966 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001967 strcat((char *) buf, "^");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001968 if(c == 127)
1969 c = '?';
1970 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001971 c += '@';
1972 }
1973 b[0] = c;
1974 strcat((char *) buf, (char *) b);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001975 if (c_is_no_print)
1976 strcat((char *) buf, SOs);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001977 if (*s == '\n') {
1978 strcat((char *) buf, "$");
1979 }
1980 }
1981}
1982
1983#ifdef CONFIG_FEATURE_VI_DOT_CMD
1984static void start_new_cmd_q(Byte c)
1985{
1986 // release old cmd
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001987 free(last_modifying_cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001988 // get buffer for new cmd
1989 last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
1990 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
1991 // if there is a current cmd count put it in the buffer first
1992 if (cmdcnt > 0)
1993 sprintf((char *) last_modifying_cmd, "%d", cmdcnt);
1994 // save char c onto queue
1995 last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
1996 adding2q = 1;
1997 return;
1998}
1999
2000static void end_cmd_q(void)
2001{
2002#ifdef CONFIG_FEATURE_VI_YANKMARK
2003 YDreg = 26; // go back to default Yank/Delete reg
2004#endif /* CONFIG_FEATURE_VI_YANKMARK */
2005 adding2q = 0;
2006 return;
2007}
2008#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2009
2010#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
2011static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
2012{
2013 int cnt, i;
2014
2015 i = strlen((char *) s);
2016 p = text_hole_make(p, i);
2017 strncpy((char *) p, (char *) s, i);
2018 for (cnt = 0; *s != '\0'; s++) {
2019 if (*s == '\n')
2020 cnt++;
2021 }
2022#ifdef CONFIG_FEATURE_VI_YANKMARK
2023 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2024#endif /* CONFIG_FEATURE_VI_YANKMARK */
2025 return (p);
2026}
2027#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
2028
2029#ifdef CONFIG_FEATURE_VI_YANKMARK
2030static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register
2031{
2032 Byte *t;
2033 int cnt;
2034
2035 if (q < p) { // they are backwards- reverse them
2036 t = q;
2037 q = p;
2038 p = t;
2039 }
2040 cnt = q - p + 1;
2041 t = reg[dest];
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002042 free(t); // if already a yank register, free it
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002043 t = (Byte *) xmalloc(cnt + 1); // get a new register
2044 memset(t, '\0', cnt + 1); // clear new text[]
2045 strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer
2046 reg[dest] = t;
2047 return (p);
2048}
2049
2050static Byte what_reg(void)
2051{
2052 Byte c;
2053 int i;
2054
2055 i = 0;
2056 c = 'D'; // default to D-reg
2057 if (0 <= YDreg && YDreg <= 25)
2058 c = 'a' + (Byte) YDreg;
2059 if (YDreg == 26)
2060 c = 'D';
2061 if (YDreg == 27)
2062 c = 'U';
2063 return (c);
2064}
2065
2066static void check_context(Byte cmd)
2067{
2068 // A context is defined to be "modifying text"
2069 // Any modifying command establishes a new context.
2070
2071 if (dot < context_start || dot > context_end) {
2072 if (strchr((char *) modifying_cmds, cmd) != NULL) {
2073 // we are trying to modify text[]- make this the current context
2074 mark[27] = mark[26]; // move cur to prev
2075 mark[26] = dot; // move local to cur
2076 context_start = prev_line(prev_line(dot));
2077 context_end = next_line(next_line(dot));
2078 //loiter= start_loiter= now;
2079 }
2080 }
2081 return;
2082}
2083
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002084static inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002085{
2086 Byte *tmp;
2087
2088 // the current context is in mark[26]
2089 // the previous context is in mark[27]
2090 // only swap context if other context is valid
2091 if (text <= mark[27] && mark[27] <= end - 1) {
2092 tmp = mark[27];
2093 mark[27] = mark[26];
2094 mark[26] = tmp;
2095 p = mark[26]; // where we are going- previous context
2096 context_start = prev_line(prev_line(prev_line(p)));
2097 context_end = next_line(next_line(next_line(p)));
2098 }
2099 return (p);
2100}
2101#endif /* CONFIG_FEATURE_VI_YANKMARK */
2102
2103static int isblnk(Byte c) // is the char a blank or tab
2104{
2105 return (c == ' ' || c == '\t');
2106}
2107
2108//----- Set terminal attributes --------------------------------
2109static void rawmode(void)
2110{
2111 tcgetattr(0, &term_orig);
2112 term_vi = term_orig;
2113 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2114 term_vi.c_iflag &= (~IXON & ~ICRNL);
2115 term_vi.c_oflag &= (~ONLCR);
2116#ifndef linux
2117 term_vi.c_cc[VMIN] = 1;
2118 term_vi.c_cc[VTIME] = 0;
2119#endif
2120 erase_char = term_vi.c_cc[VERASE];
2121 tcsetattr(0, TCSANOW, &term_vi);
2122}
2123
2124static void cookmode(void)
2125{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002126 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002127 tcsetattr(0, TCSANOW, &term_orig);
2128}
2129
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002130//----- Come here when we get a window resize signal ---------
2131#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
2132static void winch_sig(int sig)
2133{
2134 signal(SIGWINCH, winch_sig);
2135#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2136 window_size_get(0);
2137#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2138 new_screen(rows, columns); // get memory for virtual screen
2139 redraw(TRUE); // re-draw the screen
2140}
2141
2142//----- Come here when we get a continue signal -------------------
2143static void cont_sig(int sig)
2144{
2145 rawmode(); // terminal to "raw"
2146 *status_buffer = '\0'; // clear the status buffer
2147 redraw(TRUE); // re-draw the screen
2148
2149 signal(SIGTSTP, suspend_sig);
2150 signal(SIGCONT, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002151 kill(my_pid, SIGCONT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002152}
2153
2154//----- Come here when we get a Suspend signal -------------------
2155static void suspend_sig(int sig)
2156{
2157 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2158 clear_to_eol(); // Erase to end of line
2159 cookmode(); // terminal to "cooked"
2160
2161 signal(SIGCONT, cont_sig);
2162 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002163 kill(my_pid, SIGTSTP);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002164}
2165
2166//----- Come here when we get a signal ---------------------------
2167static void catch_sig(int sig)
2168{
2169 signal(SIGHUP, catch_sig);
2170 signal(SIGINT, catch_sig);
2171 signal(SIGTERM, catch_sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002172 signal(SIGALRM, catch_sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002173 if(sig)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002174 longjmp(restart, sig);
2175}
2176
2177//----- Come here when we get a core dump signal -----------------
2178static void core_sig(int sig)
2179{
2180 signal(SIGQUIT, core_sig);
2181 signal(SIGILL, core_sig);
2182 signal(SIGTRAP, core_sig);
2183 signal(SIGIOT, core_sig);
2184 signal(SIGABRT, core_sig);
2185 signal(SIGFPE, core_sig);
2186 signal(SIGBUS, core_sig);
2187 signal(SIGSEGV, core_sig);
2188#ifdef SIGSYS
2189 signal(SIGSYS, core_sig);
2190#endif
2191
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002192 if(sig) { // signaled
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002193 dot = bound_dot(dot); // make sure "dot" is valid
2194
2195 longjmp(restart, sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002196 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002197}
2198#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2199
2200static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2201{
2202 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002203 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002204 FD_ZERO(&rfds);
2205 FD_SET(0, &rfds);
2206 tv.tv_sec = 0;
2207 tv.tv_usec = hund * 10000;
2208 select(1, &rfds, NULL, NULL, &tv);
2209 return (FD_ISSET(0, &rfds));
2210}
2211
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002212static Byte readbuffer[BUFSIZ];
2213static int readed_for_parse;
2214
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002215//----- IO Routines --------------------------------------------
2216static Byte readit(void) // read (maybe cursor) key from stdin
2217{
2218 Byte c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002219 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002220 struct esc_cmds {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002221 const char *seq;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002222 Byte val;
2223 };
2224
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002225 static const struct esc_cmds esccmds[] = {
2226 {"OA", (Byte) VI_K_UP}, // cursor key Up
2227 {"OB", (Byte) VI_K_DOWN}, // cursor key Down
2228 {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2229 {"OD", (Byte) VI_K_LEFT}, // cursor key Left
2230 {"OH", (Byte) VI_K_HOME}, // Cursor Key Home
2231 {"OF", (Byte) VI_K_END}, // Cursor Key End
2232 {"[A", (Byte) VI_K_UP}, // cursor key Up
2233 {"[B", (Byte) VI_K_DOWN}, // cursor key Down
2234 {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right
2235 {"[D", (Byte) VI_K_LEFT}, // cursor key Left
2236 {"[H", (Byte) VI_K_HOME}, // Cursor Key Home
2237 {"[F", (Byte) VI_K_END}, // Cursor Key End
2238 {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home
2239 {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2240 {"[4~", (Byte) VI_K_END}, // Cursor Key End
2241 {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2242 {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2243 {"OP", (Byte) VI_K_FUN1}, // Function Key F1
2244 {"OQ", (Byte) VI_K_FUN2}, // Function Key F2
2245 {"OR", (Byte) VI_K_FUN3}, // Function Key F3
2246 {"OS", (Byte) VI_K_FUN4}, // Function Key F4
2247 {"[15~", (Byte) VI_K_FUN5}, // Function Key F5
2248 {"[17~", (Byte) VI_K_FUN6}, // Function Key F6
2249 {"[18~", (Byte) VI_K_FUN7}, // Function Key F7
2250 {"[19~", (Byte) VI_K_FUN8}, // Function Key F8
2251 {"[20~", (Byte) VI_K_FUN9}, // Function Key F9
2252 {"[21~", (Byte) VI_K_FUN10}, // Function Key F10
2253 {"[23~", (Byte) VI_K_FUN11}, // Function Key F11
2254 {"[24~", (Byte) VI_K_FUN12}, // Function Key F12
2255 {"[11~", (Byte) VI_K_FUN1}, // Function Key F1
2256 {"[12~", (Byte) VI_K_FUN2}, // Function Key F2
2257 {"[13~", (Byte) VI_K_FUN3}, // Function Key F3
2258 {"[14~", (Byte) VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002259 };
2260
2261#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2262
2263 (void) alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002264 fflush(stdout);
2265 n = readed_for_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002266 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002267 if (n <= 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002268 ri0:
2269 // the Q is empty, wait for a typed char
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002270 n = read(0, readbuffer, BUFSIZ - 1);
2271 if (n < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002272 if (errno == EINTR)
2273 goto ri0; // interrupted sys call
2274 if (errno == EBADF)
2275 editing = 0;
2276 if (errno == EFAULT)
2277 editing = 0;
2278 if (errno == EINVAL)
2279 editing = 0;
2280 if (errno == EIO)
2281 editing = 0;
2282 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002283 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002284 if(n <= 0)
2285 return 0; // error
2286 if (readbuffer[0] == 27) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002287 // This is an ESC char. Is this Esc sequence?
2288 // Could be bare Esc key. See if there are any
2289 // more chars to read after the ESC. This would
2290 // be a Function or Cursor Key sequence.
2291 FD_ZERO(&rfds);
2292 FD_SET(0, &rfds);
2293 tv.tv_sec = 0;
2294 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2295
2296 // keep reading while there are input chars and room in buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002297 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002298 // read the rest of the ESC string
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002299 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2300 if (r > 0) {
2301 n += r;
2302 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002303 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002304 }
2305 readed_for_parse = n;
2306 }
2307 c = readbuffer[0];
2308 if(c == 27 && n > 1) {
2309 // Maybe cursor or function key?
2310 const struct esc_cmds *eindex;
2311
2312 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2313 int cnt = strlen(eindex->seq);
2314
2315 if(n <= cnt)
2316 continue;
2317 if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
2318 continue;
2319 // is a Cursor key- put derived value back into Q
2320 c = eindex->val;
2321 // for squeeze out the ESC sequence
2322 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002323 break;
2324 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002325 if(eindex == &esccmds[ESCCMDS_COUNT]) {
2326 /* defined ESC sequence not found, set only one ESC */
2327 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002328 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002329 } else {
2330 n = 1;
2331 }
2332 // remove key sequence from Q
2333 readed_for_parse -= n;
2334 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002335 (void) alarm(3); // we are done waiting for input, turn alarm ON
2336 return (c);
2337}
2338
2339//----- IO Routines --------------------------------------------
2340static Byte get_one_char()
2341{
2342 static Byte c;
2343
2344#ifdef CONFIG_FEATURE_VI_DOT_CMD
2345 // ! adding2q && ioq == 0 read()
2346 // ! adding2q && ioq != 0 *ioq
2347 // adding2q *last_modifying_cmd= read()
2348 if (!adding2q) {
2349 // we are not adding to the q.
2350 // but, we may be reading from a q
2351 if (ioq == 0) {
2352 // there is no current q, read from STDIN
2353 c = readit(); // get the users input
2354 } else {
2355 // there is a queue to get chars from first
2356 c = *ioq++;
2357 if (c == '\0') {
2358 // the end of the q, read from STDIN
2359 free(ioq_start);
2360 ioq_start = ioq = 0;
2361 c = readit(); // get the users input
2362 }
2363 }
2364 } else {
2365 // adding STDIN chars to q
2366 c = readit(); // get the users input
2367 if (last_modifying_cmd != 0) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002368 int len = strlen((char *) last_modifying_cmd);
2369 if (len + 1 >= BUFSIZ) {
2370 psbs("last_modifying_cmd overrun");
2371 } else {
2372 // add new char to q
2373 last_modifying_cmd[len] = c;
2374 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002375 }
2376 }
2377#else /* CONFIG_FEATURE_VI_DOT_CMD */
2378 c = readit(); // get the users input
2379#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2380 return (c); // return the char, where ever it came from
2381}
2382
2383static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2384{
2385 Byte buf[BUFSIZ];
2386 Byte c;
2387 int i;
2388 static Byte *obufp = NULL;
2389
2390 strcpy((char *) buf, (char *) prompt);
2391 *status_buffer = '\0'; // clear the status buffer
2392 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2393 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002394 write1(prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002395
2396 for (i = strlen((char *) buf); i < BUFSIZ;) {
2397 c = get_one_char(); // read user input
2398 if (c == '\n' || c == '\r' || c == 27)
2399 break; // is this end of input
2400 if (c == erase_char) { // user wants to erase prev char
2401 i--; // backup to prev char
2402 buf[i] = '\0'; // erase the char
2403 buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002404 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002405 if (i <= 0) { // user backs up before b-o-l, exit
2406 break;
2407 }
2408 } else {
2409 buf[i] = c; // save char in buffer
2410 buf[i + 1] = '\0'; // make sure buffer is null terminated
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002411 putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002412 i++;
2413 }
2414 }
2415 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002416 free(obufp);
Manuel Novoa III cad53642003-03-19 09:13:01 +00002417 obufp = (Byte *) bb_xstrdup((char *) buf);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002418 return (obufp);
2419}
2420
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002421static int file_size(const Byte * fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002422{
2423 struct stat st_buf;
2424 int cnt, sr;
2425
2426 if (fn == 0 || strlen(fn) <= 0)
2427 return (-1);
2428 cnt = -1;
2429 sr = stat((char *) fn, &st_buf); // see if file exists
2430 if (sr >= 0) {
2431 cnt = (int) st_buf.st_size;
2432 }
2433 return (cnt);
2434}
2435
2436static int file_insert(Byte * fn, Byte * p, int size)
2437{
2438 int fd, cnt;
2439
2440 cnt = -1;
2441#ifdef CONFIG_FEATURE_VI_READONLY
2442 readonly = FALSE;
2443#endif /* CONFIG_FEATURE_VI_READONLY */
2444 if (fn == 0 || strlen((char*) fn) <= 0) {
2445 psbs("No filename given");
2446 goto fi0;
2447 }
2448 if (size == 0) {
2449 // OK- this is just a no-op
2450 cnt = 0;
2451 goto fi0;
2452 }
2453 if (size < 0) {
2454 psbs("Trying to insert a negative number (%d) of characters", size);
2455 goto fi0;
2456 }
2457 if (p < text || p > end) {
2458 psbs("Trying to insert file outside of memory");
2459 goto fi0;
2460 }
2461
2462 // see if we can open the file
2463#ifdef CONFIG_FEATURE_VI_READONLY
2464 if (vi_readonly) goto fi1; // do not try write-mode
2465#endif
2466 fd = open((char *) fn, O_RDWR); // assume read & write
2467 if (fd < 0) {
2468 // could not open for writing- maybe file is read only
2469#ifdef CONFIG_FEATURE_VI_READONLY
2470 fi1:
2471#endif
2472 fd = open((char *) fn, O_RDONLY); // try read-only
2473 if (fd < 0) {
2474 psbs("\"%s\" %s", fn, "could not open file");
2475 goto fi0;
2476 }
2477#ifdef CONFIG_FEATURE_VI_READONLY
2478 // got the file- read-only
2479 readonly = TRUE;
2480#endif /* CONFIG_FEATURE_VI_READONLY */
2481 }
2482 p = text_hole_make(p, size);
2483 cnt = read(fd, p, size);
2484 close(fd);
2485 if (cnt < 0) {
2486 cnt = -1;
2487 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2488 psbs("could not read file \"%s\"", fn);
2489 } else if (cnt < size) {
2490 // There was a partial read, shrink unused space text[]
2491 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
2492 psbs("could not read all of file \"%s\"", fn);
2493 }
2494 if (cnt >= size)
2495 file_modified = TRUE;
2496 fi0:
2497 return (cnt);
2498}
2499
2500static int file_write(Byte * fn, Byte * first, Byte * last)
2501{
2502 int fd, cnt, charcnt;
2503
2504 if (fn == 0) {
2505 psbs("No current filename");
2506 return (-1);
2507 }
2508 charcnt = 0;
2509 // FIXIT- use the correct umask()
2510 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2511 if (fd < 0)
2512 return (-1);
2513 cnt = last - first + 1;
2514 charcnt = write(fd, first, cnt);
2515 if (charcnt == cnt) {
2516 // good write
2517 //file_modified= FALSE; // the file has not been modified
2518 } else {
2519 charcnt = 0;
2520 }
2521 close(fd);
2522 return (charcnt);
2523}
2524
2525//----- Terminal Drawing ---------------------------------------
2526// The terminal is made up of 'rows' line of 'columns' columns.
2527// classicly this would be 24 x 80.
2528// screen coordinates
2529// 0,0 ... 0,79
2530// 1,0 ... 1,79
2531// . ... .
2532// . ... .
2533// 22,0 ... 22,79
2534// 23,0 ... 23,79 status line
2535//
2536
2537//----- Move the cursor to row x col (count from 0, not 1) -------
2538static void place_cursor(int row, int col, int opti)
2539{
2540 char cm1[BUFSIZ];
2541 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002542#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2543 char cm2[BUFSIZ];
2544 Byte *screenp;
2545 // char cm3[BUFSIZ];
2546 int Rrow= last_row;
2547#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2548
2549 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2550
2551 if (row < 0) row = 0;
2552 if (row >= rows) row = rows - 1;
2553 if (col < 0) col = 0;
2554 if (col >= columns) col = columns - 1;
2555
2556 //----- 1. Try the standard terminal ESC sequence
2557 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2558 cm= cm1;
2559 if (! opti) goto pc0;
2560
2561#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2562 //----- find the minimum # of chars to move cursor -------------
2563 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2564 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
2565
2566 // move to the correct row
2567 while (row < Rrow) {
2568 // the cursor has to move up
2569 strcat(cm2, CMup);
2570 Rrow--;
2571 }
2572 while (row > Rrow) {
2573 // the cursor has to move down
2574 strcat(cm2, CMdown);
2575 Rrow++;
2576 }
2577
2578 // now move to the correct column
2579 strcat(cm2, "\r"); // start at col 0
2580 // just send out orignal source char to get to correct place
2581 screenp = &screen[row * columns]; // start of screen line
2582 strncat(cm2, screenp, col);
2583
2584 //----- 3. Try some other way of moving cursor
2585 //---------------------------------------------
2586
2587 // pick the shortest cursor motion to send out
2588 cm= cm1;
2589 if (strlen(cm2) < strlen(cm)) {
2590 cm= cm2;
2591 } /* else if (strlen(cm3) < strlen(cm)) {
2592 cm= cm3;
2593 } */
2594#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2595 pc0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002596 write1(cm); // move the cursor
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002597}
2598
2599//----- Erase from cursor to end of line -----------------------
2600static void clear_to_eol()
2601{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002602 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002603}
2604
2605//----- Erase from cursor to end of screen -----------------------
2606static void clear_to_eos()
2607{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002608 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002609}
2610
2611//----- Start standout mode ------------------------------------
2612static void standout_start() // send "start reverse video" sequence
2613{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002614 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002615}
2616
2617//----- End standout mode --------------------------------------
2618static void standout_end() // send "end reverse video" sequence
2619{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002620 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002621}
2622
2623//----- Flash the screen --------------------------------------
2624static void flash(int h)
2625{
2626 standout_start(); // send "start reverse video" sequence
2627 redraw(TRUE);
2628 (void) mysleep(h);
2629 standout_end(); // send "end reverse video" sequence
2630 redraw(TRUE);
2631}
2632
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002633static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002634{
2635#ifdef CONFIG_FEATURE_VI_CRASHME
2636 if (crashme > 0)
2637 return; // generate a random command
2638#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002639 if (!err_method) {
2640 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002641 } else {
2642 flash(10);
2643 }
2644}
2645
2646//----- Screen[] Routines --------------------------------------
2647//----- Erase the Screen[] memory ------------------------------
2648static void screen_erase()
2649{
2650 memset(screen, ' ', screensize); // clear new screen
2651}
2652
2653//----- Draw the status line at bottom of the screen -------------
2654static void show_status_line(void)
2655{
2656 static int last_cksum;
2657 int l, cnt, cksum;
2658
2659 cnt = strlen((char *) status_buffer);
2660 for (cksum= l= 0; l < cnt; l++) { cksum += (int)(status_buffer[l]); }
2661 // don't write the status line unless it changes
2662 if (cnt > 0 && last_cksum != cksum) {
2663 last_cksum= cksum; // remember if we have seen this line
2664 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002665 write1(status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002666 clear_to_eol();
2667 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2668 }
2669}
2670
2671//----- format the status buffer, the bottom line of screen ------
2672// print status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002673static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002674{
2675 va_list args;
2676
2677 va_start(args, format);
2678 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
2679 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format,
2680 args);
2681 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2682 va_end(args);
2683
2684 return;
2685}
2686
2687// print status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002688static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002689{
2690 va_list args;
2691
2692 va_start(args, format);
2693 vsprintf((char *) status_buffer, format, args);
2694 va_end(args);
2695 return;
2696}
2697
2698static void ni(Byte * s) // display messages
2699{
2700 Byte buf[BUFSIZ];
2701
2702 print_literal(buf, s);
2703 psbs("\'%s\' is not implemented", buf);
2704}
2705
2706static void edit_status(void) // show file status on status line
2707{
2708 int cur, tot, percent;
2709
2710 cur = count_lines(text, dot);
2711 tot = count_lines(text, end - 1);
2712 // current line percent
2713 // ------------- ~~ ----------
2714 // total lines 100
2715 if (tot > 0) {
2716 percent = (100 * cur) / tot;
2717 } else {
2718 cur = tot = 0;
2719 percent = 100;
2720 }
2721 psb("\"%s\""
2722#ifdef CONFIG_FEATURE_VI_READONLY
2723 "%s"
2724#endif /* CONFIG_FEATURE_VI_READONLY */
2725 "%s line %d of %d --%d%%--",
2726 (cfn != 0 ? (char *) cfn : "No file"),
2727#ifdef CONFIG_FEATURE_VI_READONLY
2728 ((vi_readonly || readonly) ? " [Read only]" : ""),
2729#endif /* CONFIG_FEATURE_VI_READONLY */
2730 (file_modified ? " [modified]" : ""),
2731 cur, tot, percent);
2732}
2733
2734//----- Force refresh of all Lines -----------------------------
2735static void redraw(int full_screen)
2736{
2737 place_cursor(0, 0, FALSE); // put cursor in correct place
2738 clear_to_eos(); // tel terminal to erase display
2739 screen_erase(); // erase the internal screen buffer
2740 refresh(full_screen); // this will redraw the entire display
2741}
2742
2743//----- Format a text[] line into a buffer ---------------------
2744static void format_line(Byte *dest, Byte *src, int li)
2745{
2746 int co;
2747 Byte c;
2748
2749 for (co= 0; co < MAX_SCR_COLS; co++) {
2750 c= ' '; // assume blank
2751 if (li > 0 && co == 0) {
2752 c = '~'; // not first line, assume Tilde
2753 }
2754 // are there chars in text[] and have we gone past the end
2755 if (text < end && src < end) {
2756 c = *src++;
2757 }
2758 if (c == '\n')
2759 break;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002760 if (c > 127 && !Isprint(c)) {
2761 c = '.';
2762 }
2763 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002764 if (c == '\t') {
2765 c = ' ';
2766 // co % 8 != 7
2767 for (; (co % tabstop) != (tabstop - 1); co++) {
2768 dest[co] = c;
2769 }
2770 } else {
2771 dest[co++] = '^';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002772 if(c == 127)
2773 c = '?';
2774 else
2775 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002776 }
2777 }
2778 // the co++ is done here so that the column will
2779 // not be overwritten when we blank-out the rest of line
2780 dest[co] = c;
2781 if (src >= end)
2782 break;
2783 }
2784}
2785
2786//----- Refresh the changed screen lines -----------------------
2787// Copy the source line from text[] into the buffer and note
2788// if the current screenline is different from the new buffer.
2789// If they differ then that line needs redrawing on the terminal.
2790//
2791static void refresh(int full_screen)
2792{
2793 static int old_offset;
2794 int li, changed;
2795 Byte buf[MAX_SCR_COLS];
2796 Byte *tp, *sp; // pointer into text[] and screen[]
2797#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2798 int last_li= -2; // last line that changed- for optimizing cursor movement
2799#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2800
2801#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2802 window_size_get(0);
2803#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2804 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2805 tp = screenbegin; // index into text[] of top line
2806
2807 // compare text[] to screen[] and mark screen[] lines that need updating
2808 for (li = 0; li < rows - 1; li++) {
2809 int cs, ce; // column start & end
2810 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2811 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2812 // format current text line into buf
2813 format_line(buf, tp, li);
2814
2815 // skip to the end of the current text[] line
2816 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2817
2818 // see if there are any changes between vitual screen and buf
2819 changed = FALSE; // assume no change
2820 cs= 0;
2821 ce= columns-1;
2822 sp = &screen[li * columns]; // start of screen line
2823 if (full_screen) {
2824 // force re-draw of every single column from 0 - columns-1
2825 goto re0;
2826 }
2827 // compare newly formatted buffer with virtual screen
2828 // look forward for first difference between buf and screen
2829 for ( ; cs <= ce; cs++) {
2830 if (buf[cs + offset] != sp[cs]) {
2831 changed = TRUE; // mark for redraw
2832 break;
2833 }
2834 }
2835
2836 // look backward for last difference between buf and screen
2837 for ( ; ce >= cs; ce--) {
2838 if (buf[ce + offset] != sp[ce]) {
2839 changed = TRUE; // mark for redraw
2840 break;
2841 }
2842 }
2843 // now, cs is index of first diff, and ce is index of last diff
2844
2845 // if horz offset has changed, force a redraw
2846 if (offset != old_offset) {
2847 re0:
2848 changed = TRUE;
2849 }
2850
2851 // make a sanity check of columns indexes
2852 if (cs < 0) cs= 0;
2853 if (ce > columns-1) ce= columns-1;
2854 if (cs > ce) { cs= 0; ce= columns-1; }
2855 // is there a change between vitual screen and buf
2856 if (changed) {
2857 // copy changed part of buffer to virtual screen
2858 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2859
2860 // move cursor to column of first change
2861 if (offset != old_offset) {
2862 // opti_cur_move is still too stupid
2863 // to handle offsets correctly
2864 place_cursor(li, cs, FALSE);
2865 } else {
2866#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2867 // if this just the next line
2868 // try to optimize cursor movement
2869 // otherwise, use standard ESC sequence
2870 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2871 last_li= li;
2872#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2873 place_cursor(li, cs, FALSE); // use standard ESC sequence
2874#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2875 }
2876
2877 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002878 {
2879 int nic = ce-cs+1;
2880 char *out = sp+cs;
2881
2882 while(nic-- > 0) {
2883 putchar(*out);
2884 out++;
2885 }
2886 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002887#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2888 last_row = li;
2889#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2890 }
2891 }
2892
2893#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2894 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2895 last_row = crow;
2896#else
2897 place_cursor(crow, ccol, FALSE);
2898#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2899
2900 if (offset != old_offset)
2901 old_offset = offset;
2902}
2903
Eric Andersen3f980402001-04-04 17:31:15 +00002904//---------------------------------------------------------------------
2905//----- the Ascii Chart -----------------------------------------------
2906//
2907// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2908// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2909// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2910// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2911// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2912// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2913// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2914// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2915// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2916// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2917// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2918// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2919// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2920// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2921// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2922// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2923//---------------------------------------------------------------------
2924
2925//----- Execute a Vi Command -----------------------------------
2926static void do_cmd(Byte c)
2927{
2928 Byte c1, *p, *q, *msg, buf[9], *save_dot;
2929 int cnt, i, j, dir, yf;
2930
2931 c1 = c; // quiet the compiler
2932 cnt = yf = dir = 0; // quiet the compiler
2933 p = q = save_dot = msg = buf; // quiet the compiler
2934 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00002935
2936 /* if this is a cursor key, skip these checks */
2937 switch (c) {
2938 case VI_K_UP:
2939 case VI_K_DOWN:
2940 case VI_K_LEFT:
2941 case VI_K_RIGHT:
2942 case VI_K_HOME:
2943 case VI_K_END:
2944 case VI_K_PAGEUP:
2945 case VI_K_PAGEDOWN:
2946 goto key_cmd_mode;
2947 }
2948
Eric Andersen3f980402001-04-04 17:31:15 +00002949 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002950 // flip-flop Insert/Replace mode
2951 if (c == VI_K_INSERT) goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00002952 // we are 'R'eplacing the current *dot with new char
2953 if (*dot == '\n') {
2954 // don't Replace past E-o-l
2955 cmd_mode = 1; // convert to insert
2956 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002957 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00002958 if (c != 27)
2959 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
2960 dot = char_insert(dot, c); // insert new char
2961 }
2962 goto dc1;
2963 }
2964 }
2965 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00002966 // hitting "Insert" twice means "R" replace mode
2967 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00002968 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002969 if (1 <= c || Isprint(c)) {
2970 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00002971 }
2972 goto dc1;
2973 }
2974
Eric Andersenbff7a602001-11-17 07:15:43 +00002975key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00002976 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00002977 //case 0x01: // soh
2978 //case 0x09: // ht
2979 //case 0x0b: // vt
2980 //case 0x0e: // so
2981 //case 0x0f: // si
2982 //case 0x10: // dle
2983 //case 0x11: // dc1
2984 //case 0x13: // dc3
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002985#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00002986 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00002987 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00002988 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002989#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen822c3832001-05-07 17:37:43 +00002990 //case 0x16: // syn
2991 //case 0x17: // etb
2992 //case 0x18: // can
2993 //case 0x1c: // fs
2994 //case 0x1d: // gs
2995 //case 0x1e: // rs
2996 //case 0x1f: // us
2997 //case '!': // !-
2998 //case '#': // #-
2999 //case '&': // &-
3000 //case '(': // (-
3001 //case ')': // )-
3002 //case '*': // *-
3003 //case ',': // ,-
3004 //case '=': // =-
3005 //case '@': // @-
3006 //case 'F': // F-
3007 //case 'K': // K-
3008 //case 'Q': // Q-
3009 //case 'S': // S-
3010 //case 'T': // T-
3011 //case 'V': // V-
3012 //case '[': // [-
3013 //case '\\': // \-
3014 //case ']': // ]-
3015 //case '_': // _-
3016 //case '`': // `-
3017 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003018 //case 'u': // u- FIXME- there is no undo
Eric Andersen822c3832001-05-07 17:37:43 +00003019 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00003020 default: // unrecognised command
3021 buf[0] = c;
3022 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003023 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00003024 buf[0] = '^';
3025 buf[1] = c + '@';
3026 buf[2] = '\0';
3027 }
3028 ni((Byte *) buf);
3029 end_cmd_q(); // stop adding to q
3030 case 0x00: // nul- ignore
3031 break;
3032 case 2: // ctrl-B scroll up full screen
3033 case VI_K_PAGEUP: // Cursor Key Page Up
3034 dot_scroll(rows - 2, -1);
3035 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003036#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003037 case 0x03: // ctrl-C interrupt
3038 longjmp(restart, 1);
3039 break;
3040 case 26: // ctrl-Z suspend
3041 suspend_sig(SIGTSTP);
3042 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003043#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +00003044 case 4: // ctrl-D scroll down half screen
3045 dot_scroll((rows - 2) / 2, 1);
3046 break;
3047 case 5: // ctrl-E scroll down one line
3048 dot_scroll(1, 1);
3049 break;
3050 case 6: // ctrl-F scroll down full screen
3051 case VI_K_PAGEDOWN: // Cursor Key Page Down
3052 dot_scroll(rows - 2, 1);
3053 break;
3054 case 7: // ctrl-G show current status
3055 edit_status();
3056 break;
3057 case 'h': // h- move left
3058 case VI_K_LEFT: // cursor key Left
Eric Andersen1c0d3112001-04-16 15:46:44 +00003059 case 8: // ctrl-H- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003060 case 127: // DEL- move left (This may be ERASE char)
3061 if (cmdcnt-- > 1) {
3062 do_cmd(c);
3063 } // repeat cnt
3064 dot_left();
3065 break;
3066 case 10: // Newline ^J
3067 case 'j': // j- goto next line, same col
3068 case VI_K_DOWN: // cursor key Down
3069 if (cmdcnt-- > 1) {
3070 do_cmd(c);
3071 } // repeat cnt
3072 dot_next(); // go to next B-o-l
3073 dot = move_to_col(dot, ccol + offset); // try stay in same col
3074 break;
3075 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003076 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003077 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003078 clear_to_eos(); // tel terminal to erase display
3079 (void) mysleep(10);
3080 screen_erase(); // erase the internal screen buffer
3081 refresh(TRUE); // this will redraw the entire display
3082 break;
3083 case 13: // Carriage Return ^M
3084 case '+': // +- goto next line
3085 if (cmdcnt-- > 1) {
3086 do_cmd(c);
3087 } // repeat cnt
3088 dot_next();
3089 dot_skip_over_ws();
3090 break;
3091 case 21: // ctrl-U scroll up half screen
3092 dot_scroll((rows - 2) / 2, -1);
3093 break;
3094 case 25: // ctrl-Y scroll up one line
3095 dot_scroll(1, -1);
3096 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003097 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003098 if (cmd_mode == 0)
3099 indicate_error(c);
3100 cmd_mode = 0; // stop insrting
3101 end_cmd_q();
3102 *status_buffer = '\0'; // clear status buffer
3103 break;
3104 case ' ': // move right
3105 case 'l': // move right
3106 case VI_K_RIGHT: // Cursor Key Right
3107 if (cmdcnt-- > 1) {
3108 do_cmd(c);
3109 } // repeat cnt
3110 dot_right();
3111 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003112#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003113 case '"': // "- name a register to use for Delete/Yank
3114 c1 = get_one_char();
3115 c1 = tolower(c1);
3116 if (islower(c1)) {
3117 YDreg = c1 - 'a';
3118 } else {
3119 indicate_error(c);
3120 }
3121 break;
3122 case '\'': // '- goto a specific mark
3123 c1 = get_one_char();
3124 c1 = tolower(c1);
3125 if (islower(c1)) {
3126 c1 = c1 - 'a';
3127 // get the b-o-l
3128 q = mark[(int) c1];
3129 if (text <= q && q < end) {
3130 dot = q;
3131 dot_begin(); // go to B-o-l
3132 dot_skip_over_ws();
3133 }
3134 } else if (c1 == '\'') { // goto previous context
3135 dot = swap_context(dot); // swap current and previous context
3136 dot_begin(); // go to B-o-l
3137 dot_skip_over_ws();
3138 } else {
3139 indicate_error(c);
3140 }
3141 break;
3142 case 'm': // m- Mark a line
3143 // this is really stupid. If there are any inserts or deletes
3144 // between text[0] and dot then this mark will not point to the
3145 // correct location! It could be off by many lines!
3146 // Well..., at least its quick and dirty.
3147 c1 = get_one_char();
3148 c1 = tolower(c1);
3149 if (islower(c1)) {
3150 c1 = c1 - 'a';
3151 // remember the line
3152 mark[(int) c1] = dot;
3153 } else {
3154 indicate_error(c);
3155 }
3156 break;
3157 case 'P': // P- Put register before
3158 case 'p': // p- put register after
3159 p = reg[YDreg];
3160 if (p == 0) {
3161 psbs("Nothing in register %c", what_reg());
3162 break;
3163 }
3164 // are we putting whole lines or strings
3165 if (strchr((char *) p, '\n') != NULL) {
3166 if (c == 'P') {
3167 dot_begin(); // putting lines- Put above
3168 }
3169 if (c == 'p') {
3170 // are we putting after very last line?
3171 if (end_line(dot) == (end - 1)) {
3172 dot = end; // force dot to end of text[]
3173 } else {
3174 dot_next(); // next line, then put before
3175 }
3176 }
3177 } else {
3178 if (c == 'p')
3179 dot_right(); // move to right, can move to NL
3180 }
3181 dot = string_insert(dot, p); // insert the string
3182 end_cmd_q(); // stop adding to q
3183 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003184 case 'U': // U- Undo; replace current line with original version
3185 if (reg[Ureg] != 0) {
3186 p = begin_line(dot);
3187 q = end_line(dot);
3188 p = text_hole_delete(p, q); // delete cur line
3189 p = string_insert(p, reg[Ureg]); // insert orig line
3190 dot = p;
3191 dot_skip_over_ws();
3192 }
3193 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003194#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003195 case '$': // $- goto end of line
3196 case VI_K_END: // Cursor Key End
3197 if (cmdcnt-- > 1) {
3198 do_cmd(c);
3199 } // repeat cnt
3200 dot = end_line(dot + 1);
3201 break;
3202 case '%': // %- find matching char of pair () [] {}
3203 for (q = dot; q < end && *q != '\n'; q++) {
3204 if (strchr("()[]{}", *q) != NULL) {
3205 // we found half of a pair
3206 p = find_pair(q, *q);
3207 if (p == NULL) {
3208 indicate_error(c);
3209 } else {
3210 dot = p;
3211 }
3212 break;
3213 }
3214 }
3215 if (*q == '\n')
3216 indicate_error(c);
3217 break;
3218 case 'f': // f- forward to a user specified char
3219 last_forward_char = get_one_char(); // get the search char
3220 //
3221 // dont seperate these two commands. 'f' depends on ';'
3222 //
3223 //**** fall thru to ... 'i'
3224 case ';': // ;- look at rest of line for last forward char
3225 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003226 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003227 } // repeat cnt
Eric Andersen822c3832001-05-07 17:37:43 +00003228 if (last_forward_char == 0) break;
Eric Andersen3f980402001-04-04 17:31:15 +00003229 q = dot + 1;
3230 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3231 q++;
3232 }
3233 if (*q == last_forward_char)
3234 dot = q;
3235 break;
3236 case '-': // -- goto prev line
3237 if (cmdcnt-- > 1) {
3238 do_cmd(c);
3239 } // repeat cnt
3240 dot_prev();
3241 dot_skip_over_ws();
3242 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003243#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003244 case '.': // .- repeat the last modifying command
3245 // Stuff the last_modifying_cmd back into stdin
3246 // and let it be re-executed.
3247 if (last_modifying_cmd != 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +00003248 ioq = ioq_start = (Byte *) bb_xstrdup((char *) last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003249 }
3250 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003251#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3252#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003253 case '?': // /- search for a pattern
3254 case '/': // /- search for a pattern
3255 buf[0] = c;
3256 buf[1] = '\0';
3257 q = get_input_line(buf); // get input line- use "status line"
3258 if (strlen((char *) q) == 1)
3259 goto dc3; // if no pat re-use old pat
3260 if (strlen((char *) q) > 1) { // new pat- save it and find
3261 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003262 free(last_search_pattern);
Manuel Novoa III cad53642003-03-19 09:13:01 +00003263 last_search_pattern = (Byte *) bb_xstrdup((char *) q);
Eric Andersen3f980402001-04-04 17:31:15 +00003264 goto dc3; // now find the pattern
3265 }
3266 // user changed mind and erased the "/"- do nothing
3267 break;
3268 case 'N': // N- backward search for last pattern
3269 if (cmdcnt-- > 1) {
3270 do_cmd(c);
3271 } // repeat cnt
3272 dir = BACK; // assume BACKWARD search
3273 p = dot - 1;
3274 if (last_search_pattern[0] == '?') {
3275 dir = FORWARD;
3276 p = dot + 1;
3277 }
3278 goto dc4; // now search for pattern
3279 break;
3280 case 'n': // n- repeat search for last pattern
3281 // search rest of text[] starting at next char
3282 // if search fails return orignal "p" not the "p+1" address
3283 if (cmdcnt-- > 1) {
3284 do_cmd(c);
3285 } // repeat cnt
3286 dc3:
3287 if (last_search_pattern == 0) {
3288 msg = (Byte *) "No previous regular expression";
3289 goto dc2;
3290 }
3291 if (last_search_pattern[0] == '/') {
3292 dir = FORWARD; // assume FORWARD search
3293 p = dot + 1;
3294 }
3295 if (last_search_pattern[0] == '?') {
3296 dir = BACK;
3297 p = dot - 1;
3298 }
3299 dc4:
3300 q = char_search(p, last_search_pattern + 1, dir, FULL);
3301 if (q != NULL) {
3302 dot = q; // good search, update "dot"
3303 msg = (Byte *) "";
3304 goto dc2;
3305 }
3306 // no pattern found between "dot" and "end"- continue at top
3307 p = text;
3308 if (dir == BACK) {
3309 p = end - 1;
3310 }
3311 q = char_search(p, last_search_pattern + 1, dir, FULL);
3312 if (q != NULL) { // found something
3313 dot = q; // found new pattern- goto it
3314 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3315 if (dir == BACK) {
3316 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3317 }
3318 } else {
3319 msg = (Byte *) "Pattern not found";
3320 }
3321 dc2:
3322 psbs("%s", msg);
3323 break;
3324 case '{': // {- move backward paragraph
3325 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3326 if (q != NULL) { // found blank line
3327 dot = next_line(q); // move to next blank line
3328 }
3329 break;
3330 case '}': // }- move forward paragraph
3331 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3332 if (q != NULL) { // found blank line
3333 dot = next_line(q); // move to next blank line
3334 }
3335 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003336#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003337 case '0': // 0- goto begining of line
3338 case '1': // 1-
3339 case '2': // 2-
3340 case '3': // 3-
3341 case '4': // 4-
3342 case '5': // 5-
3343 case '6': // 6-
3344 case '7': // 7-
3345 case '8': // 8-
3346 case '9': // 9-
3347 if (c == '0' && cmdcnt < 1) {
3348 dot_begin(); // this was a standalone zero
3349 } else {
3350 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3351 }
3352 break;
3353 case ':': // :- the colon mode commands
Eric Andersen3f980402001-04-04 17:31:15 +00003354 p = get_input_line((Byte *) ":"); // get input line- use "status line"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003355#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003356 colon(p); // execute the command
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003357#else /* CONFIG_FEATURE_VI_COLON */
Eric Andersen822c3832001-05-07 17:37:43 +00003358 if (*p == ':')
3359 p++; // move past the ':'
3360 cnt = strlen((char *) p);
3361 if (cnt <= 0)
3362 break;
3363 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3364 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
Matt Kraai1f0c4362001-12-20 23:13:26 +00003365 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003366 psbs("No write since last change (:quit! overrides)");
3367 } else {
3368 editing = 0;
3369 }
Eric Andersen822c3832001-05-07 17:37:43 +00003370 } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
Robert Griebla71389b2002-07-31 21:22:21 +00003371 strncasecmp((char *) p, "wq", cnt) == 0 ||
3372 strncasecmp((char *) p, "x", cnt) == 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003373 cnt = file_write(cfn, text, end - 1);
3374 file_modified = FALSE;
3375 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
Robert Griebla71389b2002-07-31 21:22:21 +00003376 if (p[0] == 'x' || p[1] == 'q') {
Eric Andersen3f980402001-04-04 17:31:15 +00003377 editing = 0;
3378 }
Eric Andersen822c3832001-05-07 17:37:43 +00003379 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
3380 edit_status(); // show current file status
3381 } else if (sscanf((char *) p, "%d", &j) > 0) {
3382 dot = find_line(j); // go to line # j
3383 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003384 } else { // unrecognised cmd
Eric Andersen822c3832001-05-07 17:37:43 +00003385 ni((Byte *) p);
Eric Andersen3f980402001-04-04 17:31:15 +00003386 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003387#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003388 break;
3389 case '<': // <- Left shift something
3390 case '>': // >- Right shift something
3391 cnt = count_lines(text, dot); // remember what line we are on
3392 c1 = get_one_char(); // get the type of thing to delete
3393 find_range(&p, &q, c1);
3394 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3395 p = begin_line(p);
3396 q = end_line(q);
3397 i = count_lines(p, q); // # of lines we are shifting
3398 for ( ; i > 0; i--, p = next_line(p)) {
3399 if (c == '<') {
3400 // shift left- remove tab or 8 spaces
3401 if (*p == '\t') {
3402 // shrink buffer 1 char
3403 (void) text_hole_delete(p, p);
3404 } else if (*p == ' ') {
3405 // we should be calculating columns, not just SPACE
3406 for (j = 0; *p == ' ' && j < tabstop; j++) {
3407 (void) text_hole_delete(p, p);
3408 }
3409 }
3410 } else if (c == '>') {
3411 // shift right -- add tab or 8 spaces
3412 (void) char_insert(p, '\t');
3413 }
3414 }
3415 dot = find_line(cnt); // what line were we on
3416 dot_skip_over_ws();
3417 end_cmd_q(); // stop adding to q
3418 break;
3419 case 'A': // A- append at e-o-l
3420 dot_end(); // go to e-o-l
3421 //**** fall thru to ... 'a'
3422 case 'a': // a- append after current char
3423 if (*dot != '\n')
3424 dot++;
3425 goto dc_i;
3426 break;
3427 case 'B': // B- back a blank-delimited Word
3428 case 'E': // E- end of a blank-delimited word
3429 case 'W': // W- forward a blank-delimited word
3430 if (cmdcnt-- > 1) {
3431 do_cmd(c);
3432 } // repeat cnt
3433 dir = FORWARD;
3434 if (c == 'B')
3435 dir = BACK;
3436 if (c == 'W' || isspace(dot[dir])) {
3437 dot = skip_thing(dot, 1, dir, S_TO_WS);
3438 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3439 }
3440 if (c != 'W')
3441 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3442 break;
3443 case 'C': // C- Change to e-o-l
3444 case 'D': // D- delete to e-o-l
3445 save_dot = dot;
3446 dot = dollar_line(dot); // move to before NL
3447 // copy text into a register and delete
3448 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3449 if (c == 'C')
3450 goto dc_i; // start inserting
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003451#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003452 if (c == 'D')
3453 end_cmd_q(); // stop adding to q
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003454#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003455 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003456 case 'G': // G- goto to a line number (default= E-O-F)
3457 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003458 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003459 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003460 }
3461 dot_skip_over_ws();
3462 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003463 case 'H': // H- goto top line on screen
3464 dot = screenbegin;
3465 if (cmdcnt > (rows - 1)) {
3466 cmdcnt = (rows - 1);
3467 }
3468 if (cmdcnt-- > 1) {
3469 do_cmd('+');
3470 } // repeat cnt
3471 dot_skip_over_ws();
3472 break;
3473 case 'I': // I- insert before first non-blank
3474 dot_begin(); // 0
3475 dot_skip_over_ws();
3476 //**** fall thru to ... 'i'
3477 case 'i': // i- insert before current char
3478 case VI_K_INSERT: // Cursor Key Insert
3479 dc_i:
3480 cmd_mode = 1; // start insrting
3481 psb("-- Insert --");
3482 break;
3483 case 'J': // J- join current and next lines together
3484 if (cmdcnt-- > 2) {
3485 do_cmd(c);
3486 } // repeat cnt
3487 dot_end(); // move to NL
3488 if (dot < end - 1) { // make sure not last char in text[]
3489 *dot++ = ' '; // replace NL with space
3490 while (isblnk(*dot)) { // delete leading WS
3491 dot_delete();
3492 }
3493 }
3494 end_cmd_q(); // stop adding to q
3495 break;
3496 case 'L': // L- goto bottom line on screen
3497 dot = end_screen();
3498 if (cmdcnt > (rows - 1)) {
3499 cmdcnt = (rows - 1);
3500 }
3501 if (cmdcnt-- > 1) {
3502 do_cmd('-');
3503 } // repeat cnt
3504 dot_begin();
3505 dot_skip_over_ws();
3506 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003507 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003508 dot = screenbegin;
3509 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3510 dot = next_line(dot);
3511 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003512 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003513 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003514 p = begin_line(dot);
3515 if (p[-1] == '\n') {
3516 dot_prev();
3517 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3518 dot_end();
3519 dot = char_insert(dot, '\n');
3520 } else {
3521 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003522 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003523 dot_prev(); // -
3524 }
3525 goto dc_i;
3526 break;
3527 case 'R': // R- continuous Replace char
Eric Andersen1c0d3112001-04-16 15:46:44 +00003528 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003529 cmd_mode = 2;
3530 psb("-- Replace --");
3531 break;
3532 case 'X': // X- delete char before dot
3533 case 'x': // x- delete the current char
3534 case 's': // s- substitute the current char
3535 if (cmdcnt-- > 1) {
3536 do_cmd(c);
3537 } // repeat cnt
3538 dir = 0;
3539 if (c == 'X')
3540 dir = -1;
3541 if (dot[dir] != '\n') {
3542 if (c == 'X')
3543 dot--; // delete prev char
3544 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3545 }
3546 if (c == 's')
3547 goto dc_i; // start insrting
3548 end_cmd_q(); // stop adding to q
3549 break;
3550 case 'Z': // Z- if modified, {write}; exit
3551 // ZZ means to save file (if necessary), then exit
3552 c1 = get_one_char();
3553 if (c1 != 'Z') {
3554 indicate_error(c);
3555 break;
3556 }
Matt Kraai1f0c4362001-12-20 23:13:26 +00003557 if (file_modified
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003558#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00003559 && ! vi_readonly
3560 && ! readonly
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003561#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00003562 ) {
3563 cnt = file_write(cfn, text, end - 1);
3564 if (cnt == (end - 1 - text + 1)) {
3565 editing = 0;
3566 }
3567 } else {
3568 editing = 0;
3569 }
3570 break;
3571 case '^': // ^- move to first non-blank on line
3572 dot_begin();
3573 dot_skip_over_ws();
3574 break;
3575 case 'b': // b- back a word
3576 case 'e': // e- end of word
3577 if (cmdcnt-- > 1) {
3578 do_cmd(c);
3579 } // repeat cnt
3580 dir = FORWARD;
3581 if (c == 'b')
3582 dir = BACK;
3583 if ((dot + dir) < text || (dot + dir) > end - 1)
3584 break;
3585 dot += dir;
3586 if (isspace(*dot)) {
3587 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3588 }
3589 if (isalnum(*dot) || *dot == '_') {
3590 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3591 } else if (ispunct(*dot)) {
3592 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3593 }
3594 break;
3595 case 'c': // c- change something
3596 case 'd': // d- delete something
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003597#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003598 case 'y': // y- yank something
3599 case 'Y': // Y- Yank a line
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003600#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003601 yf = YANKDEL; // assume either "c" or "d"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003602#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003603 if (c == 'y' || c == 'Y')
3604 yf = YANKONLY;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003605#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003606 c1 = 'y';
3607 if (c != 'Y')
3608 c1 = get_one_char(); // get the type of thing to delete
3609 find_range(&p, &q, c1);
3610 if (c1 == 27) { // ESC- user changed mind and wants out
3611 c = c1 = 27; // Escape- do nothing
3612 } else if (strchr("wW", c1)) {
3613 if (c == 'c') {
3614 // don't include trailing WS as part of word
3615 while (isblnk(*q)) {
3616 if (q <= text || q[-1] == '\n')
3617 break;
3618 q--;
3619 }
3620 }
3621 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003622 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003623 // single line copy text into a register and delete
3624 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003625 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003626 // multiple line copy text into a register and delete
3627 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003628 if (c == 'c') {
3629 dot = char_insert(dot, '\n');
3630 // on the last line of file don't move to prev line
3631 if (dot != (end-1)) {
3632 dot_prev();
3633 }
3634 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003635 dot_begin();
3636 dot_skip_over_ws();
3637 }
3638 } else {
3639 // could not recognize object
3640 c = c1 = 27; // error-
3641 indicate_error(c);
3642 }
3643 if (c1 != 27) {
3644 // if CHANGING, not deleting, start inserting after the delete
3645 if (c == 'c') {
3646 strcpy((char *) buf, "Change");
3647 goto dc_i; // start inserting
3648 }
3649 if (c == 'd') {
3650 strcpy((char *) buf, "Delete");
3651 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003652#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003653 if (c == 'y' || c == 'Y') {
3654 strcpy((char *) buf, "Yank");
3655 }
3656 p = reg[YDreg];
3657 q = p + strlen((char *) p);
3658 for (cnt = 0; p <= q; p++) {
3659 if (*p == '\n')
3660 cnt++;
3661 }
3662 psb("%s %d lines (%d chars) using [%c]",
3663 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003664#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003665 end_cmd_q(); // stop adding to q
3666 }
3667 break;
3668 case 'k': // k- goto prev line, same col
3669 case VI_K_UP: // cursor key Up
3670 if (cmdcnt-- > 1) {
3671 do_cmd(c);
3672 } // repeat cnt
3673 dot_prev();
3674 dot = move_to_col(dot, ccol + offset); // try stay in same col
3675 break;
3676 case 'r': // r- replace the current char with user input
3677 c1 = get_one_char(); // get the replacement char
3678 if (*dot != '\n') {
3679 *dot = c1;
3680 file_modified = TRUE; // has the file been modified
3681 }
3682 end_cmd_q(); // stop adding to q
3683 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003684 case 't': // t- move to char prior to next x
3685 last_forward_char = get_one_char();
3686 do_cmd(';');
3687 if (*dot == last_forward_char)
3688 dot_left();
3689 last_forward_char= 0;
3690 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003691 case 'w': // w- forward a word
3692 if (cmdcnt-- > 1) {
3693 do_cmd(c);
3694 } // repeat cnt
3695 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3696 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3697 } else if (ispunct(*dot)) { // we are on PUNCT
3698 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3699 }
3700 if (dot < end - 1)
3701 dot++; // move over word
3702 if (isspace(*dot)) {
3703 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3704 }
3705 break;
3706 case 'z': // z-
3707 c1 = get_one_char(); // get the replacement char
3708 cnt = 0;
3709 if (c1 == '.')
3710 cnt = (rows - 2) / 2; // put dot at center
3711 if (c1 == '-')
3712 cnt = rows - 2; // put dot at bottom
3713 screenbegin = begin_line(dot); // start dot at top
3714 dot_scroll(cnt, -1);
3715 break;
3716 case '|': // |- move to column "cmdcnt"
3717 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3718 break;
3719 case '~': // ~- flip the case of letters a-z -> A-Z
3720 if (cmdcnt-- > 1) {
3721 do_cmd(c);
3722 } // repeat cnt
3723 if (islower(*dot)) {
3724 *dot = toupper(*dot);
3725 file_modified = TRUE; // has the file been modified
3726 } else if (isupper(*dot)) {
3727 *dot = tolower(*dot);
3728 file_modified = TRUE; // has the file been modified
3729 }
3730 dot_right();
3731 end_cmd_q(); // stop adding to q
3732 break;
3733 //----- The Cursor and Function Keys -----------------------------
3734 case VI_K_HOME: // Cursor Key Home
3735 dot_begin();
3736 break;
3737 // The Fn keys could point to do_macro which could translate them
3738 case VI_K_FUN1: // Function Key F1
3739 case VI_K_FUN2: // Function Key F2
3740 case VI_K_FUN3: // Function Key F3
3741 case VI_K_FUN4: // Function Key F4
3742 case VI_K_FUN5: // Function Key F5
3743 case VI_K_FUN6: // Function Key F6
3744 case VI_K_FUN7: // Function Key F7
3745 case VI_K_FUN8: // Function Key F8
3746 case VI_K_FUN9: // Function Key F9
3747 case VI_K_FUN10: // Function Key F10
3748 case VI_K_FUN11: // Function Key F11
3749 case VI_K_FUN12: // Function Key F12
3750 break;
3751 }
3752
3753 dc1:
3754 // if text[] just became empty, add back an empty line
3755 if (end == text) {
3756 (void) char_insert(text, '\n'); // start empty buf with dummy line
3757 dot = text;
3758 }
3759 // it is OK for dot to exactly equal to end, otherwise check dot validity
3760 if (dot != end) {
3761 dot = bound_dot(dot); // make sure "dot" is valid
3762 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003763#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003764 check_context(c); // update the current context
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003765#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003766
3767 if (!isdigit(c))
3768 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3769 cnt = dot - begin_line(dot);
3770 // Try to stay off of the Newline
3771 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3772 dot--;
3773}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003774
3775#ifdef CONFIG_FEATURE_VI_CRASHME
3776static int totalcmds = 0;
3777static int Mp = 85; // Movement command Probability
3778static int Np = 90; // Non-movement command Probability
3779static int Dp = 96; // Delete command Probability
3780static int Ip = 97; // Insert command Probability
3781static int Yp = 98; // Yank command Probability
3782static int Pp = 99; // Put command Probability
3783static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3784char chars[20] = "\t012345 abcdABCD-=.$";
3785char *words[20] = { "this", "is", "a", "test",
3786 "broadcast", "the", "emergency", "of",
3787 "system", "quick", "brown", "fox",
3788 "jumped", "over", "lazy", "dogs",
3789 "back", "January", "Febuary", "March"
3790};
3791char *lines[20] = {
3792 "You should have received a copy of the GNU General Public License\n",
3793 "char c, cm, *cmd, *cmd1;\n",
3794 "generate a command by percentages\n",
3795 "Numbers may be typed as a prefix to some commands.\n",
3796 "Quit, discarding changes!\n",
3797 "Forced write, if permission originally not valid.\n",
3798 "In general, any ex or ed command (such as substitute or delete).\n",
3799 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3800 "Please get w/ me and I will go over it with you.\n",
3801 "The following is a list of scheduled, committed changes.\n",
3802 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3803 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3804 "Any question about transactions please contact Sterling Huxley.\n",
3805 "I will try to get back to you by Friday, December 31.\n",
3806 "This Change will be implemented on Friday.\n",
3807 "Let me know if you have problems accessing this;\n",
3808 "Sterling Huxley recently added you to the access list.\n",
3809 "Would you like to go to lunch?\n",
3810 "The last command will be automatically run.\n",
3811 "This is too much english for a computer geek.\n",
3812};
3813char *multilines[20] = {
3814 "You should have received a copy of the GNU General Public License\n",
3815 "char c, cm, *cmd, *cmd1;\n",
3816 "generate a command by percentages\n",
3817 "Numbers may be typed as a prefix to some commands.\n",
3818 "Quit, discarding changes!\n",
3819 "Forced write, if permission originally not valid.\n",
3820 "In general, any ex or ed command (such as substitute or delete).\n",
3821 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3822 "Please get w/ me and I will go over it with you.\n",
3823 "The following is a list of scheduled, committed changes.\n",
3824 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3825 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3826 "Any question about transactions please contact Sterling Huxley.\n",
3827 "I will try to get back to you by Friday, December 31.\n",
3828 "This Change will be implemented on Friday.\n",
3829 "Let me know if you have problems accessing this;\n",
3830 "Sterling Huxley recently added you to the access list.\n",
3831 "Would you like to go to lunch?\n",
3832 "The last command will be automatically run.\n",
3833 "This is too much english for a computer geek.\n",
3834};
3835
3836// create a random command to execute
3837static void crash_dummy()
3838{
3839 static int sleeptime; // how long to pause between commands
3840 char c, cm, *cmd, *cmd1;
3841 int i, cnt, thing, rbi, startrbi, percent;
3842
3843 // "dot" movement commands
3844 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3845
3846 // is there already a command running?
3847 if (readed_for_parse > 0)
3848 goto cd1;
3849 cd0:
3850 startrbi = rbi = 0;
3851 sleeptime = 0; // how long to pause between commands
3852 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3853 // generate a command by percentages
3854 percent = (int) lrand48() % 100; // get a number from 0-99
3855 if (percent < Mp) { // Movement commands
3856 // available commands
3857 cmd = cmd1;
3858 M++;
3859 } else if (percent < Np) { // non-movement commands
3860 cmd = "mz<>\'\""; // available commands
3861 N++;
3862 } else if (percent < Dp) { // Delete commands
3863 cmd = "dx"; // available commands
3864 D++;
3865 } else if (percent < Ip) { // Inset commands
3866 cmd = "iIaAsrJ"; // available commands
3867 I++;
3868 } else if (percent < Yp) { // Yank commands
3869 cmd = "yY"; // available commands
3870 Y++;
3871 } else if (percent < Pp) { // Put commands
3872 cmd = "pP"; // available commands
3873 P++;
3874 } else {
3875 // We do not know how to handle this command, try again
3876 U++;
3877 goto cd0;
3878 }
3879 // randomly pick one of the available cmds from "cmd[]"
3880 i = (int) lrand48() % strlen(cmd);
3881 cm = cmd[i];
3882 if (strchr(":\024", cm))
3883 goto cd0; // dont allow colon or ctrl-T commands
3884 readbuffer[rbi++] = cm; // put cmd into input buffer
3885
3886 // now we have the command-
3887 // there are 1, 2, and multi char commands
3888 // find out which and generate the rest of command as necessary
3889 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3890 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3891 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3892 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3893 }
3894 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3895 c = cmd1[thing];
3896 readbuffer[rbi++] = c; // add movement to input buffer
3897 }
3898 if (strchr("iIaAsc", cm)) { // multi-char commands
3899 if (cm == 'c') {
3900 // change some thing
3901 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3902 c = cmd1[thing];
3903 readbuffer[rbi++] = c; // add movement to input buffer
3904 }
3905 thing = (int) lrand48() % 4; // what thing to insert
3906 cnt = (int) lrand48() % 10; // how many to insert
3907 for (i = 0; i < cnt; i++) {
3908 if (thing == 0) { // insert chars
3909 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3910 } else if (thing == 1) { // insert words
3911 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
3912 strcat((char *) readbuffer, " ");
3913 sleeptime = 0; // how fast to type
3914 } else if (thing == 2) { // insert lines
3915 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
3916 sleeptime = 0; // how fast to type
3917 } else { // insert multi-lines
3918 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
3919 sleeptime = 0; // how fast to type
3920 }
3921 }
3922 strcat((char *) readbuffer, "\033");
3923 }
3924 readed_for_parse = strlen(readbuffer);
3925 cd1:
3926 totalcmds++;
3927 if (sleeptime > 0)
3928 (void) mysleep(sleeptime); // sleep 1/100 sec
3929}
3930
3931// test to see if there are any errors
3932static void crash_test()
3933{
3934 static time_t oldtim;
3935 time_t tim;
Glenn L McGrath7127b582002-12-03 21:48:15 +00003936 char d[2], msg[BUFSIZ];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003937
3938 msg[0] = '\0';
3939 if (end < text) {
3940 strcat((char *) msg, "end<text ");
3941 }
3942 if (end > textend) {
3943 strcat((char *) msg, "end>textend ");
3944 }
3945 if (dot < text) {
3946 strcat((char *) msg, "dot<text ");
3947 }
3948 if (dot > end) {
3949 strcat((char *) msg, "dot>end ");
3950 }
3951 if (screenbegin < text) {
3952 strcat((char *) msg, "screenbegin<text ");
3953 }
3954 if (screenbegin > end - 1) {
3955 strcat((char *) msg, "screenbegin>end-1 ");
3956 }
3957
3958 if (strlen(msg) > 0) {
3959 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00003960 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003961 totalcmds, last_input_char, msg, SOs, SOn);
3962 fflush(stdout);
3963 while (read(0, d, 1) > 0) {
3964 if (d[0] == '\n' || d[0] == '\r')
3965 break;
3966 }
3967 alarm(3);
3968 }
3969 tim = (time_t) time((time_t *) 0);
3970 if (tim >= (oldtim + 3)) {
3971 sprintf((char *) status_buffer,
3972 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
3973 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
3974 oldtim = tim;
3975 }
3976 return;
3977}
3978#endif /* CONFIG_FEATURE_VI_CRASHME */