blob: c5a37d75086d7a3a7c9b5f6c6b4c804eaeadf18f [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 Andersenaff114c2004-04-14 17:51:38 +000022 "$Id: vi.c,v 1.36 2004/04/14 17:51:09 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
Eric Andersenaff114c2004-04-14 17:51:38 +000048//---- Feature -------------- Bytes to implement
Eric Andersen3f980402001-04-04 17:31:15 +000049#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;
Eric Andersen90fb65f2004-03-31 11:12:51 +0000378 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000379 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000380#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen822c3832001-05-07 17:37:43 +0000381 //case 'r': // recover flag- ignore- we don't use tmp file
382 //case 'x': // encryption flag- ignore
383 //case 'c': // execute command first
384 //case 'h': // help -- just use default
Eric Andersen3f980402001-04-04 17:31:15 +0000385 default:
386 show_help();
Eric Andersendd8500b2001-07-02 18:06:14 +0000387 return 1;
Eric Andersen3f980402001-04-04 17:31:15 +0000388 }
389 }
390
391 // The argv array can be used by the ":next" and ":rewind" commands
392 // save optind.
393 fn_start = optind; // remember first file name for :next and :rew
394 save_argc = argc;
395
396 //----- This is the main file handling loop --------------
397 if (optind >= argc) {
398 editing = 1; // 0= exit, 1= one file, 2= multiple files
399 edit_file(0);
400 } else {
401 for (; optind < argc; optind++) {
402 editing = 1; // 0=exit, 1=one file, 2+ =many files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000403 free(cfn);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000404 cfn = (Byte *) bb_xstrdup(argv[optind]);
Eric Andersen3f980402001-04-04 17:31:15 +0000405 edit_file(cfn);
406 }
407 }
408 //-----------------------------------------------------------
409
410 return (0);
411}
412
Eric Andersen8efe9672003-09-15 08:33:45 +0000413#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
414//----- See what the window size currently is --------------------
415static inline void window_size_get(int fd)
416{
417 get_terminal_width_height(fd, &columns, &rows);
418}
419#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
420
Eric Andersen3f980402001-04-04 17:31:15 +0000421static void edit_file(Byte * fn)
422{
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000423 Byte c;
Eric Andersen1c0d3112001-04-16 15:46:44 +0000424 int cnt, size, ch;
Eric Andersen3f980402001-04-04 17:31:15 +0000425
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000426#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000427 int sig;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000428#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
429#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000430 static Byte *cur_line;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000431#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000432
433 rawmode();
434 rows = 24;
435 columns = 80;
Eric Andersen822c3832001-05-07 17:37:43 +0000436 ch= -1;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000437#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000438 window_size_get(0);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000439#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
Eric Andersen3f980402001-04-04 17:31:15 +0000440 new_screen(rows, columns); // get memory for virtual screen
441
442 cnt = file_size(fn); // file size
443 size = 2 * cnt; // 200% of file size
444 new_text(size); // get a text[] buffer
445 screenbegin = dot = end = text;
446 if (fn != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000447 ch= file_insert(fn, text, cnt);
448 }
449 if (ch < 1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000450 (void) char_insert(text, '\n'); // start empty buf with dummy line
451 }
452 file_modified = FALSE;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000453#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000454 YDreg = 26; // default Yank/Delete reg
455 Ureg = 27; // hold orig line for "U" cmd
456 for (cnt = 0; cnt < 28; cnt++) {
457 mark[cnt] = 0;
458 } // init the marks
459 mark[26] = mark[27] = text; // init "previous context"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000460#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000461
Eric Andersen3f980402001-04-04 17:31:15 +0000462 last_forward_char = last_input_char = '\0';
463 crow = 0;
464 ccol = 0;
465 edit_status();
466
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000467#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000468 catch_sig(0);
469 core_sig(0);
Eric Andersen3f980402001-04-04 17:31:15 +0000470 signal(SIGWINCH, winch_sig);
471 signal(SIGTSTP, suspend_sig);
472 sig = setjmp(restart);
473 if (sig != 0) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000474 const char *msg = "";
475
Eric Andersen3f980402001-04-04 17:31:15 +0000476 if (sig == SIGWINCH)
477 msg = "(window resize)";
478 if (sig == SIGHUP)
479 msg = "(hangup)";
480 if (sig == SIGINT)
481 msg = "(interrupt)";
482 if (sig == SIGTERM)
483 msg = "(terminate)";
484 if (sig == SIGBUS)
485 msg = "(bus error)";
486 if (sig == SIGSEGV)
487 msg = "(I tried to touch invalid memory)";
488 if (sig == SIGALRM)
489 msg = "(alarm)";
490
491 psbs("-- caught signal %d %s--", sig, msg);
Eric Andersen1c0d3112001-04-16 15:46:44 +0000492 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000493 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000494#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +0000495
496 editing = 1;
497 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
498 cmdcnt = 0;
499 tabstop = 8;
500 offset = 0; // no horizontal offset
501 c = '\0';
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000502#ifdef CONFIG_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000503 free(last_modifying_cmd);
504 free(ioq_start);
Eric Andersen3f980402001-04-04 17:31:15 +0000505 ioq = ioq_start = last_modifying_cmd = 0;
506 adding2q = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000507#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen822c3832001-05-07 17:37:43 +0000508 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000509 show_status_line();
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000510 fflush(stdout);
Eric Andersen3f980402001-04-04 17:31:15 +0000511
512 //------This is the main Vi cmd handling loop -----------------------
513 while (editing > 0) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000514#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000515 if (crashme > 0) {
516 if ((end - text) > 1) {
517 crash_dummy(); // generate a random command
518 } else {
519 crashme = 0;
520 dot =
521 string_insert(text, (Byte *) "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
522 refresh(FALSE);
523 }
524 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000525#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000526 last_input_char = c = get_one_char(); // get a cmd from user
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000527#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000528 // save a copy of the current line- for the 'U" command
529 if (begin_line(dot) != cur_line) {
530 cur_line = begin_line(dot);
531 text_yank(begin_line(dot), end_line(dot), Ureg);
532 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000533#endif /* CONFIG_FEATURE_VI_YANKMARK */
534#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000535 // These are commands that change text[].
536 // Remember the input for the "." command
537 if (!adding2q && ioq_start == 0
538 && strchr((char *) modifying_cmds, c) != NULL) {
539 start_new_cmd_q(c);
540 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000541#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000542 do_cmd(c); // execute the user command
543 //
544 // poll to see if there is input already waiting. if we are
545 // not able to display output fast enough to keep up, skip
546 // the display update until we catch up with input.
547 if (mysleep(0) == 0) {
548 // no input pending- so update output
549 refresh(FALSE);
550 show_status_line();
551 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000552#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000553 if (crashme > 0)
554 crash_test(); // test editor variables
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000555#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000556 }
557 //-------------------------------------------------------------------
558
Eric Andersen822c3832001-05-07 17:37:43 +0000559 place_cursor(rows, 0, FALSE); // go to bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +0000560 clear_to_eol(); // Erase to end of line
561 cookmode();
562}
563
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000564//----- The Colon commands -------------------------------------
565#ifdef CONFIG_FEATURE_VI_COLON
566static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present
567{
568 int st;
569 Byte *q;
570
571#ifdef CONFIG_FEATURE_VI_YANKMARK
572 Byte c;
573#endif /* CONFIG_FEATURE_VI_YANKMARK */
574#ifdef CONFIG_FEATURE_VI_SEARCH
575 Byte *pat, buf[BUFSIZ];
576#endif /* CONFIG_FEATURE_VI_SEARCH */
577
578 *addr = -1; // assume no addr
579 if (*p == '.') { // the current line
580 p++;
581 q = begin_line(dot);
582 *addr = count_lines(text, q);
583#ifdef CONFIG_FEATURE_VI_YANKMARK
584 } else if (*p == '\'') { // is this a mark addr
585 p++;
586 c = tolower(*p);
587 p++;
588 if (c >= 'a' && c <= 'z') {
589 // we have a mark
590 c = c - 'a';
591 q = mark[(int) c];
592 if (q != NULL) { // is mark valid
593 *addr = count_lines(text, q); // count lines
594 }
595 }
596#endif /* CONFIG_FEATURE_VI_YANKMARK */
597#ifdef CONFIG_FEATURE_VI_SEARCH
598 } else if (*p == '/') { // a search pattern
599 q = buf;
600 for (p++; *p; p++) {
601 if (*p == '/')
602 break;
603 *q++ = *p;
604 *q = '\0';
605 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000606 pat = (Byte *) bb_xstrdup((char *) buf); // save copy of pattern
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000607 if (*p == '/')
608 p++;
609 q = char_search(dot, pat, FORWARD, FULL);
610 if (q != NULL) {
611 *addr = count_lines(text, q);
612 }
613 free(pat);
614#endif /* CONFIG_FEATURE_VI_SEARCH */
615 } else if (*p == '$') { // the last line in file
616 p++;
617 q = begin_line(end - 1);
618 *addr = count_lines(text, q);
619 } else if (isdigit(*p)) { // specific line number
620 sscanf((char *) p, "%d%n", addr, &st);
621 p += st;
622 } else { // I don't reconise this
623 // unrecognised address- assume -1
624 *addr = -1;
625 }
626 return (p);
627}
628
629static Byte *get_address(Byte *p, int *b, int *e) // get two colon addrs, if present
630{
631 //----- get the address' i.e., 1,3 'a,'b -----
632 // get FIRST addr, if present
633 while (isblnk(*p))
634 p++; // skip over leading spaces
635 if (*p == '%') { // alias for 1,$
636 p++;
637 *b = 1;
638 *e = count_lines(text, end-1);
639 goto ga0;
640 }
641 p = get_one_address(p, b);
642 while (isblnk(*p))
643 p++;
Eric Andersenaff114c2004-04-14 17:51:38 +0000644 if (*p == ',') { // is there a address separator
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000645 p++;
646 while (isblnk(*p))
647 p++;
648 // get SECOND addr, if present
649 p = get_one_address(p, e);
650 }
651ga0:
652 while (isblnk(*p))
653 p++; // skip over trailing spaces
654 return (p);
655}
656
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000657#ifdef CONFIG_FEATURE_VI_SETOPTS
658static void setops(const Byte *args, const char *opname, int flg_no,
659 const char *short_opname, int opt)
660{
661 const char *a = (char *) args + flg_no;
662 int l = strlen(opname) - 1; /* opname have + ' ' */
663
664 if (strncasecmp(a, opname, l) == 0 ||
665 strncasecmp(a, short_opname, 2) == 0) {
666 if(flg_no)
667 vi_setops &= ~opt;
668 else
669 vi_setops |= opt;
670 }
671}
672#endif
673
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000674static void colon(Byte * buf)
675{
676 Byte c, *orig_buf, *buf1, *q, *r;
677 Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
678 int i, l, li, ch, st, b, e;
679 int useforce, forced;
680 struct stat st_buf;
681
682 // :3154 // if (-e line 3154) goto it else stay put
683 // :4,33w! foo // write a portion of buffer to file "foo"
684 // :w // write all of buffer to current file
685 // :q // quit
686 // :q! // quit- dont care about modified file
687 // :'a,'z!sort -u // filter block through sort
688 // :'f // goto mark "f"
689 // :'fl // list literal the mark "f" line
690 // :.r bar // read file "bar" into buffer before dot
691 // :/123/,/abc/d // delete lines from "123" line to "abc" line
692 // :/xyz/ // goto the "xyz" line
693 // :s/find/replace/ // substitute pattern "find" with "replace"
694 // :!<cmd> // run <cmd> then return
695 //
696 if (strlen((char *) buf) <= 0)
697 goto vc1;
698 if (*buf == ':')
699 buf++; // move past the ':'
700
701 forced = useforce = FALSE;
702 li = st = ch = i = 0;
703 b = e = -1;
704 q = text; // assume 1,$ for the range
705 r = end - 1;
706 li = count_lines(text, end - 1);
707 fn = cfn; // default to current file
708 memset(cmd, '\0', BUFSIZ); // clear cmd[]
709 memset(args, '\0', BUFSIZ); // clear args[]
710
711 // look for optional address(es) :. :1 :1,9 :'q,'a :%
712 buf = get_address(buf, &b, &e);
713
714 // remember orig command line
715 orig_buf = buf;
716
717 // get the COMMAND into cmd[]
718 buf1 = cmd;
719 while (*buf != '\0') {
720 if (isspace(*buf))
721 break;
722 *buf1++ = *buf++;
723 }
724 // get any ARGuments
725 while (isblnk(*buf))
726 buf++;
727 strcpy((char *) args, (char *) buf);
728 buf1 = last_char_is((char *)cmd, '!');
729 if (buf1) {
730 useforce = TRUE;
731 *buf1 = '\0'; // get rid of !
732 }
733 if (b >= 0) {
734 // if there is only one addr, then the addr
735 // is the line number of the single line the
736 // user wants. So, reset the end
737 // pointer to point at end of the "b" line
738 q = find_line(b); // what line is #b
739 r = end_line(q);
740 li = 1;
741 }
742 if (e >= 0) {
743 // we were given two addrs. change the
744 // end pointer to the addr given by user.
745 r = find_line(e); // what line is #e
746 r = end_line(r);
747 li = e - b + 1;
748 }
749 // ------------ now look for the command ------------
750 i = strlen((char *) cmd);
751 if (i == 0) { // :123CR goto line #123
752 if (b >= 0) {
753 dot = find_line(b); // what line is #b
754 dot_skip_over_ws();
755 }
756 } else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd
757 // :!ls run the <cmd>
758 (void) alarm(0); // wait for input- no alarms
759 place_cursor(rows - 1, 0, FALSE); // go to Status line
760 clear_to_eol(); // clear the line
761 cookmode();
762 system(orig_buf+1); // run the cmd
763 rawmode();
764 Hit_Return(); // let user see results
765 (void) alarm(3); // done waiting for input
766 } else if (strncmp((char *) cmd, "=", i) == 0) { // where is the address
767 if (b < 0) { // no addr given- use defaults
768 b = e = count_lines(text, dot);
769 }
770 psb("%d", b);
771 } else if (strncasecmp((char *) cmd, "delete", i) == 0) { // delete lines
772 if (b < 0) { // no addr given- use defaults
773 q = begin_line(dot); // assume .,. for the range
774 r = end_line(dot);
775 }
776 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
777 dot_skip_over_ws();
778 } else if (strncasecmp((char *) cmd, "edit", i) == 0) { // Edit a file
779 int sr;
780 sr= 0;
781 // don't edit, if the current file has been modified
782 if (file_modified && ! useforce) {
783 psbs("No write since last change (:edit! overrides)");
784 goto vc1;
785 }
786 if (strlen(args) > 0) {
787 // the user supplied a file name
788 fn= args;
789 } else if (cfn != 0 && strlen(cfn) > 0) {
790 // no user supplied name- use the current filename
791 fn= cfn;
792 goto vc5;
793 } else {
794 // no user file name, no current name- punt
795 psbs("No current filename");
796 goto vc1;
797 }
798
799 // see if file exists- if not, its just a new file request
800 if ((sr=stat((char*)fn, &st_buf)) < 0) {
801 // This is just a request for a new file creation.
802 // The file_insert below will fail but we get
803 // an empty buffer with a file name. Then the "write"
804 // command can do the create.
805 } else {
806 if ((st_buf.st_mode & (S_IFREG)) == 0) {
807 // This is not a regular file
808 psbs("\"%s\" is not a regular file", fn);
809 goto vc1;
810 }
811 if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
812 // dont have any read permissions
813 psbs("\"%s\" is not readable", fn);
814 goto vc1;
815 }
816 }
817
818 // There is a read-able regular file
819 // make this the current file
Manuel Novoa III cad53642003-03-19 09:13:01 +0000820 q = (Byte *) bb_xstrdup((char *) fn); // save the cfn
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000821 free(cfn); // free the old name
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000822 cfn = q; // remember new cfn
823
824 vc5:
825 // delete all the contents of text[]
826 new_text(2 * file_size(fn));
827 screenbegin = dot = end = text;
828
829 // insert new file
830 ch = file_insert(fn, text, file_size(fn));
831
832 if (ch < 1) {
833 // start empty buf with dummy line
834 (void) char_insert(text, '\n');
835 ch= 1;
836 }
837 file_modified = FALSE;
838#ifdef CONFIG_FEATURE_VI_YANKMARK
839 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
840 free(reg[Ureg]); // free orig line reg- for 'U'
841 reg[Ureg]= 0;
842 }
843 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
844 free(reg[YDreg]); // free default yank/delete register
845 reg[YDreg]= 0;
846 }
847 for (li = 0; li < 28; li++) {
848 mark[li] = 0;
849 } // init the marks
850#endif /* CONFIG_FEATURE_VI_YANKMARK */
851 // how many lines in text[]?
852 li = count_lines(text, end - 1);
853 psb("\"%s\"%s"
854#ifdef CONFIG_FEATURE_VI_READONLY
855 "%s"
856#endif /* CONFIG_FEATURE_VI_READONLY */
857 " %dL, %dC", cfn,
858 (sr < 0 ? " [New file]" : ""),
859#ifdef CONFIG_FEATURE_VI_READONLY
860 ((vi_readonly || readonly) ? " [Read only]" : ""),
861#endif /* CONFIG_FEATURE_VI_READONLY */
862 li, ch);
863 } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this
864 if (b != -1 || e != -1) {
865 ni((Byte *) "No address allowed on this command");
866 goto vc1;
867 }
868 if (strlen((char *) args) > 0) {
869 // user wants a new filename
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000870 free(cfn);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000871 cfn = (Byte *) bb_xstrdup((char *) args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000872 } else {
873 // user wants file status info
874 edit_status();
875 }
876 } else if (strncasecmp((char *) cmd, "features", i) == 0) { // what features are available
877 // print out values of all features
878 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
879 clear_to_eol(); // clear the line
880 cookmode();
881 show_help();
882 rawmode();
883 Hit_Return();
884 } else if (strncasecmp((char *) cmd, "list", i) == 0) { // literal print line
885 if (b < 0) { // no addr given- use defaults
886 q = begin_line(dot); // assume .,. for the range
887 r = end_line(dot);
888 }
889 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
890 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000891 puts("\r");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000892 for (; q <= r; q++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000893 int c_is_no_print;
894
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000895 c = *q;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000896 c_is_no_print = c > 127 && !Isprint(c);
897 if (c_is_no_print) {
898 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000899 standout_start();
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000900 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000901 if (c == '\n') {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000902 write1("$\r");
903 } else if (c < ' ' || c == 127) {
904 putchar('^');
905 if(c == 127)
906 c = '?';
907 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000908 c += '@';
909 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000910 putchar(c);
911 if (c_is_no_print)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000912 standout_end();
913 }
914#ifdef CONFIG_FEATURE_VI_SET
915 vc2:
916#endif /* CONFIG_FEATURE_VI_SET */
917 Hit_Return();
918 } else if ((strncasecmp((char *) cmd, "quit", i) == 0) || // Quit
919 (strncasecmp((char *) cmd, "next", i) == 0)) { // edit next file
920 if (useforce) {
921 // force end of argv list
922 if (*cmd == 'q') {
923 optind = save_argc;
924 }
925 editing = 0;
926 goto vc1;
927 }
928 // don't exit if the file been modified
929 if (file_modified) {
930 psbs("No write since last change (:%s! overrides)",
931 (*cmd == 'q' ? "quit" : "next"));
932 goto vc1;
933 }
934 // are there other file to edit
935 if (*cmd == 'q' && optind < save_argc - 1) {
936 psbs("%d more file to edit", (save_argc - optind - 1));
937 goto vc1;
938 }
939 if (*cmd == 'n' && optind >= save_argc - 1) {
940 psbs("No more files to edit");
941 goto vc1;
942 }
943 editing = 0;
944 } else if (strncasecmp((char *) cmd, "read", i) == 0) { // read file into text[]
945 fn = args;
946 if (strlen((char *) fn) <= 0) {
947 psbs("No filename given");
948 goto vc1;
949 }
950 if (b < 0) { // no addr given- use defaults
951 q = begin_line(dot); // assume "dot"
952 }
953 // read after current line- unless user said ":0r foo"
954 if (b != 0)
955 q = next_line(q);
956#ifdef CONFIG_FEATURE_VI_READONLY
957 l= readonly; // remember current files' status
958#endif
959 ch = file_insert(fn, q, file_size(fn));
960#ifdef CONFIG_FEATURE_VI_READONLY
961 readonly= l;
962#endif
963 if (ch < 0)
964 goto vc1; // nothing was inserted
965 // how many lines in text[]?
966 li = count_lines(q, q + ch - 1);
967 psb("\"%s\""
968#ifdef CONFIG_FEATURE_VI_READONLY
969 "%s"
970#endif /* CONFIG_FEATURE_VI_READONLY */
971 " %dL, %dC", fn,
972#ifdef CONFIG_FEATURE_VI_READONLY
973 ((vi_readonly || readonly) ? " [Read only]" : ""),
974#endif /* CONFIG_FEATURE_VI_READONLY */
975 li, ch);
976 if (ch > 0) {
977 // if the insert is before "dot" then we need to update
978 if (q <= dot)
979 dot += ch;
980 file_modified = TRUE;
981 }
982 } else if (strncasecmp((char *) cmd, "rewind", i) == 0) { // rewind cmd line args
983 if (file_modified && ! useforce) {
984 psbs("No write since last change (:rewind! overrides)");
985 } else {
986 // reset the filenames to edit
987 optind = fn_start - 1;
988 editing = 0;
989 }
990#ifdef CONFIG_FEATURE_VI_SET
991 } else if (strncasecmp((char *) cmd, "set", i) == 0) { // set or clear features
992 i = 0; // offset into args
993 if (strlen((char *) args) == 0) {
994 // print out values of all options
995 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
996 clear_to_eol(); // clear the line
997 printf("----------------------------------------\r\n");
998#ifdef CONFIG_FEATURE_VI_SETOPTS
999 if (!autoindent)
1000 printf("no");
1001 printf("autoindent ");
1002 if (!err_method)
1003 printf("no");
1004 printf("flash ");
1005 if (!ignorecase)
1006 printf("no");
1007 printf("ignorecase ");
1008 if (!showmatch)
1009 printf("no");
1010 printf("showmatch ");
1011 printf("tabstop=%d ", tabstop);
1012#endif /* CONFIG_FEATURE_VI_SETOPTS */
1013 printf("\r\n");
1014 goto vc2;
1015 }
1016 if (strncasecmp((char *) args, "no", 2) == 0)
1017 i = 2; // ":set noautoindent"
1018#ifdef CONFIG_FEATURE_VI_SETOPTS
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001019 setops(args, "autoindent ", i, "ai", VI_AUTOINDENT);
1020 setops(args, "flash ", i, "fl", VI_ERR_METHOD);
1021 setops(args, "ignorecase ", i, "ic", VI_IGNORECASE);
1022 setops(args, "showmatch ", i, "ic", VI_SHOWMATCH);
1023 if (strncasecmp((char *) args + i, "tabstop=%d ", 7) == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001024 sscanf(strchr((char *) args + i, '='), "=%d", &ch);
1025 if (ch > 0 && ch < columns - 1)
1026 tabstop = ch;
1027 }
1028#endif /* CONFIG_FEATURE_VI_SETOPTS */
1029#endif /* CONFIG_FEATURE_VI_SET */
1030#ifdef CONFIG_FEATURE_VI_SEARCH
1031 } else if (strncasecmp((char *) cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
1032 Byte *ls, *F, *R;
1033 int gflag;
1034
1035 // F points to the "find" pattern
1036 // R points to the "replace" pattern
1037 // replace the cmd line delimiters "/" with NULLs
1038 gflag = 0; // global replace flag
1039 c = orig_buf[1]; // what is the delimiter
1040 F = orig_buf + 2; // start of "find"
1041 R = (Byte *) strchr((char *) F, c); // middle delimiter
1042 if (!R) goto colon_s_fail;
1043 *R++ = '\0'; // terminate "find"
1044 buf1 = (Byte *) strchr((char *) R, c);
1045 if (!buf1) goto colon_s_fail;
1046 *buf1++ = '\0'; // terminate "replace"
1047 if (*buf1 == 'g') { // :s/foo/bar/g
1048 buf1++;
1049 gflag++; // turn on gflag
1050 }
1051 q = begin_line(q);
1052 if (b < 0) { // maybe :s/foo/bar/
1053 q = begin_line(dot); // start with cur line
1054 b = count_lines(text, q); // cur line number
1055 }
1056 if (e < 0)
1057 e = b; // maybe :.s/foo/bar/
1058 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
1059 ls = q; // orig line start
1060 vc4:
1061 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
1062 if (buf1 != NULL) {
1063 // we found the "find" pattern- delete it
1064 (void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
1065 // inset the "replace" patern
1066 (void) string_insert(buf1, R); // insert the string
1067 // check for "global" :s/foo/bar/g
1068 if (gflag == 1) {
1069 if ((buf1 + strlen((char *) R)) < end_line(ls)) {
1070 q = buf1 + strlen((char *) R);
1071 goto vc4; // don't let q move past cur line
1072 }
1073 }
1074 }
1075 q = next_line(ls);
1076 }
1077#endif /* CONFIG_FEATURE_VI_SEARCH */
1078 } else if (strncasecmp((char *) cmd, "version", i) == 0) { // show software version
1079 psb("%s", vi_Version);
1080 } else if ((strncasecmp((char *) cmd, "write", i) == 0) || // write text to file
1081 (strncasecmp((char *) cmd, "wq", i) == 0) ||
1082 (strncasecmp((char *) cmd, "x", i) == 0)) {
1083 // is there a file name to write to?
1084 if (strlen((char *) args) > 0) {
1085 fn = args;
1086 }
1087#ifdef CONFIG_FEATURE_VI_READONLY
1088 if ((vi_readonly || readonly) && ! useforce) {
1089 psbs("\"%s\" File is read only", fn);
1090 goto vc3;
1091 }
1092#endif /* CONFIG_FEATURE_VI_READONLY */
1093 // how many lines in text[]?
1094 li = count_lines(q, r);
1095 ch = r - q + 1;
1096 // see if file exists- if not, its just a new file request
1097 if (useforce) {
1098 // if "fn" is not write-able, chmod u+w
1099 // sprintf(syscmd, "chmod u+w %s", fn);
1100 // system(syscmd);
1101 forced = TRUE;
1102 }
1103 l = file_write(fn, q, r);
1104 if (useforce && forced) {
1105 // chmod u-w
1106 // sprintf(syscmd, "chmod u-w %s", fn);
1107 // system(syscmd);
1108 forced = FALSE;
1109 }
1110 psb("\"%s\" %dL, %dC", fn, li, l);
1111 if (q == text && r == end - 1 && l == ch)
1112 file_modified = FALSE;
1113 if ((cmd[0] == 'x' || cmd[1] == 'q') && l == ch) {
1114 editing = 0;
1115 }
1116#ifdef CONFIG_FEATURE_VI_READONLY
1117 vc3:;
1118#endif /* CONFIG_FEATURE_VI_READONLY */
1119#ifdef CONFIG_FEATURE_VI_YANKMARK
1120 } else if (strncasecmp((char *) cmd, "yank", i) == 0) { // yank lines
1121 if (b < 0) { // no addr given- use defaults
1122 q = begin_line(dot); // assume .,. for the range
1123 r = end_line(dot);
1124 }
1125 text_yank(q, r, YDreg);
1126 li = count_lines(q, r);
1127 psb("Yank %d lines (%d chars) into [%c]",
1128 li, strlen((char *) reg[YDreg]), what_reg());
1129#endif /* CONFIG_FEATURE_VI_YANKMARK */
1130 } else {
1131 // cmd unknown
1132 ni((Byte *) cmd);
1133 }
1134 vc1:
1135 dot = bound_dot(dot); // make sure "dot" is valid
1136 return;
1137#ifdef CONFIG_FEATURE_VI_SEARCH
1138colon_s_fail:
1139 psb(":s expression missing delimiters");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001140#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001141}
1142
1143static void Hit_Return(void)
1144{
1145 char c;
1146
1147 standout_start(); // start reverse video
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001148 write1("[Hit return to continue]");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001149 standout_end(); // end reverse video
1150 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
1151 ;
1152 redraw(TRUE); // force redraw all
1153}
1154#endif /* CONFIG_FEATURE_VI_COLON */
1155
1156//----- Synchronize the cursor to Dot --------------------------
1157static void sync_cursor(Byte * d, int *row, int *col)
1158{
1159 Byte *beg_cur, *end_cur; // begin and end of "d" line
1160 Byte *beg_scr, *end_scr; // begin and end of screen
1161 Byte *tp;
1162 int cnt, ro, co;
1163
1164 beg_cur = begin_line(d); // first char of cur line
1165 end_cur = end_line(d); // last char of cur line
1166
1167 beg_scr = end_scr = screenbegin; // first char of screen
1168 end_scr = end_screen(); // last char of screen
1169
1170 if (beg_cur < screenbegin) {
1171 // "d" is before top line on screen
1172 // how many lines do we have to move
1173 cnt = count_lines(beg_cur, screenbegin);
1174 sc1:
1175 screenbegin = beg_cur;
1176 if (cnt > (rows - 1) / 2) {
1177 // we moved too many lines. put "dot" in middle of screen
1178 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1179 screenbegin = prev_line(screenbegin);
1180 }
1181 }
1182 } else if (beg_cur > end_scr) {
1183 // "d" is after bottom line on screen
1184 // how many lines do we have to move
1185 cnt = count_lines(end_scr, beg_cur);
1186 if (cnt > (rows - 1) / 2)
1187 goto sc1; // too many lines
1188 for (ro = 0; ro < cnt - 1; ro++) {
1189 // move screen begin the same amount
1190 screenbegin = next_line(screenbegin);
1191 // now, move the end of screen
1192 end_scr = next_line(end_scr);
1193 end_scr = end_line(end_scr);
1194 }
1195 }
1196 // "d" is on screen- find out which row
1197 tp = screenbegin;
1198 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1199 if (tp == beg_cur)
1200 break;
1201 tp = next_line(tp);
1202 }
1203
1204 // find out what col "d" is on
1205 co = 0;
1206 do { // drive "co" to correct column
1207 if (*tp == '\n' || *tp == '\0')
1208 break;
1209 if (*tp == '\t') {
1210 // 7 - (co % 8 )
1211 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001212 } else if (*tp < ' ' || *tp == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001213 co++; // display as ^X, use 2 columns
1214 }
1215 } while (tp++ < d && ++co);
1216
1217 // "co" is the column where "dot" is.
1218 // The screen has "columns" columns.
1219 // The currently displayed columns are 0+offset -- columns+ofset
1220 // |-------------------------------------------------------------|
1221 // ^ ^ ^
1222 // offset | |------- columns ----------------|
1223 //
1224 // If "co" is already in this range then we do not have to adjust offset
1225 // but, we do have to subtract the "offset" bias from "co".
1226 // If "co" is outside this range then we have to change "offset".
1227 // If the first char of a line is a tab the cursor will try to stay
1228 // in column 7, but we have to set offset to 0.
1229
1230 if (co < 0 + offset) {
1231 offset = co;
1232 }
1233 if (co >= columns + offset) {
1234 offset = co - columns + 1;
1235 }
1236 // if the first char of the line is a tab, and "dot" is sitting on it
1237 // force offset to 0.
1238 if (d == beg_cur && *d == '\t') {
1239 offset = 0;
1240 }
1241 co -= offset;
1242
1243 *row = ro;
1244 *col = co;
1245}
1246
1247//----- Text Movement Routines ---------------------------------
1248static Byte *begin_line(Byte * p) // return pointer to first char cur line
1249{
1250 while (p > text && p[-1] != '\n')
1251 p--; // go to cur line B-o-l
1252 return (p);
1253}
1254
1255static Byte *end_line(Byte * p) // return pointer to NL of cur line line
1256{
1257 while (p < end - 1 && *p != '\n')
1258 p++; // go to cur line E-o-l
1259 return (p);
1260}
1261
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001262static inline Byte *dollar_line(Byte * p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001263{
1264 while (p < end - 1 && *p != '\n')
1265 p++; // go to cur line E-o-l
1266 // Try to stay off of the Newline
1267 if (*p == '\n' && (p - begin_line(p)) > 0)
1268 p--;
1269 return (p);
1270}
1271
1272static Byte *prev_line(Byte * p) // return pointer first char prev line
1273{
1274 p = begin_line(p); // goto begining of cur line
1275 if (p[-1] == '\n' && p > text)
1276 p--; // step to prev line
1277 p = begin_line(p); // goto begining of prev line
1278 return (p);
1279}
1280
1281static Byte *next_line(Byte * p) // return pointer first char next line
1282{
1283 p = end_line(p);
1284 if (*p == '\n' && p < end - 1)
1285 p++; // step to next line
1286 return (p);
1287}
1288
1289//----- Text Information Routines ------------------------------
1290static Byte *end_screen(void)
1291{
1292 Byte *q;
1293 int cnt;
1294
1295 // find new bottom line
1296 q = screenbegin;
1297 for (cnt = 0; cnt < rows - 2; cnt++)
1298 q = next_line(q);
1299 q = end_line(q);
1300 return (q);
1301}
1302
1303static int count_lines(Byte * start, Byte * stop) // count line from start to stop
1304{
1305 Byte *q;
1306 int cnt;
1307
1308 if (stop < start) { // start and stop are backwards- reverse them
1309 q = start;
1310 start = stop;
1311 stop = q;
1312 }
1313 cnt = 0;
1314 stop = end_line(stop); // get to end of this line
1315 for (q = start; q <= stop && q <= end - 1; q++) {
1316 if (*q == '\n')
1317 cnt++;
1318 }
1319 return (cnt);
1320}
1321
1322static Byte *find_line(int li) // find begining of line #li
1323{
1324 Byte *q;
1325
1326 for (q = text; li > 1; li--) {
1327 q = next_line(q);
1328 }
1329 return (q);
1330}
1331
1332//----- Dot Movement Routines ----------------------------------
1333static void dot_left(void)
1334{
1335 if (dot > text && dot[-1] != '\n')
1336 dot--;
1337}
1338
1339static void dot_right(void)
1340{
1341 if (dot < end - 1 && *dot != '\n')
1342 dot++;
1343}
1344
1345static void dot_begin(void)
1346{
1347 dot = begin_line(dot); // return pointer to first char cur line
1348}
1349
1350static void dot_end(void)
1351{
1352 dot = end_line(dot); // return pointer to last char cur line
1353}
1354
1355static Byte *move_to_col(Byte * p, int l)
1356{
1357 int co;
1358
1359 p = begin_line(p);
1360 co = 0;
1361 do {
1362 if (*p == '\n' || *p == '\0')
1363 break;
1364 if (*p == '\t') {
1365 // 7 - (co % 8 )
1366 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001367 } else if (*p < ' ' || *p == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001368 co++; // display as ^X, use 2 columns
1369 }
1370 } while (++co <= l && p++ < end);
1371 return (p);
1372}
1373
1374static void dot_next(void)
1375{
1376 dot = next_line(dot);
1377}
1378
1379static void dot_prev(void)
1380{
1381 dot = prev_line(dot);
1382}
1383
1384static void dot_scroll(int cnt, int dir)
1385{
1386 Byte *q;
1387
1388 for (; cnt > 0; cnt--) {
1389 if (dir < 0) {
1390 // scroll Backwards
1391 // ctrl-Y scroll up one line
1392 screenbegin = prev_line(screenbegin);
1393 } else {
1394 // scroll Forwards
1395 // ctrl-E scroll down one line
1396 screenbegin = next_line(screenbegin);
1397 }
1398 }
1399 // make sure "dot" stays on the screen so we dont scroll off
1400 if (dot < screenbegin)
1401 dot = screenbegin;
1402 q = end_screen(); // find new bottom line
1403 if (dot > q)
1404 dot = begin_line(q); // is dot is below bottom line?
1405 dot_skip_over_ws();
1406}
1407
1408static void dot_skip_over_ws(void)
1409{
1410 // skip WS
1411 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1412 dot++;
1413}
1414
1415static void dot_delete(void) // delete the char at 'dot'
1416{
1417 (void) text_hole_delete(dot, dot);
1418}
1419
1420static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end"
1421{
1422 if (p >= end && end > text) {
1423 p = end - 1;
1424 indicate_error('1');
1425 }
1426 if (p < text) {
1427 p = text;
1428 indicate_error('2');
1429 }
1430 return (p);
1431}
1432
1433//----- Helper Utility Routines --------------------------------
1434
1435//----------------------------------------------------------------
1436//----- Char Routines --------------------------------------------
1437/* Chars that are part of a word-
1438 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1439 * Chars that are Not part of a word (stoppers)
1440 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1441 * Chars that are WhiteSpace
1442 * TAB NEWLINE VT FF RETURN SPACE
1443 * DO NOT COUNT NEWLINE AS WHITESPACE
1444 */
1445
1446static Byte *new_screen(int ro, int co)
1447{
1448 int li;
1449
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001450 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001451 screensize = ro * co + 8;
1452 screen = (Byte *) xmalloc(screensize);
1453 // initialize the new screen. assume this will be a empty file.
1454 screen_erase();
Eric Andersenaff114c2004-04-14 17:51:38 +00001455 // non-existent text[] lines start with a tilde (~).
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001456 for (li = 1; li < ro - 1; li++) {
1457 screen[(li * co) + 0] = '~';
1458 }
1459 return (screen);
1460}
1461
1462static Byte *new_text(int size)
1463{
1464 if (size < 10240)
1465 size = 10240; // have a minimum size for new files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001466 free(text);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001467 text = (Byte *) xmalloc(size + 8);
1468 memset(text, '\0', size); // clear new text[]
1469 //text += 4; // leave some room for "oops"
1470 textend = text + size - 1;
1471 //textend -= 4; // leave some root for "oops"
1472 return (text);
1473}
1474
1475#ifdef CONFIG_FEATURE_VI_SEARCH
1476static int mycmp(Byte * s1, Byte * s2, int len)
1477{
1478 int i;
1479
1480 i = strncmp((char *) s1, (char *) s2, len);
1481#ifdef CONFIG_FEATURE_VI_SETOPTS
1482 if (ignorecase) {
1483 i = strncasecmp((char *) s1, (char *) s2, len);
1484 }
1485#endif /* CONFIG_FEATURE_VI_SETOPTS */
1486 return (i);
1487}
1488
1489static Byte *char_search(Byte * p, Byte * pat, int dir, int range) // search for pattern starting at p
1490{
1491#ifndef REGEX_SEARCH
1492 Byte *start, *stop;
1493 int len;
1494
1495 len = strlen((char *) pat);
1496 if (dir == FORWARD) {
1497 stop = end - 1; // assume range is p - end-1
1498 if (range == LIMITED)
1499 stop = next_line(p); // range is to next line
1500 for (start = p; start < stop; start++) {
1501 if (mycmp(start, pat, len) == 0) {
1502 return (start);
1503 }
1504 }
1505 } else if (dir == BACK) {
1506 stop = text; // assume range is text - p
1507 if (range == LIMITED)
1508 stop = prev_line(p); // range is to prev line
1509 for (start = p - len; start >= stop; start--) {
1510 if (mycmp(start, pat, len) == 0) {
1511 return (start);
1512 }
1513 }
1514 }
1515 // pattern not found
1516 return (NULL);
1517#else /*REGEX_SEARCH */
1518 char *q;
1519 struct re_pattern_buffer preg;
1520 int i;
1521 int size, range;
1522
1523 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1524 preg.translate = 0;
1525 preg.fastmap = 0;
1526 preg.buffer = 0;
1527 preg.allocated = 0;
1528
1529 // assume a LIMITED forward search
1530 q = next_line(p);
1531 q = end_line(q);
1532 q = end - 1;
1533 if (dir == BACK) {
1534 q = prev_line(p);
1535 q = text;
1536 }
1537 // count the number of chars to search over, forward or backward
1538 size = q - p;
1539 if (size < 0)
1540 size = p - q;
1541 // RANGE could be negative if we are searching backwards
1542 range = q - p;
1543
1544 q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
1545 if (q != 0) {
1546 // The pattern was not compiled
1547 psbs("bad search pattern: \"%s\": %s", pat, q);
1548 i = 0; // return p if pattern not compiled
1549 goto cs1;
1550 }
1551
1552 q = p;
1553 if (range < 0) {
1554 q = p - size;
1555 if (q < text)
1556 q = text;
1557 }
1558 // search for the compiled pattern, preg, in p[]
1559 // range < 0- search backward
1560 // range > 0- search forward
1561 // 0 < start < size
1562 // re_search() < 0 not found or error
1563 // re_search() > 0 index of found pattern
1564 // struct pattern char int int int struct reg
1565 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1566 i = re_search(&preg, q, size, 0, range, 0);
1567 if (i == -1) {
1568 p = 0;
1569 i = 0; // return NULL if pattern not found
1570 }
1571 cs1:
1572 if (dir == FORWARD) {
1573 p = p + i;
1574 } else {
1575 p = p - i;
1576 }
1577 return (p);
1578#endif /*REGEX_SEARCH */
1579}
1580#endif /* CONFIG_FEATURE_VI_SEARCH */
1581
1582static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
1583{
1584 if (c == 22) { // Is this an ctrl-V?
1585 p = stupid_insert(p, '^'); // use ^ to indicate literal next
1586 p--; // backup onto ^
1587 refresh(FALSE); // show the ^
1588 c = get_one_char();
1589 *p = c;
1590 p++;
1591 file_modified = TRUE; // has the file been modified
1592 } else if (c == 27) { // Is this an ESC?
1593 cmd_mode = 0;
1594 cmdcnt = 0;
1595 end_cmd_q(); // stop adding to q
1596 strcpy((char *) status_buffer, " "); // clear the status buffer
1597 if ((p[-1] != '\n') && (dot>text)) {
1598 p--;
1599 }
1600 } else if (c == erase_char) { // Is this a BS
1601 // 123456789
1602 if ((p[-1] != '\n') && (dot>text)) {
1603 p--;
1604 p = text_hole_delete(p, p); // shrink buffer 1 char
1605#ifdef CONFIG_FEATURE_VI_DOT_CMD
1606 // also rmove char from last_modifying_cmd
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001607 if (last_modifying_cmd != 0 && strlen((char *) last_modifying_cmd) > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001608 Byte *q;
1609
1610 q = last_modifying_cmd;
1611 q[strlen((char *) q) - 1] = '\0'; // erase BS
1612 q[strlen((char *) q) - 1] = '\0'; // erase prev char
1613 }
1614#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1615 }
1616 } else {
1617 // insert a char into text[]
1618 Byte *sp; // "save p"
1619
1620 if (c == 13)
1621 c = '\n'; // translate \r to \n
1622 sp = p; // remember addr of insert
1623 p = stupid_insert(p, c); // insert the char
1624#ifdef CONFIG_FEATURE_VI_SETOPTS
1625 if (showmatch && strchr(")]}", *sp) != NULL) {
1626 showmatching(sp);
1627 }
1628 if (autoindent && c == '\n') { // auto indent the new line
1629 Byte *q;
1630
1631 q = prev_line(p); // use prev line as templet
1632 for (; isblnk(*q); q++) {
1633 p = stupid_insert(p, *q); // insert the char
1634 }
1635 }
1636#endif /* CONFIG_FEATURE_VI_SETOPTS */
1637 }
1638 return (p);
1639}
1640
1641static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
1642{
1643 p = text_hole_make(p, 1);
1644 if (p != 0) {
1645 *p = c;
1646 file_modified = TRUE; // has the file been modified
1647 p++;
1648 }
1649 return (p);
1650}
1651
1652static Byte find_range(Byte ** start, Byte ** stop, Byte c)
1653{
1654 Byte *save_dot, *p, *q;
1655 int cnt;
1656
1657 save_dot = dot;
1658 p = q = dot;
1659
1660 if (strchr("cdy><", c)) {
1661 // these cmds operate on whole lines
1662 p = q = begin_line(p);
1663 for (cnt = 1; cnt < cmdcnt; cnt++) {
1664 q = next_line(q);
1665 }
1666 q = end_line(q);
1667 } else if (strchr("^%$0bBeEft", c)) {
1668 // These cmds operate on char positions
1669 do_cmd(c); // execute movement cmd
1670 q = dot;
1671 } else if (strchr("wW", c)) {
1672 do_cmd(c); // execute movement cmd
Eric Andersenaeea32c2004-02-04 11:19:44 +00001673 // if we are at the next word's first char
1674 // step back one char
1675 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00001676 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Eric Andersenaeea32c2004-02-04 11:19:44 +00001677 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1678 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1679 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001680 if (dot > text && *dot == '\n')
1681 dot--; // stay off NL
1682 q = dot;
1683 } else if (strchr("H-k{", c)) {
1684 // these operate on multi-lines backwards
1685 q = end_line(dot); // find NL
1686 do_cmd(c); // execute movement cmd
1687 dot_begin();
1688 p = dot;
1689 } else if (strchr("L+j}\r\n", c)) {
1690 // these operate on multi-lines forwards
1691 p = begin_line(dot);
1692 do_cmd(c); // execute movement cmd
1693 dot_end(); // find NL
1694 q = dot;
1695 } else {
1696 c = 27; // error- return an ESC char
1697 //break;
1698 }
1699 *start = p;
1700 *stop = q;
1701 if (q < p) {
1702 *start = q;
1703 *stop = p;
1704 }
1705 dot = save_dot;
1706 return (c);
1707}
1708
1709static int st_test(Byte * p, int type, int dir, Byte * tested)
1710{
1711 Byte c, c0, ci;
1712 int test, inc;
1713
1714 inc = dir;
1715 c = c0 = p[0];
1716 ci = p[inc];
1717 test = 0;
1718
1719 if (type == S_BEFORE_WS) {
1720 c = ci;
1721 test = ((!isspace(c)) || c == '\n');
1722 }
1723 if (type == S_TO_WS) {
1724 c = c0;
1725 test = ((!isspace(c)) || c == '\n');
1726 }
1727 if (type == S_OVER_WS) {
1728 c = c0;
1729 test = ((isspace(c)));
1730 }
1731 if (type == S_END_PUNCT) {
1732 c = ci;
1733 test = ((ispunct(c)));
1734 }
1735 if (type == S_END_ALNUM) {
1736 c = ci;
1737 test = ((isalnum(c)) || c == '_');
1738 }
1739 *tested = c;
1740 return (test);
1741}
1742
1743static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
1744{
1745 Byte c;
1746
1747 while (st_test(p, type, dir, &c)) {
1748 // make sure we limit search to correct number of lines
1749 if (c == '\n' && --linecnt < 1)
1750 break;
1751 if (dir >= 0 && p >= end - 1)
1752 break;
1753 if (dir < 0 && p <= text)
1754 break;
1755 p += dir; // move to next char
1756 }
1757 return (p);
1758}
1759
1760// find matching char of pair () [] {}
1761static Byte *find_pair(Byte * p, Byte c)
1762{
1763 Byte match, *q;
1764 int dir, level;
1765
1766 match = ')';
1767 level = 1;
1768 dir = 1; // assume forward
1769 switch (c) {
1770 case '(':
1771 match = ')';
1772 break;
1773 case '[':
1774 match = ']';
1775 break;
1776 case '{':
1777 match = '}';
1778 break;
1779 case ')':
1780 match = '(';
1781 dir = -1;
1782 break;
1783 case ']':
1784 match = '[';
1785 dir = -1;
1786 break;
1787 case '}':
1788 match = '{';
1789 dir = -1;
1790 break;
1791 }
1792 for (q = p + dir; text <= q && q < end; q += dir) {
1793 // look for match, count levels of pairs (( ))
1794 if (*q == c)
1795 level++; // increase pair levels
1796 if (*q == match)
1797 level--; // reduce pair level
1798 if (level == 0)
1799 break; // found matching pair
1800 }
1801 if (level != 0)
1802 q = NULL; // indicate no match
1803 return (q);
1804}
1805
1806#ifdef CONFIG_FEATURE_VI_SETOPTS
1807// show the matching char of a pair, () [] {}
1808static void showmatching(Byte * p)
1809{
1810 Byte *q, *save_dot;
1811
1812 // we found half of a pair
1813 q = find_pair(p, *p); // get loc of matching char
1814 if (q == NULL) {
1815 indicate_error('3'); // no matching char
1816 } else {
1817 // "q" now points to matching pair
1818 save_dot = dot; // remember where we are
1819 dot = q; // go to new loc
1820 refresh(FALSE); // let the user see it
1821 (void) mysleep(40); // give user some time
1822 dot = save_dot; // go back to old loc
1823 refresh(FALSE);
1824 }
1825}
1826#endif /* CONFIG_FEATURE_VI_SETOPTS */
1827
1828// open a hole in text[]
1829static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
1830{
1831 Byte *src, *dest;
1832 int cnt;
1833
1834 if (size <= 0)
1835 goto thm0;
1836 src = p;
1837 dest = p + size;
1838 cnt = end - src; // the rest of buffer
1839 if (memmove(dest, src, cnt) != dest) {
1840 psbs("can't create room for new characters");
1841 }
1842 memset(p, ' ', size); // clear new hole
1843 end = end + size; // adjust the new END
1844 file_modified = TRUE; // has the file been modified
1845 thm0:
1846 return (p);
1847}
1848
1849// close a hole in text[]
1850static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
1851{
1852 Byte *src, *dest;
1853 int cnt, hole_size;
1854
1855 // move forwards, from beginning
1856 // assume p <= q
1857 src = q + 1;
1858 dest = p;
1859 if (q < p) { // they are backward- swap them
1860 src = p + 1;
1861 dest = q;
1862 }
1863 hole_size = q - p + 1;
1864 cnt = end - src;
1865 if (src < text || src > end)
1866 goto thd0;
1867 if (dest < text || dest >= end)
1868 goto thd0;
1869 if (src >= end)
1870 goto thd_atend; // just delete the end of the buffer
1871 if (memmove(dest, src, cnt) != dest) {
1872 psbs("can't delete the character");
1873 }
1874 thd_atend:
1875 end = end - hole_size; // adjust the new END
1876 if (dest >= end)
1877 dest = end - 1; // make sure dest in below end-1
1878 if (end <= text)
1879 dest = end = text; // keep pointers valid
1880 file_modified = TRUE; // has the file been modified
1881 thd0:
1882 return (dest);
1883}
1884
1885// copy text into register, then delete text.
1886// if dist <= 0, do not include, or go past, a NewLine
1887//
1888static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
1889{
1890 Byte *p;
1891
1892 // make sure start <= stop
1893 if (start > stop) {
1894 // they are backwards, reverse them
1895 p = start;
1896 start = stop;
1897 stop = p;
1898 }
1899 if (dist <= 0) {
1900 // we can not cross NL boundaries
1901 p = start;
1902 if (*p == '\n')
1903 return (p);
1904 // dont go past a NewLine
1905 for (; p + 1 <= stop; p++) {
1906 if (p[1] == '\n') {
1907 stop = p; // "stop" just before NewLine
1908 break;
1909 }
1910 }
1911 }
1912 p = start;
1913#ifdef CONFIG_FEATURE_VI_YANKMARK
1914 text_yank(start, stop, YDreg);
1915#endif /* CONFIG_FEATURE_VI_YANKMARK */
1916 if (yf == YANKDEL) {
1917 p = text_hole_delete(start, stop);
1918 } // delete lines
1919 return (p);
1920}
1921
1922static void show_help(void)
1923{
1924 puts("These features are available:"
1925#ifdef CONFIG_FEATURE_VI_SEARCH
1926 "\n\tPattern searches with / and ?"
1927#endif /* CONFIG_FEATURE_VI_SEARCH */
1928#ifdef CONFIG_FEATURE_VI_DOT_CMD
1929 "\n\tLast command repeat with \'.\'"
1930#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1931#ifdef CONFIG_FEATURE_VI_YANKMARK
1932 "\n\tLine marking with 'x"
1933 "\n\tNamed buffers with \"x"
1934#endif /* CONFIG_FEATURE_VI_YANKMARK */
1935#ifdef CONFIG_FEATURE_VI_READONLY
1936 "\n\tReadonly if vi is called as \"view\""
1937 "\n\tReadonly with -R command line arg"
1938#endif /* CONFIG_FEATURE_VI_READONLY */
1939#ifdef CONFIG_FEATURE_VI_SET
1940 "\n\tSome colon mode commands with \':\'"
1941#endif /* CONFIG_FEATURE_VI_SET */
1942#ifdef CONFIG_FEATURE_VI_SETOPTS
1943 "\n\tSettable options with \":set\""
1944#endif /* CONFIG_FEATURE_VI_SETOPTS */
1945#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
1946 "\n\tSignal catching- ^C"
1947 "\n\tJob suspend and resume with ^Z"
1948#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
1949#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
1950 "\n\tAdapt to window re-sizes"
1951#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
1952 );
1953}
1954
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001955static inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001956{
1957 Byte c, b[2];
1958
1959 b[1] = '\0';
1960 strcpy((char *) buf, ""); // init buf
1961 if (strlen((char *) s) <= 0)
1962 s = (Byte *) "(NULL)";
1963 for (; *s > '\0'; s++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001964 int c_is_no_print;
1965
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001966 c = *s;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001967 c_is_no_print = c > 127 && !Isprint(c);
1968 if (c_is_no_print) {
1969 strcat((char *) buf, SOn);
1970 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001971 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001972 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001973 strcat((char *) buf, "^");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001974 if(c == 127)
1975 c = '?';
1976 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001977 c += '@';
1978 }
1979 b[0] = c;
1980 strcat((char *) buf, (char *) b);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001981 if (c_is_no_print)
1982 strcat((char *) buf, SOs);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001983 if (*s == '\n') {
1984 strcat((char *) buf, "$");
1985 }
1986 }
1987}
1988
1989#ifdef CONFIG_FEATURE_VI_DOT_CMD
1990static void start_new_cmd_q(Byte c)
1991{
1992 // release old cmd
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001993 free(last_modifying_cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001994 // get buffer for new cmd
1995 last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
1996 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
1997 // if there is a current cmd count put it in the buffer first
1998 if (cmdcnt > 0)
1999 sprintf((char *) last_modifying_cmd, "%d", cmdcnt);
2000 // save char c onto queue
2001 last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
2002 adding2q = 1;
2003 return;
2004}
2005
2006static void end_cmd_q(void)
2007{
2008#ifdef CONFIG_FEATURE_VI_YANKMARK
2009 YDreg = 26; // go back to default Yank/Delete reg
2010#endif /* CONFIG_FEATURE_VI_YANKMARK */
2011 adding2q = 0;
2012 return;
2013}
2014#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2015
2016#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
2017static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
2018{
2019 int cnt, i;
2020
2021 i = strlen((char *) s);
2022 p = text_hole_make(p, i);
2023 strncpy((char *) p, (char *) s, i);
2024 for (cnt = 0; *s != '\0'; s++) {
2025 if (*s == '\n')
2026 cnt++;
2027 }
2028#ifdef CONFIG_FEATURE_VI_YANKMARK
2029 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2030#endif /* CONFIG_FEATURE_VI_YANKMARK */
2031 return (p);
2032}
2033#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
2034
2035#ifdef CONFIG_FEATURE_VI_YANKMARK
2036static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register
2037{
2038 Byte *t;
2039 int cnt;
2040
2041 if (q < p) { // they are backwards- reverse them
2042 t = q;
2043 q = p;
2044 p = t;
2045 }
2046 cnt = q - p + 1;
2047 t = reg[dest];
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002048 free(t); // if already a yank register, free it
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002049 t = (Byte *) xmalloc(cnt + 1); // get a new register
2050 memset(t, '\0', cnt + 1); // clear new text[]
2051 strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer
2052 reg[dest] = t;
2053 return (p);
2054}
2055
2056static Byte what_reg(void)
2057{
2058 Byte c;
2059 int i;
2060
2061 i = 0;
2062 c = 'D'; // default to D-reg
2063 if (0 <= YDreg && YDreg <= 25)
2064 c = 'a' + (Byte) YDreg;
2065 if (YDreg == 26)
2066 c = 'D';
2067 if (YDreg == 27)
2068 c = 'U';
2069 return (c);
2070}
2071
2072static void check_context(Byte cmd)
2073{
2074 // A context is defined to be "modifying text"
2075 // Any modifying command establishes a new context.
2076
2077 if (dot < context_start || dot > context_end) {
2078 if (strchr((char *) modifying_cmds, cmd) != NULL) {
2079 // we are trying to modify text[]- make this the current context
2080 mark[27] = mark[26]; // move cur to prev
2081 mark[26] = dot; // move local to cur
2082 context_start = prev_line(prev_line(dot));
2083 context_end = next_line(next_line(dot));
2084 //loiter= start_loiter= now;
2085 }
2086 }
2087 return;
2088}
2089
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002090static inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002091{
2092 Byte *tmp;
2093
2094 // the current context is in mark[26]
2095 // the previous context is in mark[27]
2096 // only swap context if other context is valid
2097 if (text <= mark[27] && mark[27] <= end - 1) {
2098 tmp = mark[27];
2099 mark[27] = mark[26];
2100 mark[26] = tmp;
2101 p = mark[26]; // where we are going- previous context
2102 context_start = prev_line(prev_line(prev_line(p)));
2103 context_end = next_line(next_line(next_line(p)));
2104 }
2105 return (p);
2106}
2107#endif /* CONFIG_FEATURE_VI_YANKMARK */
2108
2109static int isblnk(Byte c) // is the char a blank or tab
2110{
2111 return (c == ' ' || c == '\t');
2112}
2113
2114//----- Set terminal attributes --------------------------------
2115static void rawmode(void)
2116{
2117 tcgetattr(0, &term_orig);
2118 term_vi = term_orig;
2119 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2120 term_vi.c_iflag &= (~IXON & ~ICRNL);
2121 term_vi.c_oflag &= (~ONLCR);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002122 term_vi.c_cc[VMIN] = 1;
2123 term_vi.c_cc[VTIME] = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002124 erase_char = term_vi.c_cc[VERASE];
2125 tcsetattr(0, TCSANOW, &term_vi);
2126}
2127
2128static void cookmode(void)
2129{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002130 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002131 tcsetattr(0, TCSANOW, &term_orig);
2132}
2133
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002134//----- Come here when we get a window resize signal ---------
2135#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
2136static void winch_sig(int sig)
2137{
2138 signal(SIGWINCH, winch_sig);
2139#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2140 window_size_get(0);
2141#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2142 new_screen(rows, columns); // get memory for virtual screen
2143 redraw(TRUE); // re-draw the screen
2144}
2145
2146//----- Come here when we get a continue signal -------------------
2147static void cont_sig(int sig)
2148{
2149 rawmode(); // terminal to "raw"
2150 *status_buffer = '\0'; // clear the status buffer
2151 redraw(TRUE); // re-draw the screen
2152
2153 signal(SIGTSTP, suspend_sig);
2154 signal(SIGCONT, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002155 kill(my_pid, SIGCONT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002156}
2157
2158//----- Come here when we get a Suspend signal -------------------
2159static void suspend_sig(int sig)
2160{
2161 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2162 clear_to_eol(); // Erase to end of line
2163 cookmode(); // terminal to "cooked"
2164
2165 signal(SIGCONT, cont_sig);
2166 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002167 kill(my_pid, SIGTSTP);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002168}
2169
2170//----- Come here when we get a signal ---------------------------
2171static void catch_sig(int sig)
2172{
2173 signal(SIGHUP, catch_sig);
2174 signal(SIGINT, catch_sig);
2175 signal(SIGTERM, catch_sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002176 signal(SIGALRM, catch_sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002177 if(sig)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002178 longjmp(restart, sig);
2179}
2180
2181//----- Come here when we get a core dump signal -----------------
2182static void core_sig(int sig)
2183{
2184 signal(SIGQUIT, core_sig);
2185 signal(SIGILL, core_sig);
2186 signal(SIGTRAP, core_sig);
2187 signal(SIGIOT, core_sig);
2188 signal(SIGABRT, core_sig);
2189 signal(SIGFPE, core_sig);
2190 signal(SIGBUS, core_sig);
2191 signal(SIGSEGV, core_sig);
2192#ifdef SIGSYS
2193 signal(SIGSYS, core_sig);
2194#endif
2195
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002196 if(sig) { // signaled
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002197 dot = bound_dot(dot); // make sure "dot" is valid
2198
2199 longjmp(restart, sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002200 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002201}
2202#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2203
2204static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2205{
2206 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002207 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002208 FD_ZERO(&rfds);
2209 FD_SET(0, &rfds);
2210 tv.tv_sec = 0;
2211 tv.tv_usec = hund * 10000;
2212 select(1, &rfds, NULL, NULL, &tv);
2213 return (FD_ISSET(0, &rfds));
2214}
2215
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002216static Byte readbuffer[BUFSIZ];
2217static int readed_for_parse;
2218
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002219//----- IO Routines --------------------------------------------
2220static Byte readit(void) // read (maybe cursor) key from stdin
2221{
2222 Byte c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002223 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002224 struct esc_cmds {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002225 const char *seq;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002226 Byte val;
2227 };
2228
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002229 static const struct esc_cmds esccmds[] = {
2230 {"OA", (Byte) VI_K_UP}, // cursor key Up
2231 {"OB", (Byte) VI_K_DOWN}, // cursor key Down
2232 {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2233 {"OD", (Byte) VI_K_LEFT}, // cursor key Left
2234 {"OH", (Byte) VI_K_HOME}, // Cursor Key Home
2235 {"OF", (Byte) VI_K_END}, // Cursor Key End
2236 {"[A", (Byte) VI_K_UP}, // cursor key Up
2237 {"[B", (Byte) VI_K_DOWN}, // cursor key Down
2238 {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right
2239 {"[D", (Byte) VI_K_LEFT}, // cursor key Left
2240 {"[H", (Byte) VI_K_HOME}, // Cursor Key Home
2241 {"[F", (Byte) VI_K_END}, // Cursor Key End
2242 {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home
2243 {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2244 {"[4~", (Byte) VI_K_END}, // Cursor Key End
2245 {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2246 {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2247 {"OP", (Byte) VI_K_FUN1}, // Function Key F1
2248 {"OQ", (Byte) VI_K_FUN2}, // Function Key F2
2249 {"OR", (Byte) VI_K_FUN3}, // Function Key F3
2250 {"OS", (Byte) VI_K_FUN4}, // Function Key F4
2251 {"[15~", (Byte) VI_K_FUN5}, // Function Key F5
2252 {"[17~", (Byte) VI_K_FUN6}, // Function Key F6
2253 {"[18~", (Byte) VI_K_FUN7}, // Function Key F7
2254 {"[19~", (Byte) VI_K_FUN8}, // Function Key F8
2255 {"[20~", (Byte) VI_K_FUN9}, // Function Key F9
2256 {"[21~", (Byte) VI_K_FUN10}, // Function Key F10
2257 {"[23~", (Byte) VI_K_FUN11}, // Function Key F11
2258 {"[24~", (Byte) VI_K_FUN12}, // Function Key F12
2259 {"[11~", (Byte) VI_K_FUN1}, // Function Key F1
2260 {"[12~", (Byte) VI_K_FUN2}, // Function Key F2
2261 {"[13~", (Byte) VI_K_FUN3}, // Function Key F3
2262 {"[14~", (Byte) VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002263 };
2264
2265#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2266
2267 (void) alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002268 fflush(stdout);
2269 n = readed_for_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002270 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002271 if (n <= 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002272 ri0:
2273 // the Q is empty, wait for a typed char
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002274 n = read(0, readbuffer, BUFSIZ - 1);
2275 if (n < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002276 if (errno == EINTR)
2277 goto ri0; // interrupted sys call
2278 if (errno == EBADF)
2279 editing = 0;
2280 if (errno == EFAULT)
2281 editing = 0;
2282 if (errno == EINVAL)
2283 editing = 0;
2284 if (errno == EIO)
2285 editing = 0;
2286 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002287 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002288 if(n <= 0)
2289 return 0; // error
2290 if (readbuffer[0] == 27) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002291 // This is an ESC char. Is this Esc sequence?
2292 // Could be bare Esc key. See if there are any
2293 // more chars to read after the ESC. This would
2294 // be a Function or Cursor Key sequence.
2295 FD_ZERO(&rfds);
2296 FD_SET(0, &rfds);
2297 tv.tv_sec = 0;
2298 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2299
2300 // keep reading while there are input chars and room in buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002301 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002302 // read the rest of the ESC string
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002303 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2304 if (r > 0) {
2305 n += r;
2306 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002307 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002308 }
2309 readed_for_parse = n;
2310 }
2311 c = readbuffer[0];
2312 if(c == 27 && n > 1) {
2313 // Maybe cursor or function key?
2314 const struct esc_cmds *eindex;
2315
2316 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2317 int cnt = strlen(eindex->seq);
2318
2319 if(n <= cnt)
2320 continue;
2321 if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
2322 continue;
2323 // is a Cursor key- put derived value back into Q
2324 c = eindex->val;
2325 // for squeeze out the ESC sequence
2326 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002327 break;
2328 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002329 if(eindex == &esccmds[ESCCMDS_COUNT]) {
2330 /* defined ESC sequence not found, set only one ESC */
2331 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002332 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002333 } else {
2334 n = 1;
2335 }
2336 // remove key sequence from Q
2337 readed_for_parse -= n;
2338 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002339 (void) alarm(3); // we are done waiting for input, turn alarm ON
2340 return (c);
2341}
2342
2343//----- IO Routines --------------------------------------------
2344static Byte get_one_char()
2345{
2346 static Byte c;
2347
2348#ifdef CONFIG_FEATURE_VI_DOT_CMD
2349 // ! adding2q && ioq == 0 read()
2350 // ! adding2q && ioq != 0 *ioq
2351 // adding2q *last_modifying_cmd= read()
2352 if (!adding2q) {
2353 // we are not adding to the q.
2354 // but, we may be reading from a q
2355 if (ioq == 0) {
2356 // there is no current q, read from STDIN
2357 c = readit(); // get the users input
2358 } else {
2359 // there is a queue to get chars from first
2360 c = *ioq++;
2361 if (c == '\0') {
2362 // the end of the q, read from STDIN
2363 free(ioq_start);
2364 ioq_start = ioq = 0;
2365 c = readit(); // get the users input
2366 }
2367 }
2368 } else {
2369 // adding STDIN chars to q
2370 c = readit(); // get the users input
2371 if (last_modifying_cmd != 0) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002372 int len = strlen((char *) last_modifying_cmd);
2373 if (len + 1 >= BUFSIZ) {
2374 psbs("last_modifying_cmd overrun");
2375 } else {
2376 // add new char to q
2377 last_modifying_cmd[len] = c;
2378 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002379 }
2380 }
2381#else /* CONFIG_FEATURE_VI_DOT_CMD */
2382 c = readit(); // get the users input
2383#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2384 return (c); // return the char, where ever it came from
2385}
2386
2387static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2388{
2389 Byte buf[BUFSIZ];
2390 Byte c;
2391 int i;
2392 static Byte *obufp = NULL;
2393
2394 strcpy((char *) buf, (char *) prompt);
2395 *status_buffer = '\0'; // clear the status buffer
2396 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2397 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002398 write1(prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002399
2400 for (i = strlen((char *) buf); i < BUFSIZ;) {
2401 c = get_one_char(); // read user input
2402 if (c == '\n' || c == '\r' || c == 27)
2403 break; // is this end of input
2404 if (c == erase_char) { // user wants to erase prev char
2405 i--; // backup to prev char
2406 buf[i] = '\0'; // erase the char
2407 buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002408 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002409 if (i <= 0) { // user backs up before b-o-l, exit
2410 break;
2411 }
2412 } else {
2413 buf[i] = c; // save char in buffer
2414 buf[i + 1] = '\0'; // make sure buffer is null terminated
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002415 putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002416 i++;
2417 }
2418 }
2419 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002420 free(obufp);
Manuel Novoa III cad53642003-03-19 09:13:01 +00002421 obufp = (Byte *) bb_xstrdup((char *) buf);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002422 return (obufp);
2423}
2424
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002425static int file_size(const Byte * fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002426{
2427 struct stat st_buf;
2428 int cnt, sr;
2429
2430 if (fn == 0 || strlen(fn) <= 0)
2431 return (-1);
2432 cnt = -1;
2433 sr = stat((char *) fn, &st_buf); // see if file exists
2434 if (sr >= 0) {
2435 cnt = (int) st_buf.st_size;
2436 }
2437 return (cnt);
2438}
2439
2440static int file_insert(Byte * fn, Byte * p, int size)
2441{
2442 int fd, cnt;
2443
2444 cnt = -1;
2445#ifdef CONFIG_FEATURE_VI_READONLY
2446 readonly = FALSE;
2447#endif /* CONFIG_FEATURE_VI_READONLY */
2448 if (fn == 0 || strlen((char*) fn) <= 0) {
2449 psbs("No filename given");
2450 goto fi0;
2451 }
2452 if (size == 0) {
2453 // OK- this is just a no-op
2454 cnt = 0;
2455 goto fi0;
2456 }
2457 if (size < 0) {
2458 psbs("Trying to insert a negative number (%d) of characters", size);
2459 goto fi0;
2460 }
2461 if (p < text || p > end) {
2462 psbs("Trying to insert file outside of memory");
2463 goto fi0;
2464 }
2465
2466 // see if we can open the file
2467#ifdef CONFIG_FEATURE_VI_READONLY
2468 if (vi_readonly) goto fi1; // do not try write-mode
2469#endif
2470 fd = open((char *) fn, O_RDWR); // assume read & write
2471 if (fd < 0) {
2472 // could not open for writing- maybe file is read only
2473#ifdef CONFIG_FEATURE_VI_READONLY
2474 fi1:
2475#endif
2476 fd = open((char *) fn, O_RDONLY); // try read-only
2477 if (fd < 0) {
2478 psbs("\"%s\" %s", fn, "could not open file");
2479 goto fi0;
2480 }
2481#ifdef CONFIG_FEATURE_VI_READONLY
2482 // got the file- read-only
2483 readonly = TRUE;
2484#endif /* CONFIG_FEATURE_VI_READONLY */
2485 }
2486 p = text_hole_make(p, size);
2487 cnt = read(fd, p, size);
2488 close(fd);
2489 if (cnt < 0) {
2490 cnt = -1;
2491 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2492 psbs("could not read file \"%s\"", fn);
2493 } else if (cnt < size) {
2494 // There was a partial read, shrink unused space text[]
2495 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
2496 psbs("could not read all of file \"%s\"", fn);
2497 }
2498 if (cnt >= size)
2499 file_modified = TRUE;
2500 fi0:
2501 return (cnt);
2502}
2503
2504static int file_write(Byte * fn, Byte * first, Byte * last)
2505{
2506 int fd, cnt, charcnt;
2507
2508 if (fn == 0) {
2509 psbs("No current filename");
2510 return (-1);
2511 }
2512 charcnt = 0;
2513 // FIXIT- use the correct umask()
2514 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2515 if (fd < 0)
2516 return (-1);
2517 cnt = last - first + 1;
2518 charcnt = write(fd, first, cnt);
2519 if (charcnt == cnt) {
2520 // good write
2521 //file_modified= FALSE; // the file has not been modified
2522 } else {
2523 charcnt = 0;
2524 }
2525 close(fd);
2526 return (charcnt);
2527}
2528
2529//----- Terminal Drawing ---------------------------------------
2530// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002531// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002532// screen coordinates
2533// 0,0 ... 0,79
2534// 1,0 ... 1,79
2535// . ... .
2536// . ... .
2537// 22,0 ... 22,79
2538// 23,0 ... 23,79 status line
2539//
2540
2541//----- Move the cursor to row x col (count from 0, not 1) -------
2542static void place_cursor(int row, int col, int opti)
2543{
2544 char cm1[BUFSIZ];
2545 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002546#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2547 char cm2[BUFSIZ];
2548 Byte *screenp;
2549 // char cm3[BUFSIZ];
2550 int Rrow= last_row;
2551#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002552
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002553 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2554
2555 if (row < 0) row = 0;
2556 if (row >= rows) row = rows - 1;
2557 if (col < 0) col = 0;
2558 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002559
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002560 //----- 1. Try the standard terminal ESC sequence
2561 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2562 cm= cm1;
2563 if (! opti) goto pc0;
2564
2565#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2566 //----- find the minimum # of chars to move cursor -------------
2567 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2568 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002569
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002570 // move to the correct row
2571 while (row < Rrow) {
2572 // the cursor has to move up
2573 strcat(cm2, CMup);
2574 Rrow--;
2575 }
2576 while (row > Rrow) {
2577 // the cursor has to move down
2578 strcat(cm2, CMdown);
2579 Rrow++;
2580 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002581
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002582 // now move to the correct column
2583 strcat(cm2, "\r"); // start at col 0
2584 // just send out orignal source char to get to correct place
2585 screenp = &screen[row * columns]; // start of screen line
2586 strncat(cm2, screenp, col);
2587
2588 //----- 3. Try some other way of moving cursor
2589 //---------------------------------------------
2590
2591 // pick the shortest cursor motion to send out
2592 cm= cm1;
2593 if (strlen(cm2) < strlen(cm)) {
2594 cm= cm2;
2595 } /* else if (strlen(cm3) < strlen(cm)) {
2596 cm= cm3;
2597 } */
2598#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2599 pc0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002600 write1(cm); // move the cursor
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002601}
2602
2603//----- Erase from cursor to end of line -----------------------
2604static void clear_to_eol()
2605{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002606 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002607}
2608
2609//----- Erase from cursor to end of screen -----------------------
2610static void clear_to_eos()
2611{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002612 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002613}
2614
2615//----- Start standout mode ------------------------------------
2616static void standout_start() // send "start reverse video" sequence
2617{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002618 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002619}
2620
2621//----- End standout mode --------------------------------------
2622static void standout_end() // send "end reverse video" sequence
2623{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002624 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002625}
2626
2627//----- Flash the screen --------------------------------------
2628static void flash(int h)
2629{
2630 standout_start(); // send "start reverse video" sequence
2631 redraw(TRUE);
2632 (void) mysleep(h);
2633 standout_end(); // send "end reverse video" sequence
2634 redraw(TRUE);
2635}
2636
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002637static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002638{
2639#ifdef CONFIG_FEATURE_VI_CRASHME
2640 if (crashme > 0)
2641 return; // generate a random command
2642#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002643 if (!err_method) {
2644 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002645 } else {
2646 flash(10);
2647 }
2648}
2649
2650//----- Screen[] Routines --------------------------------------
2651//----- Erase the Screen[] memory ------------------------------
2652static void screen_erase()
2653{
2654 memset(screen, ' ', screensize); // clear new screen
2655}
2656
2657//----- Draw the status line at bottom of the screen -------------
2658static void show_status_line(void)
2659{
2660 static int last_cksum;
2661 int l, cnt, cksum;
2662
2663 cnt = strlen((char *) status_buffer);
2664 for (cksum= l= 0; l < cnt; l++) { cksum += (int)(status_buffer[l]); }
2665 // don't write the status line unless it changes
2666 if (cnt > 0 && last_cksum != cksum) {
2667 last_cksum= cksum; // remember if we have seen this line
2668 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002669 write1(status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002670 clear_to_eol();
2671 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2672 }
2673}
2674
2675//----- format the status buffer, the bottom line of screen ------
2676// print status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002677static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002678{
2679 va_list args;
2680
2681 va_start(args, format);
2682 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
2683 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format,
2684 args);
2685 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2686 va_end(args);
2687
2688 return;
2689}
2690
2691// print status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002692static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002693{
2694 va_list args;
2695
2696 va_start(args, format);
2697 vsprintf((char *) status_buffer, format, args);
2698 va_end(args);
2699 return;
2700}
2701
2702static void ni(Byte * s) // display messages
2703{
2704 Byte buf[BUFSIZ];
2705
2706 print_literal(buf, s);
2707 psbs("\'%s\' is not implemented", buf);
2708}
2709
2710static void edit_status(void) // show file status on status line
2711{
2712 int cur, tot, percent;
2713
2714 cur = count_lines(text, dot);
2715 tot = count_lines(text, end - 1);
2716 // current line percent
2717 // ------------- ~~ ----------
2718 // total lines 100
2719 if (tot > 0) {
2720 percent = (100 * cur) / tot;
2721 } else {
2722 cur = tot = 0;
2723 percent = 100;
2724 }
2725 psb("\"%s\""
2726#ifdef CONFIG_FEATURE_VI_READONLY
2727 "%s"
2728#endif /* CONFIG_FEATURE_VI_READONLY */
2729 "%s line %d of %d --%d%%--",
2730 (cfn != 0 ? (char *) cfn : "No file"),
2731#ifdef CONFIG_FEATURE_VI_READONLY
2732 ((vi_readonly || readonly) ? " [Read only]" : ""),
2733#endif /* CONFIG_FEATURE_VI_READONLY */
2734 (file_modified ? " [modified]" : ""),
2735 cur, tot, percent);
2736}
2737
2738//----- Force refresh of all Lines -----------------------------
2739static void redraw(int full_screen)
2740{
2741 place_cursor(0, 0, FALSE); // put cursor in correct place
2742 clear_to_eos(); // tel terminal to erase display
2743 screen_erase(); // erase the internal screen buffer
2744 refresh(full_screen); // this will redraw the entire display
2745}
2746
2747//----- Format a text[] line into a buffer ---------------------
2748static void format_line(Byte *dest, Byte *src, int li)
2749{
2750 int co;
2751 Byte c;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002752
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002753 for (co= 0; co < MAX_SCR_COLS; co++) {
2754 c= ' '; // assume blank
2755 if (li > 0 && co == 0) {
2756 c = '~'; // not first line, assume Tilde
2757 }
2758 // are there chars in text[] and have we gone past the end
2759 if (text < end && src < end) {
2760 c = *src++;
2761 }
2762 if (c == '\n')
2763 break;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002764 if (c > 127 && !Isprint(c)) {
2765 c = '.';
2766 }
2767 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002768 if (c == '\t') {
2769 c = ' ';
2770 // co % 8 != 7
2771 for (; (co % tabstop) != (tabstop - 1); co++) {
2772 dest[co] = c;
2773 }
2774 } else {
2775 dest[co++] = '^';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002776 if(c == 127)
2777 c = '?';
2778 else
2779 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002780 }
2781 }
2782 // the co++ is done here so that the column will
2783 // not be overwritten when we blank-out the rest of line
2784 dest[co] = c;
2785 if (src >= end)
2786 break;
2787 }
2788}
2789
2790//----- Refresh the changed screen lines -----------------------
2791// Copy the source line from text[] into the buffer and note
2792// if the current screenline is different from the new buffer.
2793// If they differ then that line needs redrawing on the terminal.
2794//
2795static void refresh(int full_screen)
2796{
2797 static int old_offset;
2798 int li, changed;
2799 Byte buf[MAX_SCR_COLS];
2800 Byte *tp, *sp; // pointer into text[] and screen[]
2801#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2802 int last_li= -2; // last line that changed- for optimizing cursor movement
2803#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2804
2805#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2806 window_size_get(0);
2807#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2808 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2809 tp = screenbegin; // index into text[] of top line
2810
2811 // compare text[] to screen[] and mark screen[] lines that need updating
2812 for (li = 0; li < rows - 1; li++) {
2813 int cs, ce; // column start & end
2814 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2815 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2816 // format current text line into buf
2817 format_line(buf, tp, li);
2818
2819 // skip to the end of the current text[] line
2820 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2821
2822 // see if there are any changes between vitual screen and buf
2823 changed = FALSE; // assume no change
2824 cs= 0;
2825 ce= columns-1;
2826 sp = &screen[li * columns]; // start of screen line
2827 if (full_screen) {
2828 // force re-draw of every single column from 0 - columns-1
2829 goto re0;
2830 }
2831 // compare newly formatted buffer with virtual screen
2832 // look forward for first difference between buf and screen
2833 for ( ; cs <= ce; cs++) {
2834 if (buf[cs + offset] != sp[cs]) {
2835 changed = TRUE; // mark for redraw
2836 break;
2837 }
2838 }
2839
2840 // look backward for last difference between buf and screen
2841 for ( ; ce >= cs; ce--) {
2842 if (buf[ce + offset] != sp[ce]) {
2843 changed = TRUE; // mark for redraw
2844 break;
2845 }
2846 }
2847 // now, cs is index of first diff, and ce is index of last diff
2848
2849 // if horz offset has changed, force a redraw
2850 if (offset != old_offset) {
2851 re0:
2852 changed = TRUE;
2853 }
2854
2855 // make a sanity check of columns indexes
2856 if (cs < 0) cs= 0;
2857 if (ce > columns-1) ce= columns-1;
2858 if (cs > ce) { cs= 0; ce= columns-1; }
2859 // is there a change between vitual screen and buf
2860 if (changed) {
2861 // copy changed part of buffer to virtual screen
2862 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2863
2864 // move cursor to column of first change
2865 if (offset != old_offset) {
2866 // opti_cur_move is still too stupid
2867 // to handle offsets correctly
2868 place_cursor(li, cs, FALSE);
2869 } else {
2870#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2871 // if this just the next line
2872 // try to optimize cursor movement
2873 // otherwise, use standard ESC sequence
2874 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2875 last_li= li;
2876#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2877 place_cursor(li, cs, FALSE); // use standard ESC sequence
2878#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2879 }
2880
2881 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002882 {
2883 int nic = ce-cs+1;
2884 char *out = sp+cs;
2885
2886 while(nic-- > 0) {
2887 putchar(*out);
2888 out++;
2889 }
2890 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002891#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2892 last_row = li;
2893#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2894 }
2895 }
2896
2897#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2898 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2899 last_row = crow;
2900#else
2901 place_cursor(crow, ccol, FALSE);
2902#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002903
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002904 if (offset != old_offset)
2905 old_offset = offset;
2906}
2907
Eric Andersen3f980402001-04-04 17:31:15 +00002908//---------------------------------------------------------------------
2909//----- the Ascii Chart -----------------------------------------------
2910//
2911// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2912// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2913// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2914// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2915// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2916// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2917// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2918// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2919// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2920// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2921// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2922// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2923// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2924// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2925// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2926// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2927//---------------------------------------------------------------------
2928
2929//----- Execute a Vi Command -----------------------------------
2930static void do_cmd(Byte c)
2931{
2932 Byte c1, *p, *q, *msg, buf[9], *save_dot;
2933 int cnt, i, j, dir, yf;
2934
2935 c1 = c; // quiet the compiler
2936 cnt = yf = dir = 0; // quiet the compiler
2937 p = q = save_dot = msg = buf; // quiet the compiler
2938 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00002939
2940 /* if this is a cursor key, skip these checks */
2941 switch (c) {
2942 case VI_K_UP:
2943 case VI_K_DOWN:
2944 case VI_K_LEFT:
2945 case VI_K_RIGHT:
2946 case VI_K_HOME:
2947 case VI_K_END:
2948 case VI_K_PAGEUP:
2949 case VI_K_PAGEDOWN:
2950 goto key_cmd_mode;
2951 }
2952
Eric Andersen3f980402001-04-04 17:31:15 +00002953 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002954 // flip-flop Insert/Replace mode
2955 if (c == VI_K_INSERT) goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00002956 // we are 'R'eplacing the current *dot with new char
2957 if (*dot == '\n') {
2958 // don't Replace past E-o-l
2959 cmd_mode = 1; // convert to insert
2960 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002961 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00002962 if (c != 27)
2963 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
2964 dot = char_insert(dot, c); // insert new char
2965 }
2966 goto dc1;
2967 }
2968 }
2969 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00002970 // hitting "Insert" twice means "R" replace mode
2971 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00002972 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002973 if (1 <= c || Isprint(c)) {
2974 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00002975 }
2976 goto dc1;
2977 }
2978
Eric Andersenbff7a602001-11-17 07:15:43 +00002979key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00002980 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00002981 //case 0x01: // soh
2982 //case 0x09: // ht
2983 //case 0x0b: // vt
2984 //case 0x0e: // so
2985 //case 0x0f: // si
2986 //case 0x10: // dle
2987 //case 0x11: // dc1
2988 //case 0x13: // dc3
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002989#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00002990 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00002991 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00002992 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002993#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen822c3832001-05-07 17:37:43 +00002994 //case 0x16: // syn
2995 //case 0x17: // etb
2996 //case 0x18: // can
2997 //case 0x1c: // fs
2998 //case 0x1d: // gs
2999 //case 0x1e: // rs
3000 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003001 //case '!': // !-
3002 //case '#': // #-
3003 //case '&': // &-
3004 //case '(': // (-
3005 //case ')': // )-
3006 //case '*': // *-
3007 //case ',': // ,-
3008 //case '=': // =-
3009 //case '@': // @-
3010 //case 'F': // F-
3011 //case 'K': // K-
3012 //case 'Q': // Q-
3013 //case 'S': // S-
3014 //case 'T': // T-
3015 //case 'V': // V-
3016 //case '[': // [-
3017 //case '\\': // \-
3018 //case ']': // ]-
3019 //case '_': // _-
3020 //case '`': // `-
3021 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003022 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003023 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00003024 default: // unrecognised command
3025 buf[0] = c;
3026 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003027 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00003028 buf[0] = '^';
3029 buf[1] = c + '@';
3030 buf[2] = '\0';
3031 }
3032 ni((Byte *) buf);
3033 end_cmd_q(); // stop adding to q
3034 case 0x00: // nul- ignore
3035 break;
3036 case 2: // ctrl-B scroll up full screen
3037 case VI_K_PAGEUP: // Cursor Key Page Up
3038 dot_scroll(rows - 2, -1);
3039 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003040#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003041 case 0x03: // ctrl-C interrupt
3042 longjmp(restart, 1);
3043 break;
3044 case 26: // ctrl-Z suspend
3045 suspend_sig(SIGTSTP);
3046 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003047#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +00003048 case 4: // ctrl-D scroll down half screen
3049 dot_scroll((rows - 2) / 2, 1);
3050 break;
3051 case 5: // ctrl-E scroll down one line
3052 dot_scroll(1, 1);
3053 break;
3054 case 6: // ctrl-F scroll down full screen
3055 case VI_K_PAGEDOWN: // Cursor Key Page Down
3056 dot_scroll(rows - 2, 1);
3057 break;
3058 case 7: // ctrl-G show current status
3059 edit_status();
3060 break;
3061 case 'h': // h- move left
3062 case VI_K_LEFT: // cursor key Left
Eric Andersen1c0d3112001-04-16 15:46:44 +00003063 case 8: // ctrl-H- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003064 case 127: // DEL- move left (This may be ERASE char)
3065 if (cmdcnt-- > 1) {
3066 do_cmd(c);
3067 } // repeat cnt
3068 dot_left();
3069 break;
3070 case 10: // Newline ^J
3071 case 'j': // j- goto next line, same col
3072 case VI_K_DOWN: // cursor key Down
3073 if (cmdcnt-- > 1) {
3074 do_cmd(c);
3075 } // repeat cnt
3076 dot_next(); // go to next B-o-l
3077 dot = move_to_col(dot, ccol + offset); // try stay in same col
3078 break;
3079 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003080 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003081 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003082 clear_to_eos(); // tel terminal to erase display
3083 (void) mysleep(10);
3084 screen_erase(); // erase the internal screen buffer
3085 refresh(TRUE); // this will redraw the entire display
3086 break;
3087 case 13: // Carriage Return ^M
3088 case '+': // +- goto next line
3089 if (cmdcnt-- > 1) {
3090 do_cmd(c);
3091 } // repeat cnt
3092 dot_next();
3093 dot_skip_over_ws();
3094 break;
3095 case 21: // ctrl-U scroll up half screen
3096 dot_scroll((rows - 2) / 2, -1);
3097 break;
3098 case 25: // ctrl-Y scroll up one line
3099 dot_scroll(1, -1);
3100 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003101 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003102 if (cmd_mode == 0)
3103 indicate_error(c);
3104 cmd_mode = 0; // stop insrting
3105 end_cmd_q();
3106 *status_buffer = '\0'; // clear status buffer
3107 break;
3108 case ' ': // move right
3109 case 'l': // move right
3110 case VI_K_RIGHT: // Cursor Key Right
3111 if (cmdcnt-- > 1) {
3112 do_cmd(c);
3113 } // repeat cnt
3114 dot_right();
3115 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003116#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003117 case '"': // "- name a register to use for Delete/Yank
3118 c1 = get_one_char();
3119 c1 = tolower(c1);
3120 if (islower(c1)) {
3121 YDreg = c1 - 'a';
3122 } else {
3123 indicate_error(c);
3124 }
3125 break;
3126 case '\'': // '- goto a specific mark
3127 c1 = get_one_char();
3128 c1 = tolower(c1);
3129 if (islower(c1)) {
3130 c1 = c1 - 'a';
3131 // get the b-o-l
3132 q = mark[(int) c1];
3133 if (text <= q && q < end) {
3134 dot = q;
3135 dot_begin(); // go to B-o-l
3136 dot_skip_over_ws();
3137 }
3138 } else if (c1 == '\'') { // goto previous context
3139 dot = swap_context(dot); // swap current and previous context
3140 dot_begin(); // go to B-o-l
3141 dot_skip_over_ws();
3142 } else {
3143 indicate_error(c);
3144 }
3145 break;
3146 case 'm': // m- Mark a line
3147 // this is really stupid. If there are any inserts or deletes
3148 // between text[0] and dot then this mark will not point to the
3149 // correct location! It could be off by many lines!
3150 // Well..., at least its quick and dirty.
3151 c1 = get_one_char();
3152 c1 = tolower(c1);
3153 if (islower(c1)) {
3154 c1 = c1 - 'a';
3155 // remember the line
3156 mark[(int) c1] = dot;
3157 } else {
3158 indicate_error(c);
3159 }
3160 break;
3161 case 'P': // P- Put register before
3162 case 'p': // p- put register after
3163 p = reg[YDreg];
3164 if (p == 0) {
3165 psbs("Nothing in register %c", what_reg());
3166 break;
3167 }
3168 // are we putting whole lines or strings
3169 if (strchr((char *) p, '\n') != NULL) {
3170 if (c == 'P') {
3171 dot_begin(); // putting lines- Put above
3172 }
3173 if (c == 'p') {
3174 // are we putting after very last line?
3175 if (end_line(dot) == (end - 1)) {
3176 dot = end; // force dot to end of text[]
3177 } else {
3178 dot_next(); // next line, then put before
3179 }
3180 }
3181 } else {
3182 if (c == 'p')
3183 dot_right(); // move to right, can move to NL
3184 }
3185 dot = string_insert(dot, p); // insert the string
3186 end_cmd_q(); // stop adding to q
3187 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003188 case 'U': // U- Undo; replace current line with original version
3189 if (reg[Ureg] != 0) {
3190 p = begin_line(dot);
3191 q = end_line(dot);
3192 p = text_hole_delete(p, q); // delete cur line
3193 p = string_insert(p, reg[Ureg]); // insert orig line
3194 dot = p;
3195 dot_skip_over_ws();
3196 }
3197 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003198#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003199 case '$': // $- goto end of line
3200 case VI_K_END: // Cursor Key End
3201 if (cmdcnt-- > 1) {
3202 do_cmd(c);
3203 } // repeat cnt
Glenn L McGrathee829062004-01-21 10:59:45 +00003204 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003205 break;
3206 case '%': // %- find matching char of pair () [] {}
3207 for (q = dot; q < end && *q != '\n'; q++) {
3208 if (strchr("()[]{}", *q) != NULL) {
3209 // we found half of a pair
3210 p = find_pair(q, *q);
3211 if (p == NULL) {
3212 indicate_error(c);
3213 } else {
3214 dot = p;
3215 }
3216 break;
3217 }
3218 }
3219 if (*q == '\n')
3220 indicate_error(c);
3221 break;
3222 case 'f': // f- forward to a user specified char
3223 last_forward_char = get_one_char(); // get the search char
3224 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003225 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003226 //
3227 //**** fall thru to ... 'i'
3228 case ';': // ;- look at rest of line for last forward char
3229 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003230 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003231 } // repeat cnt
Eric Andersen822c3832001-05-07 17:37:43 +00003232 if (last_forward_char == 0) break;
Eric Andersen3f980402001-04-04 17:31:15 +00003233 q = dot + 1;
3234 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3235 q++;
3236 }
3237 if (*q == last_forward_char)
3238 dot = q;
3239 break;
3240 case '-': // -- goto prev line
3241 if (cmdcnt-- > 1) {
3242 do_cmd(c);
3243 } // repeat cnt
3244 dot_prev();
3245 dot_skip_over_ws();
3246 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003247#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003248 case '.': // .- repeat the last modifying command
3249 // Stuff the last_modifying_cmd back into stdin
3250 // and let it be re-executed.
3251 if (last_modifying_cmd != 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +00003252 ioq = ioq_start = (Byte *) bb_xstrdup((char *) last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003253 }
3254 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003255#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3256#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003257 case '?': // /- search for a pattern
3258 case '/': // /- search for a pattern
3259 buf[0] = c;
3260 buf[1] = '\0';
3261 q = get_input_line(buf); // get input line- use "status line"
3262 if (strlen((char *) q) == 1)
3263 goto dc3; // if no pat re-use old pat
3264 if (strlen((char *) q) > 1) { // new pat- save it and find
3265 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003266 free(last_search_pattern);
Manuel Novoa III cad53642003-03-19 09:13:01 +00003267 last_search_pattern = (Byte *) bb_xstrdup((char *) q);
Eric Andersen3f980402001-04-04 17:31:15 +00003268 goto dc3; // now find the pattern
3269 }
3270 // user changed mind and erased the "/"- do nothing
3271 break;
3272 case 'N': // N- backward search for last pattern
3273 if (cmdcnt-- > 1) {
3274 do_cmd(c);
3275 } // repeat cnt
3276 dir = BACK; // assume BACKWARD search
3277 p = dot - 1;
3278 if (last_search_pattern[0] == '?') {
3279 dir = FORWARD;
3280 p = dot + 1;
3281 }
3282 goto dc4; // now search for pattern
3283 break;
3284 case 'n': // n- repeat search for last pattern
3285 // search rest of text[] starting at next char
3286 // if search fails return orignal "p" not the "p+1" address
3287 if (cmdcnt-- > 1) {
3288 do_cmd(c);
3289 } // repeat cnt
3290 dc3:
3291 if (last_search_pattern == 0) {
3292 msg = (Byte *) "No previous regular expression";
3293 goto dc2;
3294 }
3295 if (last_search_pattern[0] == '/') {
3296 dir = FORWARD; // assume FORWARD search
3297 p = dot + 1;
3298 }
3299 if (last_search_pattern[0] == '?') {
3300 dir = BACK;
3301 p = dot - 1;
3302 }
3303 dc4:
3304 q = char_search(p, last_search_pattern + 1, dir, FULL);
3305 if (q != NULL) {
3306 dot = q; // good search, update "dot"
3307 msg = (Byte *) "";
3308 goto dc2;
3309 }
3310 // no pattern found between "dot" and "end"- continue at top
3311 p = text;
3312 if (dir == BACK) {
3313 p = end - 1;
3314 }
3315 q = char_search(p, last_search_pattern + 1, dir, FULL);
3316 if (q != NULL) { // found something
3317 dot = q; // found new pattern- goto it
3318 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3319 if (dir == BACK) {
3320 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3321 }
3322 } else {
3323 msg = (Byte *) "Pattern not found";
3324 }
3325 dc2:
3326 psbs("%s", msg);
3327 break;
3328 case '{': // {- move backward paragraph
3329 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3330 if (q != NULL) { // found blank line
3331 dot = next_line(q); // move to next blank line
3332 }
3333 break;
3334 case '}': // }- move forward paragraph
3335 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3336 if (q != NULL) { // found blank line
3337 dot = next_line(q); // move to next blank line
3338 }
3339 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003340#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003341 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003342 case '1': // 1-
3343 case '2': // 2-
3344 case '3': // 3-
3345 case '4': // 4-
3346 case '5': // 5-
3347 case '6': // 6-
3348 case '7': // 7-
3349 case '8': // 8-
3350 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003351 if (c == '0' && cmdcnt < 1) {
3352 dot_begin(); // this was a standalone zero
3353 } else {
3354 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3355 }
3356 break;
3357 case ':': // :- the colon mode commands
Eric Andersen3f980402001-04-04 17:31:15 +00003358 p = get_input_line((Byte *) ":"); // get input line- use "status line"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003359#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003360 colon(p); // execute the command
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003361#else /* CONFIG_FEATURE_VI_COLON */
Eric Andersen822c3832001-05-07 17:37:43 +00003362 if (*p == ':')
3363 p++; // move past the ':'
3364 cnt = strlen((char *) p);
3365 if (cnt <= 0)
3366 break;
3367 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3368 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
Matt Kraai1f0c4362001-12-20 23:13:26 +00003369 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003370 psbs("No write since last change (:quit! overrides)");
3371 } else {
3372 editing = 0;
3373 }
Eric Andersen822c3832001-05-07 17:37:43 +00003374 } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
Robert Griebla71389b2002-07-31 21:22:21 +00003375 strncasecmp((char *) p, "wq", cnt) == 0 ||
3376 strncasecmp((char *) p, "x", cnt) == 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003377 cnt = file_write(cfn, text, end - 1);
3378 file_modified = FALSE;
3379 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
Robert Griebla71389b2002-07-31 21:22:21 +00003380 if (p[0] == 'x' || p[1] == 'q') {
Eric Andersen3f980402001-04-04 17:31:15 +00003381 editing = 0;
3382 }
Eric Andersen822c3832001-05-07 17:37:43 +00003383 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
3384 edit_status(); // show current file status
3385 } else if (sscanf((char *) p, "%d", &j) > 0) {
3386 dot = find_line(j); // go to line # j
3387 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003388 } else { // unrecognised cmd
Eric Andersen822c3832001-05-07 17:37:43 +00003389 ni((Byte *) p);
Eric Andersen3f980402001-04-04 17:31:15 +00003390 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003391#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003392 break;
3393 case '<': // <- Left shift something
3394 case '>': // >- Right shift something
3395 cnt = count_lines(text, dot); // remember what line we are on
3396 c1 = get_one_char(); // get the type of thing to delete
3397 find_range(&p, &q, c1);
3398 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3399 p = begin_line(p);
3400 q = end_line(q);
3401 i = count_lines(p, q); // # of lines we are shifting
3402 for ( ; i > 0; i--, p = next_line(p)) {
3403 if (c == '<') {
3404 // shift left- remove tab or 8 spaces
3405 if (*p == '\t') {
3406 // shrink buffer 1 char
3407 (void) text_hole_delete(p, p);
3408 } else if (*p == ' ') {
3409 // we should be calculating columns, not just SPACE
3410 for (j = 0; *p == ' ' && j < tabstop; j++) {
3411 (void) text_hole_delete(p, p);
3412 }
3413 }
3414 } else if (c == '>') {
3415 // shift right -- add tab or 8 spaces
3416 (void) char_insert(p, '\t');
3417 }
3418 }
3419 dot = find_line(cnt); // what line were we on
3420 dot_skip_over_ws();
3421 end_cmd_q(); // stop adding to q
3422 break;
3423 case 'A': // A- append at e-o-l
3424 dot_end(); // go to e-o-l
3425 //**** fall thru to ... 'a'
3426 case 'a': // a- append after current char
3427 if (*dot != '\n')
3428 dot++;
3429 goto dc_i;
3430 break;
3431 case 'B': // B- back a blank-delimited Word
3432 case 'E': // E- end of a blank-delimited word
3433 case 'W': // W- forward a blank-delimited word
3434 if (cmdcnt-- > 1) {
3435 do_cmd(c);
3436 } // repeat cnt
3437 dir = FORWARD;
3438 if (c == 'B')
3439 dir = BACK;
3440 if (c == 'W' || isspace(dot[dir])) {
3441 dot = skip_thing(dot, 1, dir, S_TO_WS);
3442 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3443 }
3444 if (c != 'W')
3445 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3446 break;
3447 case 'C': // C- Change to e-o-l
3448 case 'D': // D- delete to e-o-l
3449 save_dot = dot;
3450 dot = dollar_line(dot); // move to before NL
3451 // copy text into a register and delete
3452 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3453 if (c == 'C')
3454 goto dc_i; // start inserting
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003455#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003456 if (c == 'D')
3457 end_cmd_q(); // stop adding to q
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003458#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003459 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003460 case 'G': // G- goto to a line number (default= E-O-F)
3461 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003462 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003463 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003464 }
3465 dot_skip_over_ws();
3466 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003467 case 'H': // H- goto top line on screen
3468 dot = screenbegin;
3469 if (cmdcnt > (rows - 1)) {
3470 cmdcnt = (rows - 1);
3471 }
3472 if (cmdcnt-- > 1) {
3473 do_cmd('+');
3474 } // repeat cnt
3475 dot_skip_over_ws();
3476 break;
3477 case 'I': // I- insert before first non-blank
3478 dot_begin(); // 0
3479 dot_skip_over_ws();
3480 //**** fall thru to ... 'i'
3481 case 'i': // i- insert before current char
3482 case VI_K_INSERT: // Cursor Key Insert
3483 dc_i:
3484 cmd_mode = 1; // start insrting
3485 psb("-- Insert --");
3486 break;
3487 case 'J': // J- join current and next lines together
3488 if (cmdcnt-- > 2) {
3489 do_cmd(c);
3490 } // repeat cnt
3491 dot_end(); // move to NL
3492 if (dot < end - 1) { // make sure not last char in text[]
3493 *dot++ = ' '; // replace NL with space
3494 while (isblnk(*dot)) { // delete leading WS
3495 dot_delete();
3496 }
3497 }
3498 end_cmd_q(); // stop adding to q
3499 break;
3500 case 'L': // L- goto bottom line on screen
3501 dot = end_screen();
3502 if (cmdcnt > (rows - 1)) {
3503 cmdcnt = (rows - 1);
3504 }
3505 if (cmdcnt-- > 1) {
3506 do_cmd('-');
3507 } // repeat cnt
3508 dot_begin();
3509 dot_skip_over_ws();
3510 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003511 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003512 dot = screenbegin;
3513 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3514 dot = next_line(dot);
3515 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003516 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003517 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003518 p = begin_line(dot);
3519 if (p[-1] == '\n') {
3520 dot_prev();
3521 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3522 dot_end();
3523 dot = char_insert(dot, '\n');
3524 } else {
3525 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003526 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003527 dot_prev(); // -
3528 }
3529 goto dc_i;
3530 break;
3531 case 'R': // R- continuous Replace char
Eric Andersen1c0d3112001-04-16 15:46:44 +00003532 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003533 cmd_mode = 2;
3534 psb("-- Replace --");
3535 break;
3536 case 'X': // X- delete char before dot
3537 case 'x': // x- delete the current char
3538 case 's': // s- substitute the current char
3539 if (cmdcnt-- > 1) {
3540 do_cmd(c);
3541 } // repeat cnt
3542 dir = 0;
3543 if (c == 'X')
3544 dir = -1;
3545 if (dot[dir] != '\n') {
3546 if (c == 'X')
3547 dot--; // delete prev char
3548 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3549 }
3550 if (c == 's')
3551 goto dc_i; // start insrting
3552 end_cmd_q(); // stop adding to q
3553 break;
3554 case 'Z': // Z- if modified, {write}; exit
3555 // ZZ means to save file (if necessary), then exit
3556 c1 = get_one_char();
3557 if (c1 != 'Z') {
3558 indicate_error(c);
3559 break;
3560 }
Matt Kraai1f0c4362001-12-20 23:13:26 +00003561 if (file_modified
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003562#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00003563 && ! vi_readonly
3564 && ! readonly
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003565#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00003566 ) {
3567 cnt = file_write(cfn, text, end - 1);
3568 if (cnt == (end - 1 - text + 1)) {
3569 editing = 0;
3570 }
3571 } else {
3572 editing = 0;
3573 }
3574 break;
3575 case '^': // ^- move to first non-blank on line
3576 dot_begin();
3577 dot_skip_over_ws();
3578 break;
3579 case 'b': // b- back a word
3580 case 'e': // e- end of word
3581 if (cmdcnt-- > 1) {
3582 do_cmd(c);
3583 } // repeat cnt
3584 dir = FORWARD;
3585 if (c == 'b')
3586 dir = BACK;
3587 if ((dot + dir) < text || (dot + dir) > end - 1)
3588 break;
3589 dot += dir;
3590 if (isspace(*dot)) {
3591 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3592 }
3593 if (isalnum(*dot) || *dot == '_') {
3594 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3595 } else if (ispunct(*dot)) {
3596 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3597 }
3598 break;
3599 case 'c': // c- change something
3600 case 'd': // d- delete something
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003601#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003602 case 'y': // y- yank something
3603 case 'Y': // Y- Yank a line
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003604#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003605 yf = YANKDEL; // assume either "c" or "d"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003606#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003607 if (c == 'y' || c == 'Y')
3608 yf = YANKONLY;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003609#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003610 c1 = 'y';
3611 if (c != 'Y')
3612 c1 = get_one_char(); // get the type of thing to delete
3613 find_range(&p, &q, c1);
3614 if (c1 == 27) { // ESC- user changed mind and wants out
3615 c = c1 = 27; // Escape- do nothing
3616 } else if (strchr("wW", c1)) {
3617 if (c == 'c') {
3618 // don't include trailing WS as part of word
3619 while (isblnk(*q)) {
3620 if (q <= text || q[-1] == '\n')
3621 break;
3622 q--;
3623 }
3624 }
3625 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003626 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003627 // single line copy text into a register and delete
3628 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003629 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003630 // multiple line copy text into a register and delete
3631 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003632 if (c == 'c') {
3633 dot = char_insert(dot, '\n');
3634 // on the last line of file don't move to prev line
3635 if (dot != (end-1)) {
3636 dot_prev();
3637 }
3638 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003639 dot_begin();
3640 dot_skip_over_ws();
3641 }
3642 } else {
3643 // could not recognize object
3644 c = c1 = 27; // error-
3645 indicate_error(c);
3646 }
3647 if (c1 != 27) {
3648 // if CHANGING, not deleting, start inserting after the delete
3649 if (c == 'c') {
3650 strcpy((char *) buf, "Change");
3651 goto dc_i; // start inserting
3652 }
3653 if (c == 'd') {
3654 strcpy((char *) buf, "Delete");
3655 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003656#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003657 if (c == 'y' || c == 'Y') {
3658 strcpy((char *) buf, "Yank");
3659 }
3660 p = reg[YDreg];
3661 q = p + strlen((char *) p);
3662 for (cnt = 0; p <= q; p++) {
3663 if (*p == '\n')
3664 cnt++;
3665 }
3666 psb("%s %d lines (%d chars) using [%c]",
3667 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003668#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003669 end_cmd_q(); // stop adding to q
3670 }
3671 break;
3672 case 'k': // k- goto prev line, same col
3673 case VI_K_UP: // cursor key Up
3674 if (cmdcnt-- > 1) {
3675 do_cmd(c);
3676 } // repeat cnt
3677 dot_prev();
3678 dot = move_to_col(dot, ccol + offset); // try stay in same col
3679 break;
3680 case 'r': // r- replace the current char with user input
3681 c1 = get_one_char(); // get the replacement char
3682 if (*dot != '\n') {
3683 *dot = c1;
3684 file_modified = TRUE; // has the file been modified
3685 }
3686 end_cmd_q(); // stop adding to q
3687 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003688 case 't': // t- move to char prior to next x
3689 last_forward_char = get_one_char();
3690 do_cmd(';');
3691 if (*dot == last_forward_char)
3692 dot_left();
3693 last_forward_char= 0;
3694 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003695 case 'w': // w- forward a word
3696 if (cmdcnt-- > 1) {
3697 do_cmd(c);
3698 } // repeat cnt
3699 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3700 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3701 } else if (ispunct(*dot)) { // we are on PUNCT
3702 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3703 }
3704 if (dot < end - 1)
3705 dot++; // move over word
3706 if (isspace(*dot)) {
3707 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3708 }
3709 break;
3710 case 'z': // z-
3711 c1 = get_one_char(); // get the replacement char
3712 cnt = 0;
3713 if (c1 == '.')
3714 cnt = (rows - 2) / 2; // put dot at center
3715 if (c1 == '-')
3716 cnt = rows - 2; // put dot at bottom
3717 screenbegin = begin_line(dot); // start dot at top
3718 dot_scroll(cnt, -1);
3719 break;
3720 case '|': // |- move to column "cmdcnt"
3721 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3722 break;
3723 case '~': // ~- flip the case of letters a-z -> A-Z
3724 if (cmdcnt-- > 1) {
3725 do_cmd(c);
3726 } // repeat cnt
3727 if (islower(*dot)) {
3728 *dot = toupper(*dot);
3729 file_modified = TRUE; // has the file been modified
3730 } else if (isupper(*dot)) {
3731 *dot = tolower(*dot);
3732 file_modified = TRUE; // has the file been modified
3733 }
3734 dot_right();
3735 end_cmd_q(); // stop adding to q
3736 break;
3737 //----- The Cursor and Function Keys -----------------------------
3738 case VI_K_HOME: // Cursor Key Home
3739 dot_begin();
3740 break;
3741 // The Fn keys could point to do_macro which could translate them
3742 case VI_K_FUN1: // Function Key F1
3743 case VI_K_FUN2: // Function Key F2
3744 case VI_K_FUN3: // Function Key F3
3745 case VI_K_FUN4: // Function Key F4
3746 case VI_K_FUN5: // Function Key F5
3747 case VI_K_FUN6: // Function Key F6
3748 case VI_K_FUN7: // Function Key F7
3749 case VI_K_FUN8: // Function Key F8
3750 case VI_K_FUN9: // Function Key F9
3751 case VI_K_FUN10: // Function Key F10
3752 case VI_K_FUN11: // Function Key F11
3753 case VI_K_FUN12: // Function Key F12
3754 break;
3755 }
3756
3757 dc1:
3758 // if text[] just became empty, add back an empty line
3759 if (end == text) {
3760 (void) char_insert(text, '\n'); // start empty buf with dummy line
3761 dot = text;
3762 }
3763 // it is OK for dot to exactly equal to end, otherwise check dot validity
3764 if (dot != end) {
3765 dot = bound_dot(dot); // make sure "dot" is valid
3766 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003767#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003768 check_context(c); // update the current context
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003769#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003770
3771 if (!isdigit(c))
3772 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3773 cnt = dot - begin_line(dot);
3774 // Try to stay off of the Newline
3775 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3776 dot--;
3777}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003778
3779#ifdef CONFIG_FEATURE_VI_CRASHME
3780static int totalcmds = 0;
3781static int Mp = 85; // Movement command Probability
3782static int Np = 90; // Non-movement command Probability
3783static int Dp = 96; // Delete command Probability
3784static int Ip = 97; // Insert command Probability
3785static int Yp = 98; // Yank command Probability
3786static int Pp = 99; // Put command Probability
3787static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3788char chars[20] = "\t012345 abcdABCD-=.$";
3789char *words[20] = { "this", "is", "a", "test",
3790 "broadcast", "the", "emergency", "of",
3791 "system", "quick", "brown", "fox",
3792 "jumped", "over", "lazy", "dogs",
3793 "back", "January", "Febuary", "March"
3794};
3795char *lines[20] = {
3796 "You should have received a copy of the GNU General Public License\n",
3797 "char c, cm, *cmd, *cmd1;\n",
3798 "generate a command by percentages\n",
3799 "Numbers may be typed as a prefix to some commands.\n",
3800 "Quit, discarding changes!\n",
3801 "Forced write, if permission originally not valid.\n",
3802 "In general, any ex or ed command (such as substitute or delete).\n",
3803 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3804 "Please get w/ me and I will go over it with you.\n",
3805 "The following is a list of scheduled, committed changes.\n",
3806 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3807 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3808 "Any question about transactions please contact Sterling Huxley.\n",
3809 "I will try to get back to you by Friday, December 31.\n",
3810 "This Change will be implemented on Friday.\n",
3811 "Let me know if you have problems accessing this;\n",
3812 "Sterling Huxley recently added you to the access list.\n",
3813 "Would you like to go to lunch?\n",
3814 "The last command will be automatically run.\n",
3815 "This is too much english for a computer geek.\n",
3816};
3817char *multilines[20] = {
3818 "You should have received a copy of the GNU General Public License\n",
3819 "char c, cm, *cmd, *cmd1;\n",
3820 "generate a command by percentages\n",
3821 "Numbers may be typed as a prefix to some commands.\n",
3822 "Quit, discarding changes!\n",
3823 "Forced write, if permission originally not valid.\n",
3824 "In general, any ex or ed command (such as substitute or delete).\n",
3825 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3826 "Please get w/ me and I will go over it with you.\n",
3827 "The following is a list of scheduled, committed changes.\n",
3828 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3829 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3830 "Any question about transactions please contact Sterling Huxley.\n",
3831 "I will try to get back to you by Friday, December 31.\n",
3832 "This Change will be implemented on Friday.\n",
3833 "Let me know if you have problems accessing this;\n",
3834 "Sterling Huxley recently added you to the access list.\n",
3835 "Would you like to go to lunch?\n",
3836 "The last command will be automatically run.\n",
3837 "This is too much english for a computer geek.\n",
3838};
3839
3840// create a random command to execute
3841static void crash_dummy()
3842{
3843 static int sleeptime; // how long to pause between commands
3844 char c, cm, *cmd, *cmd1;
3845 int i, cnt, thing, rbi, startrbi, percent;
3846
3847 // "dot" movement commands
3848 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3849
3850 // is there already a command running?
3851 if (readed_for_parse > 0)
3852 goto cd1;
3853 cd0:
3854 startrbi = rbi = 0;
3855 sleeptime = 0; // how long to pause between commands
3856 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3857 // generate a command by percentages
3858 percent = (int) lrand48() % 100; // get a number from 0-99
3859 if (percent < Mp) { // Movement commands
3860 // available commands
3861 cmd = cmd1;
3862 M++;
3863 } else if (percent < Np) { // non-movement commands
3864 cmd = "mz<>\'\""; // available commands
3865 N++;
3866 } else if (percent < Dp) { // Delete commands
3867 cmd = "dx"; // available commands
3868 D++;
3869 } else if (percent < Ip) { // Inset commands
3870 cmd = "iIaAsrJ"; // available commands
3871 I++;
3872 } else if (percent < Yp) { // Yank commands
3873 cmd = "yY"; // available commands
3874 Y++;
3875 } else if (percent < Pp) { // Put commands
3876 cmd = "pP"; // available commands
3877 P++;
3878 } else {
3879 // We do not know how to handle this command, try again
3880 U++;
3881 goto cd0;
3882 }
3883 // randomly pick one of the available cmds from "cmd[]"
3884 i = (int) lrand48() % strlen(cmd);
3885 cm = cmd[i];
3886 if (strchr(":\024", cm))
3887 goto cd0; // dont allow colon or ctrl-T commands
3888 readbuffer[rbi++] = cm; // put cmd into input buffer
3889
3890 // now we have the command-
3891 // there are 1, 2, and multi char commands
3892 // find out which and generate the rest of command as necessary
3893 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3894 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3895 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3896 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3897 }
3898 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3899 c = cmd1[thing];
3900 readbuffer[rbi++] = c; // add movement to input buffer
3901 }
3902 if (strchr("iIaAsc", cm)) { // multi-char commands
3903 if (cm == 'c') {
3904 // change some thing
3905 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3906 c = cmd1[thing];
3907 readbuffer[rbi++] = c; // add movement to input buffer
3908 }
3909 thing = (int) lrand48() % 4; // what thing to insert
3910 cnt = (int) lrand48() % 10; // how many to insert
3911 for (i = 0; i < cnt; i++) {
3912 if (thing == 0) { // insert chars
3913 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3914 } else if (thing == 1) { // insert words
3915 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
3916 strcat((char *) readbuffer, " ");
3917 sleeptime = 0; // how fast to type
3918 } else if (thing == 2) { // insert lines
3919 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
3920 sleeptime = 0; // how fast to type
3921 } else { // insert multi-lines
3922 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
3923 sleeptime = 0; // how fast to type
3924 }
3925 }
3926 strcat((char *) readbuffer, "\033");
3927 }
3928 readed_for_parse = strlen(readbuffer);
3929 cd1:
3930 totalcmds++;
3931 if (sleeptime > 0)
3932 (void) mysleep(sleeptime); // sleep 1/100 sec
3933}
3934
3935// test to see if there are any errors
3936static void crash_test()
3937{
3938 static time_t oldtim;
3939 time_t tim;
Glenn L McGrath7127b582002-12-03 21:48:15 +00003940 char d[2], msg[BUFSIZ];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003941
3942 msg[0] = '\0';
3943 if (end < text) {
3944 strcat((char *) msg, "end<text ");
3945 }
3946 if (end > textend) {
3947 strcat((char *) msg, "end>textend ");
3948 }
3949 if (dot < text) {
3950 strcat((char *) msg, "dot<text ");
3951 }
3952 if (dot > end) {
3953 strcat((char *) msg, "dot>end ");
3954 }
3955 if (screenbegin < text) {
3956 strcat((char *) msg, "screenbegin<text ");
3957 }
3958 if (screenbegin > end - 1) {
3959 strcat((char *) msg, "screenbegin>end-1 ");
3960 }
3961
3962 if (strlen(msg) > 0) {
3963 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00003964 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003965 totalcmds, last_input_char, msg, SOs, SOn);
3966 fflush(stdout);
3967 while (read(0, d, 1) > 0) {
3968 if (d[0] == '\n' || d[0] == '\r')
3969 break;
3970 }
3971 alarm(3);
3972 }
3973 tim = (time_t) time((time_t *) 0);
3974 if (tim >= (oldtim + 3)) {
3975 sprintf((char *) status_buffer,
3976 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
3977 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
3978 oldtim = tim;
3979 }
3980 return;
3981}
3982#endif /* CONFIG_FEATURE_VI_CRASHME */