blob: 144e9d7601e36f1a79360b8cd5e5804fd5e56a4c [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[] =
Manuel Novoa III cad53642003-03-19 09:13:01 +000022 "$Id: vi.c,v 1.28 2003/03/19 09:11:45 mjn3 Exp $";
Eric Andersen3f980402001-04-04 17:31:15 +000023
24/*
25 * To compile for standalone use:
Eric Andersend402edf2001-04-04 19:29:48 +000026 * gcc -Wall -Os -s -DSTANDALONE -o vi vi.c
Eric Andersen3f980402001-04-04 17:31:15 +000027 * or
Eric Andersenbdfd0d72001-10-24 05:00:29 +000028 * gcc -Wall -Os -s -DSTANDALONE -DCONFIG_FEATURE_VI_CRASHME -o vi vi.c # include testing features
Eric Andersen3f980402001-04-04 17:31:15 +000029 * strip vi
30 */
31
32/*
33 * Things To Do:
34 * EXINIT
Eric Andersen1c0d3112001-04-16 15:46:44 +000035 * $HOME/.exrc and ./.exrc
Eric Andersen3f980402001-04-04 17:31:15 +000036 * add magic to search /foo.*bar
37 * add :help command
38 * :map macros
39 * how about mode lines: vi: set sw=8 ts=8:
40 * if mark[] values were line numbers rather than pointers
41 * it would be easier to change the mark when add/delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +000042 * More intelligence in refresh()
43 * ":r !cmd" and "!cmd" to filter text through an external command
44 * A true "undo" facility
45 * An "ex" line oriented mode- maybe using "cmdedit"
Eric Andersen3f980402001-04-04 17:31:15 +000046 */
47
48//---- Feature -------------- Bytes to immplement
49#ifdef STANDALONE
Eric Andersen822c3832001-05-07 17:37:43 +000050#define vi_main main
Eric Andersenbdfd0d72001-10-24 05:00:29 +000051#define CONFIG_FEATURE_VI_COLON // 4288
52#define CONFIG_FEATURE_VI_YANKMARK // 1408
53#define CONFIG_FEATURE_VI_SEARCH // 1088
54#define CONFIG_FEATURE_VI_USE_SIGNALS // 1056
55#define CONFIG_FEATURE_VI_DOT_CMD // 576
56#define CONFIG_FEATURE_VI_READONLY // 128
57#define CONFIG_FEATURE_VI_SETOPTS // 576
58#define CONFIG_FEATURE_VI_SET // 224
59#define CONFIG_FEATURE_VI_WIN_RESIZE // 256 WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +000060// To test editor using CRASHME:
61// vi -C filename
62// To stop testing, wait until all to text[] is deleted, or
63// Ctrl-Z and kill -9 %1
64// while in the editor Ctrl-T will toggle the crashme function on and off.
Eric Andersenbdfd0d72001-10-24 05:00:29 +000065//#define CONFIG_FEATURE_VI_CRASHME // randomly pick commands to execute
Eric Andersen3f980402001-04-04 17:31:15 +000066#endif /* STANDALONE */
67
Eric Andersen3f980402001-04-04 17:31:15 +000068#include <stdio.h>
69#include <stdlib.h>
70#include <string.h>
71#include <termios.h>
72#include <unistd.h>
73#include <sys/ioctl.h>
74#include <sys/time.h>
75#include <sys/types.h>
76#include <sys/stat.h>
77#include <time.h>
78#include <fcntl.h>
79#include <signal.h>
80#include <setjmp.h>
81#include <regex.h>
82#include <ctype.h>
83#include <assert.h>
84#include <errno.h>
85#include <stdarg.h>
Eric Andersene0c07572001-06-23 13:49:14 +000086#ifndef STANDALONE
87#include "busybox.h"
88#endif /* STANDALONE */
Eric Andersen3f980402001-04-04 17:31:15 +000089
Glenn L McGrath09adaca2002-12-02 21:18:10 +000090#ifdef CONFIG_LOCALE_SUPPORT
91#define Isprint(c) isprint((c))
92#else
93#define Isprint(c) ( (c) >= ' ' && (c) != 127 && (c) != ((unsigned char)'\233') )
94#endif
95
Eric Andersen3f980402001-04-04 17:31:15 +000096#ifndef TRUE
97#define TRUE ((int)1)
98#define FALSE ((int)0)
99#endif /* TRUE */
Eric Andersen1c0d3112001-04-16 15:46:44 +0000100#define MAX_SCR_COLS BUFSIZ
Eric Andersen3f980402001-04-04 17:31:15 +0000101
102// Misc. non-Ascii keys that report an escape sequence
103#define VI_K_UP 128 // cursor key Up
104#define VI_K_DOWN 129 // cursor key Down
105#define VI_K_RIGHT 130 // Cursor Key Right
106#define VI_K_LEFT 131 // cursor key Left
107#define VI_K_HOME 132 // Cursor Key Home
108#define VI_K_END 133 // Cursor Key End
109#define VI_K_INSERT 134 // Cursor Key Insert
110#define VI_K_PAGEUP 135 // Cursor Key Page Up
111#define VI_K_PAGEDOWN 136 // Cursor Key Page Down
112#define VI_K_FUN1 137 // Function Key F1
113#define VI_K_FUN2 138 // Function Key F2
114#define VI_K_FUN3 139 // Function Key F3
115#define VI_K_FUN4 140 // Function Key F4
116#define VI_K_FUN5 141 // Function Key F5
117#define VI_K_FUN6 142 // Function Key F6
118#define VI_K_FUN7 143 // Function Key F7
119#define VI_K_FUN8 144 // Function Key F8
120#define VI_K_FUN9 145 // Function Key F9
121#define VI_K_FUN10 146 // Function Key F10
122#define VI_K_FUN11 147 // Function Key F11
123#define VI_K_FUN12 148 // Function Key F12
124
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000125/* vt102 typical ESC sequence */
126/* terminal standout start/normal ESC sequence */
127static const char SOs[] = "\033[7m";
128static const char SOn[] = "\033[0m";
129/* terminal bell sequence */
130static const char bell[] = "\007";
131/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
132static const char Ceol[] = "\033[0K";
133static const char Ceos [] = "\033[0J";
134/* Cursor motion arbitrary destination ESC sequence */
135static const char CMrc[] = "\033[%d;%dH";
136/* Cursor motion up and down ESC sequence */
137static const char CMup[] = "\033[A";
138static const char CMdown[] = "\n";
139
140
Eric Andersen3f980402001-04-04 17:31:15 +0000141static const int YANKONLY = FALSE;
142static const int YANKDEL = TRUE;
143static const int FORWARD = 1; // code depends on "1" for array index
144static const int BACK = -1; // code depends on "-1" for array index
145static const int LIMITED = 0; // how much of text[] in char_search
146static const int FULL = 1; // how much of text[] in char_search
147
148static const int S_BEFORE_WS = 1; // used in skip_thing() for moving "dot"
149static const int S_TO_WS = 2; // used in skip_thing() for moving "dot"
150static const int S_OVER_WS = 3; // used in skip_thing() for moving "dot"
151static const int S_END_PUNCT = 4; // used in skip_thing() for moving "dot"
152static const int S_END_ALNUM = 5; // used in skip_thing() for moving "dot"
153
154typedef unsigned char Byte;
155
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000156static int vi_setops;
157#define VI_AUTOINDENT 1
158#define VI_SHOWMATCH 2
159#define VI_IGNORECASE 4
160#define VI_ERR_METHOD 8
161#define autoindent (vi_setops & VI_AUTOINDENT)
162#define showmatch (vi_setops & VI_SHOWMATCH )
163#define ignorecase (vi_setops & VI_IGNORECASE)
164/* indicate error with beep or flash */
165#define err_method (vi_setops & VI_ERR_METHOD)
166
Eric Andersen3f980402001-04-04 17:31:15 +0000167
168static int editing; // >0 while we are editing a file
169static int cmd_mode; // 0=command 1=insert
170static int file_modified; // buffer contents changed
Eric Andersen3f980402001-04-04 17:31:15 +0000171static int fn_start; // index of first cmd line file name
172static int save_argc; // how many file names on cmd line
173static int cmdcnt; // repetition count
174static fd_set rfds; // use select() for small sleeps
175static struct timeval tv; // use select() for small sleeps
Eric Andersen3f980402001-04-04 17:31:15 +0000176static int rows, columns; // the terminal screen is this size
177static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset
Eric Andersen3f980402001-04-04 17:31:15 +0000178static Byte *status_buffer; // mesages to the user
Eric Andersen3f980402001-04-04 17:31:15 +0000179static Byte *cfn; // previous, current, and next file name
180static Byte *text, *end, *textend; // pointers to the user data in memory
181static Byte *screen; // pointer to the virtual screen buffer
182static int screensize; // and its size
183static Byte *screenbegin; // index into text[], of top line on the screen
184static Byte *dot; // where all the action takes place
185static int tabstop;
186static struct termios term_orig, term_vi; // remember what the cooked mode was
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000187static Byte erase_char; // the users erase character
188static Byte last_input_char; // last char read from user
189static Byte last_forward_char; // last char searched for with 'f'
Eric Andersen3f980402001-04-04 17:31:15 +0000190
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000191#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +0000192static int last_row; // where the cursor was last moved to
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000193#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
194#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000195static jmp_buf restart; // catch_sig()
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000196#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000197#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
198static int my_pid;
199#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000200#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000201static struct winsize winsize; // remember the window size
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000202#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
203#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000204static int adding2q; // are we currently adding user input to q
205static Byte *last_modifying_cmd; // last modifying cmd for "."
206static Byte *ioq, *ioq_start; // pointer to string for get_one_char to "read"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000207#endif /* CONFIG_FEATURE_VI_DOT_CMD */
208#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
Eric Andersen3f980402001-04-04 17:31:15 +0000209static Byte *modifying_cmds; // cmds that modify text[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000210#endif /* CONFIG_FEATURE_VI_DOT_CMD || CONFIG_FEATURE_VI_YANKMARK */
211#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000212static int vi_readonly, readonly;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000213#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000214#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000215static Byte *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
216static int YDreg, Ureg; // default delete register and orig line for "U"
217static Byte *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
218static Byte *context_start, *context_end;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000219#endif /* CONFIG_FEATURE_VI_YANKMARK */
220#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000221static Byte *last_search_pattern; // last pattern from a '/' or '?' search
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000222#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +0000223
224
225static void edit_file(Byte *); // edit one file
226static void do_cmd(Byte); // execute a command
227static void sync_cursor(Byte *, int *, int *); // synchronize the screen cursor to dot
228static Byte *begin_line(Byte *); // return pointer to cur line B-o-l
229static Byte *end_line(Byte *); // return pointer to cur line E-o-l
Eric Andersen3f980402001-04-04 17:31:15 +0000230static Byte *prev_line(Byte *); // return pointer to prev line B-o-l
231static Byte *next_line(Byte *); // return pointer to next line B-o-l
232static Byte *end_screen(void); // get pointer to last char on screen
233static int count_lines(Byte *, Byte *); // count line from start to stop
234static Byte *find_line(int); // find begining of line #li
235static Byte *move_to_col(Byte *, int); // move "p" to column l
236static int isblnk(Byte); // is the char a blank or tab
237static void dot_left(void); // move dot left- dont leave line
238static void dot_right(void); // move dot right- dont leave line
239static void dot_begin(void); // move dot to B-o-l
240static void dot_end(void); // move dot to E-o-l
241static void dot_next(void); // move dot to next line B-o-l
242static void dot_prev(void); // move dot to prev line B-o-l
243static void dot_scroll(int, int); // move the screen up or down
244static void dot_skip_over_ws(void); // move dot pat WS
245static void dot_delete(void); // delete the char at 'dot'
246static Byte *bound_dot(Byte *); // make sure text[0] <= P < "end"
247static Byte *new_screen(int, int); // malloc virtual screen memory
248static Byte *new_text(int); // malloc memory for text[] buffer
249static Byte *char_insert(Byte *, Byte); // insert the char c at 'p'
250static Byte *stupid_insert(Byte *, Byte); // stupidly insert the char c at 'p'
251static Byte find_range(Byte **, Byte **, Byte); // return pointers for an object
252static int st_test(Byte *, int, int, Byte *); // helper for skip_thing()
253static Byte *skip_thing(Byte *, int, int, int); // skip some object
254static Byte *find_pair(Byte *, Byte); // find matching pair () [] {}
255static Byte *text_hole_delete(Byte *, Byte *); // at "p", delete a 'size' byte hole
256static Byte *text_hole_make(Byte *, int); // at "p", make a 'size' byte hole
257static Byte *yank_delete(Byte *, Byte *, int, int); // yank text[] into register then delete
258static void show_help(void); // display some help info
Eric Andersen3f980402001-04-04 17:31:15 +0000259static void rawmode(void); // set "raw" mode on tty
260static void cookmode(void); // return to "cooked" mode on tty
261static int mysleep(int); // sleep for 'h' 1/100 seconds
262static Byte readit(void); // read (maybe cursor) key from stdin
263static Byte get_one_char(void); // read 1 char from stdin
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000264static int file_size(const Byte *); // what is the byte size of "fn"
Eric Andersen3f980402001-04-04 17:31:15 +0000265static int file_insert(Byte *, Byte *, int);
266static int file_write(Byte *, Byte *, Byte *);
Eric Andersen822c3832001-05-07 17:37:43 +0000267static void place_cursor(int, int, int);
Eric Andersenbff7a602001-11-17 07:15:43 +0000268static void screen_erase(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000269static void clear_to_eol(void);
270static void clear_to_eos(void);
271static void standout_start(void); // send "start reverse video" sequence
272static void standout_end(void); // send "end reverse video" sequence
273static void flash(int); // flash the terminal screen
Eric Andersen3f980402001-04-04 17:31:15 +0000274static void show_status_line(void); // put a message on the bottom line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000275static void psb(const char *, ...); // Print Status Buf
276static void psbs(const char *, ...); // Print Status Buf in standout mode
Eric Andersen3f980402001-04-04 17:31:15 +0000277static void ni(Byte *); // display messages
278static void edit_status(void); // show file status on status line
279static void redraw(int); // force a full screen refresh
Eric Andersen822c3832001-05-07 17:37:43 +0000280static void format_line(Byte*, Byte*, int);
Eric Andersen3f980402001-04-04 17:31:15 +0000281static void refresh(int); // update the terminal from screen[]
282
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000283static void Indicate_Error(void); // use flash or beep to indicate error
284#define indicate_error(c) Indicate_Error()
285
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000286#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000287static Byte *char_search(Byte *, Byte *, int, int); // search for pattern starting at p
288static int mycmp(Byte *, Byte *, int); // string cmp based in "ignorecase"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000289#endif /* CONFIG_FEATURE_VI_SEARCH */
290#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +0000291static void Hit_Return(void);
Eric Andersen1c0d3112001-04-16 15:46:44 +0000292static Byte *get_one_address(Byte *, int *); // get colon addr, if present
293static Byte *get_address(Byte *, int *, int *); // get two colon addrs, if present
Eric Andersen3f980402001-04-04 17:31:15 +0000294static void colon(Byte *); // execute the "colon" mode cmds
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000295#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000296#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000297static void winch_sig(int); // catch window size changes
298static void suspend_sig(int); // catch ctrl-Z
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000299static void catch_sig(int); // catch ctrl-C and alarm time-outs
Eric Andersen3f980402001-04-04 17:31:15 +0000300static void core_sig(int); // catch a core dump signal
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000301#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
302#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000303static void start_new_cmd_q(Byte); // new queue for command
Eric Andersenbff7a602001-11-17 07:15:43 +0000304static void end_cmd_q(void); // stop saving input chars
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000305#else /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000306#define end_cmd_q()
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000307#endif /* CONFIG_FEATURE_VI_DOT_CMD */
308#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000309static void window_size_get(int); // find out what size the window is
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000310#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
311#ifdef CONFIG_FEATURE_VI_SETOPTS
Eric Andersen3f980402001-04-04 17:31:15 +0000312static void showmatching(Byte *); // show the matching pair () [] {}
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000313#endif /* CONFIG_FEATURE_VI_SETOPTS */
314#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
Eric Andersen3f980402001-04-04 17:31:15 +0000315static Byte *string_insert(Byte *, Byte *); // insert the string at 'p'
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000316#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
317#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000318static Byte *text_yank(Byte *, Byte *, int); // save copy of "p" into a register
319static Byte what_reg(void); // what is letter of current YDreg
320static void check_context(Byte); // remember context for '' command
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000321#endif /* CONFIG_FEATURE_VI_YANKMARK */
322#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000323static void crash_dummy();
324static void crash_test();
325static int crashme = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000326#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000327
328
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000329static void write1(const char *out)
330{
331 fputs(out, stdout);
332}
333
Eric Andersen3f980402001-04-04 17:31:15 +0000334extern int vi_main(int argc, char **argv)
Eric Andersen3f980402001-04-04 17:31:15 +0000335{
Eric Andersend402edf2001-04-04 19:29:48 +0000336 int c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000337 RESERVE_CONFIG_BUFFER(STATUS_BUFFER, 200);
Eric Andersen3f980402001-04-04 17:31:15 +0000338
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000339#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000340 int i;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000341#endif /* CONFIG_FEATURE_VI_YANKMARK */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000342#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
343 my_pid = getpid();
344#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000345#ifdef CONFIG_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000346 (void) srand((long) my_pid);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000347#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000348
349 status_buffer = STATUS_BUFFER;
350
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000351#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000352 vi_readonly = readonly = FALSE;
Eric Andersen3f980402001-04-04 17:31:15 +0000353 if (strncmp(argv[0], "view", 4) == 0) {
354 readonly = TRUE;
Eric Andersen822c3832001-05-07 17:37:43 +0000355 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000356 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000357#endif /* CONFIG_FEATURE_VI_READONLY */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000358 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000359#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000360 for (i = 0; i < 28; i++) {
361 reg[i] = 0;
362 } // init the yank regs
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000363#endif /* CONFIG_FEATURE_VI_YANKMARK */
Tim Riker86c76a92002-04-26 07:41:22 +0000364#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
Eric Andersen3f980402001-04-04 17:31:15 +0000365 modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000366#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000367
368 // 1- process $HOME/.exrc file
369 // 2- process EXINIT variable from environment
370 // 3- process command line args
371 while ((c = getopt(argc, argv, "hCR")) != -1) {
372 switch (c) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000373#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000374 case 'C':
375 crashme = 1;
376 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000377#endif /* CONFIG_FEATURE_VI_CRASHME */
378#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +0000379 case 'R': // Read-only flag
380 readonly = TRUE;
381 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000382#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen822c3832001-05-07 17:37:43 +0000383 //case 'r': // recover flag- ignore- we don't use tmp file
384 //case 'x': // encryption flag- ignore
385 //case 'c': // execute command first
386 //case 'h': // help -- just use default
Eric Andersen3f980402001-04-04 17:31:15 +0000387 default:
388 show_help();
Eric Andersendd8500b2001-07-02 18:06:14 +0000389 return 1;
Eric Andersen3f980402001-04-04 17:31:15 +0000390 }
391 }
392
393 // The argv array can be used by the ":next" and ":rewind" commands
394 // save optind.
395 fn_start = optind; // remember first file name for :next and :rew
396 save_argc = argc;
397
398 //----- This is the main file handling loop --------------
399 if (optind >= argc) {
400 editing = 1; // 0= exit, 1= one file, 2= multiple files
401 edit_file(0);
402 } else {
403 for (; optind < argc; optind++) {
404 editing = 1; // 0=exit, 1=one file, 2+ =many files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000405 free(cfn);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000406 cfn = (Byte *) bb_xstrdup(argv[optind]);
Eric Andersen3f980402001-04-04 17:31:15 +0000407 edit_file(cfn);
408 }
409 }
410 //-----------------------------------------------------------
411
412 return (0);
413}
414
415static void edit_file(Byte * fn)
416{
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000417 Byte c;
Eric Andersen1c0d3112001-04-16 15:46:44 +0000418 int cnt, size, ch;
Eric Andersen3f980402001-04-04 17:31:15 +0000419
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000420#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000421 int sig;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000422#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
423#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000424 static Byte *cur_line;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000425#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000426
427 rawmode();
428 rows = 24;
429 columns = 80;
Eric Andersen822c3832001-05-07 17:37:43 +0000430 ch= -1;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000431#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000432 window_size_get(0);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000433#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
Eric Andersen3f980402001-04-04 17:31:15 +0000434 new_screen(rows, columns); // get memory for virtual screen
435
436 cnt = file_size(fn); // file size
437 size = 2 * cnt; // 200% of file size
438 new_text(size); // get a text[] buffer
439 screenbegin = dot = end = text;
440 if (fn != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000441 ch= file_insert(fn, text, cnt);
442 }
443 if (ch < 1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000444 (void) char_insert(text, '\n'); // start empty buf with dummy line
445 }
446 file_modified = FALSE;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000447#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000448 YDreg = 26; // default Yank/Delete reg
449 Ureg = 27; // hold orig line for "U" cmd
450 for (cnt = 0; cnt < 28; cnt++) {
451 mark[cnt] = 0;
452 } // init the marks
453 mark[26] = mark[27] = text; // init "previous context"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000454#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000455
Eric Andersen3f980402001-04-04 17:31:15 +0000456 last_forward_char = last_input_char = '\0';
457 crow = 0;
458 ccol = 0;
459 edit_status();
460
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000461#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000462 catch_sig(0);
463 core_sig(0);
Eric Andersen3f980402001-04-04 17:31:15 +0000464 signal(SIGWINCH, winch_sig);
465 signal(SIGTSTP, suspend_sig);
466 sig = setjmp(restart);
467 if (sig != 0) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000468 const char *msg = "";
469
Eric Andersen3f980402001-04-04 17:31:15 +0000470 if (sig == SIGWINCH)
471 msg = "(window resize)";
472 if (sig == SIGHUP)
473 msg = "(hangup)";
474 if (sig == SIGINT)
475 msg = "(interrupt)";
476 if (sig == SIGTERM)
477 msg = "(terminate)";
478 if (sig == SIGBUS)
479 msg = "(bus error)";
480 if (sig == SIGSEGV)
481 msg = "(I tried to touch invalid memory)";
482 if (sig == SIGALRM)
483 msg = "(alarm)";
484
485 psbs("-- caught signal %d %s--", sig, msg);
Eric Andersen1c0d3112001-04-16 15:46:44 +0000486 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000487 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000488#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +0000489
490 editing = 1;
491 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
492 cmdcnt = 0;
493 tabstop = 8;
494 offset = 0; // no horizontal offset
495 c = '\0';
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000496#ifdef CONFIG_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000497 free(last_modifying_cmd);
498 free(ioq_start);
Eric Andersen3f980402001-04-04 17:31:15 +0000499 ioq = ioq_start = last_modifying_cmd = 0;
500 adding2q = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000501#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen822c3832001-05-07 17:37:43 +0000502 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000503 show_status_line();
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000504 fflush(stdout);
Eric Andersen3f980402001-04-04 17:31:15 +0000505
506 //------This is the main Vi cmd handling loop -----------------------
507 while (editing > 0) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000508#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000509 if (crashme > 0) {
510 if ((end - text) > 1) {
511 crash_dummy(); // generate a random command
512 } else {
513 crashme = 0;
514 dot =
515 string_insert(text, (Byte *) "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
516 refresh(FALSE);
517 }
518 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000519#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000520 last_input_char = c = get_one_char(); // get a cmd from user
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000521#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000522 // save a copy of the current line- for the 'U" command
523 if (begin_line(dot) != cur_line) {
524 cur_line = begin_line(dot);
525 text_yank(begin_line(dot), end_line(dot), Ureg);
526 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000527#endif /* CONFIG_FEATURE_VI_YANKMARK */
528#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000529 // These are commands that change text[].
530 // Remember the input for the "." command
531 if (!adding2q && ioq_start == 0
532 && strchr((char *) modifying_cmds, c) != NULL) {
533 start_new_cmd_q(c);
534 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000535#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000536 do_cmd(c); // execute the user command
537 //
538 // poll to see if there is input already waiting. if we are
539 // not able to display output fast enough to keep up, skip
540 // the display update until we catch up with input.
541 if (mysleep(0) == 0) {
542 // no input pending- so update output
543 refresh(FALSE);
544 show_status_line();
545 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000546#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000547 if (crashme > 0)
548 crash_test(); // test editor variables
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000549#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000550 }
551 //-------------------------------------------------------------------
552
Eric Andersen822c3832001-05-07 17:37:43 +0000553 place_cursor(rows, 0, FALSE); // go to bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +0000554 clear_to_eol(); // Erase to end of line
555 cookmode();
556}
557
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000558//----- The Colon commands -------------------------------------
559#ifdef CONFIG_FEATURE_VI_COLON
560static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present
561{
562 int st;
563 Byte *q;
564
565#ifdef CONFIG_FEATURE_VI_YANKMARK
566 Byte c;
567#endif /* CONFIG_FEATURE_VI_YANKMARK */
568#ifdef CONFIG_FEATURE_VI_SEARCH
569 Byte *pat, buf[BUFSIZ];
570#endif /* CONFIG_FEATURE_VI_SEARCH */
571
572 *addr = -1; // assume no addr
573 if (*p == '.') { // the current line
574 p++;
575 q = begin_line(dot);
576 *addr = count_lines(text, q);
577#ifdef CONFIG_FEATURE_VI_YANKMARK
578 } else if (*p == '\'') { // is this a mark addr
579 p++;
580 c = tolower(*p);
581 p++;
582 if (c >= 'a' && c <= 'z') {
583 // we have a mark
584 c = c - 'a';
585 q = mark[(int) c];
586 if (q != NULL) { // is mark valid
587 *addr = count_lines(text, q); // count lines
588 }
589 }
590#endif /* CONFIG_FEATURE_VI_YANKMARK */
591#ifdef CONFIG_FEATURE_VI_SEARCH
592 } else if (*p == '/') { // a search pattern
593 q = buf;
594 for (p++; *p; p++) {
595 if (*p == '/')
596 break;
597 *q++ = *p;
598 *q = '\0';
599 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000600 pat = (Byte *) bb_xstrdup((char *) buf); // save copy of pattern
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000601 if (*p == '/')
602 p++;
603 q = char_search(dot, pat, FORWARD, FULL);
604 if (q != NULL) {
605 *addr = count_lines(text, q);
606 }
607 free(pat);
608#endif /* CONFIG_FEATURE_VI_SEARCH */
609 } else if (*p == '$') { // the last line in file
610 p++;
611 q = begin_line(end - 1);
612 *addr = count_lines(text, q);
613 } else if (isdigit(*p)) { // specific line number
614 sscanf((char *) p, "%d%n", addr, &st);
615 p += st;
616 } else { // I don't reconise this
617 // unrecognised address- assume -1
618 *addr = -1;
619 }
620 return (p);
621}
622
623static Byte *get_address(Byte *p, int *b, int *e) // get two colon addrs, if present
624{
625 //----- get the address' i.e., 1,3 'a,'b -----
626 // get FIRST addr, if present
627 while (isblnk(*p))
628 p++; // skip over leading spaces
629 if (*p == '%') { // alias for 1,$
630 p++;
631 *b = 1;
632 *e = count_lines(text, end-1);
633 goto ga0;
634 }
635 p = get_one_address(p, b);
636 while (isblnk(*p))
637 p++;
638 if (*p == ',') { // is there a address seperator
639 p++;
640 while (isblnk(*p))
641 p++;
642 // get SECOND addr, if present
643 p = get_one_address(p, e);
644 }
645ga0:
646 while (isblnk(*p))
647 p++; // skip over trailing spaces
648 return (p);
649}
650
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000651#ifdef CONFIG_FEATURE_VI_SETOPTS
652static void setops(const Byte *args, const char *opname, int flg_no,
653 const char *short_opname, int opt)
654{
655 const char *a = (char *) args + flg_no;
656 int l = strlen(opname) - 1; /* opname have + ' ' */
657
658 if (strncasecmp(a, opname, l) == 0 ||
659 strncasecmp(a, short_opname, 2) == 0) {
660 if(flg_no)
661 vi_setops &= ~opt;
662 else
663 vi_setops |= opt;
664 }
665}
666#endif
667
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000668static void colon(Byte * buf)
669{
670 Byte c, *orig_buf, *buf1, *q, *r;
671 Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
672 int i, l, li, ch, st, b, e;
673 int useforce, forced;
674 struct stat st_buf;
675
676 // :3154 // if (-e line 3154) goto it else stay put
677 // :4,33w! foo // write a portion of buffer to file "foo"
678 // :w // write all of buffer to current file
679 // :q // quit
680 // :q! // quit- dont care about modified file
681 // :'a,'z!sort -u // filter block through sort
682 // :'f // goto mark "f"
683 // :'fl // list literal the mark "f" line
684 // :.r bar // read file "bar" into buffer before dot
685 // :/123/,/abc/d // delete lines from "123" line to "abc" line
686 // :/xyz/ // goto the "xyz" line
687 // :s/find/replace/ // substitute pattern "find" with "replace"
688 // :!<cmd> // run <cmd> then return
689 //
690 if (strlen((char *) buf) <= 0)
691 goto vc1;
692 if (*buf == ':')
693 buf++; // move past the ':'
694
695 forced = useforce = FALSE;
696 li = st = ch = i = 0;
697 b = e = -1;
698 q = text; // assume 1,$ for the range
699 r = end - 1;
700 li = count_lines(text, end - 1);
701 fn = cfn; // default to current file
702 memset(cmd, '\0', BUFSIZ); // clear cmd[]
703 memset(args, '\0', BUFSIZ); // clear args[]
704
705 // look for optional address(es) :. :1 :1,9 :'q,'a :%
706 buf = get_address(buf, &b, &e);
707
708 // remember orig command line
709 orig_buf = buf;
710
711 // get the COMMAND into cmd[]
712 buf1 = cmd;
713 while (*buf != '\0') {
714 if (isspace(*buf))
715 break;
716 *buf1++ = *buf++;
717 }
718 // get any ARGuments
719 while (isblnk(*buf))
720 buf++;
721 strcpy((char *) args, (char *) buf);
722 buf1 = last_char_is((char *)cmd, '!');
723 if (buf1) {
724 useforce = TRUE;
725 *buf1 = '\0'; // get rid of !
726 }
727 if (b >= 0) {
728 // if there is only one addr, then the addr
729 // is the line number of the single line the
730 // user wants. So, reset the end
731 // pointer to point at end of the "b" line
732 q = find_line(b); // what line is #b
733 r = end_line(q);
734 li = 1;
735 }
736 if (e >= 0) {
737 // we were given two addrs. change the
738 // end pointer to the addr given by user.
739 r = find_line(e); // what line is #e
740 r = end_line(r);
741 li = e - b + 1;
742 }
743 // ------------ now look for the command ------------
744 i = strlen((char *) cmd);
745 if (i == 0) { // :123CR goto line #123
746 if (b >= 0) {
747 dot = find_line(b); // what line is #b
748 dot_skip_over_ws();
749 }
750 } else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd
751 // :!ls run the <cmd>
752 (void) alarm(0); // wait for input- no alarms
753 place_cursor(rows - 1, 0, FALSE); // go to Status line
754 clear_to_eol(); // clear the line
755 cookmode();
756 system(orig_buf+1); // run the cmd
757 rawmode();
758 Hit_Return(); // let user see results
759 (void) alarm(3); // done waiting for input
760 } else if (strncmp((char *) cmd, "=", i) == 0) { // where is the address
761 if (b < 0) { // no addr given- use defaults
762 b = e = count_lines(text, dot);
763 }
764 psb("%d", b);
765 } else if (strncasecmp((char *) cmd, "delete", i) == 0) { // delete lines
766 if (b < 0) { // no addr given- use defaults
767 q = begin_line(dot); // assume .,. for the range
768 r = end_line(dot);
769 }
770 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
771 dot_skip_over_ws();
772 } else if (strncasecmp((char *) cmd, "edit", i) == 0) { // Edit a file
773 int sr;
774 sr= 0;
775 // don't edit, if the current file has been modified
776 if (file_modified && ! useforce) {
777 psbs("No write since last change (:edit! overrides)");
778 goto vc1;
779 }
780 if (strlen(args) > 0) {
781 // the user supplied a file name
782 fn= args;
783 } else if (cfn != 0 && strlen(cfn) > 0) {
784 // no user supplied name- use the current filename
785 fn= cfn;
786 goto vc5;
787 } else {
788 // no user file name, no current name- punt
789 psbs("No current filename");
790 goto vc1;
791 }
792
793 // see if file exists- if not, its just a new file request
794 if ((sr=stat((char*)fn, &st_buf)) < 0) {
795 // This is just a request for a new file creation.
796 // The file_insert below will fail but we get
797 // an empty buffer with a file name. Then the "write"
798 // command can do the create.
799 } else {
800 if ((st_buf.st_mode & (S_IFREG)) == 0) {
801 // This is not a regular file
802 psbs("\"%s\" is not a regular file", fn);
803 goto vc1;
804 }
805 if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
806 // dont have any read permissions
807 psbs("\"%s\" is not readable", fn);
808 goto vc1;
809 }
810 }
811
812 // There is a read-able regular file
813 // make this the current file
Manuel Novoa III cad53642003-03-19 09:13:01 +0000814 q = (Byte *) bb_xstrdup((char *) fn); // save the cfn
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000815 free(cfn); // free the old name
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000816 cfn = q; // remember new cfn
817
818 vc5:
819 // delete all the contents of text[]
820 new_text(2 * file_size(fn));
821 screenbegin = dot = end = text;
822
823 // insert new file
824 ch = file_insert(fn, text, file_size(fn));
825
826 if (ch < 1) {
827 // start empty buf with dummy line
828 (void) char_insert(text, '\n');
829 ch= 1;
830 }
831 file_modified = FALSE;
832#ifdef CONFIG_FEATURE_VI_YANKMARK
833 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
834 free(reg[Ureg]); // free orig line reg- for 'U'
835 reg[Ureg]= 0;
836 }
837 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
838 free(reg[YDreg]); // free default yank/delete register
839 reg[YDreg]= 0;
840 }
841 for (li = 0; li < 28; li++) {
842 mark[li] = 0;
843 } // init the marks
844#endif /* CONFIG_FEATURE_VI_YANKMARK */
845 // how many lines in text[]?
846 li = count_lines(text, end - 1);
847 psb("\"%s\"%s"
848#ifdef CONFIG_FEATURE_VI_READONLY
849 "%s"
850#endif /* CONFIG_FEATURE_VI_READONLY */
851 " %dL, %dC", cfn,
852 (sr < 0 ? " [New file]" : ""),
853#ifdef CONFIG_FEATURE_VI_READONLY
854 ((vi_readonly || readonly) ? " [Read only]" : ""),
855#endif /* CONFIG_FEATURE_VI_READONLY */
856 li, ch);
857 } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this
858 if (b != -1 || e != -1) {
859 ni((Byte *) "No address allowed on this command");
860 goto vc1;
861 }
862 if (strlen((char *) args) > 0) {
863 // user wants a new filename
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000864 free(cfn);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000865 cfn = (Byte *) bb_xstrdup((char *) args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000866 } else {
867 // user wants file status info
868 edit_status();
869 }
870 } else if (strncasecmp((char *) cmd, "features", i) == 0) { // what features are available
871 // print out values of all features
872 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
873 clear_to_eol(); // clear the line
874 cookmode();
875 show_help();
876 rawmode();
877 Hit_Return();
878 } else if (strncasecmp((char *) cmd, "list", i) == 0) { // literal print line
879 if (b < 0) { // no addr given- use defaults
880 q = begin_line(dot); // assume .,. for the range
881 r = end_line(dot);
882 }
883 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
884 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000885 puts("\r");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000886 for (; q <= r; q++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000887 int c_is_no_print;
888
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000889 c = *q;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000890 c_is_no_print = c > 127 && !Isprint(c);
891 if (c_is_no_print) {
892 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000893 standout_start();
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000894 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000895 if (c == '\n') {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000896 write1("$\r");
897 } else if (c < ' ' || c == 127) {
898 putchar('^');
899 if(c == 127)
900 c = '?';
901 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000902 c += '@';
903 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000904 putchar(c);
905 if (c_is_no_print)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000906 standout_end();
907 }
908#ifdef CONFIG_FEATURE_VI_SET
909 vc2:
910#endif /* CONFIG_FEATURE_VI_SET */
911 Hit_Return();
912 } else if ((strncasecmp((char *) cmd, "quit", i) == 0) || // Quit
913 (strncasecmp((char *) cmd, "next", i) == 0)) { // edit next file
914 if (useforce) {
915 // force end of argv list
916 if (*cmd == 'q') {
917 optind = save_argc;
918 }
919 editing = 0;
920 goto vc1;
921 }
922 // don't exit if the file been modified
923 if (file_modified) {
924 psbs("No write since last change (:%s! overrides)",
925 (*cmd == 'q' ? "quit" : "next"));
926 goto vc1;
927 }
928 // are there other file to edit
929 if (*cmd == 'q' && optind < save_argc - 1) {
930 psbs("%d more file to edit", (save_argc - optind - 1));
931 goto vc1;
932 }
933 if (*cmd == 'n' && optind >= save_argc - 1) {
934 psbs("No more files to edit");
935 goto vc1;
936 }
937 editing = 0;
938 } else if (strncasecmp((char *) cmd, "read", i) == 0) { // read file into text[]
939 fn = args;
940 if (strlen((char *) fn) <= 0) {
941 psbs("No filename given");
942 goto vc1;
943 }
944 if (b < 0) { // no addr given- use defaults
945 q = begin_line(dot); // assume "dot"
946 }
947 // read after current line- unless user said ":0r foo"
948 if (b != 0)
949 q = next_line(q);
950#ifdef CONFIG_FEATURE_VI_READONLY
951 l= readonly; // remember current files' status
952#endif
953 ch = file_insert(fn, q, file_size(fn));
954#ifdef CONFIG_FEATURE_VI_READONLY
955 readonly= l;
956#endif
957 if (ch < 0)
958 goto vc1; // nothing was inserted
959 // how many lines in text[]?
960 li = count_lines(q, q + ch - 1);
961 psb("\"%s\""
962#ifdef CONFIG_FEATURE_VI_READONLY
963 "%s"
964#endif /* CONFIG_FEATURE_VI_READONLY */
965 " %dL, %dC", fn,
966#ifdef CONFIG_FEATURE_VI_READONLY
967 ((vi_readonly || readonly) ? " [Read only]" : ""),
968#endif /* CONFIG_FEATURE_VI_READONLY */
969 li, ch);
970 if (ch > 0) {
971 // if the insert is before "dot" then we need to update
972 if (q <= dot)
973 dot += ch;
974 file_modified = TRUE;
975 }
976 } else if (strncasecmp((char *) cmd, "rewind", i) == 0) { // rewind cmd line args
977 if (file_modified && ! useforce) {
978 psbs("No write since last change (:rewind! overrides)");
979 } else {
980 // reset the filenames to edit
981 optind = fn_start - 1;
982 editing = 0;
983 }
984#ifdef CONFIG_FEATURE_VI_SET
985 } else if (strncasecmp((char *) cmd, "set", i) == 0) { // set or clear features
986 i = 0; // offset into args
987 if (strlen((char *) args) == 0) {
988 // print out values of all options
989 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
990 clear_to_eol(); // clear the line
991 printf("----------------------------------------\r\n");
992#ifdef CONFIG_FEATURE_VI_SETOPTS
993 if (!autoindent)
994 printf("no");
995 printf("autoindent ");
996 if (!err_method)
997 printf("no");
998 printf("flash ");
999 if (!ignorecase)
1000 printf("no");
1001 printf("ignorecase ");
1002 if (!showmatch)
1003 printf("no");
1004 printf("showmatch ");
1005 printf("tabstop=%d ", tabstop);
1006#endif /* CONFIG_FEATURE_VI_SETOPTS */
1007 printf("\r\n");
1008 goto vc2;
1009 }
1010 if (strncasecmp((char *) args, "no", 2) == 0)
1011 i = 2; // ":set noautoindent"
1012#ifdef CONFIG_FEATURE_VI_SETOPTS
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001013 setops(args, "autoindent ", i, "ai", VI_AUTOINDENT);
1014 setops(args, "flash ", i, "fl", VI_ERR_METHOD);
1015 setops(args, "ignorecase ", i, "ic", VI_IGNORECASE);
1016 setops(args, "showmatch ", i, "ic", VI_SHOWMATCH);
1017 if (strncasecmp((char *) args + i, "tabstop=%d ", 7) == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001018 sscanf(strchr((char *) args + i, '='), "=%d", &ch);
1019 if (ch > 0 && ch < columns - 1)
1020 tabstop = ch;
1021 }
1022#endif /* CONFIG_FEATURE_VI_SETOPTS */
1023#endif /* CONFIG_FEATURE_VI_SET */
1024#ifdef CONFIG_FEATURE_VI_SEARCH
1025 } else if (strncasecmp((char *) cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
1026 Byte *ls, *F, *R;
1027 int gflag;
1028
1029 // F points to the "find" pattern
1030 // R points to the "replace" pattern
1031 // replace the cmd line delimiters "/" with NULLs
1032 gflag = 0; // global replace flag
1033 c = orig_buf[1]; // what is the delimiter
1034 F = orig_buf + 2; // start of "find"
1035 R = (Byte *) strchr((char *) F, c); // middle delimiter
1036 if (!R) goto colon_s_fail;
1037 *R++ = '\0'; // terminate "find"
1038 buf1 = (Byte *) strchr((char *) R, c);
1039 if (!buf1) goto colon_s_fail;
1040 *buf1++ = '\0'; // terminate "replace"
1041 if (*buf1 == 'g') { // :s/foo/bar/g
1042 buf1++;
1043 gflag++; // turn on gflag
1044 }
1045 q = begin_line(q);
1046 if (b < 0) { // maybe :s/foo/bar/
1047 q = begin_line(dot); // start with cur line
1048 b = count_lines(text, q); // cur line number
1049 }
1050 if (e < 0)
1051 e = b; // maybe :.s/foo/bar/
1052 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
1053 ls = q; // orig line start
1054 vc4:
1055 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
1056 if (buf1 != NULL) {
1057 // we found the "find" pattern- delete it
1058 (void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
1059 // inset the "replace" patern
1060 (void) string_insert(buf1, R); // insert the string
1061 // check for "global" :s/foo/bar/g
1062 if (gflag == 1) {
1063 if ((buf1 + strlen((char *) R)) < end_line(ls)) {
1064 q = buf1 + strlen((char *) R);
1065 goto vc4; // don't let q move past cur line
1066 }
1067 }
1068 }
1069 q = next_line(ls);
1070 }
1071#endif /* CONFIG_FEATURE_VI_SEARCH */
1072 } else if (strncasecmp((char *) cmd, "version", i) == 0) { // show software version
1073 psb("%s", vi_Version);
1074 } else if ((strncasecmp((char *) cmd, "write", i) == 0) || // write text to file
1075 (strncasecmp((char *) cmd, "wq", i) == 0) ||
1076 (strncasecmp((char *) cmd, "x", i) == 0)) {
1077 // is there a file name to write to?
1078 if (strlen((char *) args) > 0) {
1079 fn = args;
1080 }
1081#ifdef CONFIG_FEATURE_VI_READONLY
1082 if ((vi_readonly || readonly) && ! useforce) {
1083 psbs("\"%s\" File is read only", fn);
1084 goto vc3;
1085 }
1086#endif /* CONFIG_FEATURE_VI_READONLY */
1087 // how many lines in text[]?
1088 li = count_lines(q, r);
1089 ch = r - q + 1;
1090 // see if file exists- if not, its just a new file request
1091 if (useforce) {
1092 // if "fn" is not write-able, chmod u+w
1093 // sprintf(syscmd, "chmod u+w %s", fn);
1094 // system(syscmd);
1095 forced = TRUE;
1096 }
1097 l = file_write(fn, q, r);
1098 if (useforce && forced) {
1099 // chmod u-w
1100 // sprintf(syscmd, "chmod u-w %s", fn);
1101 // system(syscmd);
1102 forced = FALSE;
1103 }
1104 psb("\"%s\" %dL, %dC", fn, li, l);
1105 if (q == text && r == end - 1 && l == ch)
1106 file_modified = FALSE;
1107 if ((cmd[0] == 'x' || cmd[1] == 'q') && l == ch) {
1108 editing = 0;
1109 }
1110#ifdef CONFIG_FEATURE_VI_READONLY
1111 vc3:;
1112#endif /* CONFIG_FEATURE_VI_READONLY */
1113#ifdef CONFIG_FEATURE_VI_YANKMARK
1114 } else if (strncasecmp((char *) cmd, "yank", i) == 0) { // yank lines
1115 if (b < 0) { // no addr given- use defaults
1116 q = begin_line(dot); // assume .,. for the range
1117 r = end_line(dot);
1118 }
1119 text_yank(q, r, YDreg);
1120 li = count_lines(q, r);
1121 psb("Yank %d lines (%d chars) into [%c]",
1122 li, strlen((char *) reg[YDreg]), what_reg());
1123#endif /* CONFIG_FEATURE_VI_YANKMARK */
1124 } else {
1125 // cmd unknown
1126 ni((Byte *) cmd);
1127 }
1128 vc1:
1129 dot = bound_dot(dot); // make sure "dot" is valid
1130 return;
1131#ifdef CONFIG_FEATURE_VI_SEARCH
1132colon_s_fail:
1133 psb(":s expression missing delimiters");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001134#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001135}
1136
1137static void Hit_Return(void)
1138{
1139 char c;
1140
1141 standout_start(); // start reverse video
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001142 write1("[Hit return to continue]");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001143 standout_end(); // end reverse video
1144 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
1145 ;
1146 redraw(TRUE); // force redraw all
1147}
1148#endif /* CONFIG_FEATURE_VI_COLON */
1149
1150//----- Synchronize the cursor to Dot --------------------------
1151static void sync_cursor(Byte * d, int *row, int *col)
1152{
1153 Byte *beg_cur, *end_cur; // begin and end of "d" line
1154 Byte *beg_scr, *end_scr; // begin and end of screen
1155 Byte *tp;
1156 int cnt, ro, co;
1157
1158 beg_cur = begin_line(d); // first char of cur line
1159 end_cur = end_line(d); // last char of cur line
1160
1161 beg_scr = end_scr = screenbegin; // first char of screen
1162 end_scr = end_screen(); // last char of screen
1163
1164 if (beg_cur < screenbegin) {
1165 // "d" is before top line on screen
1166 // how many lines do we have to move
1167 cnt = count_lines(beg_cur, screenbegin);
1168 sc1:
1169 screenbegin = beg_cur;
1170 if (cnt > (rows - 1) / 2) {
1171 // we moved too many lines. put "dot" in middle of screen
1172 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1173 screenbegin = prev_line(screenbegin);
1174 }
1175 }
1176 } else if (beg_cur > end_scr) {
1177 // "d" is after bottom line on screen
1178 // how many lines do we have to move
1179 cnt = count_lines(end_scr, beg_cur);
1180 if (cnt > (rows - 1) / 2)
1181 goto sc1; // too many lines
1182 for (ro = 0; ro < cnt - 1; ro++) {
1183 // move screen begin the same amount
1184 screenbegin = next_line(screenbegin);
1185 // now, move the end of screen
1186 end_scr = next_line(end_scr);
1187 end_scr = end_line(end_scr);
1188 }
1189 }
1190 // "d" is on screen- find out which row
1191 tp = screenbegin;
1192 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1193 if (tp == beg_cur)
1194 break;
1195 tp = next_line(tp);
1196 }
1197
1198 // find out what col "d" is on
1199 co = 0;
1200 do { // drive "co" to correct column
1201 if (*tp == '\n' || *tp == '\0')
1202 break;
1203 if (*tp == '\t') {
1204 // 7 - (co % 8 )
1205 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001206 } else if (*tp < ' ' || *tp == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001207 co++; // display as ^X, use 2 columns
1208 }
1209 } while (tp++ < d && ++co);
1210
1211 // "co" is the column where "dot" is.
1212 // The screen has "columns" columns.
1213 // The currently displayed columns are 0+offset -- columns+ofset
1214 // |-------------------------------------------------------------|
1215 // ^ ^ ^
1216 // offset | |------- columns ----------------|
1217 //
1218 // If "co" is already in this range then we do not have to adjust offset
1219 // but, we do have to subtract the "offset" bias from "co".
1220 // If "co" is outside this range then we have to change "offset".
1221 // If the first char of a line is a tab the cursor will try to stay
1222 // in column 7, but we have to set offset to 0.
1223
1224 if (co < 0 + offset) {
1225 offset = co;
1226 }
1227 if (co >= columns + offset) {
1228 offset = co - columns + 1;
1229 }
1230 // if the first char of the line is a tab, and "dot" is sitting on it
1231 // force offset to 0.
1232 if (d == beg_cur && *d == '\t') {
1233 offset = 0;
1234 }
1235 co -= offset;
1236
1237 *row = ro;
1238 *col = co;
1239}
1240
1241//----- Text Movement Routines ---------------------------------
1242static Byte *begin_line(Byte * p) // return pointer to first char cur line
1243{
1244 while (p > text && p[-1] != '\n')
1245 p--; // go to cur line B-o-l
1246 return (p);
1247}
1248
1249static Byte *end_line(Byte * p) // return pointer to NL of cur line line
1250{
1251 while (p < end - 1 && *p != '\n')
1252 p++; // go to cur line E-o-l
1253 return (p);
1254}
1255
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001256static inline Byte *dollar_line(Byte * p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001257{
1258 while (p < end - 1 && *p != '\n')
1259 p++; // go to cur line E-o-l
1260 // Try to stay off of the Newline
1261 if (*p == '\n' && (p - begin_line(p)) > 0)
1262 p--;
1263 return (p);
1264}
1265
1266static Byte *prev_line(Byte * p) // return pointer first char prev line
1267{
1268 p = begin_line(p); // goto begining of cur line
1269 if (p[-1] == '\n' && p > text)
1270 p--; // step to prev line
1271 p = begin_line(p); // goto begining of prev line
1272 return (p);
1273}
1274
1275static Byte *next_line(Byte * p) // return pointer first char next line
1276{
1277 p = end_line(p);
1278 if (*p == '\n' && p < end - 1)
1279 p++; // step to next line
1280 return (p);
1281}
1282
1283//----- Text Information Routines ------------------------------
1284static Byte *end_screen(void)
1285{
1286 Byte *q;
1287 int cnt;
1288
1289 // find new bottom line
1290 q = screenbegin;
1291 for (cnt = 0; cnt < rows - 2; cnt++)
1292 q = next_line(q);
1293 q = end_line(q);
1294 return (q);
1295}
1296
1297static int count_lines(Byte * start, Byte * stop) // count line from start to stop
1298{
1299 Byte *q;
1300 int cnt;
1301
1302 if (stop < start) { // start and stop are backwards- reverse them
1303 q = start;
1304 start = stop;
1305 stop = q;
1306 }
1307 cnt = 0;
1308 stop = end_line(stop); // get to end of this line
1309 for (q = start; q <= stop && q <= end - 1; q++) {
1310 if (*q == '\n')
1311 cnt++;
1312 }
1313 return (cnt);
1314}
1315
1316static Byte *find_line(int li) // find begining of line #li
1317{
1318 Byte *q;
1319
1320 for (q = text; li > 1; li--) {
1321 q = next_line(q);
1322 }
1323 return (q);
1324}
1325
1326//----- Dot Movement Routines ----------------------------------
1327static void dot_left(void)
1328{
1329 if (dot > text && dot[-1] != '\n')
1330 dot--;
1331}
1332
1333static void dot_right(void)
1334{
1335 if (dot < end - 1 && *dot != '\n')
1336 dot++;
1337}
1338
1339static void dot_begin(void)
1340{
1341 dot = begin_line(dot); // return pointer to first char cur line
1342}
1343
1344static void dot_end(void)
1345{
1346 dot = end_line(dot); // return pointer to last char cur line
1347}
1348
1349static Byte *move_to_col(Byte * p, int l)
1350{
1351 int co;
1352
1353 p = begin_line(p);
1354 co = 0;
1355 do {
1356 if (*p == '\n' || *p == '\0')
1357 break;
1358 if (*p == '\t') {
1359 // 7 - (co % 8 )
1360 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001361 } else if (*p < ' ' || *p == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001362 co++; // display as ^X, use 2 columns
1363 }
1364 } while (++co <= l && p++ < end);
1365 return (p);
1366}
1367
1368static void dot_next(void)
1369{
1370 dot = next_line(dot);
1371}
1372
1373static void dot_prev(void)
1374{
1375 dot = prev_line(dot);
1376}
1377
1378static void dot_scroll(int cnt, int dir)
1379{
1380 Byte *q;
1381
1382 for (; cnt > 0; cnt--) {
1383 if (dir < 0) {
1384 // scroll Backwards
1385 // ctrl-Y scroll up one line
1386 screenbegin = prev_line(screenbegin);
1387 } else {
1388 // scroll Forwards
1389 // ctrl-E scroll down one line
1390 screenbegin = next_line(screenbegin);
1391 }
1392 }
1393 // make sure "dot" stays on the screen so we dont scroll off
1394 if (dot < screenbegin)
1395 dot = screenbegin;
1396 q = end_screen(); // find new bottom line
1397 if (dot > q)
1398 dot = begin_line(q); // is dot is below bottom line?
1399 dot_skip_over_ws();
1400}
1401
1402static void dot_skip_over_ws(void)
1403{
1404 // skip WS
1405 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1406 dot++;
1407}
1408
1409static void dot_delete(void) // delete the char at 'dot'
1410{
1411 (void) text_hole_delete(dot, dot);
1412}
1413
1414static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end"
1415{
1416 if (p >= end && end > text) {
1417 p = end - 1;
1418 indicate_error('1');
1419 }
1420 if (p < text) {
1421 p = text;
1422 indicate_error('2');
1423 }
1424 return (p);
1425}
1426
1427//----- Helper Utility Routines --------------------------------
1428
1429//----------------------------------------------------------------
1430//----- Char Routines --------------------------------------------
1431/* Chars that are part of a word-
1432 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1433 * Chars that are Not part of a word (stoppers)
1434 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1435 * Chars that are WhiteSpace
1436 * TAB NEWLINE VT FF RETURN SPACE
1437 * DO NOT COUNT NEWLINE AS WHITESPACE
1438 */
1439
1440static Byte *new_screen(int ro, int co)
1441{
1442 int li;
1443
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001444 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001445 screensize = ro * co + 8;
1446 screen = (Byte *) xmalloc(screensize);
1447 // initialize the new screen. assume this will be a empty file.
1448 screen_erase();
1449 // non-existant text[] lines start with a tilde (~).
1450 for (li = 1; li < ro - 1; li++) {
1451 screen[(li * co) + 0] = '~';
1452 }
1453 return (screen);
1454}
1455
1456static Byte *new_text(int size)
1457{
1458 if (size < 10240)
1459 size = 10240; // have a minimum size for new files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001460 free(text);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001461 text = (Byte *) xmalloc(size + 8);
1462 memset(text, '\0', size); // clear new text[]
1463 //text += 4; // leave some room for "oops"
1464 textend = text + size - 1;
1465 //textend -= 4; // leave some root for "oops"
1466 return (text);
1467}
1468
1469#ifdef CONFIG_FEATURE_VI_SEARCH
1470static int mycmp(Byte * s1, Byte * s2, int len)
1471{
1472 int i;
1473
1474 i = strncmp((char *) s1, (char *) s2, len);
1475#ifdef CONFIG_FEATURE_VI_SETOPTS
1476 if (ignorecase) {
1477 i = strncasecmp((char *) s1, (char *) s2, len);
1478 }
1479#endif /* CONFIG_FEATURE_VI_SETOPTS */
1480 return (i);
1481}
1482
1483static Byte *char_search(Byte * p, Byte * pat, int dir, int range) // search for pattern starting at p
1484{
1485#ifndef REGEX_SEARCH
1486 Byte *start, *stop;
1487 int len;
1488
1489 len = strlen((char *) pat);
1490 if (dir == FORWARD) {
1491 stop = end - 1; // assume range is p - end-1
1492 if (range == LIMITED)
1493 stop = next_line(p); // range is to next line
1494 for (start = p; start < stop; start++) {
1495 if (mycmp(start, pat, len) == 0) {
1496 return (start);
1497 }
1498 }
1499 } else if (dir == BACK) {
1500 stop = text; // assume range is text - p
1501 if (range == LIMITED)
1502 stop = prev_line(p); // range is to prev line
1503 for (start = p - len; start >= stop; start--) {
1504 if (mycmp(start, pat, len) == 0) {
1505 return (start);
1506 }
1507 }
1508 }
1509 // pattern not found
1510 return (NULL);
1511#else /*REGEX_SEARCH */
1512 char *q;
1513 struct re_pattern_buffer preg;
1514 int i;
1515 int size, range;
1516
1517 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1518 preg.translate = 0;
1519 preg.fastmap = 0;
1520 preg.buffer = 0;
1521 preg.allocated = 0;
1522
1523 // assume a LIMITED forward search
1524 q = next_line(p);
1525 q = end_line(q);
1526 q = end - 1;
1527 if (dir == BACK) {
1528 q = prev_line(p);
1529 q = text;
1530 }
1531 // count the number of chars to search over, forward or backward
1532 size = q - p;
1533 if (size < 0)
1534 size = p - q;
1535 // RANGE could be negative if we are searching backwards
1536 range = q - p;
1537
1538 q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
1539 if (q != 0) {
1540 // The pattern was not compiled
1541 psbs("bad search pattern: \"%s\": %s", pat, q);
1542 i = 0; // return p if pattern not compiled
1543 goto cs1;
1544 }
1545
1546 q = p;
1547 if (range < 0) {
1548 q = p - size;
1549 if (q < text)
1550 q = text;
1551 }
1552 // search for the compiled pattern, preg, in p[]
1553 // range < 0- search backward
1554 // range > 0- search forward
1555 // 0 < start < size
1556 // re_search() < 0 not found or error
1557 // re_search() > 0 index of found pattern
1558 // struct pattern char int int int struct reg
1559 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1560 i = re_search(&preg, q, size, 0, range, 0);
1561 if (i == -1) {
1562 p = 0;
1563 i = 0; // return NULL if pattern not found
1564 }
1565 cs1:
1566 if (dir == FORWARD) {
1567 p = p + i;
1568 } else {
1569 p = p - i;
1570 }
1571 return (p);
1572#endif /*REGEX_SEARCH */
1573}
1574#endif /* CONFIG_FEATURE_VI_SEARCH */
1575
1576static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
1577{
1578 if (c == 22) { // Is this an ctrl-V?
1579 p = stupid_insert(p, '^'); // use ^ to indicate literal next
1580 p--; // backup onto ^
1581 refresh(FALSE); // show the ^
1582 c = get_one_char();
1583 *p = c;
1584 p++;
1585 file_modified = TRUE; // has the file been modified
1586 } else if (c == 27) { // Is this an ESC?
1587 cmd_mode = 0;
1588 cmdcnt = 0;
1589 end_cmd_q(); // stop adding to q
1590 strcpy((char *) status_buffer, " "); // clear the status buffer
1591 if ((p[-1] != '\n') && (dot>text)) {
1592 p--;
1593 }
1594 } else if (c == erase_char) { // Is this a BS
1595 // 123456789
1596 if ((p[-1] != '\n') && (dot>text)) {
1597 p--;
1598 p = text_hole_delete(p, p); // shrink buffer 1 char
1599#ifdef CONFIG_FEATURE_VI_DOT_CMD
1600 // also rmove char from last_modifying_cmd
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001601 if (last_modifying_cmd != 0 && strlen((char *) last_modifying_cmd) > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001602 Byte *q;
1603
1604 q = last_modifying_cmd;
1605 q[strlen((char *) q) - 1] = '\0'; // erase BS
1606 q[strlen((char *) q) - 1] = '\0'; // erase prev char
1607 }
1608#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1609 }
1610 } else {
1611 // insert a char into text[]
1612 Byte *sp; // "save p"
1613
1614 if (c == 13)
1615 c = '\n'; // translate \r to \n
1616 sp = p; // remember addr of insert
1617 p = stupid_insert(p, c); // insert the char
1618#ifdef CONFIG_FEATURE_VI_SETOPTS
1619 if (showmatch && strchr(")]}", *sp) != NULL) {
1620 showmatching(sp);
1621 }
1622 if (autoindent && c == '\n') { // auto indent the new line
1623 Byte *q;
1624
1625 q = prev_line(p); // use prev line as templet
1626 for (; isblnk(*q); q++) {
1627 p = stupid_insert(p, *q); // insert the char
1628 }
1629 }
1630#endif /* CONFIG_FEATURE_VI_SETOPTS */
1631 }
1632 return (p);
1633}
1634
1635static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
1636{
1637 p = text_hole_make(p, 1);
1638 if (p != 0) {
1639 *p = c;
1640 file_modified = TRUE; // has the file been modified
1641 p++;
1642 }
1643 return (p);
1644}
1645
1646static Byte find_range(Byte ** start, Byte ** stop, Byte c)
1647{
1648 Byte *save_dot, *p, *q;
1649 int cnt;
1650
1651 save_dot = dot;
1652 p = q = dot;
1653
1654 if (strchr("cdy><", c)) {
1655 // these cmds operate on whole lines
1656 p = q = begin_line(p);
1657 for (cnt = 1; cnt < cmdcnt; cnt++) {
1658 q = next_line(q);
1659 }
1660 q = end_line(q);
1661 } else if (strchr("^%$0bBeEft", c)) {
1662 // These cmds operate on char positions
1663 do_cmd(c); // execute movement cmd
1664 q = dot;
1665 } else if (strchr("wW", c)) {
1666 do_cmd(c); // execute movement cmd
1667 if (dot > text)
1668 dot--; // move back off of next word
1669 if (dot > text && *dot == '\n')
1670 dot--; // stay off NL
1671 q = dot;
1672 } else if (strchr("H-k{", c)) {
1673 // these operate on multi-lines backwards
1674 q = end_line(dot); // find NL
1675 do_cmd(c); // execute movement cmd
1676 dot_begin();
1677 p = dot;
1678 } else if (strchr("L+j}\r\n", c)) {
1679 // these operate on multi-lines forwards
1680 p = begin_line(dot);
1681 do_cmd(c); // execute movement cmd
1682 dot_end(); // find NL
1683 q = dot;
1684 } else {
1685 c = 27; // error- return an ESC char
1686 //break;
1687 }
1688 *start = p;
1689 *stop = q;
1690 if (q < p) {
1691 *start = q;
1692 *stop = p;
1693 }
1694 dot = save_dot;
1695 return (c);
1696}
1697
1698static int st_test(Byte * p, int type, int dir, Byte * tested)
1699{
1700 Byte c, c0, ci;
1701 int test, inc;
1702
1703 inc = dir;
1704 c = c0 = p[0];
1705 ci = p[inc];
1706 test = 0;
1707
1708 if (type == S_BEFORE_WS) {
1709 c = ci;
1710 test = ((!isspace(c)) || c == '\n');
1711 }
1712 if (type == S_TO_WS) {
1713 c = c0;
1714 test = ((!isspace(c)) || c == '\n');
1715 }
1716 if (type == S_OVER_WS) {
1717 c = c0;
1718 test = ((isspace(c)));
1719 }
1720 if (type == S_END_PUNCT) {
1721 c = ci;
1722 test = ((ispunct(c)));
1723 }
1724 if (type == S_END_ALNUM) {
1725 c = ci;
1726 test = ((isalnum(c)) || c == '_');
1727 }
1728 *tested = c;
1729 return (test);
1730}
1731
1732static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
1733{
1734 Byte c;
1735
1736 while (st_test(p, type, dir, &c)) {
1737 // make sure we limit search to correct number of lines
1738 if (c == '\n' && --linecnt < 1)
1739 break;
1740 if (dir >= 0 && p >= end - 1)
1741 break;
1742 if (dir < 0 && p <= text)
1743 break;
1744 p += dir; // move to next char
1745 }
1746 return (p);
1747}
1748
1749// find matching char of pair () [] {}
1750static Byte *find_pair(Byte * p, Byte c)
1751{
1752 Byte match, *q;
1753 int dir, level;
1754
1755 match = ')';
1756 level = 1;
1757 dir = 1; // assume forward
1758 switch (c) {
1759 case '(':
1760 match = ')';
1761 break;
1762 case '[':
1763 match = ']';
1764 break;
1765 case '{':
1766 match = '}';
1767 break;
1768 case ')':
1769 match = '(';
1770 dir = -1;
1771 break;
1772 case ']':
1773 match = '[';
1774 dir = -1;
1775 break;
1776 case '}':
1777 match = '{';
1778 dir = -1;
1779 break;
1780 }
1781 for (q = p + dir; text <= q && q < end; q += dir) {
1782 // look for match, count levels of pairs (( ))
1783 if (*q == c)
1784 level++; // increase pair levels
1785 if (*q == match)
1786 level--; // reduce pair level
1787 if (level == 0)
1788 break; // found matching pair
1789 }
1790 if (level != 0)
1791 q = NULL; // indicate no match
1792 return (q);
1793}
1794
1795#ifdef CONFIG_FEATURE_VI_SETOPTS
1796// show the matching char of a pair, () [] {}
1797static void showmatching(Byte * p)
1798{
1799 Byte *q, *save_dot;
1800
1801 // we found half of a pair
1802 q = find_pair(p, *p); // get loc of matching char
1803 if (q == NULL) {
1804 indicate_error('3'); // no matching char
1805 } else {
1806 // "q" now points to matching pair
1807 save_dot = dot; // remember where we are
1808 dot = q; // go to new loc
1809 refresh(FALSE); // let the user see it
1810 (void) mysleep(40); // give user some time
1811 dot = save_dot; // go back to old loc
1812 refresh(FALSE);
1813 }
1814}
1815#endif /* CONFIG_FEATURE_VI_SETOPTS */
1816
1817// open a hole in text[]
1818static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
1819{
1820 Byte *src, *dest;
1821 int cnt;
1822
1823 if (size <= 0)
1824 goto thm0;
1825 src = p;
1826 dest = p + size;
1827 cnt = end - src; // the rest of buffer
1828 if (memmove(dest, src, cnt) != dest) {
1829 psbs("can't create room for new characters");
1830 }
1831 memset(p, ' ', size); // clear new hole
1832 end = end + size; // adjust the new END
1833 file_modified = TRUE; // has the file been modified
1834 thm0:
1835 return (p);
1836}
1837
1838// close a hole in text[]
1839static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
1840{
1841 Byte *src, *dest;
1842 int cnt, hole_size;
1843
1844 // move forwards, from beginning
1845 // assume p <= q
1846 src = q + 1;
1847 dest = p;
1848 if (q < p) { // they are backward- swap them
1849 src = p + 1;
1850 dest = q;
1851 }
1852 hole_size = q - p + 1;
1853 cnt = end - src;
1854 if (src < text || src > end)
1855 goto thd0;
1856 if (dest < text || dest >= end)
1857 goto thd0;
1858 if (src >= end)
1859 goto thd_atend; // just delete the end of the buffer
1860 if (memmove(dest, src, cnt) != dest) {
1861 psbs("can't delete the character");
1862 }
1863 thd_atend:
1864 end = end - hole_size; // adjust the new END
1865 if (dest >= end)
1866 dest = end - 1; // make sure dest in below end-1
1867 if (end <= text)
1868 dest = end = text; // keep pointers valid
1869 file_modified = TRUE; // has the file been modified
1870 thd0:
1871 return (dest);
1872}
1873
1874// copy text into register, then delete text.
1875// if dist <= 0, do not include, or go past, a NewLine
1876//
1877static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
1878{
1879 Byte *p;
1880
1881 // make sure start <= stop
1882 if (start > stop) {
1883 // they are backwards, reverse them
1884 p = start;
1885 start = stop;
1886 stop = p;
1887 }
1888 if (dist <= 0) {
1889 // we can not cross NL boundaries
1890 p = start;
1891 if (*p == '\n')
1892 return (p);
1893 // dont go past a NewLine
1894 for (; p + 1 <= stop; p++) {
1895 if (p[1] == '\n') {
1896 stop = p; // "stop" just before NewLine
1897 break;
1898 }
1899 }
1900 }
1901 p = start;
1902#ifdef CONFIG_FEATURE_VI_YANKMARK
1903 text_yank(start, stop, YDreg);
1904#endif /* CONFIG_FEATURE_VI_YANKMARK */
1905 if (yf == YANKDEL) {
1906 p = text_hole_delete(start, stop);
1907 } // delete lines
1908 return (p);
1909}
1910
1911static void show_help(void)
1912{
1913 puts("These features are available:"
1914#ifdef CONFIG_FEATURE_VI_SEARCH
1915 "\n\tPattern searches with / and ?"
1916#endif /* CONFIG_FEATURE_VI_SEARCH */
1917#ifdef CONFIG_FEATURE_VI_DOT_CMD
1918 "\n\tLast command repeat with \'.\'"
1919#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1920#ifdef CONFIG_FEATURE_VI_YANKMARK
1921 "\n\tLine marking with 'x"
1922 "\n\tNamed buffers with \"x"
1923#endif /* CONFIG_FEATURE_VI_YANKMARK */
1924#ifdef CONFIG_FEATURE_VI_READONLY
1925 "\n\tReadonly if vi is called as \"view\""
1926 "\n\tReadonly with -R command line arg"
1927#endif /* CONFIG_FEATURE_VI_READONLY */
1928#ifdef CONFIG_FEATURE_VI_SET
1929 "\n\tSome colon mode commands with \':\'"
1930#endif /* CONFIG_FEATURE_VI_SET */
1931#ifdef CONFIG_FEATURE_VI_SETOPTS
1932 "\n\tSettable options with \":set\""
1933#endif /* CONFIG_FEATURE_VI_SETOPTS */
1934#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
1935 "\n\tSignal catching- ^C"
1936 "\n\tJob suspend and resume with ^Z"
1937#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
1938#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
1939 "\n\tAdapt to window re-sizes"
1940#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
1941 );
1942}
1943
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001944static inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001945{
1946 Byte c, b[2];
1947
1948 b[1] = '\0';
1949 strcpy((char *) buf, ""); // init buf
1950 if (strlen((char *) s) <= 0)
1951 s = (Byte *) "(NULL)";
1952 for (; *s > '\0'; s++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001953 int c_is_no_print;
1954
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001955 c = *s;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001956 c_is_no_print = c > 127 && !Isprint(c);
1957 if (c_is_no_print) {
1958 strcat((char *) buf, SOn);
1959 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001960 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001961 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001962 strcat((char *) buf, "^");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001963 if(c == 127)
1964 c = '?';
1965 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001966 c += '@';
1967 }
1968 b[0] = c;
1969 strcat((char *) buf, (char *) b);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001970 if (c_is_no_print)
1971 strcat((char *) buf, SOs);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001972 if (*s == '\n') {
1973 strcat((char *) buf, "$");
1974 }
1975 }
1976}
1977
1978#ifdef CONFIG_FEATURE_VI_DOT_CMD
1979static void start_new_cmd_q(Byte c)
1980{
1981 // release old cmd
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001982 free(last_modifying_cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001983 // get buffer for new cmd
1984 last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
1985 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
1986 // if there is a current cmd count put it in the buffer first
1987 if (cmdcnt > 0)
1988 sprintf((char *) last_modifying_cmd, "%d", cmdcnt);
1989 // save char c onto queue
1990 last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
1991 adding2q = 1;
1992 return;
1993}
1994
1995static void end_cmd_q(void)
1996{
1997#ifdef CONFIG_FEATURE_VI_YANKMARK
1998 YDreg = 26; // go back to default Yank/Delete reg
1999#endif /* CONFIG_FEATURE_VI_YANKMARK */
2000 adding2q = 0;
2001 return;
2002}
2003#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2004
2005#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
2006static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
2007{
2008 int cnt, i;
2009
2010 i = strlen((char *) s);
2011 p = text_hole_make(p, i);
2012 strncpy((char *) p, (char *) s, i);
2013 for (cnt = 0; *s != '\0'; s++) {
2014 if (*s == '\n')
2015 cnt++;
2016 }
2017#ifdef CONFIG_FEATURE_VI_YANKMARK
2018 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2019#endif /* CONFIG_FEATURE_VI_YANKMARK */
2020 return (p);
2021}
2022#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
2023
2024#ifdef CONFIG_FEATURE_VI_YANKMARK
2025static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register
2026{
2027 Byte *t;
2028 int cnt;
2029
2030 if (q < p) { // they are backwards- reverse them
2031 t = q;
2032 q = p;
2033 p = t;
2034 }
2035 cnt = q - p + 1;
2036 t = reg[dest];
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002037 free(t); // if already a yank register, free it
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002038 t = (Byte *) xmalloc(cnt + 1); // get a new register
2039 memset(t, '\0', cnt + 1); // clear new text[]
2040 strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer
2041 reg[dest] = t;
2042 return (p);
2043}
2044
2045static Byte what_reg(void)
2046{
2047 Byte c;
2048 int i;
2049
2050 i = 0;
2051 c = 'D'; // default to D-reg
2052 if (0 <= YDreg && YDreg <= 25)
2053 c = 'a' + (Byte) YDreg;
2054 if (YDreg == 26)
2055 c = 'D';
2056 if (YDreg == 27)
2057 c = 'U';
2058 return (c);
2059}
2060
2061static void check_context(Byte cmd)
2062{
2063 // A context is defined to be "modifying text"
2064 // Any modifying command establishes a new context.
2065
2066 if (dot < context_start || dot > context_end) {
2067 if (strchr((char *) modifying_cmds, cmd) != NULL) {
2068 // we are trying to modify text[]- make this the current context
2069 mark[27] = mark[26]; // move cur to prev
2070 mark[26] = dot; // move local to cur
2071 context_start = prev_line(prev_line(dot));
2072 context_end = next_line(next_line(dot));
2073 //loiter= start_loiter= now;
2074 }
2075 }
2076 return;
2077}
2078
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002079static inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002080{
2081 Byte *tmp;
2082
2083 // the current context is in mark[26]
2084 // the previous context is in mark[27]
2085 // only swap context if other context is valid
2086 if (text <= mark[27] && mark[27] <= end - 1) {
2087 tmp = mark[27];
2088 mark[27] = mark[26];
2089 mark[26] = tmp;
2090 p = mark[26]; // where we are going- previous context
2091 context_start = prev_line(prev_line(prev_line(p)));
2092 context_end = next_line(next_line(next_line(p)));
2093 }
2094 return (p);
2095}
2096#endif /* CONFIG_FEATURE_VI_YANKMARK */
2097
2098static int isblnk(Byte c) // is the char a blank or tab
2099{
2100 return (c == ' ' || c == '\t');
2101}
2102
2103//----- Set terminal attributes --------------------------------
2104static void rawmode(void)
2105{
2106 tcgetattr(0, &term_orig);
2107 term_vi = term_orig;
2108 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2109 term_vi.c_iflag &= (~IXON & ~ICRNL);
2110 term_vi.c_oflag &= (~ONLCR);
2111#ifndef linux
2112 term_vi.c_cc[VMIN] = 1;
2113 term_vi.c_cc[VTIME] = 0;
2114#endif
2115 erase_char = term_vi.c_cc[VERASE];
2116 tcsetattr(0, TCSANOW, &term_vi);
2117}
2118
2119static void cookmode(void)
2120{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002121 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002122 tcsetattr(0, TCSANOW, &term_orig);
2123}
2124
2125#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2126//----- See what the window size currently is --------------------
2127static void window_size_get(int sig)
2128{
2129 int i;
2130
2131 i = ioctl(0, TIOCGWINSZ, &winsize);
2132 if (i != 0) {
2133 // force 24x80
2134 winsize.ws_row = 24;
2135 winsize.ws_col = 80;
2136 }
2137 if (winsize.ws_row <= 1) {
2138 winsize.ws_row = 24;
2139 }
2140 if (winsize.ws_col <= 1) {
2141 winsize.ws_col = 80;
2142 }
2143 rows = (int) winsize.ws_row;
2144 columns = (int) winsize.ws_col;
2145}
2146#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2147
2148//----- Come here when we get a window resize signal ---------
2149#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
2150static void winch_sig(int sig)
2151{
2152 signal(SIGWINCH, winch_sig);
2153#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2154 window_size_get(0);
2155#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2156 new_screen(rows, columns); // get memory for virtual screen
2157 redraw(TRUE); // re-draw the screen
2158}
2159
2160//----- Come here when we get a continue signal -------------------
2161static void cont_sig(int sig)
2162{
2163 rawmode(); // terminal to "raw"
2164 *status_buffer = '\0'; // clear the status buffer
2165 redraw(TRUE); // re-draw the screen
2166
2167 signal(SIGTSTP, suspend_sig);
2168 signal(SIGCONT, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002169 kill(my_pid, SIGCONT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002170}
2171
2172//----- Come here when we get a Suspend signal -------------------
2173static void suspend_sig(int sig)
2174{
2175 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2176 clear_to_eol(); // Erase to end of line
2177 cookmode(); // terminal to "cooked"
2178
2179 signal(SIGCONT, cont_sig);
2180 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002181 kill(my_pid, SIGTSTP);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002182}
2183
2184//----- Come here when we get a signal ---------------------------
2185static void catch_sig(int sig)
2186{
2187 signal(SIGHUP, catch_sig);
2188 signal(SIGINT, catch_sig);
2189 signal(SIGTERM, catch_sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002190 signal(SIGALRM, catch_sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002191 if(sig)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002192 longjmp(restart, sig);
2193}
2194
2195//----- Come here when we get a core dump signal -----------------
2196static void core_sig(int sig)
2197{
2198 signal(SIGQUIT, core_sig);
2199 signal(SIGILL, core_sig);
2200 signal(SIGTRAP, core_sig);
2201 signal(SIGIOT, core_sig);
2202 signal(SIGABRT, core_sig);
2203 signal(SIGFPE, core_sig);
2204 signal(SIGBUS, core_sig);
2205 signal(SIGSEGV, core_sig);
2206#ifdef SIGSYS
2207 signal(SIGSYS, core_sig);
2208#endif
2209
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002210 if(sig) { // signaled
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002211 dot = bound_dot(dot); // make sure "dot" is valid
2212
2213 longjmp(restart, sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002214 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002215}
2216#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2217
2218static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2219{
2220 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002221 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002222 FD_ZERO(&rfds);
2223 FD_SET(0, &rfds);
2224 tv.tv_sec = 0;
2225 tv.tv_usec = hund * 10000;
2226 select(1, &rfds, NULL, NULL, &tv);
2227 return (FD_ISSET(0, &rfds));
2228}
2229
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002230static Byte readbuffer[BUFSIZ];
2231static int readed_for_parse;
2232
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002233//----- IO Routines --------------------------------------------
2234static Byte readit(void) // read (maybe cursor) key from stdin
2235{
2236 Byte c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002237 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002238 struct esc_cmds {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002239 const char *seq;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002240 Byte val;
2241 };
2242
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002243 static const struct esc_cmds esccmds[] = {
2244 {"OA", (Byte) VI_K_UP}, // cursor key Up
2245 {"OB", (Byte) VI_K_DOWN}, // cursor key Down
2246 {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2247 {"OD", (Byte) VI_K_LEFT}, // cursor key Left
2248 {"OH", (Byte) VI_K_HOME}, // Cursor Key Home
2249 {"OF", (Byte) VI_K_END}, // Cursor Key End
2250 {"[A", (Byte) VI_K_UP}, // cursor key Up
2251 {"[B", (Byte) VI_K_DOWN}, // cursor key Down
2252 {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right
2253 {"[D", (Byte) VI_K_LEFT}, // cursor key Left
2254 {"[H", (Byte) VI_K_HOME}, // Cursor Key Home
2255 {"[F", (Byte) VI_K_END}, // Cursor Key End
2256 {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home
2257 {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2258 {"[4~", (Byte) VI_K_END}, // Cursor Key End
2259 {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2260 {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2261 {"OP", (Byte) VI_K_FUN1}, // Function Key F1
2262 {"OQ", (Byte) VI_K_FUN2}, // Function Key F2
2263 {"OR", (Byte) VI_K_FUN3}, // Function Key F3
2264 {"OS", (Byte) VI_K_FUN4}, // Function Key F4
2265 {"[15~", (Byte) VI_K_FUN5}, // Function Key F5
2266 {"[17~", (Byte) VI_K_FUN6}, // Function Key F6
2267 {"[18~", (Byte) VI_K_FUN7}, // Function Key F7
2268 {"[19~", (Byte) VI_K_FUN8}, // Function Key F8
2269 {"[20~", (Byte) VI_K_FUN9}, // Function Key F9
2270 {"[21~", (Byte) VI_K_FUN10}, // Function Key F10
2271 {"[23~", (Byte) VI_K_FUN11}, // Function Key F11
2272 {"[24~", (Byte) VI_K_FUN12}, // Function Key F12
2273 {"[11~", (Byte) VI_K_FUN1}, // Function Key F1
2274 {"[12~", (Byte) VI_K_FUN2}, // Function Key F2
2275 {"[13~", (Byte) VI_K_FUN3}, // Function Key F3
2276 {"[14~", (Byte) VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002277 };
2278
2279#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2280
2281 (void) alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002282 fflush(stdout);
2283 n = readed_for_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002284 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002285 if (n <= 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002286 ri0:
2287 // the Q is empty, wait for a typed char
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002288 n = read(0, readbuffer, BUFSIZ - 1);
2289 if (n < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002290 if (errno == EINTR)
2291 goto ri0; // interrupted sys call
2292 if (errno == EBADF)
2293 editing = 0;
2294 if (errno == EFAULT)
2295 editing = 0;
2296 if (errno == EINVAL)
2297 editing = 0;
2298 if (errno == EIO)
2299 editing = 0;
2300 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002301 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002302 if(n <= 0)
2303 return 0; // error
2304 if (readbuffer[0] == 27) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002305 // This is an ESC char. Is this Esc sequence?
2306 // Could be bare Esc key. See if there are any
2307 // more chars to read after the ESC. This would
2308 // be a Function or Cursor Key sequence.
2309 FD_ZERO(&rfds);
2310 FD_SET(0, &rfds);
2311 tv.tv_sec = 0;
2312 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2313
2314 // keep reading while there are input chars and room in buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002315 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002316 // read the rest of the ESC string
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002317 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2318 if (r > 0) {
2319 n += r;
2320 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002321 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002322 }
2323 readed_for_parse = n;
2324 }
2325 c = readbuffer[0];
2326 if(c == 27 && n > 1) {
2327 // Maybe cursor or function key?
2328 const struct esc_cmds *eindex;
2329
2330 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2331 int cnt = strlen(eindex->seq);
2332
2333 if(n <= cnt)
2334 continue;
2335 if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
2336 continue;
2337 // is a Cursor key- put derived value back into Q
2338 c = eindex->val;
2339 // for squeeze out the ESC sequence
2340 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002341 break;
2342 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002343 if(eindex == &esccmds[ESCCMDS_COUNT]) {
2344 /* defined ESC sequence not found, set only one ESC */
2345 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002346 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002347 } else {
2348 n = 1;
2349 }
2350 // remove key sequence from Q
2351 readed_for_parse -= n;
2352 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002353 (void) alarm(3); // we are done waiting for input, turn alarm ON
2354 return (c);
2355}
2356
2357//----- IO Routines --------------------------------------------
2358static Byte get_one_char()
2359{
2360 static Byte c;
2361
2362#ifdef CONFIG_FEATURE_VI_DOT_CMD
2363 // ! adding2q && ioq == 0 read()
2364 // ! adding2q && ioq != 0 *ioq
2365 // adding2q *last_modifying_cmd= read()
2366 if (!adding2q) {
2367 // we are not adding to the q.
2368 // but, we may be reading from a q
2369 if (ioq == 0) {
2370 // there is no current q, read from STDIN
2371 c = readit(); // get the users input
2372 } else {
2373 // there is a queue to get chars from first
2374 c = *ioq++;
2375 if (c == '\0') {
2376 // the end of the q, read from STDIN
2377 free(ioq_start);
2378 ioq_start = ioq = 0;
2379 c = readit(); // get the users input
2380 }
2381 }
2382 } else {
2383 // adding STDIN chars to q
2384 c = readit(); // get the users input
2385 if (last_modifying_cmd != 0) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002386 int len = strlen((char *) last_modifying_cmd);
2387 if (len + 1 >= BUFSIZ) {
2388 psbs("last_modifying_cmd overrun");
2389 } else {
2390 // add new char to q
2391 last_modifying_cmd[len] = c;
2392 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002393 }
2394 }
2395#else /* CONFIG_FEATURE_VI_DOT_CMD */
2396 c = readit(); // get the users input
2397#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2398 return (c); // return the char, where ever it came from
2399}
2400
2401static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2402{
2403 Byte buf[BUFSIZ];
2404 Byte c;
2405 int i;
2406 static Byte *obufp = NULL;
2407
2408 strcpy((char *) buf, (char *) prompt);
2409 *status_buffer = '\0'; // clear the status buffer
2410 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2411 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002412 write1(prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002413
2414 for (i = strlen((char *) buf); i < BUFSIZ;) {
2415 c = get_one_char(); // read user input
2416 if (c == '\n' || c == '\r' || c == 27)
2417 break; // is this end of input
2418 if (c == erase_char) { // user wants to erase prev char
2419 i--; // backup to prev char
2420 buf[i] = '\0'; // erase the char
2421 buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002422 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002423 if (i <= 0) { // user backs up before b-o-l, exit
2424 break;
2425 }
2426 } else {
2427 buf[i] = c; // save char in buffer
2428 buf[i + 1] = '\0'; // make sure buffer is null terminated
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002429 putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002430 i++;
2431 }
2432 }
2433 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002434 free(obufp);
Manuel Novoa III cad53642003-03-19 09:13:01 +00002435 obufp = (Byte *) bb_xstrdup((char *) buf);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002436 return (obufp);
2437}
2438
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002439static int file_size(const Byte * fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002440{
2441 struct stat st_buf;
2442 int cnt, sr;
2443
2444 if (fn == 0 || strlen(fn) <= 0)
2445 return (-1);
2446 cnt = -1;
2447 sr = stat((char *) fn, &st_buf); // see if file exists
2448 if (sr >= 0) {
2449 cnt = (int) st_buf.st_size;
2450 }
2451 return (cnt);
2452}
2453
2454static int file_insert(Byte * fn, Byte * p, int size)
2455{
2456 int fd, cnt;
2457
2458 cnt = -1;
2459#ifdef CONFIG_FEATURE_VI_READONLY
2460 readonly = FALSE;
2461#endif /* CONFIG_FEATURE_VI_READONLY */
2462 if (fn == 0 || strlen((char*) fn) <= 0) {
2463 psbs("No filename given");
2464 goto fi0;
2465 }
2466 if (size == 0) {
2467 // OK- this is just a no-op
2468 cnt = 0;
2469 goto fi0;
2470 }
2471 if (size < 0) {
2472 psbs("Trying to insert a negative number (%d) of characters", size);
2473 goto fi0;
2474 }
2475 if (p < text || p > end) {
2476 psbs("Trying to insert file outside of memory");
2477 goto fi0;
2478 }
2479
2480 // see if we can open the file
2481#ifdef CONFIG_FEATURE_VI_READONLY
2482 if (vi_readonly) goto fi1; // do not try write-mode
2483#endif
2484 fd = open((char *) fn, O_RDWR); // assume read & write
2485 if (fd < 0) {
2486 // could not open for writing- maybe file is read only
2487#ifdef CONFIG_FEATURE_VI_READONLY
2488 fi1:
2489#endif
2490 fd = open((char *) fn, O_RDONLY); // try read-only
2491 if (fd < 0) {
2492 psbs("\"%s\" %s", fn, "could not open file");
2493 goto fi0;
2494 }
2495#ifdef CONFIG_FEATURE_VI_READONLY
2496 // got the file- read-only
2497 readonly = TRUE;
2498#endif /* CONFIG_FEATURE_VI_READONLY */
2499 }
2500 p = text_hole_make(p, size);
2501 cnt = read(fd, p, size);
2502 close(fd);
2503 if (cnt < 0) {
2504 cnt = -1;
2505 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2506 psbs("could not read file \"%s\"", fn);
2507 } else if (cnt < size) {
2508 // There was a partial read, shrink unused space text[]
2509 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
2510 psbs("could not read all of file \"%s\"", fn);
2511 }
2512 if (cnt >= size)
2513 file_modified = TRUE;
2514 fi0:
2515 return (cnt);
2516}
2517
2518static int file_write(Byte * fn, Byte * first, Byte * last)
2519{
2520 int fd, cnt, charcnt;
2521
2522 if (fn == 0) {
2523 psbs("No current filename");
2524 return (-1);
2525 }
2526 charcnt = 0;
2527 // FIXIT- use the correct umask()
2528 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2529 if (fd < 0)
2530 return (-1);
2531 cnt = last - first + 1;
2532 charcnt = write(fd, first, cnt);
2533 if (charcnt == cnt) {
2534 // good write
2535 //file_modified= FALSE; // the file has not been modified
2536 } else {
2537 charcnt = 0;
2538 }
2539 close(fd);
2540 return (charcnt);
2541}
2542
2543//----- Terminal Drawing ---------------------------------------
2544// The terminal is made up of 'rows' line of 'columns' columns.
2545// classicly this would be 24 x 80.
2546// screen coordinates
2547// 0,0 ... 0,79
2548// 1,0 ... 1,79
2549// . ... .
2550// . ... .
2551// 22,0 ... 22,79
2552// 23,0 ... 23,79 status line
2553//
2554
2555//----- Move the cursor to row x col (count from 0, not 1) -------
2556static void place_cursor(int row, int col, int opti)
2557{
2558 char cm1[BUFSIZ];
2559 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002560#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2561 char cm2[BUFSIZ];
2562 Byte *screenp;
2563 // char cm3[BUFSIZ];
2564 int Rrow= last_row;
2565#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2566
2567 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2568
2569 if (row < 0) row = 0;
2570 if (row >= rows) row = rows - 1;
2571 if (col < 0) col = 0;
2572 if (col >= columns) col = columns - 1;
2573
2574 //----- 1. Try the standard terminal ESC sequence
2575 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2576 cm= cm1;
2577 if (! opti) goto pc0;
2578
2579#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2580 //----- find the minimum # of chars to move cursor -------------
2581 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2582 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
2583
2584 // move to the correct row
2585 while (row < Rrow) {
2586 // the cursor has to move up
2587 strcat(cm2, CMup);
2588 Rrow--;
2589 }
2590 while (row > Rrow) {
2591 // the cursor has to move down
2592 strcat(cm2, CMdown);
2593 Rrow++;
2594 }
2595
2596 // now move to the correct column
2597 strcat(cm2, "\r"); // start at col 0
2598 // just send out orignal source char to get to correct place
2599 screenp = &screen[row * columns]; // start of screen line
2600 strncat(cm2, screenp, col);
2601
2602 //----- 3. Try some other way of moving cursor
2603 //---------------------------------------------
2604
2605 // pick the shortest cursor motion to send out
2606 cm= cm1;
2607 if (strlen(cm2) < strlen(cm)) {
2608 cm= cm2;
2609 } /* else if (strlen(cm3) < strlen(cm)) {
2610 cm= cm3;
2611 } */
2612#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2613 pc0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002614 write1(cm); // move the cursor
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002615}
2616
2617//----- Erase from cursor to end of line -----------------------
2618static void clear_to_eol()
2619{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002620 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002621}
2622
2623//----- Erase from cursor to end of screen -----------------------
2624static void clear_to_eos()
2625{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002626 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002627}
2628
2629//----- Start standout mode ------------------------------------
2630static void standout_start() // send "start reverse video" sequence
2631{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002632 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002633}
2634
2635//----- End standout mode --------------------------------------
2636static void standout_end() // send "end reverse video" sequence
2637{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002638 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002639}
2640
2641//----- Flash the screen --------------------------------------
2642static void flash(int h)
2643{
2644 standout_start(); // send "start reverse video" sequence
2645 redraw(TRUE);
2646 (void) mysleep(h);
2647 standout_end(); // send "end reverse video" sequence
2648 redraw(TRUE);
2649}
2650
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002651static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002652{
2653#ifdef CONFIG_FEATURE_VI_CRASHME
2654 if (crashme > 0)
2655 return; // generate a random command
2656#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002657 if (!err_method) {
2658 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002659 } else {
2660 flash(10);
2661 }
2662}
2663
2664//----- Screen[] Routines --------------------------------------
2665//----- Erase the Screen[] memory ------------------------------
2666static void screen_erase()
2667{
2668 memset(screen, ' ', screensize); // clear new screen
2669}
2670
2671//----- Draw the status line at bottom of the screen -------------
2672static void show_status_line(void)
2673{
2674 static int last_cksum;
2675 int l, cnt, cksum;
2676
2677 cnt = strlen((char *) status_buffer);
2678 for (cksum= l= 0; l < cnt; l++) { cksum += (int)(status_buffer[l]); }
2679 // don't write the status line unless it changes
2680 if (cnt > 0 && last_cksum != cksum) {
2681 last_cksum= cksum; // remember if we have seen this line
2682 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002683 write1(status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002684 clear_to_eol();
2685 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2686 }
2687}
2688
2689//----- format the status buffer, the bottom line of screen ------
2690// print status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002691static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002692{
2693 va_list args;
2694
2695 va_start(args, format);
2696 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
2697 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format,
2698 args);
2699 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2700 va_end(args);
2701
2702 return;
2703}
2704
2705// print status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002706static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002707{
2708 va_list args;
2709
2710 va_start(args, format);
2711 vsprintf((char *) status_buffer, format, args);
2712 va_end(args);
2713 return;
2714}
2715
2716static void ni(Byte * s) // display messages
2717{
2718 Byte buf[BUFSIZ];
2719
2720 print_literal(buf, s);
2721 psbs("\'%s\' is not implemented", buf);
2722}
2723
2724static void edit_status(void) // show file status on status line
2725{
2726 int cur, tot, percent;
2727
2728 cur = count_lines(text, dot);
2729 tot = count_lines(text, end - 1);
2730 // current line percent
2731 // ------------- ~~ ----------
2732 // total lines 100
2733 if (tot > 0) {
2734 percent = (100 * cur) / tot;
2735 } else {
2736 cur = tot = 0;
2737 percent = 100;
2738 }
2739 psb("\"%s\""
2740#ifdef CONFIG_FEATURE_VI_READONLY
2741 "%s"
2742#endif /* CONFIG_FEATURE_VI_READONLY */
2743 "%s line %d of %d --%d%%--",
2744 (cfn != 0 ? (char *) cfn : "No file"),
2745#ifdef CONFIG_FEATURE_VI_READONLY
2746 ((vi_readonly || readonly) ? " [Read only]" : ""),
2747#endif /* CONFIG_FEATURE_VI_READONLY */
2748 (file_modified ? " [modified]" : ""),
2749 cur, tot, percent);
2750}
2751
2752//----- Force refresh of all Lines -----------------------------
2753static void redraw(int full_screen)
2754{
2755 place_cursor(0, 0, FALSE); // put cursor in correct place
2756 clear_to_eos(); // tel terminal to erase display
2757 screen_erase(); // erase the internal screen buffer
2758 refresh(full_screen); // this will redraw the entire display
2759}
2760
2761//----- Format a text[] line into a buffer ---------------------
2762static void format_line(Byte *dest, Byte *src, int li)
2763{
2764 int co;
2765 Byte c;
2766
2767 for (co= 0; co < MAX_SCR_COLS; co++) {
2768 c= ' '; // assume blank
2769 if (li > 0 && co == 0) {
2770 c = '~'; // not first line, assume Tilde
2771 }
2772 // are there chars in text[] and have we gone past the end
2773 if (text < end && src < end) {
2774 c = *src++;
2775 }
2776 if (c == '\n')
2777 break;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002778 if (c > 127 && !Isprint(c)) {
2779 c = '.';
2780 }
2781 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002782 if (c == '\t') {
2783 c = ' ';
2784 // co % 8 != 7
2785 for (; (co % tabstop) != (tabstop - 1); co++) {
2786 dest[co] = c;
2787 }
2788 } else {
2789 dest[co++] = '^';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002790 if(c == 127)
2791 c = '?';
2792 else
2793 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002794 }
2795 }
2796 // the co++ is done here so that the column will
2797 // not be overwritten when we blank-out the rest of line
2798 dest[co] = c;
2799 if (src >= end)
2800 break;
2801 }
2802}
2803
2804//----- Refresh the changed screen lines -----------------------
2805// Copy the source line from text[] into the buffer and note
2806// if the current screenline is different from the new buffer.
2807// If they differ then that line needs redrawing on the terminal.
2808//
2809static void refresh(int full_screen)
2810{
2811 static int old_offset;
2812 int li, changed;
2813 Byte buf[MAX_SCR_COLS];
2814 Byte *tp, *sp; // pointer into text[] and screen[]
2815#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2816 int last_li= -2; // last line that changed- for optimizing cursor movement
2817#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2818
2819#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2820 window_size_get(0);
2821#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2822 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2823 tp = screenbegin; // index into text[] of top line
2824
2825 // compare text[] to screen[] and mark screen[] lines that need updating
2826 for (li = 0; li < rows - 1; li++) {
2827 int cs, ce; // column start & end
2828 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2829 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2830 // format current text line into buf
2831 format_line(buf, tp, li);
2832
2833 // skip to the end of the current text[] line
2834 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2835
2836 // see if there are any changes between vitual screen and buf
2837 changed = FALSE; // assume no change
2838 cs= 0;
2839 ce= columns-1;
2840 sp = &screen[li * columns]; // start of screen line
2841 if (full_screen) {
2842 // force re-draw of every single column from 0 - columns-1
2843 goto re0;
2844 }
2845 // compare newly formatted buffer with virtual screen
2846 // look forward for first difference between buf and screen
2847 for ( ; cs <= ce; cs++) {
2848 if (buf[cs + offset] != sp[cs]) {
2849 changed = TRUE; // mark for redraw
2850 break;
2851 }
2852 }
2853
2854 // look backward for last difference between buf and screen
2855 for ( ; ce >= cs; ce--) {
2856 if (buf[ce + offset] != sp[ce]) {
2857 changed = TRUE; // mark for redraw
2858 break;
2859 }
2860 }
2861 // now, cs is index of first diff, and ce is index of last diff
2862
2863 // if horz offset has changed, force a redraw
2864 if (offset != old_offset) {
2865 re0:
2866 changed = TRUE;
2867 }
2868
2869 // make a sanity check of columns indexes
2870 if (cs < 0) cs= 0;
2871 if (ce > columns-1) ce= columns-1;
2872 if (cs > ce) { cs= 0; ce= columns-1; }
2873 // is there a change between vitual screen and buf
2874 if (changed) {
2875 // copy changed part of buffer to virtual screen
2876 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2877
2878 // move cursor to column of first change
2879 if (offset != old_offset) {
2880 // opti_cur_move is still too stupid
2881 // to handle offsets correctly
2882 place_cursor(li, cs, FALSE);
2883 } else {
2884#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2885 // if this just the next line
2886 // try to optimize cursor movement
2887 // otherwise, use standard ESC sequence
2888 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2889 last_li= li;
2890#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2891 place_cursor(li, cs, FALSE); // use standard ESC sequence
2892#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2893 }
2894
2895 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002896 {
2897 int nic = ce-cs+1;
2898 char *out = sp+cs;
2899
2900 while(nic-- > 0) {
2901 putchar(*out);
2902 out++;
2903 }
2904 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002905#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2906 last_row = li;
2907#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2908 }
2909 }
2910
2911#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2912 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2913 last_row = crow;
2914#else
2915 place_cursor(crow, ccol, FALSE);
2916#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2917
2918 if (offset != old_offset)
2919 old_offset = offset;
2920}
2921
Eric Andersen3f980402001-04-04 17:31:15 +00002922//---------------------------------------------------------------------
2923//----- the Ascii Chart -----------------------------------------------
2924//
2925// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2926// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2927// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2928// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2929// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2930// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2931// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2932// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2933// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2934// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2935// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2936// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2937// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2938// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2939// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2940// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2941//---------------------------------------------------------------------
2942
2943//----- Execute a Vi Command -----------------------------------
2944static void do_cmd(Byte c)
2945{
2946 Byte c1, *p, *q, *msg, buf[9], *save_dot;
2947 int cnt, i, j, dir, yf;
2948
2949 c1 = c; // quiet the compiler
2950 cnt = yf = dir = 0; // quiet the compiler
2951 p = q = save_dot = msg = buf; // quiet the compiler
2952 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00002953
2954 /* if this is a cursor key, skip these checks */
2955 switch (c) {
2956 case VI_K_UP:
2957 case VI_K_DOWN:
2958 case VI_K_LEFT:
2959 case VI_K_RIGHT:
2960 case VI_K_HOME:
2961 case VI_K_END:
2962 case VI_K_PAGEUP:
2963 case VI_K_PAGEDOWN:
2964 goto key_cmd_mode;
2965 }
2966
Eric Andersen3f980402001-04-04 17:31:15 +00002967 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002968 // flip-flop Insert/Replace mode
2969 if (c == VI_K_INSERT) goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00002970 // we are 'R'eplacing the current *dot with new char
2971 if (*dot == '\n') {
2972 // don't Replace past E-o-l
2973 cmd_mode = 1; // convert to insert
2974 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002975 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00002976 if (c != 27)
2977 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
2978 dot = char_insert(dot, c); // insert new char
2979 }
2980 goto dc1;
2981 }
2982 }
2983 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00002984 // hitting "Insert" twice means "R" replace mode
2985 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00002986 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002987 if (1 <= c || Isprint(c)) {
2988 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00002989 }
2990 goto dc1;
2991 }
2992
Eric Andersenbff7a602001-11-17 07:15:43 +00002993key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00002994 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00002995 //case 0x01: // soh
2996 //case 0x09: // ht
2997 //case 0x0b: // vt
2998 //case 0x0e: // so
2999 //case 0x0f: // si
3000 //case 0x10: // dle
3001 //case 0x11: // dc1
3002 //case 0x13: // dc3
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003003#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00003004 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00003005 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003006 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003007#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen822c3832001-05-07 17:37:43 +00003008 //case 0x16: // syn
3009 //case 0x17: // etb
3010 //case 0x18: // can
3011 //case 0x1c: // fs
3012 //case 0x1d: // gs
3013 //case 0x1e: // rs
3014 //case 0x1f: // us
3015 //case '!': // !-
3016 //case '#': // #-
3017 //case '&': // &-
3018 //case '(': // (-
3019 //case ')': // )-
3020 //case '*': // *-
3021 //case ',': // ,-
3022 //case '=': // =-
3023 //case '@': // @-
3024 //case 'F': // F-
3025 //case 'K': // K-
3026 //case 'Q': // Q-
3027 //case 'S': // S-
3028 //case 'T': // T-
3029 //case 'V': // V-
3030 //case '[': // [-
3031 //case '\\': // \-
3032 //case ']': // ]-
3033 //case '_': // _-
3034 //case '`': // `-
3035 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003036 //case 'u': // u- FIXME- there is no undo
Eric Andersen822c3832001-05-07 17:37:43 +00003037 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00003038 default: // unrecognised command
3039 buf[0] = c;
3040 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003041 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00003042 buf[0] = '^';
3043 buf[1] = c + '@';
3044 buf[2] = '\0';
3045 }
3046 ni((Byte *) buf);
3047 end_cmd_q(); // stop adding to q
3048 case 0x00: // nul- ignore
3049 break;
3050 case 2: // ctrl-B scroll up full screen
3051 case VI_K_PAGEUP: // Cursor Key Page Up
3052 dot_scroll(rows - 2, -1);
3053 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003054#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003055 case 0x03: // ctrl-C interrupt
3056 longjmp(restart, 1);
3057 break;
3058 case 26: // ctrl-Z suspend
3059 suspend_sig(SIGTSTP);
3060 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003061#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +00003062 case 4: // ctrl-D scroll down half screen
3063 dot_scroll((rows - 2) / 2, 1);
3064 break;
3065 case 5: // ctrl-E scroll down one line
3066 dot_scroll(1, 1);
3067 break;
3068 case 6: // ctrl-F scroll down full screen
3069 case VI_K_PAGEDOWN: // Cursor Key Page Down
3070 dot_scroll(rows - 2, 1);
3071 break;
3072 case 7: // ctrl-G show current status
3073 edit_status();
3074 break;
3075 case 'h': // h- move left
3076 case VI_K_LEFT: // cursor key Left
Eric Andersen1c0d3112001-04-16 15:46:44 +00003077 case 8: // ctrl-H- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003078 case 127: // DEL- move left (This may be ERASE char)
3079 if (cmdcnt-- > 1) {
3080 do_cmd(c);
3081 } // repeat cnt
3082 dot_left();
3083 break;
3084 case 10: // Newline ^J
3085 case 'j': // j- goto next line, same col
3086 case VI_K_DOWN: // cursor key Down
3087 if (cmdcnt-- > 1) {
3088 do_cmd(c);
3089 } // repeat cnt
3090 dot_next(); // go to next B-o-l
3091 dot = move_to_col(dot, ccol + offset); // try stay in same col
3092 break;
3093 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003094 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003095 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003096 clear_to_eos(); // tel terminal to erase display
3097 (void) mysleep(10);
3098 screen_erase(); // erase the internal screen buffer
3099 refresh(TRUE); // this will redraw the entire display
3100 break;
3101 case 13: // Carriage Return ^M
3102 case '+': // +- goto next line
3103 if (cmdcnt-- > 1) {
3104 do_cmd(c);
3105 } // repeat cnt
3106 dot_next();
3107 dot_skip_over_ws();
3108 break;
3109 case 21: // ctrl-U scroll up half screen
3110 dot_scroll((rows - 2) / 2, -1);
3111 break;
3112 case 25: // ctrl-Y scroll up one line
3113 dot_scroll(1, -1);
3114 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003115 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003116 if (cmd_mode == 0)
3117 indicate_error(c);
3118 cmd_mode = 0; // stop insrting
3119 end_cmd_q();
3120 *status_buffer = '\0'; // clear status buffer
3121 break;
3122 case ' ': // move right
3123 case 'l': // move right
3124 case VI_K_RIGHT: // Cursor Key Right
3125 if (cmdcnt-- > 1) {
3126 do_cmd(c);
3127 } // repeat cnt
3128 dot_right();
3129 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003130#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003131 case '"': // "- name a register to use for Delete/Yank
3132 c1 = get_one_char();
3133 c1 = tolower(c1);
3134 if (islower(c1)) {
3135 YDreg = c1 - 'a';
3136 } else {
3137 indicate_error(c);
3138 }
3139 break;
3140 case '\'': // '- goto a specific mark
3141 c1 = get_one_char();
3142 c1 = tolower(c1);
3143 if (islower(c1)) {
3144 c1 = c1 - 'a';
3145 // get the b-o-l
3146 q = mark[(int) c1];
3147 if (text <= q && q < end) {
3148 dot = q;
3149 dot_begin(); // go to B-o-l
3150 dot_skip_over_ws();
3151 }
3152 } else if (c1 == '\'') { // goto previous context
3153 dot = swap_context(dot); // swap current and previous context
3154 dot_begin(); // go to B-o-l
3155 dot_skip_over_ws();
3156 } else {
3157 indicate_error(c);
3158 }
3159 break;
3160 case 'm': // m- Mark a line
3161 // this is really stupid. If there are any inserts or deletes
3162 // between text[0] and dot then this mark will not point to the
3163 // correct location! It could be off by many lines!
3164 // Well..., at least its quick and dirty.
3165 c1 = get_one_char();
3166 c1 = tolower(c1);
3167 if (islower(c1)) {
3168 c1 = c1 - 'a';
3169 // remember the line
3170 mark[(int) c1] = dot;
3171 } else {
3172 indicate_error(c);
3173 }
3174 break;
3175 case 'P': // P- Put register before
3176 case 'p': // p- put register after
3177 p = reg[YDreg];
3178 if (p == 0) {
3179 psbs("Nothing in register %c", what_reg());
3180 break;
3181 }
3182 // are we putting whole lines or strings
3183 if (strchr((char *) p, '\n') != NULL) {
3184 if (c == 'P') {
3185 dot_begin(); // putting lines- Put above
3186 }
3187 if (c == 'p') {
3188 // are we putting after very last line?
3189 if (end_line(dot) == (end - 1)) {
3190 dot = end; // force dot to end of text[]
3191 } else {
3192 dot_next(); // next line, then put before
3193 }
3194 }
3195 } else {
3196 if (c == 'p')
3197 dot_right(); // move to right, can move to NL
3198 }
3199 dot = string_insert(dot, p); // insert the string
3200 end_cmd_q(); // stop adding to q
3201 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003202 case 'U': // U- Undo; replace current line with original version
3203 if (reg[Ureg] != 0) {
3204 p = begin_line(dot);
3205 q = end_line(dot);
3206 p = text_hole_delete(p, q); // delete cur line
3207 p = string_insert(p, reg[Ureg]); // insert orig line
3208 dot = p;
3209 dot_skip_over_ws();
3210 }
3211 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003212#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003213 case '$': // $- goto end of line
3214 case VI_K_END: // Cursor Key End
3215 if (cmdcnt-- > 1) {
3216 do_cmd(c);
3217 } // repeat cnt
3218 dot = end_line(dot + 1);
3219 break;
3220 case '%': // %- find matching char of pair () [] {}
3221 for (q = dot; q < end && *q != '\n'; q++) {
3222 if (strchr("()[]{}", *q) != NULL) {
3223 // we found half of a pair
3224 p = find_pair(q, *q);
3225 if (p == NULL) {
3226 indicate_error(c);
3227 } else {
3228 dot = p;
3229 }
3230 break;
3231 }
3232 }
3233 if (*q == '\n')
3234 indicate_error(c);
3235 break;
3236 case 'f': // f- forward to a user specified char
3237 last_forward_char = get_one_char(); // get the search char
3238 //
3239 // dont seperate these two commands. 'f' depends on ';'
3240 //
3241 //**** fall thru to ... 'i'
3242 case ';': // ;- look at rest of line for last forward char
3243 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003244 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003245 } // repeat cnt
Eric Andersen822c3832001-05-07 17:37:43 +00003246 if (last_forward_char == 0) break;
Eric Andersen3f980402001-04-04 17:31:15 +00003247 q = dot + 1;
3248 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3249 q++;
3250 }
3251 if (*q == last_forward_char)
3252 dot = q;
3253 break;
3254 case '-': // -- goto prev line
3255 if (cmdcnt-- > 1) {
3256 do_cmd(c);
3257 } // repeat cnt
3258 dot_prev();
3259 dot_skip_over_ws();
3260 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003261#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003262 case '.': // .- repeat the last modifying command
3263 // Stuff the last_modifying_cmd back into stdin
3264 // and let it be re-executed.
3265 if (last_modifying_cmd != 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +00003266 ioq = ioq_start = (Byte *) bb_xstrdup((char *) last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003267 }
3268 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003269#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3270#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003271 case '?': // /- search for a pattern
3272 case '/': // /- search for a pattern
3273 buf[0] = c;
3274 buf[1] = '\0';
3275 q = get_input_line(buf); // get input line- use "status line"
3276 if (strlen((char *) q) == 1)
3277 goto dc3; // if no pat re-use old pat
3278 if (strlen((char *) q) > 1) { // new pat- save it and find
3279 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003280 free(last_search_pattern);
Manuel Novoa III cad53642003-03-19 09:13:01 +00003281 last_search_pattern = (Byte *) bb_xstrdup((char *) q);
Eric Andersen3f980402001-04-04 17:31:15 +00003282 goto dc3; // now find the pattern
3283 }
3284 // user changed mind and erased the "/"- do nothing
3285 break;
3286 case 'N': // N- backward search for last pattern
3287 if (cmdcnt-- > 1) {
3288 do_cmd(c);
3289 } // repeat cnt
3290 dir = BACK; // assume BACKWARD search
3291 p = dot - 1;
3292 if (last_search_pattern[0] == '?') {
3293 dir = FORWARD;
3294 p = dot + 1;
3295 }
3296 goto dc4; // now search for pattern
3297 break;
3298 case 'n': // n- repeat search for last pattern
3299 // search rest of text[] starting at next char
3300 // if search fails return orignal "p" not the "p+1" address
3301 if (cmdcnt-- > 1) {
3302 do_cmd(c);
3303 } // repeat cnt
3304 dc3:
3305 if (last_search_pattern == 0) {
3306 msg = (Byte *) "No previous regular expression";
3307 goto dc2;
3308 }
3309 if (last_search_pattern[0] == '/') {
3310 dir = FORWARD; // assume FORWARD search
3311 p = dot + 1;
3312 }
3313 if (last_search_pattern[0] == '?') {
3314 dir = BACK;
3315 p = dot - 1;
3316 }
3317 dc4:
3318 q = char_search(p, last_search_pattern + 1, dir, FULL);
3319 if (q != NULL) {
3320 dot = q; // good search, update "dot"
3321 msg = (Byte *) "";
3322 goto dc2;
3323 }
3324 // no pattern found between "dot" and "end"- continue at top
3325 p = text;
3326 if (dir == BACK) {
3327 p = end - 1;
3328 }
3329 q = char_search(p, last_search_pattern + 1, dir, FULL);
3330 if (q != NULL) { // found something
3331 dot = q; // found new pattern- goto it
3332 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3333 if (dir == BACK) {
3334 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3335 }
3336 } else {
3337 msg = (Byte *) "Pattern not found";
3338 }
3339 dc2:
3340 psbs("%s", msg);
3341 break;
3342 case '{': // {- move backward paragraph
3343 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3344 if (q != NULL) { // found blank line
3345 dot = next_line(q); // move to next blank line
3346 }
3347 break;
3348 case '}': // }- move forward paragraph
3349 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3350 if (q != NULL) { // found blank line
3351 dot = next_line(q); // move to next blank line
3352 }
3353 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003354#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003355 case '0': // 0- goto begining of line
3356 case '1': // 1-
3357 case '2': // 2-
3358 case '3': // 3-
3359 case '4': // 4-
3360 case '5': // 5-
3361 case '6': // 6-
3362 case '7': // 7-
3363 case '8': // 8-
3364 case '9': // 9-
3365 if (c == '0' && cmdcnt < 1) {
3366 dot_begin(); // this was a standalone zero
3367 } else {
3368 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3369 }
3370 break;
3371 case ':': // :- the colon mode commands
Eric Andersen3f980402001-04-04 17:31:15 +00003372 p = get_input_line((Byte *) ":"); // get input line- use "status line"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003373#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003374 colon(p); // execute the command
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003375#else /* CONFIG_FEATURE_VI_COLON */
Eric Andersen822c3832001-05-07 17:37:43 +00003376 if (*p == ':')
3377 p++; // move past the ':'
3378 cnt = strlen((char *) p);
3379 if (cnt <= 0)
3380 break;
3381 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3382 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
Matt Kraai1f0c4362001-12-20 23:13:26 +00003383 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003384 psbs("No write since last change (:quit! overrides)");
3385 } else {
3386 editing = 0;
3387 }
Eric Andersen822c3832001-05-07 17:37:43 +00003388 } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
Robert Griebla71389b2002-07-31 21:22:21 +00003389 strncasecmp((char *) p, "wq", cnt) == 0 ||
3390 strncasecmp((char *) p, "x", cnt) == 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003391 cnt = file_write(cfn, text, end - 1);
3392 file_modified = FALSE;
3393 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
Robert Griebla71389b2002-07-31 21:22:21 +00003394 if (p[0] == 'x' || p[1] == 'q') {
Eric Andersen3f980402001-04-04 17:31:15 +00003395 editing = 0;
3396 }
Eric Andersen822c3832001-05-07 17:37:43 +00003397 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
3398 edit_status(); // show current file status
3399 } else if (sscanf((char *) p, "%d", &j) > 0) {
3400 dot = find_line(j); // go to line # j
3401 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003402 } else { // unrecognised cmd
Eric Andersen822c3832001-05-07 17:37:43 +00003403 ni((Byte *) p);
Eric Andersen3f980402001-04-04 17:31:15 +00003404 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003405#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003406 break;
3407 case '<': // <- Left shift something
3408 case '>': // >- Right shift something
3409 cnt = count_lines(text, dot); // remember what line we are on
3410 c1 = get_one_char(); // get the type of thing to delete
3411 find_range(&p, &q, c1);
3412 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3413 p = begin_line(p);
3414 q = end_line(q);
3415 i = count_lines(p, q); // # of lines we are shifting
3416 for ( ; i > 0; i--, p = next_line(p)) {
3417 if (c == '<') {
3418 // shift left- remove tab or 8 spaces
3419 if (*p == '\t') {
3420 // shrink buffer 1 char
3421 (void) text_hole_delete(p, p);
3422 } else if (*p == ' ') {
3423 // we should be calculating columns, not just SPACE
3424 for (j = 0; *p == ' ' && j < tabstop; j++) {
3425 (void) text_hole_delete(p, p);
3426 }
3427 }
3428 } else if (c == '>') {
3429 // shift right -- add tab or 8 spaces
3430 (void) char_insert(p, '\t');
3431 }
3432 }
3433 dot = find_line(cnt); // what line were we on
3434 dot_skip_over_ws();
3435 end_cmd_q(); // stop adding to q
3436 break;
3437 case 'A': // A- append at e-o-l
3438 dot_end(); // go to e-o-l
3439 //**** fall thru to ... 'a'
3440 case 'a': // a- append after current char
3441 if (*dot != '\n')
3442 dot++;
3443 goto dc_i;
3444 break;
3445 case 'B': // B- back a blank-delimited Word
3446 case 'E': // E- end of a blank-delimited word
3447 case 'W': // W- forward a blank-delimited word
3448 if (cmdcnt-- > 1) {
3449 do_cmd(c);
3450 } // repeat cnt
3451 dir = FORWARD;
3452 if (c == 'B')
3453 dir = BACK;
3454 if (c == 'W' || isspace(dot[dir])) {
3455 dot = skip_thing(dot, 1, dir, S_TO_WS);
3456 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3457 }
3458 if (c != 'W')
3459 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3460 break;
3461 case 'C': // C- Change to e-o-l
3462 case 'D': // D- delete to e-o-l
3463 save_dot = dot;
3464 dot = dollar_line(dot); // move to before NL
3465 // copy text into a register and delete
3466 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3467 if (c == 'C')
3468 goto dc_i; // start inserting
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003469#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003470 if (c == 'D')
3471 end_cmd_q(); // stop adding to q
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003472#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003473 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003474 case 'G': // G- goto to a line number (default= E-O-F)
3475 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003476 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003477 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003478 }
3479 dot_skip_over_ws();
3480 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003481 case 'H': // H- goto top line on screen
3482 dot = screenbegin;
3483 if (cmdcnt > (rows - 1)) {
3484 cmdcnt = (rows - 1);
3485 }
3486 if (cmdcnt-- > 1) {
3487 do_cmd('+');
3488 } // repeat cnt
3489 dot_skip_over_ws();
3490 break;
3491 case 'I': // I- insert before first non-blank
3492 dot_begin(); // 0
3493 dot_skip_over_ws();
3494 //**** fall thru to ... 'i'
3495 case 'i': // i- insert before current char
3496 case VI_K_INSERT: // Cursor Key Insert
3497 dc_i:
3498 cmd_mode = 1; // start insrting
3499 psb("-- Insert --");
3500 break;
3501 case 'J': // J- join current and next lines together
3502 if (cmdcnt-- > 2) {
3503 do_cmd(c);
3504 } // repeat cnt
3505 dot_end(); // move to NL
3506 if (dot < end - 1) { // make sure not last char in text[]
3507 *dot++ = ' '; // replace NL with space
3508 while (isblnk(*dot)) { // delete leading WS
3509 dot_delete();
3510 }
3511 }
3512 end_cmd_q(); // stop adding to q
3513 break;
3514 case 'L': // L- goto bottom line on screen
3515 dot = end_screen();
3516 if (cmdcnt > (rows - 1)) {
3517 cmdcnt = (rows - 1);
3518 }
3519 if (cmdcnt-- > 1) {
3520 do_cmd('-');
3521 } // repeat cnt
3522 dot_begin();
3523 dot_skip_over_ws();
3524 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003525 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003526 dot = screenbegin;
3527 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3528 dot = next_line(dot);
3529 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003530 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003531 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003532 p = begin_line(dot);
3533 if (p[-1] == '\n') {
3534 dot_prev();
3535 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3536 dot_end();
3537 dot = char_insert(dot, '\n');
3538 } else {
3539 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003540 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003541 dot_prev(); // -
3542 }
3543 goto dc_i;
3544 break;
3545 case 'R': // R- continuous Replace char
Eric Andersen1c0d3112001-04-16 15:46:44 +00003546 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003547 cmd_mode = 2;
3548 psb("-- Replace --");
3549 break;
3550 case 'X': // X- delete char before dot
3551 case 'x': // x- delete the current char
3552 case 's': // s- substitute the current char
3553 if (cmdcnt-- > 1) {
3554 do_cmd(c);
3555 } // repeat cnt
3556 dir = 0;
3557 if (c == 'X')
3558 dir = -1;
3559 if (dot[dir] != '\n') {
3560 if (c == 'X')
3561 dot--; // delete prev char
3562 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3563 }
3564 if (c == 's')
3565 goto dc_i; // start insrting
3566 end_cmd_q(); // stop adding to q
3567 break;
3568 case 'Z': // Z- if modified, {write}; exit
3569 // ZZ means to save file (if necessary), then exit
3570 c1 = get_one_char();
3571 if (c1 != 'Z') {
3572 indicate_error(c);
3573 break;
3574 }
Matt Kraai1f0c4362001-12-20 23:13:26 +00003575 if (file_modified
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003576#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00003577 && ! vi_readonly
3578 && ! readonly
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003579#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00003580 ) {
3581 cnt = file_write(cfn, text, end - 1);
3582 if (cnt == (end - 1 - text + 1)) {
3583 editing = 0;
3584 }
3585 } else {
3586 editing = 0;
3587 }
3588 break;
3589 case '^': // ^- move to first non-blank on line
3590 dot_begin();
3591 dot_skip_over_ws();
3592 break;
3593 case 'b': // b- back a word
3594 case 'e': // e- end of word
3595 if (cmdcnt-- > 1) {
3596 do_cmd(c);
3597 } // repeat cnt
3598 dir = FORWARD;
3599 if (c == 'b')
3600 dir = BACK;
3601 if ((dot + dir) < text || (dot + dir) > end - 1)
3602 break;
3603 dot += dir;
3604 if (isspace(*dot)) {
3605 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3606 }
3607 if (isalnum(*dot) || *dot == '_') {
3608 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3609 } else if (ispunct(*dot)) {
3610 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3611 }
3612 break;
3613 case 'c': // c- change something
3614 case 'd': // d- delete something
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003615#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003616 case 'y': // y- yank something
3617 case 'Y': // Y- Yank a line
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003618#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003619 yf = YANKDEL; // assume either "c" or "d"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003620#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003621 if (c == 'y' || c == 'Y')
3622 yf = YANKONLY;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003623#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003624 c1 = 'y';
3625 if (c != 'Y')
3626 c1 = get_one_char(); // get the type of thing to delete
3627 find_range(&p, &q, c1);
3628 if (c1 == 27) { // ESC- user changed mind and wants out
3629 c = c1 = 27; // Escape- do nothing
3630 } else if (strchr("wW", c1)) {
3631 if (c == 'c') {
3632 // don't include trailing WS as part of word
3633 while (isblnk(*q)) {
3634 if (q <= text || q[-1] == '\n')
3635 break;
3636 q--;
3637 }
3638 }
3639 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003640 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003641 // single line copy text into a register and delete
3642 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003643 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003644 // multiple line copy text into a register and delete
3645 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003646 if (c == 'c') {
3647 dot = char_insert(dot, '\n');
3648 // on the last line of file don't move to prev line
3649 if (dot != (end-1)) {
3650 dot_prev();
3651 }
3652 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003653 dot_begin();
3654 dot_skip_over_ws();
3655 }
3656 } else {
3657 // could not recognize object
3658 c = c1 = 27; // error-
3659 indicate_error(c);
3660 }
3661 if (c1 != 27) {
3662 // if CHANGING, not deleting, start inserting after the delete
3663 if (c == 'c') {
3664 strcpy((char *) buf, "Change");
3665 goto dc_i; // start inserting
3666 }
3667 if (c == 'd') {
3668 strcpy((char *) buf, "Delete");
3669 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003670#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003671 if (c == 'y' || c == 'Y') {
3672 strcpy((char *) buf, "Yank");
3673 }
3674 p = reg[YDreg];
3675 q = p + strlen((char *) p);
3676 for (cnt = 0; p <= q; p++) {
3677 if (*p == '\n')
3678 cnt++;
3679 }
3680 psb("%s %d lines (%d chars) using [%c]",
3681 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003682#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003683 end_cmd_q(); // stop adding to q
3684 }
3685 break;
3686 case 'k': // k- goto prev line, same col
3687 case VI_K_UP: // cursor key Up
3688 if (cmdcnt-- > 1) {
3689 do_cmd(c);
3690 } // repeat cnt
3691 dot_prev();
3692 dot = move_to_col(dot, ccol + offset); // try stay in same col
3693 break;
3694 case 'r': // r- replace the current char with user input
3695 c1 = get_one_char(); // get the replacement char
3696 if (*dot != '\n') {
3697 *dot = c1;
3698 file_modified = TRUE; // has the file been modified
3699 }
3700 end_cmd_q(); // stop adding to q
3701 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003702 case 't': // t- move to char prior to next x
3703 last_forward_char = get_one_char();
3704 do_cmd(';');
3705 if (*dot == last_forward_char)
3706 dot_left();
3707 last_forward_char= 0;
3708 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003709 case 'w': // w- forward a word
3710 if (cmdcnt-- > 1) {
3711 do_cmd(c);
3712 } // repeat cnt
3713 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3714 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3715 } else if (ispunct(*dot)) { // we are on PUNCT
3716 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3717 }
3718 if (dot < end - 1)
3719 dot++; // move over word
3720 if (isspace(*dot)) {
3721 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3722 }
3723 break;
3724 case 'z': // z-
3725 c1 = get_one_char(); // get the replacement char
3726 cnt = 0;
3727 if (c1 == '.')
3728 cnt = (rows - 2) / 2; // put dot at center
3729 if (c1 == '-')
3730 cnt = rows - 2; // put dot at bottom
3731 screenbegin = begin_line(dot); // start dot at top
3732 dot_scroll(cnt, -1);
3733 break;
3734 case '|': // |- move to column "cmdcnt"
3735 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3736 break;
3737 case '~': // ~- flip the case of letters a-z -> A-Z
3738 if (cmdcnt-- > 1) {
3739 do_cmd(c);
3740 } // repeat cnt
3741 if (islower(*dot)) {
3742 *dot = toupper(*dot);
3743 file_modified = TRUE; // has the file been modified
3744 } else if (isupper(*dot)) {
3745 *dot = tolower(*dot);
3746 file_modified = TRUE; // has the file been modified
3747 }
3748 dot_right();
3749 end_cmd_q(); // stop adding to q
3750 break;
3751 //----- The Cursor and Function Keys -----------------------------
3752 case VI_K_HOME: // Cursor Key Home
3753 dot_begin();
3754 break;
3755 // The Fn keys could point to do_macro which could translate them
3756 case VI_K_FUN1: // Function Key F1
3757 case VI_K_FUN2: // Function Key F2
3758 case VI_K_FUN3: // Function Key F3
3759 case VI_K_FUN4: // Function Key F4
3760 case VI_K_FUN5: // Function Key F5
3761 case VI_K_FUN6: // Function Key F6
3762 case VI_K_FUN7: // Function Key F7
3763 case VI_K_FUN8: // Function Key F8
3764 case VI_K_FUN9: // Function Key F9
3765 case VI_K_FUN10: // Function Key F10
3766 case VI_K_FUN11: // Function Key F11
3767 case VI_K_FUN12: // Function Key F12
3768 break;
3769 }
3770
3771 dc1:
3772 // if text[] just became empty, add back an empty line
3773 if (end == text) {
3774 (void) char_insert(text, '\n'); // start empty buf with dummy line
3775 dot = text;
3776 }
3777 // it is OK for dot to exactly equal to end, otherwise check dot validity
3778 if (dot != end) {
3779 dot = bound_dot(dot); // make sure "dot" is valid
3780 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003781#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003782 check_context(c); // update the current context
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003783#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003784
3785 if (!isdigit(c))
3786 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3787 cnt = dot - begin_line(dot);
3788 // Try to stay off of the Newline
3789 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3790 dot--;
3791}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003792
3793#ifdef CONFIG_FEATURE_VI_CRASHME
3794static int totalcmds = 0;
3795static int Mp = 85; // Movement command Probability
3796static int Np = 90; // Non-movement command Probability
3797static int Dp = 96; // Delete command Probability
3798static int Ip = 97; // Insert command Probability
3799static int Yp = 98; // Yank command Probability
3800static int Pp = 99; // Put command Probability
3801static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3802char chars[20] = "\t012345 abcdABCD-=.$";
3803char *words[20] = { "this", "is", "a", "test",
3804 "broadcast", "the", "emergency", "of",
3805 "system", "quick", "brown", "fox",
3806 "jumped", "over", "lazy", "dogs",
3807 "back", "January", "Febuary", "March"
3808};
3809char *lines[20] = {
3810 "You should have received a copy of the GNU General Public License\n",
3811 "char c, cm, *cmd, *cmd1;\n",
3812 "generate a command by percentages\n",
3813 "Numbers may be typed as a prefix to some commands.\n",
3814 "Quit, discarding changes!\n",
3815 "Forced write, if permission originally not valid.\n",
3816 "In general, any ex or ed command (such as substitute or delete).\n",
3817 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3818 "Please get w/ me and I will go over it with you.\n",
3819 "The following is a list of scheduled, committed changes.\n",
3820 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3821 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3822 "Any question about transactions please contact Sterling Huxley.\n",
3823 "I will try to get back to you by Friday, December 31.\n",
3824 "This Change will be implemented on Friday.\n",
3825 "Let me know if you have problems accessing this;\n",
3826 "Sterling Huxley recently added you to the access list.\n",
3827 "Would you like to go to lunch?\n",
3828 "The last command will be automatically run.\n",
3829 "This is too much english for a computer geek.\n",
3830};
3831char *multilines[20] = {
3832 "You should have received a copy of the GNU General Public License\n",
3833 "char c, cm, *cmd, *cmd1;\n",
3834 "generate a command by percentages\n",
3835 "Numbers may be typed as a prefix to some commands.\n",
3836 "Quit, discarding changes!\n",
3837 "Forced write, if permission originally not valid.\n",
3838 "In general, any ex or ed command (such as substitute or delete).\n",
3839 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3840 "Please get w/ me and I will go over it with you.\n",
3841 "The following is a list of scheduled, committed changes.\n",
3842 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3843 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3844 "Any question about transactions please contact Sterling Huxley.\n",
3845 "I will try to get back to you by Friday, December 31.\n",
3846 "This Change will be implemented on Friday.\n",
3847 "Let me know if you have problems accessing this;\n",
3848 "Sterling Huxley recently added you to the access list.\n",
3849 "Would you like to go to lunch?\n",
3850 "The last command will be automatically run.\n",
3851 "This is too much english for a computer geek.\n",
3852};
3853
3854// create a random command to execute
3855static void crash_dummy()
3856{
3857 static int sleeptime; // how long to pause between commands
3858 char c, cm, *cmd, *cmd1;
3859 int i, cnt, thing, rbi, startrbi, percent;
3860
3861 // "dot" movement commands
3862 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3863
3864 // is there already a command running?
3865 if (readed_for_parse > 0)
3866 goto cd1;
3867 cd0:
3868 startrbi = rbi = 0;
3869 sleeptime = 0; // how long to pause between commands
3870 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3871 // generate a command by percentages
3872 percent = (int) lrand48() % 100; // get a number from 0-99
3873 if (percent < Mp) { // Movement commands
3874 // available commands
3875 cmd = cmd1;
3876 M++;
3877 } else if (percent < Np) { // non-movement commands
3878 cmd = "mz<>\'\""; // available commands
3879 N++;
3880 } else if (percent < Dp) { // Delete commands
3881 cmd = "dx"; // available commands
3882 D++;
3883 } else if (percent < Ip) { // Inset commands
3884 cmd = "iIaAsrJ"; // available commands
3885 I++;
3886 } else if (percent < Yp) { // Yank commands
3887 cmd = "yY"; // available commands
3888 Y++;
3889 } else if (percent < Pp) { // Put commands
3890 cmd = "pP"; // available commands
3891 P++;
3892 } else {
3893 // We do not know how to handle this command, try again
3894 U++;
3895 goto cd0;
3896 }
3897 // randomly pick one of the available cmds from "cmd[]"
3898 i = (int) lrand48() % strlen(cmd);
3899 cm = cmd[i];
3900 if (strchr(":\024", cm))
3901 goto cd0; // dont allow colon or ctrl-T commands
3902 readbuffer[rbi++] = cm; // put cmd into input buffer
3903
3904 // now we have the command-
3905 // there are 1, 2, and multi char commands
3906 // find out which and generate the rest of command as necessary
3907 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3908 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3909 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3910 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3911 }
3912 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3913 c = cmd1[thing];
3914 readbuffer[rbi++] = c; // add movement to input buffer
3915 }
3916 if (strchr("iIaAsc", cm)) { // multi-char commands
3917 if (cm == 'c') {
3918 // change some thing
3919 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3920 c = cmd1[thing];
3921 readbuffer[rbi++] = c; // add movement to input buffer
3922 }
3923 thing = (int) lrand48() % 4; // what thing to insert
3924 cnt = (int) lrand48() % 10; // how many to insert
3925 for (i = 0; i < cnt; i++) {
3926 if (thing == 0) { // insert chars
3927 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3928 } else if (thing == 1) { // insert words
3929 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
3930 strcat((char *) readbuffer, " ");
3931 sleeptime = 0; // how fast to type
3932 } else if (thing == 2) { // insert lines
3933 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
3934 sleeptime = 0; // how fast to type
3935 } else { // insert multi-lines
3936 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
3937 sleeptime = 0; // how fast to type
3938 }
3939 }
3940 strcat((char *) readbuffer, "\033");
3941 }
3942 readed_for_parse = strlen(readbuffer);
3943 cd1:
3944 totalcmds++;
3945 if (sleeptime > 0)
3946 (void) mysleep(sleeptime); // sleep 1/100 sec
3947}
3948
3949// test to see if there are any errors
3950static void crash_test()
3951{
3952 static time_t oldtim;
3953 time_t tim;
Glenn L McGrath7127b582002-12-03 21:48:15 +00003954 char d[2], msg[BUFSIZ];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003955
3956 msg[0] = '\0';
3957 if (end < text) {
3958 strcat((char *) msg, "end<text ");
3959 }
3960 if (end > textend) {
3961 strcat((char *) msg, "end>textend ");
3962 }
3963 if (dot < text) {
3964 strcat((char *) msg, "dot<text ");
3965 }
3966 if (dot > end) {
3967 strcat((char *) msg, "dot>end ");
3968 }
3969 if (screenbegin < text) {
3970 strcat((char *) msg, "screenbegin<text ");
3971 }
3972 if (screenbegin > end - 1) {
3973 strcat((char *) msg, "screenbegin>end-1 ");
3974 }
3975
3976 if (strlen(msg) > 0) {
3977 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00003978 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003979 totalcmds, last_input_char, msg, SOs, SOn);
3980 fflush(stdout);
3981 while (read(0, d, 1) > 0) {
3982 if (d[0] == '\n' || d[0] == '\r')
3983 break;
3984 }
3985 alarm(3);
3986 }
3987 tim = (time_t) time((time_t *) 0);
3988 if (tim >= (oldtim + 3)) {
3989 sprintf((char *) status_buffer,
3990 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
3991 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
3992 oldtim = tim;
3993 }
3994 return;
3995}
3996#endif /* CONFIG_FEATURE_VI_CRASHME */