blob: 57b011444bc4d3b512fafd6c4b81b91731537336 [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 Andersenaeea32c2004-02-04 11:19:44 +000022 "$Id: vi.c,v 1.32 2004/02/04 11:19:44 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
Eric Andersenaeea32c2004-02-04 11:19:44 +00001672 // if we are at the next word's first char
1673 // step back one char
1674 // but check the possibilities when it is true
1675 if (dot > text && ((isspace(dot[0]) && !isspace(dot[0]))
1676 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1677 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1678 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001679 if (dot > text && *dot == '\n')
1680 dot--; // stay off NL
1681 q = dot;
1682 } else if (strchr("H-k{", c)) {
1683 // these operate on multi-lines backwards
1684 q = end_line(dot); // find NL
1685 do_cmd(c); // execute movement cmd
1686 dot_begin();
1687 p = dot;
1688 } else if (strchr("L+j}\r\n", c)) {
1689 // these operate on multi-lines forwards
1690 p = begin_line(dot);
1691 do_cmd(c); // execute movement cmd
1692 dot_end(); // find NL
1693 q = dot;
1694 } else {
1695 c = 27; // error- return an ESC char
1696 //break;
1697 }
1698 *start = p;
1699 *stop = q;
1700 if (q < p) {
1701 *start = q;
1702 *stop = p;
1703 }
1704 dot = save_dot;
1705 return (c);
1706}
1707
1708static int st_test(Byte * p, int type, int dir, Byte * tested)
1709{
1710 Byte c, c0, ci;
1711 int test, inc;
1712
1713 inc = dir;
1714 c = c0 = p[0];
1715 ci = p[inc];
1716 test = 0;
1717
1718 if (type == S_BEFORE_WS) {
1719 c = ci;
1720 test = ((!isspace(c)) || c == '\n');
1721 }
1722 if (type == S_TO_WS) {
1723 c = c0;
1724 test = ((!isspace(c)) || c == '\n');
1725 }
1726 if (type == S_OVER_WS) {
1727 c = c0;
1728 test = ((isspace(c)));
1729 }
1730 if (type == S_END_PUNCT) {
1731 c = ci;
1732 test = ((ispunct(c)));
1733 }
1734 if (type == S_END_ALNUM) {
1735 c = ci;
1736 test = ((isalnum(c)) || c == '_');
1737 }
1738 *tested = c;
1739 return (test);
1740}
1741
1742static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
1743{
1744 Byte c;
1745
1746 while (st_test(p, type, dir, &c)) {
1747 // make sure we limit search to correct number of lines
1748 if (c == '\n' && --linecnt < 1)
1749 break;
1750 if (dir >= 0 && p >= end - 1)
1751 break;
1752 if (dir < 0 && p <= text)
1753 break;
1754 p += dir; // move to next char
1755 }
1756 return (p);
1757}
1758
1759// find matching char of pair () [] {}
1760static Byte *find_pair(Byte * p, Byte c)
1761{
1762 Byte match, *q;
1763 int dir, level;
1764
1765 match = ')';
1766 level = 1;
1767 dir = 1; // assume forward
1768 switch (c) {
1769 case '(':
1770 match = ')';
1771 break;
1772 case '[':
1773 match = ']';
1774 break;
1775 case '{':
1776 match = '}';
1777 break;
1778 case ')':
1779 match = '(';
1780 dir = -1;
1781 break;
1782 case ']':
1783 match = '[';
1784 dir = -1;
1785 break;
1786 case '}':
1787 match = '{';
1788 dir = -1;
1789 break;
1790 }
1791 for (q = p + dir; text <= q && q < end; q += dir) {
1792 // look for match, count levels of pairs (( ))
1793 if (*q == c)
1794 level++; // increase pair levels
1795 if (*q == match)
1796 level--; // reduce pair level
1797 if (level == 0)
1798 break; // found matching pair
1799 }
1800 if (level != 0)
1801 q = NULL; // indicate no match
1802 return (q);
1803}
1804
1805#ifdef CONFIG_FEATURE_VI_SETOPTS
1806// show the matching char of a pair, () [] {}
1807static void showmatching(Byte * p)
1808{
1809 Byte *q, *save_dot;
1810
1811 // we found half of a pair
1812 q = find_pair(p, *p); // get loc of matching char
1813 if (q == NULL) {
1814 indicate_error('3'); // no matching char
1815 } else {
1816 // "q" now points to matching pair
1817 save_dot = dot; // remember where we are
1818 dot = q; // go to new loc
1819 refresh(FALSE); // let the user see it
1820 (void) mysleep(40); // give user some time
1821 dot = save_dot; // go back to old loc
1822 refresh(FALSE);
1823 }
1824}
1825#endif /* CONFIG_FEATURE_VI_SETOPTS */
1826
1827// open a hole in text[]
1828static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
1829{
1830 Byte *src, *dest;
1831 int cnt;
1832
1833 if (size <= 0)
1834 goto thm0;
1835 src = p;
1836 dest = p + size;
1837 cnt = end - src; // the rest of buffer
1838 if (memmove(dest, src, cnt) != dest) {
1839 psbs("can't create room for new characters");
1840 }
1841 memset(p, ' ', size); // clear new hole
1842 end = end + size; // adjust the new END
1843 file_modified = TRUE; // has the file been modified
1844 thm0:
1845 return (p);
1846}
1847
1848// close a hole in text[]
1849static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
1850{
1851 Byte *src, *dest;
1852 int cnt, hole_size;
1853
1854 // move forwards, from beginning
1855 // assume p <= q
1856 src = q + 1;
1857 dest = p;
1858 if (q < p) { // they are backward- swap them
1859 src = p + 1;
1860 dest = q;
1861 }
1862 hole_size = q - p + 1;
1863 cnt = end - src;
1864 if (src < text || src > end)
1865 goto thd0;
1866 if (dest < text || dest >= end)
1867 goto thd0;
1868 if (src >= end)
1869 goto thd_atend; // just delete the end of the buffer
1870 if (memmove(dest, src, cnt) != dest) {
1871 psbs("can't delete the character");
1872 }
1873 thd_atend:
1874 end = end - hole_size; // adjust the new END
1875 if (dest >= end)
1876 dest = end - 1; // make sure dest in below end-1
1877 if (end <= text)
1878 dest = end = text; // keep pointers valid
1879 file_modified = TRUE; // has the file been modified
1880 thd0:
1881 return (dest);
1882}
1883
1884// copy text into register, then delete text.
1885// if dist <= 0, do not include, or go past, a NewLine
1886//
1887static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
1888{
1889 Byte *p;
1890
1891 // make sure start <= stop
1892 if (start > stop) {
1893 // they are backwards, reverse them
1894 p = start;
1895 start = stop;
1896 stop = p;
1897 }
1898 if (dist <= 0) {
1899 // we can not cross NL boundaries
1900 p = start;
1901 if (*p == '\n')
1902 return (p);
1903 // dont go past a NewLine
1904 for (; p + 1 <= stop; p++) {
1905 if (p[1] == '\n') {
1906 stop = p; // "stop" just before NewLine
1907 break;
1908 }
1909 }
1910 }
1911 p = start;
1912#ifdef CONFIG_FEATURE_VI_YANKMARK
1913 text_yank(start, stop, YDreg);
1914#endif /* CONFIG_FEATURE_VI_YANKMARK */
1915 if (yf == YANKDEL) {
1916 p = text_hole_delete(start, stop);
1917 } // delete lines
1918 return (p);
1919}
1920
1921static void show_help(void)
1922{
1923 puts("These features are available:"
1924#ifdef CONFIG_FEATURE_VI_SEARCH
1925 "\n\tPattern searches with / and ?"
1926#endif /* CONFIG_FEATURE_VI_SEARCH */
1927#ifdef CONFIG_FEATURE_VI_DOT_CMD
1928 "\n\tLast command repeat with \'.\'"
1929#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1930#ifdef CONFIG_FEATURE_VI_YANKMARK
1931 "\n\tLine marking with 'x"
1932 "\n\tNamed buffers with \"x"
1933#endif /* CONFIG_FEATURE_VI_YANKMARK */
1934#ifdef CONFIG_FEATURE_VI_READONLY
1935 "\n\tReadonly if vi is called as \"view\""
1936 "\n\tReadonly with -R command line arg"
1937#endif /* CONFIG_FEATURE_VI_READONLY */
1938#ifdef CONFIG_FEATURE_VI_SET
1939 "\n\tSome colon mode commands with \':\'"
1940#endif /* CONFIG_FEATURE_VI_SET */
1941#ifdef CONFIG_FEATURE_VI_SETOPTS
1942 "\n\tSettable options with \":set\""
1943#endif /* CONFIG_FEATURE_VI_SETOPTS */
1944#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
1945 "\n\tSignal catching- ^C"
1946 "\n\tJob suspend and resume with ^Z"
1947#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
1948#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
1949 "\n\tAdapt to window re-sizes"
1950#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
1951 );
1952}
1953
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001954static inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001955{
1956 Byte c, b[2];
1957
1958 b[1] = '\0';
1959 strcpy((char *) buf, ""); // init buf
1960 if (strlen((char *) s) <= 0)
1961 s = (Byte *) "(NULL)";
1962 for (; *s > '\0'; s++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001963 int c_is_no_print;
1964
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001965 c = *s;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001966 c_is_no_print = c > 127 && !Isprint(c);
1967 if (c_is_no_print) {
1968 strcat((char *) buf, SOn);
1969 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001970 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001971 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001972 strcat((char *) buf, "^");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001973 if(c == 127)
1974 c = '?';
1975 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001976 c += '@';
1977 }
1978 b[0] = c;
1979 strcat((char *) buf, (char *) b);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001980 if (c_is_no_print)
1981 strcat((char *) buf, SOs);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001982 if (*s == '\n') {
1983 strcat((char *) buf, "$");
1984 }
1985 }
1986}
1987
1988#ifdef CONFIG_FEATURE_VI_DOT_CMD
1989static void start_new_cmd_q(Byte c)
1990{
1991 // release old cmd
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001992 free(last_modifying_cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001993 // get buffer for new cmd
1994 last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
1995 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
1996 // if there is a current cmd count put it in the buffer first
1997 if (cmdcnt > 0)
1998 sprintf((char *) last_modifying_cmd, "%d", cmdcnt);
1999 // save char c onto queue
2000 last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
2001 adding2q = 1;
2002 return;
2003}
2004
2005static void end_cmd_q(void)
2006{
2007#ifdef CONFIG_FEATURE_VI_YANKMARK
2008 YDreg = 26; // go back to default Yank/Delete reg
2009#endif /* CONFIG_FEATURE_VI_YANKMARK */
2010 adding2q = 0;
2011 return;
2012}
2013#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2014
2015#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
2016static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
2017{
2018 int cnt, i;
2019
2020 i = strlen((char *) s);
2021 p = text_hole_make(p, i);
2022 strncpy((char *) p, (char *) s, i);
2023 for (cnt = 0; *s != '\0'; s++) {
2024 if (*s == '\n')
2025 cnt++;
2026 }
2027#ifdef CONFIG_FEATURE_VI_YANKMARK
2028 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2029#endif /* CONFIG_FEATURE_VI_YANKMARK */
2030 return (p);
2031}
2032#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
2033
2034#ifdef CONFIG_FEATURE_VI_YANKMARK
2035static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register
2036{
2037 Byte *t;
2038 int cnt;
2039
2040 if (q < p) { // they are backwards- reverse them
2041 t = q;
2042 q = p;
2043 p = t;
2044 }
2045 cnt = q - p + 1;
2046 t = reg[dest];
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002047 free(t); // if already a yank register, free it
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002048 t = (Byte *) xmalloc(cnt + 1); // get a new register
2049 memset(t, '\0', cnt + 1); // clear new text[]
2050 strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer
2051 reg[dest] = t;
2052 return (p);
2053}
2054
2055static Byte what_reg(void)
2056{
2057 Byte c;
2058 int i;
2059
2060 i = 0;
2061 c = 'D'; // default to D-reg
2062 if (0 <= YDreg && YDreg <= 25)
2063 c = 'a' + (Byte) YDreg;
2064 if (YDreg == 26)
2065 c = 'D';
2066 if (YDreg == 27)
2067 c = 'U';
2068 return (c);
2069}
2070
2071static void check_context(Byte cmd)
2072{
2073 // A context is defined to be "modifying text"
2074 // Any modifying command establishes a new context.
2075
2076 if (dot < context_start || dot > context_end) {
2077 if (strchr((char *) modifying_cmds, cmd) != NULL) {
2078 // we are trying to modify text[]- make this the current context
2079 mark[27] = mark[26]; // move cur to prev
2080 mark[26] = dot; // move local to cur
2081 context_start = prev_line(prev_line(dot));
2082 context_end = next_line(next_line(dot));
2083 //loiter= start_loiter= now;
2084 }
2085 }
2086 return;
2087}
2088
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002089static inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002090{
2091 Byte *tmp;
2092
2093 // the current context is in mark[26]
2094 // the previous context is in mark[27]
2095 // only swap context if other context is valid
2096 if (text <= mark[27] && mark[27] <= end - 1) {
2097 tmp = mark[27];
2098 mark[27] = mark[26];
2099 mark[26] = tmp;
2100 p = mark[26]; // where we are going- previous context
2101 context_start = prev_line(prev_line(prev_line(p)));
2102 context_end = next_line(next_line(next_line(p)));
2103 }
2104 return (p);
2105}
2106#endif /* CONFIG_FEATURE_VI_YANKMARK */
2107
2108static int isblnk(Byte c) // is the char a blank or tab
2109{
2110 return (c == ' ' || c == '\t');
2111}
2112
2113//----- Set terminal attributes --------------------------------
2114static void rawmode(void)
2115{
2116 tcgetattr(0, &term_orig);
2117 term_vi = term_orig;
2118 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2119 term_vi.c_iflag &= (~IXON & ~ICRNL);
2120 term_vi.c_oflag &= (~ONLCR);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002121 term_vi.c_cc[VMIN] = 1;
2122 term_vi.c_cc[VTIME] = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002123 erase_char = term_vi.c_cc[VERASE];
2124 tcsetattr(0, TCSANOW, &term_vi);
2125}
2126
2127static void cookmode(void)
2128{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002129 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002130 tcsetattr(0, TCSANOW, &term_orig);
2131}
2132
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002133//----- Come here when we get a window resize signal ---------
2134#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
2135static void winch_sig(int sig)
2136{
2137 signal(SIGWINCH, winch_sig);
2138#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2139 window_size_get(0);
2140#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2141 new_screen(rows, columns); // get memory for virtual screen
2142 redraw(TRUE); // re-draw the screen
2143}
2144
2145//----- Come here when we get a continue signal -------------------
2146static void cont_sig(int sig)
2147{
2148 rawmode(); // terminal to "raw"
2149 *status_buffer = '\0'; // clear the status buffer
2150 redraw(TRUE); // re-draw the screen
2151
2152 signal(SIGTSTP, suspend_sig);
2153 signal(SIGCONT, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002154 kill(my_pid, SIGCONT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002155}
2156
2157//----- Come here when we get a Suspend signal -------------------
2158static void suspend_sig(int sig)
2159{
2160 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2161 clear_to_eol(); // Erase to end of line
2162 cookmode(); // terminal to "cooked"
2163
2164 signal(SIGCONT, cont_sig);
2165 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002166 kill(my_pid, SIGTSTP);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002167}
2168
2169//----- Come here when we get a signal ---------------------------
2170static void catch_sig(int sig)
2171{
2172 signal(SIGHUP, catch_sig);
2173 signal(SIGINT, catch_sig);
2174 signal(SIGTERM, catch_sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002175 signal(SIGALRM, catch_sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002176 if(sig)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002177 longjmp(restart, sig);
2178}
2179
2180//----- Come here when we get a core dump signal -----------------
2181static void core_sig(int sig)
2182{
2183 signal(SIGQUIT, core_sig);
2184 signal(SIGILL, core_sig);
2185 signal(SIGTRAP, core_sig);
2186 signal(SIGIOT, core_sig);
2187 signal(SIGABRT, core_sig);
2188 signal(SIGFPE, core_sig);
2189 signal(SIGBUS, core_sig);
2190 signal(SIGSEGV, core_sig);
2191#ifdef SIGSYS
2192 signal(SIGSYS, core_sig);
2193#endif
2194
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002195 if(sig) { // signaled
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002196 dot = bound_dot(dot); // make sure "dot" is valid
2197
2198 longjmp(restart, sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002199 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002200}
2201#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2202
2203static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2204{
2205 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002206 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002207 FD_ZERO(&rfds);
2208 FD_SET(0, &rfds);
2209 tv.tv_sec = 0;
2210 tv.tv_usec = hund * 10000;
2211 select(1, &rfds, NULL, NULL, &tv);
2212 return (FD_ISSET(0, &rfds));
2213}
2214
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002215static Byte readbuffer[BUFSIZ];
2216static int readed_for_parse;
2217
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002218//----- IO Routines --------------------------------------------
2219static Byte readit(void) // read (maybe cursor) key from stdin
2220{
2221 Byte c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002222 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002223 struct esc_cmds {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002224 const char *seq;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002225 Byte val;
2226 };
2227
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002228 static const struct esc_cmds esccmds[] = {
2229 {"OA", (Byte) VI_K_UP}, // cursor key Up
2230 {"OB", (Byte) VI_K_DOWN}, // cursor key Down
2231 {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2232 {"OD", (Byte) VI_K_LEFT}, // cursor key Left
2233 {"OH", (Byte) VI_K_HOME}, // Cursor Key Home
2234 {"OF", (Byte) VI_K_END}, // Cursor Key End
2235 {"[A", (Byte) VI_K_UP}, // cursor key Up
2236 {"[B", (Byte) VI_K_DOWN}, // cursor key Down
2237 {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right
2238 {"[D", (Byte) VI_K_LEFT}, // cursor key Left
2239 {"[H", (Byte) VI_K_HOME}, // Cursor Key Home
2240 {"[F", (Byte) VI_K_END}, // Cursor Key End
2241 {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home
2242 {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2243 {"[4~", (Byte) VI_K_END}, // Cursor Key End
2244 {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2245 {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2246 {"OP", (Byte) VI_K_FUN1}, // Function Key F1
2247 {"OQ", (Byte) VI_K_FUN2}, // Function Key F2
2248 {"OR", (Byte) VI_K_FUN3}, // Function Key F3
2249 {"OS", (Byte) VI_K_FUN4}, // Function Key F4
2250 {"[15~", (Byte) VI_K_FUN5}, // Function Key F5
2251 {"[17~", (Byte) VI_K_FUN6}, // Function Key F6
2252 {"[18~", (Byte) VI_K_FUN7}, // Function Key F7
2253 {"[19~", (Byte) VI_K_FUN8}, // Function Key F8
2254 {"[20~", (Byte) VI_K_FUN9}, // Function Key F9
2255 {"[21~", (Byte) VI_K_FUN10}, // Function Key F10
2256 {"[23~", (Byte) VI_K_FUN11}, // Function Key F11
2257 {"[24~", (Byte) VI_K_FUN12}, // Function Key F12
2258 {"[11~", (Byte) VI_K_FUN1}, // Function Key F1
2259 {"[12~", (Byte) VI_K_FUN2}, // Function Key F2
2260 {"[13~", (Byte) VI_K_FUN3}, // Function Key F3
2261 {"[14~", (Byte) VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002262 };
2263
2264#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2265
2266 (void) alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002267 fflush(stdout);
2268 n = readed_for_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002269 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002270 if (n <= 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002271 ri0:
2272 // the Q is empty, wait for a typed char
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002273 n = read(0, readbuffer, BUFSIZ - 1);
2274 if (n < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002275 if (errno == EINTR)
2276 goto ri0; // interrupted sys call
2277 if (errno == EBADF)
2278 editing = 0;
2279 if (errno == EFAULT)
2280 editing = 0;
2281 if (errno == EINVAL)
2282 editing = 0;
2283 if (errno == EIO)
2284 editing = 0;
2285 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002286 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002287 if(n <= 0)
2288 return 0; // error
2289 if (readbuffer[0] == 27) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002290 // This is an ESC char. Is this Esc sequence?
2291 // Could be bare Esc key. See if there are any
2292 // more chars to read after the ESC. This would
2293 // be a Function or Cursor Key sequence.
2294 FD_ZERO(&rfds);
2295 FD_SET(0, &rfds);
2296 tv.tv_sec = 0;
2297 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2298
2299 // keep reading while there are input chars and room in buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002300 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002301 // read the rest of the ESC string
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002302 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2303 if (r > 0) {
2304 n += r;
2305 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002306 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002307 }
2308 readed_for_parse = n;
2309 }
2310 c = readbuffer[0];
2311 if(c == 27 && n > 1) {
2312 // Maybe cursor or function key?
2313 const struct esc_cmds *eindex;
2314
2315 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2316 int cnt = strlen(eindex->seq);
2317
2318 if(n <= cnt)
2319 continue;
2320 if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
2321 continue;
2322 // is a Cursor key- put derived value back into Q
2323 c = eindex->val;
2324 // for squeeze out the ESC sequence
2325 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002326 break;
2327 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002328 if(eindex == &esccmds[ESCCMDS_COUNT]) {
2329 /* defined ESC sequence not found, set only one ESC */
2330 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002331 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002332 } else {
2333 n = 1;
2334 }
2335 // remove key sequence from Q
2336 readed_for_parse -= n;
2337 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002338 (void) alarm(3); // we are done waiting for input, turn alarm ON
2339 return (c);
2340}
2341
2342//----- IO Routines --------------------------------------------
2343static Byte get_one_char()
2344{
2345 static Byte c;
2346
2347#ifdef CONFIG_FEATURE_VI_DOT_CMD
2348 // ! adding2q && ioq == 0 read()
2349 // ! adding2q && ioq != 0 *ioq
2350 // adding2q *last_modifying_cmd= read()
2351 if (!adding2q) {
2352 // we are not adding to the q.
2353 // but, we may be reading from a q
2354 if (ioq == 0) {
2355 // there is no current q, read from STDIN
2356 c = readit(); // get the users input
2357 } else {
2358 // there is a queue to get chars from first
2359 c = *ioq++;
2360 if (c == '\0') {
2361 // the end of the q, read from STDIN
2362 free(ioq_start);
2363 ioq_start = ioq = 0;
2364 c = readit(); // get the users input
2365 }
2366 }
2367 } else {
2368 // adding STDIN chars to q
2369 c = readit(); // get the users input
2370 if (last_modifying_cmd != 0) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002371 int len = strlen((char *) last_modifying_cmd);
2372 if (len + 1 >= BUFSIZ) {
2373 psbs("last_modifying_cmd overrun");
2374 } else {
2375 // add new char to q
2376 last_modifying_cmd[len] = c;
2377 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002378 }
2379 }
2380#else /* CONFIG_FEATURE_VI_DOT_CMD */
2381 c = readit(); // get the users input
2382#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2383 return (c); // return the char, where ever it came from
2384}
2385
2386static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2387{
2388 Byte buf[BUFSIZ];
2389 Byte c;
2390 int i;
2391 static Byte *obufp = NULL;
2392
2393 strcpy((char *) buf, (char *) prompt);
2394 *status_buffer = '\0'; // clear the status buffer
2395 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2396 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002397 write1(prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002398
2399 for (i = strlen((char *) buf); i < BUFSIZ;) {
2400 c = get_one_char(); // read user input
2401 if (c == '\n' || c == '\r' || c == 27)
2402 break; // is this end of input
2403 if (c == erase_char) { // user wants to erase prev char
2404 i--; // backup to prev char
2405 buf[i] = '\0'; // erase the char
2406 buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002407 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002408 if (i <= 0) { // user backs up before b-o-l, exit
2409 break;
2410 }
2411 } else {
2412 buf[i] = c; // save char in buffer
2413 buf[i + 1] = '\0'; // make sure buffer is null terminated
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002414 putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002415 i++;
2416 }
2417 }
2418 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002419 free(obufp);
Manuel Novoa III cad53642003-03-19 09:13:01 +00002420 obufp = (Byte *) bb_xstrdup((char *) buf);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002421 return (obufp);
2422}
2423
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002424static int file_size(const Byte * fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002425{
2426 struct stat st_buf;
2427 int cnt, sr;
2428
2429 if (fn == 0 || strlen(fn) <= 0)
2430 return (-1);
2431 cnt = -1;
2432 sr = stat((char *) fn, &st_buf); // see if file exists
2433 if (sr >= 0) {
2434 cnt = (int) st_buf.st_size;
2435 }
2436 return (cnt);
2437}
2438
2439static int file_insert(Byte * fn, Byte * p, int size)
2440{
2441 int fd, cnt;
2442
2443 cnt = -1;
2444#ifdef CONFIG_FEATURE_VI_READONLY
2445 readonly = FALSE;
2446#endif /* CONFIG_FEATURE_VI_READONLY */
2447 if (fn == 0 || strlen((char*) fn) <= 0) {
2448 psbs("No filename given");
2449 goto fi0;
2450 }
2451 if (size == 0) {
2452 // OK- this is just a no-op
2453 cnt = 0;
2454 goto fi0;
2455 }
2456 if (size < 0) {
2457 psbs("Trying to insert a negative number (%d) of characters", size);
2458 goto fi0;
2459 }
2460 if (p < text || p > end) {
2461 psbs("Trying to insert file outside of memory");
2462 goto fi0;
2463 }
2464
2465 // see if we can open the file
2466#ifdef CONFIG_FEATURE_VI_READONLY
2467 if (vi_readonly) goto fi1; // do not try write-mode
2468#endif
2469 fd = open((char *) fn, O_RDWR); // assume read & write
2470 if (fd < 0) {
2471 // could not open for writing- maybe file is read only
2472#ifdef CONFIG_FEATURE_VI_READONLY
2473 fi1:
2474#endif
2475 fd = open((char *) fn, O_RDONLY); // try read-only
2476 if (fd < 0) {
2477 psbs("\"%s\" %s", fn, "could not open file");
2478 goto fi0;
2479 }
2480#ifdef CONFIG_FEATURE_VI_READONLY
2481 // got the file- read-only
2482 readonly = TRUE;
2483#endif /* CONFIG_FEATURE_VI_READONLY */
2484 }
2485 p = text_hole_make(p, size);
2486 cnt = read(fd, p, size);
2487 close(fd);
2488 if (cnt < 0) {
2489 cnt = -1;
2490 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2491 psbs("could not read file \"%s\"", fn);
2492 } else if (cnt < size) {
2493 // There was a partial read, shrink unused space text[]
2494 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
2495 psbs("could not read all of file \"%s\"", fn);
2496 }
2497 if (cnt >= size)
2498 file_modified = TRUE;
2499 fi0:
2500 return (cnt);
2501}
2502
2503static int file_write(Byte * fn, Byte * first, Byte * last)
2504{
2505 int fd, cnt, charcnt;
2506
2507 if (fn == 0) {
2508 psbs("No current filename");
2509 return (-1);
2510 }
2511 charcnt = 0;
2512 // FIXIT- use the correct umask()
2513 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2514 if (fd < 0)
2515 return (-1);
2516 cnt = last - first + 1;
2517 charcnt = write(fd, first, cnt);
2518 if (charcnt == cnt) {
2519 // good write
2520 //file_modified= FALSE; // the file has not been modified
2521 } else {
2522 charcnt = 0;
2523 }
2524 close(fd);
2525 return (charcnt);
2526}
2527
2528//----- Terminal Drawing ---------------------------------------
2529// The terminal is made up of 'rows' line of 'columns' columns.
2530// classicly this would be 24 x 80.
2531// screen coordinates
2532// 0,0 ... 0,79
2533// 1,0 ... 1,79
2534// . ... .
2535// . ... .
2536// 22,0 ... 22,79
2537// 23,0 ... 23,79 status line
2538//
2539
2540//----- Move the cursor to row x col (count from 0, not 1) -------
2541static void place_cursor(int row, int col, int opti)
2542{
2543 char cm1[BUFSIZ];
2544 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002545#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2546 char cm2[BUFSIZ];
2547 Byte *screenp;
2548 // char cm3[BUFSIZ];
2549 int Rrow= last_row;
2550#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2551
2552 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2553
2554 if (row < 0) row = 0;
2555 if (row >= rows) row = rows - 1;
2556 if (col < 0) col = 0;
2557 if (col >= columns) col = columns - 1;
2558
2559 //----- 1. Try the standard terminal ESC sequence
2560 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2561 cm= cm1;
2562 if (! opti) goto pc0;
2563
2564#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2565 //----- find the minimum # of chars to move cursor -------------
2566 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2567 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
2568
2569 // move to the correct row
2570 while (row < Rrow) {
2571 // the cursor has to move up
2572 strcat(cm2, CMup);
2573 Rrow--;
2574 }
2575 while (row > Rrow) {
2576 // the cursor has to move down
2577 strcat(cm2, CMdown);
2578 Rrow++;
2579 }
2580
2581 // now move to the correct column
2582 strcat(cm2, "\r"); // start at col 0
2583 // just send out orignal source char to get to correct place
2584 screenp = &screen[row * columns]; // start of screen line
2585 strncat(cm2, screenp, col);
2586
2587 //----- 3. Try some other way of moving cursor
2588 //---------------------------------------------
2589
2590 // pick the shortest cursor motion to send out
2591 cm= cm1;
2592 if (strlen(cm2) < strlen(cm)) {
2593 cm= cm2;
2594 } /* else if (strlen(cm3) < strlen(cm)) {
2595 cm= cm3;
2596 } */
2597#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2598 pc0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002599 write1(cm); // move the cursor
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002600}
2601
2602//----- Erase from cursor to end of line -----------------------
2603static void clear_to_eol()
2604{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002605 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002606}
2607
2608//----- Erase from cursor to end of screen -----------------------
2609static void clear_to_eos()
2610{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002611 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002612}
2613
2614//----- Start standout mode ------------------------------------
2615static void standout_start() // send "start reverse video" sequence
2616{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002617 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002618}
2619
2620//----- End standout mode --------------------------------------
2621static void standout_end() // send "end reverse video" sequence
2622{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002623 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002624}
2625
2626//----- Flash the screen --------------------------------------
2627static void flash(int h)
2628{
2629 standout_start(); // send "start reverse video" sequence
2630 redraw(TRUE);
2631 (void) mysleep(h);
2632 standout_end(); // send "end reverse video" sequence
2633 redraw(TRUE);
2634}
2635
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002636static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002637{
2638#ifdef CONFIG_FEATURE_VI_CRASHME
2639 if (crashme > 0)
2640 return; // generate a random command
2641#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002642 if (!err_method) {
2643 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002644 } else {
2645 flash(10);
2646 }
2647}
2648
2649//----- Screen[] Routines --------------------------------------
2650//----- Erase the Screen[] memory ------------------------------
2651static void screen_erase()
2652{
2653 memset(screen, ' ', screensize); // clear new screen
2654}
2655
2656//----- Draw the status line at bottom of the screen -------------
2657static void show_status_line(void)
2658{
2659 static int last_cksum;
2660 int l, cnt, cksum;
2661
2662 cnt = strlen((char *) status_buffer);
2663 for (cksum= l= 0; l < cnt; l++) { cksum += (int)(status_buffer[l]); }
2664 // don't write the status line unless it changes
2665 if (cnt > 0 && last_cksum != cksum) {
2666 last_cksum= cksum; // remember if we have seen this line
2667 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002668 write1(status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002669 clear_to_eol();
2670 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2671 }
2672}
2673
2674//----- format the status buffer, the bottom line of screen ------
2675// print status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002676static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002677{
2678 va_list args;
2679
2680 va_start(args, format);
2681 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
2682 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format,
2683 args);
2684 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2685 va_end(args);
2686
2687 return;
2688}
2689
2690// print status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002691static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002692{
2693 va_list args;
2694
2695 va_start(args, format);
2696 vsprintf((char *) status_buffer, format, args);
2697 va_end(args);
2698 return;
2699}
2700
2701static void ni(Byte * s) // display messages
2702{
2703 Byte buf[BUFSIZ];
2704
2705 print_literal(buf, s);
2706 psbs("\'%s\' is not implemented", buf);
2707}
2708
2709static void edit_status(void) // show file status on status line
2710{
2711 int cur, tot, percent;
2712
2713 cur = count_lines(text, dot);
2714 tot = count_lines(text, end - 1);
2715 // current line percent
2716 // ------------- ~~ ----------
2717 // total lines 100
2718 if (tot > 0) {
2719 percent = (100 * cur) / tot;
2720 } else {
2721 cur = tot = 0;
2722 percent = 100;
2723 }
2724 psb("\"%s\""
2725#ifdef CONFIG_FEATURE_VI_READONLY
2726 "%s"
2727#endif /* CONFIG_FEATURE_VI_READONLY */
2728 "%s line %d of %d --%d%%--",
2729 (cfn != 0 ? (char *) cfn : "No file"),
2730#ifdef CONFIG_FEATURE_VI_READONLY
2731 ((vi_readonly || readonly) ? " [Read only]" : ""),
2732#endif /* CONFIG_FEATURE_VI_READONLY */
2733 (file_modified ? " [modified]" : ""),
2734 cur, tot, percent);
2735}
2736
2737//----- Force refresh of all Lines -----------------------------
2738static void redraw(int full_screen)
2739{
2740 place_cursor(0, 0, FALSE); // put cursor in correct place
2741 clear_to_eos(); // tel terminal to erase display
2742 screen_erase(); // erase the internal screen buffer
2743 refresh(full_screen); // this will redraw the entire display
2744}
2745
2746//----- Format a text[] line into a buffer ---------------------
2747static void format_line(Byte *dest, Byte *src, int li)
2748{
2749 int co;
2750 Byte c;
2751
2752 for (co= 0; co < MAX_SCR_COLS; co++) {
2753 c= ' '; // assume blank
2754 if (li > 0 && co == 0) {
2755 c = '~'; // not first line, assume Tilde
2756 }
2757 // are there chars in text[] and have we gone past the end
2758 if (text < end && src < end) {
2759 c = *src++;
2760 }
2761 if (c == '\n')
2762 break;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002763 if (c > 127 && !Isprint(c)) {
2764 c = '.';
2765 }
2766 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002767 if (c == '\t') {
2768 c = ' ';
2769 // co % 8 != 7
2770 for (; (co % tabstop) != (tabstop - 1); co++) {
2771 dest[co] = c;
2772 }
2773 } else {
2774 dest[co++] = '^';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002775 if(c == 127)
2776 c = '?';
2777 else
2778 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002779 }
2780 }
2781 // the co++ is done here so that the column will
2782 // not be overwritten when we blank-out the rest of line
2783 dest[co] = c;
2784 if (src >= end)
2785 break;
2786 }
2787}
2788
2789//----- Refresh the changed screen lines -----------------------
2790// Copy the source line from text[] into the buffer and note
2791// if the current screenline is different from the new buffer.
2792// If they differ then that line needs redrawing on the terminal.
2793//
2794static void refresh(int full_screen)
2795{
2796 static int old_offset;
2797 int li, changed;
2798 Byte buf[MAX_SCR_COLS];
2799 Byte *tp, *sp; // pointer into text[] and screen[]
2800#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2801 int last_li= -2; // last line that changed- for optimizing cursor movement
2802#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2803
2804#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2805 window_size_get(0);
2806#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2807 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2808 tp = screenbegin; // index into text[] of top line
2809
2810 // compare text[] to screen[] and mark screen[] lines that need updating
2811 for (li = 0; li < rows - 1; li++) {
2812 int cs, ce; // column start & end
2813 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2814 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2815 // format current text line into buf
2816 format_line(buf, tp, li);
2817
2818 // skip to the end of the current text[] line
2819 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2820
2821 // see if there are any changes between vitual screen and buf
2822 changed = FALSE; // assume no change
2823 cs= 0;
2824 ce= columns-1;
2825 sp = &screen[li * columns]; // start of screen line
2826 if (full_screen) {
2827 // force re-draw of every single column from 0 - columns-1
2828 goto re0;
2829 }
2830 // compare newly formatted buffer with virtual screen
2831 // look forward for first difference between buf and screen
2832 for ( ; cs <= ce; cs++) {
2833 if (buf[cs + offset] != sp[cs]) {
2834 changed = TRUE; // mark for redraw
2835 break;
2836 }
2837 }
2838
2839 // look backward for last difference between buf and screen
2840 for ( ; ce >= cs; ce--) {
2841 if (buf[ce + offset] != sp[ce]) {
2842 changed = TRUE; // mark for redraw
2843 break;
2844 }
2845 }
2846 // now, cs is index of first diff, and ce is index of last diff
2847
2848 // if horz offset has changed, force a redraw
2849 if (offset != old_offset) {
2850 re0:
2851 changed = TRUE;
2852 }
2853
2854 // make a sanity check of columns indexes
2855 if (cs < 0) cs= 0;
2856 if (ce > columns-1) ce= columns-1;
2857 if (cs > ce) { cs= 0; ce= columns-1; }
2858 // is there a change between vitual screen and buf
2859 if (changed) {
2860 // copy changed part of buffer to virtual screen
2861 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2862
2863 // move cursor to column of first change
2864 if (offset != old_offset) {
2865 // opti_cur_move is still too stupid
2866 // to handle offsets correctly
2867 place_cursor(li, cs, FALSE);
2868 } else {
2869#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2870 // if this just the next line
2871 // try to optimize cursor movement
2872 // otherwise, use standard ESC sequence
2873 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2874 last_li= li;
2875#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2876 place_cursor(li, cs, FALSE); // use standard ESC sequence
2877#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2878 }
2879
2880 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002881 {
2882 int nic = ce-cs+1;
2883 char *out = sp+cs;
2884
2885 while(nic-- > 0) {
2886 putchar(*out);
2887 out++;
2888 }
2889 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002890#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2891 last_row = li;
2892#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2893 }
2894 }
2895
2896#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2897 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2898 last_row = crow;
2899#else
2900 place_cursor(crow, ccol, FALSE);
2901#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2902
2903 if (offset != old_offset)
2904 old_offset = offset;
2905}
2906
Eric Andersen3f980402001-04-04 17:31:15 +00002907//---------------------------------------------------------------------
2908//----- the Ascii Chart -----------------------------------------------
2909//
2910// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2911// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2912// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2913// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2914// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2915// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2916// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2917// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2918// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2919// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2920// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2921// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2922// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2923// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2924// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2925// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2926//---------------------------------------------------------------------
2927
2928//----- Execute a Vi Command -----------------------------------
2929static void do_cmd(Byte c)
2930{
2931 Byte c1, *p, *q, *msg, buf[9], *save_dot;
2932 int cnt, i, j, dir, yf;
2933
2934 c1 = c; // quiet the compiler
2935 cnt = yf = dir = 0; // quiet the compiler
2936 p = q = save_dot = msg = buf; // quiet the compiler
2937 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00002938
2939 /* if this is a cursor key, skip these checks */
2940 switch (c) {
2941 case VI_K_UP:
2942 case VI_K_DOWN:
2943 case VI_K_LEFT:
2944 case VI_K_RIGHT:
2945 case VI_K_HOME:
2946 case VI_K_END:
2947 case VI_K_PAGEUP:
2948 case VI_K_PAGEDOWN:
2949 goto key_cmd_mode;
2950 }
2951
Eric Andersen3f980402001-04-04 17:31:15 +00002952 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002953 // flip-flop Insert/Replace mode
2954 if (c == VI_K_INSERT) goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00002955 // we are 'R'eplacing the current *dot with new char
2956 if (*dot == '\n') {
2957 // don't Replace past E-o-l
2958 cmd_mode = 1; // convert to insert
2959 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002960 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00002961 if (c != 27)
2962 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
2963 dot = char_insert(dot, c); // insert new char
2964 }
2965 goto dc1;
2966 }
2967 }
2968 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00002969 // hitting "Insert" twice means "R" replace mode
2970 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00002971 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002972 if (1 <= c || Isprint(c)) {
2973 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00002974 }
2975 goto dc1;
2976 }
2977
Eric Andersenbff7a602001-11-17 07:15:43 +00002978key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00002979 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00002980 //case 0x01: // soh
2981 //case 0x09: // ht
2982 //case 0x0b: // vt
2983 //case 0x0e: // so
2984 //case 0x0f: // si
2985 //case 0x10: // dle
2986 //case 0x11: // dc1
2987 //case 0x13: // dc3
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002988#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00002989 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00002990 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00002991 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002992#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen822c3832001-05-07 17:37:43 +00002993 //case 0x16: // syn
2994 //case 0x17: // etb
2995 //case 0x18: // can
2996 //case 0x1c: // fs
2997 //case 0x1d: // gs
2998 //case 0x1e: // rs
2999 //case 0x1f: // us
3000 //case '!': // !-
3001 //case '#': // #-
3002 //case '&': // &-
3003 //case '(': // (-
3004 //case ')': // )-
3005 //case '*': // *-
3006 //case ',': // ,-
3007 //case '=': // =-
3008 //case '@': // @-
3009 //case 'F': // F-
3010 //case 'K': // K-
3011 //case 'Q': // Q-
3012 //case 'S': // S-
3013 //case 'T': // T-
3014 //case 'V': // V-
3015 //case '[': // [-
3016 //case '\\': // \-
3017 //case ']': // ]-
3018 //case '_': // _-
3019 //case '`': // `-
3020 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003021 //case 'u': // u- FIXME- there is no undo
Eric Andersen822c3832001-05-07 17:37:43 +00003022 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00003023 default: // unrecognised command
3024 buf[0] = c;
3025 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003026 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00003027 buf[0] = '^';
3028 buf[1] = c + '@';
3029 buf[2] = '\0';
3030 }
3031 ni((Byte *) buf);
3032 end_cmd_q(); // stop adding to q
3033 case 0x00: // nul- ignore
3034 break;
3035 case 2: // ctrl-B scroll up full screen
3036 case VI_K_PAGEUP: // Cursor Key Page Up
3037 dot_scroll(rows - 2, -1);
3038 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003039#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003040 case 0x03: // ctrl-C interrupt
3041 longjmp(restart, 1);
3042 break;
3043 case 26: // ctrl-Z suspend
3044 suspend_sig(SIGTSTP);
3045 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003046#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +00003047 case 4: // ctrl-D scroll down half screen
3048 dot_scroll((rows - 2) / 2, 1);
3049 break;
3050 case 5: // ctrl-E scroll down one line
3051 dot_scroll(1, 1);
3052 break;
3053 case 6: // ctrl-F scroll down full screen
3054 case VI_K_PAGEDOWN: // Cursor Key Page Down
3055 dot_scroll(rows - 2, 1);
3056 break;
3057 case 7: // ctrl-G show current status
3058 edit_status();
3059 break;
3060 case 'h': // h- move left
3061 case VI_K_LEFT: // cursor key Left
Eric Andersen1c0d3112001-04-16 15:46:44 +00003062 case 8: // ctrl-H- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003063 case 127: // DEL- move left (This may be ERASE char)
3064 if (cmdcnt-- > 1) {
3065 do_cmd(c);
3066 } // repeat cnt
3067 dot_left();
3068 break;
3069 case 10: // Newline ^J
3070 case 'j': // j- goto next line, same col
3071 case VI_K_DOWN: // cursor key Down
3072 if (cmdcnt-- > 1) {
3073 do_cmd(c);
3074 } // repeat cnt
3075 dot_next(); // go to next B-o-l
3076 dot = move_to_col(dot, ccol + offset); // try stay in same col
3077 break;
3078 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003079 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003080 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003081 clear_to_eos(); // tel terminal to erase display
3082 (void) mysleep(10);
3083 screen_erase(); // erase the internal screen buffer
3084 refresh(TRUE); // this will redraw the entire display
3085 break;
3086 case 13: // Carriage Return ^M
3087 case '+': // +- goto next line
3088 if (cmdcnt-- > 1) {
3089 do_cmd(c);
3090 } // repeat cnt
3091 dot_next();
3092 dot_skip_over_ws();
3093 break;
3094 case 21: // ctrl-U scroll up half screen
3095 dot_scroll((rows - 2) / 2, -1);
3096 break;
3097 case 25: // ctrl-Y scroll up one line
3098 dot_scroll(1, -1);
3099 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003100 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003101 if (cmd_mode == 0)
3102 indicate_error(c);
3103 cmd_mode = 0; // stop insrting
3104 end_cmd_q();
3105 *status_buffer = '\0'; // clear status buffer
3106 break;
3107 case ' ': // move right
3108 case 'l': // move right
3109 case VI_K_RIGHT: // Cursor Key Right
3110 if (cmdcnt-- > 1) {
3111 do_cmd(c);
3112 } // repeat cnt
3113 dot_right();
3114 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003115#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003116 case '"': // "- name a register to use for Delete/Yank
3117 c1 = get_one_char();
3118 c1 = tolower(c1);
3119 if (islower(c1)) {
3120 YDreg = c1 - 'a';
3121 } else {
3122 indicate_error(c);
3123 }
3124 break;
3125 case '\'': // '- goto a specific mark
3126 c1 = get_one_char();
3127 c1 = tolower(c1);
3128 if (islower(c1)) {
3129 c1 = c1 - 'a';
3130 // get the b-o-l
3131 q = mark[(int) c1];
3132 if (text <= q && q < end) {
3133 dot = q;
3134 dot_begin(); // go to B-o-l
3135 dot_skip_over_ws();
3136 }
3137 } else if (c1 == '\'') { // goto previous context
3138 dot = swap_context(dot); // swap current and previous context
3139 dot_begin(); // go to B-o-l
3140 dot_skip_over_ws();
3141 } else {
3142 indicate_error(c);
3143 }
3144 break;
3145 case 'm': // m- Mark a line
3146 // this is really stupid. If there are any inserts or deletes
3147 // between text[0] and dot then this mark will not point to the
3148 // correct location! It could be off by many lines!
3149 // Well..., at least its quick and dirty.
3150 c1 = get_one_char();
3151 c1 = tolower(c1);
3152 if (islower(c1)) {
3153 c1 = c1 - 'a';
3154 // remember the line
3155 mark[(int) c1] = dot;
3156 } else {
3157 indicate_error(c);
3158 }
3159 break;
3160 case 'P': // P- Put register before
3161 case 'p': // p- put register after
3162 p = reg[YDreg];
3163 if (p == 0) {
3164 psbs("Nothing in register %c", what_reg());
3165 break;
3166 }
3167 // are we putting whole lines or strings
3168 if (strchr((char *) p, '\n') != NULL) {
3169 if (c == 'P') {
3170 dot_begin(); // putting lines- Put above
3171 }
3172 if (c == 'p') {
3173 // are we putting after very last line?
3174 if (end_line(dot) == (end - 1)) {
3175 dot = end; // force dot to end of text[]
3176 } else {
3177 dot_next(); // next line, then put before
3178 }
3179 }
3180 } else {
3181 if (c == 'p')
3182 dot_right(); // move to right, can move to NL
3183 }
3184 dot = string_insert(dot, p); // insert the string
3185 end_cmd_q(); // stop adding to q
3186 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003187 case 'U': // U- Undo; replace current line with original version
3188 if (reg[Ureg] != 0) {
3189 p = begin_line(dot);
3190 q = end_line(dot);
3191 p = text_hole_delete(p, q); // delete cur line
3192 p = string_insert(p, reg[Ureg]); // insert orig line
3193 dot = p;
3194 dot_skip_over_ws();
3195 }
3196 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003197#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003198 case '$': // $- goto end of line
3199 case VI_K_END: // Cursor Key End
3200 if (cmdcnt-- > 1) {
3201 do_cmd(c);
3202 } // repeat cnt
Glenn L McGrathee829062004-01-21 10:59:45 +00003203 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003204 break;
3205 case '%': // %- find matching char of pair () [] {}
3206 for (q = dot; q < end && *q != '\n'; q++) {
3207 if (strchr("()[]{}", *q) != NULL) {
3208 // we found half of a pair
3209 p = find_pair(q, *q);
3210 if (p == NULL) {
3211 indicate_error(c);
3212 } else {
3213 dot = p;
3214 }
3215 break;
3216 }
3217 }
3218 if (*q == '\n')
3219 indicate_error(c);
3220 break;
3221 case 'f': // f- forward to a user specified char
3222 last_forward_char = get_one_char(); // get the search char
3223 //
3224 // dont seperate these two commands. 'f' depends on ';'
3225 //
3226 //**** fall thru to ... 'i'
3227 case ';': // ;- look at rest of line for last forward char
3228 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003229 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003230 } // repeat cnt
Eric Andersen822c3832001-05-07 17:37:43 +00003231 if (last_forward_char == 0) break;
Eric Andersen3f980402001-04-04 17:31:15 +00003232 q = dot + 1;
3233 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3234 q++;
3235 }
3236 if (*q == last_forward_char)
3237 dot = q;
3238 break;
3239 case '-': // -- goto prev line
3240 if (cmdcnt-- > 1) {
3241 do_cmd(c);
3242 } // repeat cnt
3243 dot_prev();
3244 dot_skip_over_ws();
3245 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003246#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003247 case '.': // .- repeat the last modifying command
3248 // Stuff the last_modifying_cmd back into stdin
3249 // and let it be re-executed.
3250 if (last_modifying_cmd != 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +00003251 ioq = ioq_start = (Byte *) bb_xstrdup((char *) last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003252 }
3253 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003254#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3255#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003256 case '?': // /- search for a pattern
3257 case '/': // /- search for a pattern
3258 buf[0] = c;
3259 buf[1] = '\0';
3260 q = get_input_line(buf); // get input line- use "status line"
3261 if (strlen((char *) q) == 1)
3262 goto dc3; // if no pat re-use old pat
3263 if (strlen((char *) q) > 1) { // new pat- save it and find
3264 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003265 free(last_search_pattern);
Manuel Novoa III cad53642003-03-19 09:13:01 +00003266 last_search_pattern = (Byte *) bb_xstrdup((char *) q);
Eric Andersen3f980402001-04-04 17:31:15 +00003267 goto dc3; // now find the pattern
3268 }
3269 // user changed mind and erased the "/"- do nothing
3270 break;
3271 case 'N': // N- backward search for last pattern
3272 if (cmdcnt-- > 1) {
3273 do_cmd(c);
3274 } // repeat cnt
3275 dir = BACK; // assume BACKWARD search
3276 p = dot - 1;
3277 if (last_search_pattern[0] == '?') {
3278 dir = FORWARD;
3279 p = dot + 1;
3280 }
3281 goto dc4; // now search for pattern
3282 break;
3283 case 'n': // n- repeat search for last pattern
3284 // search rest of text[] starting at next char
3285 // if search fails return orignal "p" not the "p+1" address
3286 if (cmdcnt-- > 1) {
3287 do_cmd(c);
3288 } // repeat cnt
3289 dc3:
3290 if (last_search_pattern == 0) {
3291 msg = (Byte *) "No previous regular expression";
3292 goto dc2;
3293 }
3294 if (last_search_pattern[0] == '/') {
3295 dir = FORWARD; // assume FORWARD search
3296 p = dot + 1;
3297 }
3298 if (last_search_pattern[0] == '?') {
3299 dir = BACK;
3300 p = dot - 1;
3301 }
3302 dc4:
3303 q = char_search(p, last_search_pattern + 1, dir, FULL);
3304 if (q != NULL) {
3305 dot = q; // good search, update "dot"
3306 msg = (Byte *) "";
3307 goto dc2;
3308 }
3309 // no pattern found between "dot" and "end"- continue at top
3310 p = text;
3311 if (dir == BACK) {
3312 p = end - 1;
3313 }
3314 q = char_search(p, last_search_pattern + 1, dir, FULL);
3315 if (q != NULL) { // found something
3316 dot = q; // found new pattern- goto it
3317 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3318 if (dir == BACK) {
3319 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3320 }
3321 } else {
3322 msg = (Byte *) "Pattern not found";
3323 }
3324 dc2:
3325 psbs("%s", msg);
3326 break;
3327 case '{': // {- move backward paragraph
3328 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3329 if (q != NULL) { // found blank line
3330 dot = next_line(q); // move to next blank line
3331 }
3332 break;
3333 case '}': // }- move forward paragraph
3334 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3335 if (q != NULL) { // found blank line
3336 dot = next_line(q); // move to next blank line
3337 }
3338 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003339#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003340 case '0': // 0- goto begining of line
3341 case '1': // 1-
3342 case '2': // 2-
3343 case '3': // 3-
3344 case '4': // 4-
3345 case '5': // 5-
3346 case '6': // 6-
3347 case '7': // 7-
3348 case '8': // 8-
3349 case '9': // 9-
3350 if (c == '0' && cmdcnt < 1) {
3351 dot_begin(); // this was a standalone zero
3352 } else {
3353 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3354 }
3355 break;
3356 case ':': // :- the colon mode commands
Eric Andersen3f980402001-04-04 17:31:15 +00003357 p = get_input_line((Byte *) ":"); // get input line- use "status line"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003358#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003359 colon(p); // execute the command
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003360#else /* CONFIG_FEATURE_VI_COLON */
Eric Andersen822c3832001-05-07 17:37:43 +00003361 if (*p == ':')
3362 p++; // move past the ':'
3363 cnt = strlen((char *) p);
3364 if (cnt <= 0)
3365 break;
3366 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3367 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
Matt Kraai1f0c4362001-12-20 23:13:26 +00003368 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003369 psbs("No write since last change (:quit! overrides)");
3370 } else {
3371 editing = 0;
3372 }
Eric Andersen822c3832001-05-07 17:37:43 +00003373 } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
Robert Griebla71389b2002-07-31 21:22:21 +00003374 strncasecmp((char *) p, "wq", cnt) == 0 ||
3375 strncasecmp((char *) p, "x", cnt) == 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003376 cnt = file_write(cfn, text, end - 1);
3377 file_modified = FALSE;
3378 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
Robert Griebla71389b2002-07-31 21:22:21 +00003379 if (p[0] == 'x' || p[1] == 'q') {
Eric Andersen3f980402001-04-04 17:31:15 +00003380 editing = 0;
3381 }
Eric Andersen822c3832001-05-07 17:37:43 +00003382 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
3383 edit_status(); // show current file status
3384 } else if (sscanf((char *) p, "%d", &j) > 0) {
3385 dot = find_line(j); // go to line # j
3386 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003387 } else { // unrecognised cmd
Eric Andersen822c3832001-05-07 17:37:43 +00003388 ni((Byte *) p);
Eric Andersen3f980402001-04-04 17:31:15 +00003389 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003390#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003391 break;
3392 case '<': // <- Left shift something
3393 case '>': // >- Right shift something
3394 cnt = count_lines(text, dot); // remember what line we are on
3395 c1 = get_one_char(); // get the type of thing to delete
3396 find_range(&p, &q, c1);
3397 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3398 p = begin_line(p);
3399 q = end_line(q);
3400 i = count_lines(p, q); // # of lines we are shifting
3401 for ( ; i > 0; i--, p = next_line(p)) {
3402 if (c == '<') {
3403 // shift left- remove tab or 8 spaces
3404 if (*p == '\t') {
3405 // shrink buffer 1 char
3406 (void) text_hole_delete(p, p);
3407 } else if (*p == ' ') {
3408 // we should be calculating columns, not just SPACE
3409 for (j = 0; *p == ' ' && j < tabstop; j++) {
3410 (void) text_hole_delete(p, p);
3411 }
3412 }
3413 } else if (c == '>') {
3414 // shift right -- add tab or 8 spaces
3415 (void) char_insert(p, '\t');
3416 }
3417 }
3418 dot = find_line(cnt); // what line were we on
3419 dot_skip_over_ws();
3420 end_cmd_q(); // stop adding to q
3421 break;
3422 case 'A': // A- append at e-o-l
3423 dot_end(); // go to e-o-l
3424 //**** fall thru to ... 'a'
3425 case 'a': // a- append after current char
3426 if (*dot != '\n')
3427 dot++;
3428 goto dc_i;
3429 break;
3430 case 'B': // B- back a blank-delimited Word
3431 case 'E': // E- end of a blank-delimited word
3432 case 'W': // W- forward a blank-delimited word
3433 if (cmdcnt-- > 1) {
3434 do_cmd(c);
3435 } // repeat cnt
3436 dir = FORWARD;
3437 if (c == 'B')
3438 dir = BACK;
3439 if (c == 'W' || isspace(dot[dir])) {
3440 dot = skip_thing(dot, 1, dir, S_TO_WS);
3441 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3442 }
3443 if (c != 'W')
3444 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3445 break;
3446 case 'C': // C- Change to e-o-l
3447 case 'D': // D- delete to e-o-l
3448 save_dot = dot;
3449 dot = dollar_line(dot); // move to before NL
3450 // copy text into a register and delete
3451 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3452 if (c == 'C')
3453 goto dc_i; // start inserting
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003454#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003455 if (c == 'D')
3456 end_cmd_q(); // stop adding to q
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003457#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003458 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003459 case 'G': // G- goto to a line number (default= E-O-F)
3460 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003461 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003462 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003463 }
3464 dot_skip_over_ws();
3465 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003466 case 'H': // H- goto top line on screen
3467 dot = screenbegin;
3468 if (cmdcnt > (rows - 1)) {
3469 cmdcnt = (rows - 1);
3470 }
3471 if (cmdcnt-- > 1) {
3472 do_cmd('+');
3473 } // repeat cnt
3474 dot_skip_over_ws();
3475 break;
3476 case 'I': // I- insert before first non-blank
3477 dot_begin(); // 0
3478 dot_skip_over_ws();
3479 //**** fall thru to ... 'i'
3480 case 'i': // i- insert before current char
3481 case VI_K_INSERT: // Cursor Key Insert
3482 dc_i:
3483 cmd_mode = 1; // start insrting
3484 psb("-- Insert --");
3485 break;
3486 case 'J': // J- join current and next lines together
3487 if (cmdcnt-- > 2) {
3488 do_cmd(c);
3489 } // repeat cnt
3490 dot_end(); // move to NL
3491 if (dot < end - 1) { // make sure not last char in text[]
3492 *dot++ = ' '; // replace NL with space
3493 while (isblnk(*dot)) { // delete leading WS
3494 dot_delete();
3495 }
3496 }
3497 end_cmd_q(); // stop adding to q
3498 break;
3499 case 'L': // L- goto bottom line on screen
3500 dot = end_screen();
3501 if (cmdcnt > (rows - 1)) {
3502 cmdcnt = (rows - 1);
3503 }
3504 if (cmdcnt-- > 1) {
3505 do_cmd('-');
3506 } // repeat cnt
3507 dot_begin();
3508 dot_skip_over_ws();
3509 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003510 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003511 dot = screenbegin;
3512 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3513 dot = next_line(dot);
3514 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003515 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003516 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003517 p = begin_line(dot);
3518 if (p[-1] == '\n') {
3519 dot_prev();
3520 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3521 dot_end();
3522 dot = char_insert(dot, '\n');
3523 } else {
3524 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003525 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003526 dot_prev(); // -
3527 }
3528 goto dc_i;
3529 break;
3530 case 'R': // R- continuous Replace char
Eric Andersen1c0d3112001-04-16 15:46:44 +00003531 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003532 cmd_mode = 2;
3533 psb("-- Replace --");
3534 break;
3535 case 'X': // X- delete char before dot
3536 case 'x': // x- delete the current char
3537 case 's': // s- substitute the current char
3538 if (cmdcnt-- > 1) {
3539 do_cmd(c);
3540 } // repeat cnt
3541 dir = 0;
3542 if (c == 'X')
3543 dir = -1;
3544 if (dot[dir] != '\n') {
3545 if (c == 'X')
3546 dot--; // delete prev char
3547 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3548 }
3549 if (c == 's')
3550 goto dc_i; // start insrting
3551 end_cmd_q(); // stop adding to q
3552 break;
3553 case 'Z': // Z- if modified, {write}; exit
3554 // ZZ means to save file (if necessary), then exit
3555 c1 = get_one_char();
3556 if (c1 != 'Z') {
3557 indicate_error(c);
3558 break;
3559 }
Matt Kraai1f0c4362001-12-20 23:13:26 +00003560 if (file_modified
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003561#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00003562 && ! vi_readonly
3563 && ! readonly
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003564#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00003565 ) {
3566 cnt = file_write(cfn, text, end - 1);
3567 if (cnt == (end - 1 - text + 1)) {
3568 editing = 0;
3569 }
3570 } else {
3571 editing = 0;
3572 }
3573 break;
3574 case '^': // ^- move to first non-blank on line
3575 dot_begin();
3576 dot_skip_over_ws();
3577 break;
3578 case 'b': // b- back a word
3579 case 'e': // e- end of word
3580 if (cmdcnt-- > 1) {
3581 do_cmd(c);
3582 } // repeat cnt
3583 dir = FORWARD;
3584 if (c == 'b')
3585 dir = BACK;
3586 if ((dot + dir) < text || (dot + dir) > end - 1)
3587 break;
3588 dot += dir;
3589 if (isspace(*dot)) {
3590 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3591 }
3592 if (isalnum(*dot) || *dot == '_') {
3593 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3594 } else if (ispunct(*dot)) {
3595 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3596 }
3597 break;
3598 case 'c': // c- change something
3599 case 'd': // d- delete something
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003600#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003601 case 'y': // y- yank something
3602 case 'Y': // Y- Yank a line
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003603#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003604 yf = YANKDEL; // assume either "c" or "d"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003605#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003606 if (c == 'y' || c == 'Y')
3607 yf = YANKONLY;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003608#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003609 c1 = 'y';
3610 if (c != 'Y')
3611 c1 = get_one_char(); // get the type of thing to delete
3612 find_range(&p, &q, c1);
3613 if (c1 == 27) { // ESC- user changed mind and wants out
3614 c = c1 = 27; // Escape- do nothing
3615 } else if (strchr("wW", c1)) {
3616 if (c == 'c') {
3617 // don't include trailing WS as part of word
3618 while (isblnk(*q)) {
3619 if (q <= text || q[-1] == '\n')
3620 break;
3621 q--;
3622 }
3623 }
3624 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003625 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003626 // single line copy text into a register and delete
3627 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003628 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003629 // multiple line copy text into a register and delete
3630 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003631 if (c == 'c') {
3632 dot = char_insert(dot, '\n');
3633 // on the last line of file don't move to prev line
3634 if (dot != (end-1)) {
3635 dot_prev();
3636 }
3637 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003638 dot_begin();
3639 dot_skip_over_ws();
3640 }
3641 } else {
3642 // could not recognize object
3643 c = c1 = 27; // error-
3644 indicate_error(c);
3645 }
3646 if (c1 != 27) {
3647 // if CHANGING, not deleting, start inserting after the delete
3648 if (c == 'c') {
3649 strcpy((char *) buf, "Change");
3650 goto dc_i; // start inserting
3651 }
3652 if (c == 'd') {
3653 strcpy((char *) buf, "Delete");
3654 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003655#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003656 if (c == 'y' || c == 'Y') {
3657 strcpy((char *) buf, "Yank");
3658 }
3659 p = reg[YDreg];
3660 q = p + strlen((char *) p);
3661 for (cnt = 0; p <= q; p++) {
3662 if (*p == '\n')
3663 cnt++;
3664 }
3665 psb("%s %d lines (%d chars) using [%c]",
3666 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003667#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003668 end_cmd_q(); // stop adding to q
3669 }
3670 break;
3671 case 'k': // k- goto prev line, same col
3672 case VI_K_UP: // cursor key Up
3673 if (cmdcnt-- > 1) {
3674 do_cmd(c);
3675 } // repeat cnt
3676 dot_prev();
3677 dot = move_to_col(dot, ccol + offset); // try stay in same col
3678 break;
3679 case 'r': // r- replace the current char with user input
3680 c1 = get_one_char(); // get the replacement char
3681 if (*dot != '\n') {
3682 *dot = c1;
3683 file_modified = TRUE; // has the file been modified
3684 }
3685 end_cmd_q(); // stop adding to q
3686 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003687 case 't': // t- move to char prior to next x
3688 last_forward_char = get_one_char();
3689 do_cmd(';');
3690 if (*dot == last_forward_char)
3691 dot_left();
3692 last_forward_char= 0;
3693 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003694 case 'w': // w- forward a word
3695 if (cmdcnt-- > 1) {
3696 do_cmd(c);
3697 } // repeat cnt
3698 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3699 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3700 } else if (ispunct(*dot)) { // we are on PUNCT
3701 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3702 }
3703 if (dot < end - 1)
3704 dot++; // move over word
3705 if (isspace(*dot)) {
3706 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3707 }
3708 break;
3709 case 'z': // z-
3710 c1 = get_one_char(); // get the replacement char
3711 cnt = 0;
3712 if (c1 == '.')
3713 cnt = (rows - 2) / 2; // put dot at center
3714 if (c1 == '-')
3715 cnt = rows - 2; // put dot at bottom
3716 screenbegin = begin_line(dot); // start dot at top
3717 dot_scroll(cnt, -1);
3718 break;
3719 case '|': // |- move to column "cmdcnt"
3720 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3721 break;
3722 case '~': // ~- flip the case of letters a-z -> A-Z
3723 if (cmdcnt-- > 1) {
3724 do_cmd(c);
3725 } // repeat cnt
3726 if (islower(*dot)) {
3727 *dot = toupper(*dot);
3728 file_modified = TRUE; // has the file been modified
3729 } else if (isupper(*dot)) {
3730 *dot = tolower(*dot);
3731 file_modified = TRUE; // has the file been modified
3732 }
3733 dot_right();
3734 end_cmd_q(); // stop adding to q
3735 break;
3736 //----- The Cursor and Function Keys -----------------------------
3737 case VI_K_HOME: // Cursor Key Home
3738 dot_begin();
3739 break;
3740 // The Fn keys could point to do_macro which could translate them
3741 case VI_K_FUN1: // Function Key F1
3742 case VI_K_FUN2: // Function Key F2
3743 case VI_K_FUN3: // Function Key F3
3744 case VI_K_FUN4: // Function Key F4
3745 case VI_K_FUN5: // Function Key F5
3746 case VI_K_FUN6: // Function Key F6
3747 case VI_K_FUN7: // Function Key F7
3748 case VI_K_FUN8: // Function Key F8
3749 case VI_K_FUN9: // Function Key F9
3750 case VI_K_FUN10: // Function Key F10
3751 case VI_K_FUN11: // Function Key F11
3752 case VI_K_FUN12: // Function Key F12
3753 break;
3754 }
3755
3756 dc1:
3757 // if text[] just became empty, add back an empty line
3758 if (end == text) {
3759 (void) char_insert(text, '\n'); // start empty buf with dummy line
3760 dot = text;
3761 }
3762 // it is OK for dot to exactly equal to end, otherwise check dot validity
3763 if (dot != end) {
3764 dot = bound_dot(dot); // make sure "dot" is valid
3765 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003766#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003767 check_context(c); // update the current context
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003768#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003769
3770 if (!isdigit(c))
3771 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3772 cnt = dot - begin_line(dot);
3773 // Try to stay off of the Newline
3774 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3775 dot--;
3776}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003777
3778#ifdef CONFIG_FEATURE_VI_CRASHME
3779static int totalcmds = 0;
3780static int Mp = 85; // Movement command Probability
3781static int Np = 90; // Non-movement command Probability
3782static int Dp = 96; // Delete command Probability
3783static int Ip = 97; // Insert command Probability
3784static int Yp = 98; // Yank command Probability
3785static int Pp = 99; // Put command Probability
3786static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3787char chars[20] = "\t012345 abcdABCD-=.$";
3788char *words[20] = { "this", "is", "a", "test",
3789 "broadcast", "the", "emergency", "of",
3790 "system", "quick", "brown", "fox",
3791 "jumped", "over", "lazy", "dogs",
3792 "back", "January", "Febuary", "March"
3793};
3794char *lines[20] = {
3795 "You should have received a copy of the GNU General Public License\n",
3796 "char c, cm, *cmd, *cmd1;\n",
3797 "generate a command by percentages\n",
3798 "Numbers may be typed as a prefix to some commands.\n",
3799 "Quit, discarding changes!\n",
3800 "Forced write, if permission originally not valid.\n",
3801 "In general, any ex or ed command (such as substitute or delete).\n",
3802 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3803 "Please get w/ me and I will go over it with you.\n",
3804 "The following is a list of scheduled, committed changes.\n",
3805 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3806 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3807 "Any question about transactions please contact Sterling Huxley.\n",
3808 "I will try to get back to you by Friday, December 31.\n",
3809 "This Change will be implemented on Friday.\n",
3810 "Let me know if you have problems accessing this;\n",
3811 "Sterling Huxley recently added you to the access list.\n",
3812 "Would you like to go to lunch?\n",
3813 "The last command will be automatically run.\n",
3814 "This is too much english for a computer geek.\n",
3815};
3816char *multilines[20] = {
3817 "You should have received a copy of the GNU General Public License\n",
3818 "char c, cm, *cmd, *cmd1;\n",
3819 "generate a command by percentages\n",
3820 "Numbers may be typed as a prefix to some commands.\n",
3821 "Quit, discarding changes!\n",
3822 "Forced write, if permission originally not valid.\n",
3823 "In general, any ex or ed command (such as substitute or delete).\n",
3824 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3825 "Please get w/ me and I will go over it with you.\n",
3826 "The following is a list of scheduled, committed changes.\n",
3827 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3828 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3829 "Any question about transactions please contact Sterling Huxley.\n",
3830 "I will try to get back to you by Friday, December 31.\n",
3831 "This Change will be implemented on Friday.\n",
3832 "Let me know if you have problems accessing this;\n",
3833 "Sterling Huxley recently added you to the access list.\n",
3834 "Would you like to go to lunch?\n",
3835 "The last command will be automatically run.\n",
3836 "This is too much english for a computer geek.\n",
3837};
3838
3839// create a random command to execute
3840static void crash_dummy()
3841{
3842 static int sleeptime; // how long to pause between commands
3843 char c, cm, *cmd, *cmd1;
3844 int i, cnt, thing, rbi, startrbi, percent;
3845
3846 // "dot" movement commands
3847 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3848
3849 // is there already a command running?
3850 if (readed_for_parse > 0)
3851 goto cd1;
3852 cd0:
3853 startrbi = rbi = 0;
3854 sleeptime = 0; // how long to pause between commands
3855 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3856 // generate a command by percentages
3857 percent = (int) lrand48() % 100; // get a number from 0-99
3858 if (percent < Mp) { // Movement commands
3859 // available commands
3860 cmd = cmd1;
3861 M++;
3862 } else if (percent < Np) { // non-movement commands
3863 cmd = "mz<>\'\""; // available commands
3864 N++;
3865 } else if (percent < Dp) { // Delete commands
3866 cmd = "dx"; // available commands
3867 D++;
3868 } else if (percent < Ip) { // Inset commands
3869 cmd = "iIaAsrJ"; // available commands
3870 I++;
3871 } else if (percent < Yp) { // Yank commands
3872 cmd = "yY"; // available commands
3873 Y++;
3874 } else if (percent < Pp) { // Put commands
3875 cmd = "pP"; // available commands
3876 P++;
3877 } else {
3878 // We do not know how to handle this command, try again
3879 U++;
3880 goto cd0;
3881 }
3882 // randomly pick one of the available cmds from "cmd[]"
3883 i = (int) lrand48() % strlen(cmd);
3884 cm = cmd[i];
3885 if (strchr(":\024", cm))
3886 goto cd0; // dont allow colon or ctrl-T commands
3887 readbuffer[rbi++] = cm; // put cmd into input buffer
3888
3889 // now we have the command-
3890 // there are 1, 2, and multi char commands
3891 // find out which and generate the rest of command as necessary
3892 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3893 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3894 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3895 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3896 }
3897 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3898 c = cmd1[thing];
3899 readbuffer[rbi++] = c; // add movement to input buffer
3900 }
3901 if (strchr("iIaAsc", cm)) { // multi-char commands
3902 if (cm == 'c') {
3903 // change some thing
3904 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3905 c = cmd1[thing];
3906 readbuffer[rbi++] = c; // add movement to input buffer
3907 }
3908 thing = (int) lrand48() % 4; // what thing to insert
3909 cnt = (int) lrand48() % 10; // how many to insert
3910 for (i = 0; i < cnt; i++) {
3911 if (thing == 0) { // insert chars
3912 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3913 } else if (thing == 1) { // insert words
3914 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
3915 strcat((char *) readbuffer, " ");
3916 sleeptime = 0; // how fast to type
3917 } else if (thing == 2) { // insert lines
3918 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
3919 sleeptime = 0; // how fast to type
3920 } else { // insert multi-lines
3921 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
3922 sleeptime = 0; // how fast to type
3923 }
3924 }
3925 strcat((char *) readbuffer, "\033");
3926 }
3927 readed_for_parse = strlen(readbuffer);
3928 cd1:
3929 totalcmds++;
3930 if (sleeptime > 0)
3931 (void) mysleep(sleeptime); // sleep 1/100 sec
3932}
3933
3934// test to see if there are any errors
3935static void crash_test()
3936{
3937 static time_t oldtim;
3938 time_t tim;
Glenn L McGrath7127b582002-12-03 21:48:15 +00003939 char d[2], msg[BUFSIZ];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003940
3941 msg[0] = '\0';
3942 if (end < text) {
3943 strcat((char *) msg, "end<text ");
3944 }
3945 if (end > textend) {
3946 strcat((char *) msg, "end>textend ");
3947 }
3948 if (dot < text) {
3949 strcat((char *) msg, "dot<text ");
3950 }
3951 if (dot > end) {
3952 strcat((char *) msg, "dot>end ");
3953 }
3954 if (screenbegin < text) {
3955 strcat((char *) msg, "screenbegin<text ");
3956 }
3957 if (screenbegin > end - 1) {
3958 strcat((char *) msg, "screenbegin>end-1 ");
3959 }
3960
3961 if (strlen(msg) > 0) {
3962 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00003963 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003964 totalcmds, last_input_char, msg, SOs, SOn);
3965 fflush(stdout);
3966 while (read(0, d, 1) > 0) {
3967 if (d[0] == '\n' || d[0] == '\r')
3968 break;
3969 }
3970 alarm(3);
3971 }
3972 tim = (time_t) time((time_t *) 0);
3973 if (tim >= (oldtim + 3)) {
3974 sprintf((char *) status_buffer,
3975 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
3976 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
3977 oldtim = tim;
3978 }
3979 return;
3980}
3981#endif /* CONFIG_FEATURE_VI_CRASHME */