blob: 6f739a7143fe25a83af9ecdbb98ea8e2e6bc7242 [file] [log] [blame]
Eric Andersen3f980402001-04-04 17:31:15 +00001/* vi: set sw=8 ts=8: */
2/*
3 * tiny vi.c: A small 'vi' clone
4 * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
Eric Andersen044228d2001-07-17 01:12:36 +000021static const char vi_Version[] =
Eric Andersena9eb33d2004-08-19 19:15:06 +000022 "$Id: vi.c,v 1.38 2004/08/19 19:15:06 andersen Exp $";
Eric Andersen3f980402001-04-04 17:31:15 +000023
24/*
25 * To compile for standalone use:
Eric Andersend402edf2001-04-04 19:29:48 +000026 * gcc -Wall -Os -s -DSTANDALONE -o vi vi.c
Eric Andersen3f980402001-04-04 17:31:15 +000027 * or
Eric Andersenbdfd0d72001-10-24 05:00:29 +000028 * gcc -Wall -Os -s -DSTANDALONE -DCONFIG_FEATURE_VI_CRASHME -o vi vi.c # include testing features
Eric Andersen3f980402001-04-04 17:31:15 +000029 * strip vi
30 */
31
32/*
33 * Things To Do:
34 * EXINIT
Eric Andersen1c0d3112001-04-16 15:46:44 +000035 * $HOME/.exrc and ./.exrc
Eric Andersen3f980402001-04-04 17:31:15 +000036 * add magic to search /foo.*bar
37 * add :help command
38 * :map macros
39 * how about mode lines: vi: set sw=8 ts=8:
40 * if mark[] values were line numbers rather than pointers
41 * it would be easier to change the mark when add/delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +000042 * More intelligence in refresh()
43 * ":r !cmd" and "!cmd" to filter text through an external command
44 * A true "undo" facility
45 * An "ex" line oriented mode- maybe using "cmdedit"
Eric Andersen3f980402001-04-04 17:31:15 +000046 */
47
Eric Andersenaff114c2004-04-14 17:51:38 +000048//---- Feature -------------- Bytes to implement
Eric Andersen3f980402001-04-04 17:31:15 +000049#ifdef STANDALONE
Eric Andersen822c3832001-05-07 17:37:43 +000050#define vi_main main
Eric Andersenbdfd0d72001-10-24 05:00:29 +000051#define CONFIG_FEATURE_VI_COLON // 4288
52#define CONFIG_FEATURE_VI_YANKMARK // 1408
53#define CONFIG_FEATURE_VI_SEARCH // 1088
54#define CONFIG_FEATURE_VI_USE_SIGNALS // 1056
55#define CONFIG_FEATURE_VI_DOT_CMD // 576
56#define CONFIG_FEATURE_VI_READONLY // 128
57#define CONFIG_FEATURE_VI_SETOPTS // 576
58#define CONFIG_FEATURE_VI_SET // 224
59#define CONFIG_FEATURE_VI_WIN_RESIZE // 256 WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +000060// To test editor using CRASHME:
61// vi -C filename
62// To stop testing, wait until all to text[] is deleted, or
63// Ctrl-Z and kill -9 %1
64// while in the editor Ctrl-T will toggle the crashme function on and off.
Eric Andersenbdfd0d72001-10-24 05:00:29 +000065//#define CONFIG_FEATURE_VI_CRASHME // randomly pick commands to execute
Eric Andersen3f980402001-04-04 17:31:15 +000066#endif /* STANDALONE */
67
Eric Andersen3f980402001-04-04 17:31:15 +000068#include <stdio.h>
69#include <stdlib.h>
70#include <string.h>
71#include <termios.h>
72#include <unistd.h>
73#include <sys/ioctl.h>
74#include <sys/time.h>
75#include <sys/types.h>
76#include <sys/stat.h>
77#include <time.h>
78#include <fcntl.h>
79#include <signal.h>
80#include <setjmp.h>
81#include <regex.h>
82#include <ctype.h>
83#include <assert.h>
84#include <errno.h>
85#include <stdarg.h>
Eric Andersene0c07572001-06-23 13:49:14 +000086#ifndef STANDALONE
87#include "busybox.h"
88#endif /* STANDALONE */
Eric Andersen3f980402001-04-04 17:31:15 +000089
Glenn L McGrath09adaca2002-12-02 21:18:10 +000090#ifdef CONFIG_LOCALE_SUPPORT
91#define Isprint(c) isprint((c))
92#else
93#define Isprint(c) ( (c) >= ' ' && (c) != 127 && (c) != ((unsigned char)'\233') )
94#endif
95
Eric Andersen3f980402001-04-04 17:31:15 +000096#ifndef TRUE
97#define TRUE ((int)1)
98#define FALSE ((int)0)
99#endif /* TRUE */
Eric Andersen1c0d3112001-04-16 15:46:44 +0000100#define MAX_SCR_COLS BUFSIZ
Eric Andersen3f980402001-04-04 17:31:15 +0000101
102// Misc. non-Ascii keys that report an escape sequence
103#define VI_K_UP 128 // cursor key Up
104#define VI_K_DOWN 129 // cursor key Down
105#define VI_K_RIGHT 130 // Cursor Key Right
106#define VI_K_LEFT 131 // cursor key Left
107#define VI_K_HOME 132 // Cursor Key Home
108#define VI_K_END 133 // Cursor Key End
109#define VI_K_INSERT 134 // Cursor Key Insert
110#define VI_K_PAGEUP 135 // Cursor Key Page Up
111#define VI_K_PAGEDOWN 136 // Cursor Key Page Down
112#define VI_K_FUN1 137 // Function Key F1
113#define VI_K_FUN2 138 // Function Key F2
114#define VI_K_FUN3 139 // Function Key F3
115#define VI_K_FUN4 140 // Function Key F4
116#define VI_K_FUN5 141 // Function Key F5
117#define VI_K_FUN6 142 // Function Key F6
118#define VI_K_FUN7 143 // Function Key F7
119#define VI_K_FUN8 144 // Function Key F8
120#define VI_K_FUN9 145 // Function Key F9
121#define VI_K_FUN10 146 // Function Key F10
122#define VI_K_FUN11 147 // Function Key F11
123#define VI_K_FUN12 148 // Function Key F12
124
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000125/* vt102 typical ESC sequence */
126/* terminal standout start/normal ESC sequence */
127static const char SOs[] = "\033[7m";
128static const char SOn[] = "\033[0m";
129/* terminal bell sequence */
130static const char bell[] = "\007";
131/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
132static const char Ceol[] = "\033[0K";
133static const char Ceos [] = "\033[0J";
134/* Cursor motion arbitrary destination ESC sequence */
135static const char CMrc[] = "\033[%d;%dH";
136/* Cursor motion up and down ESC sequence */
137static const char CMup[] = "\033[A";
138static const char CMdown[] = "\n";
139
140
Eric Andersen3f980402001-04-04 17:31:15 +0000141static const int YANKONLY = FALSE;
142static const int YANKDEL = TRUE;
143static const int FORWARD = 1; // code depends on "1" for array index
144static const int BACK = -1; // code depends on "-1" for array index
145static const int LIMITED = 0; // how much of text[] in char_search
146static const int FULL = 1; // how much of text[] in char_search
147
148static const int S_BEFORE_WS = 1; // used in skip_thing() for moving "dot"
149static const int S_TO_WS = 2; // used in skip_thing() for moving "dot"
150static const int S_OVER_WS = 3; // used in skip_thing() for moving "dot"
151static const int S_END_PUNCT = 4; // used in skip_thing() for moving "dot"
152static const int S_END_ALNUM = 5; // used in skip_thing() for moving "dot"
153
154typedef unsigned char Byte;
155
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000156static int vi_setops;
157#define VI_AUTOINDENT 1
158#define VI_SHOWMATCH 2
159#define VI_IGNORECASE 4
160#define VI_ERR_METHOD 8
161#define autoindent (vi_setops & VI_AUTOINDENT)
162#define showmatch (vi_setops & VI_SHOWMATCH )
163#define ignorecase (vi_setops & VI_IGNORECASE)
164/* indicate error with beep or flash */
165#define err_method (vi_setops & VI_ERR_METHOD)
166
Eric Andersen3f980402001-04-04 17:31:15 +0000167
168static int editing; // >0 while we are editing a file
169static int cmd_mode; // 0=command 1=insert
170static int file_modified; // buffer contents changed
Eric Andersen3f980402001-04-04 17:31:15 +0000171static int fn_start; // index of first cmd line file name
172static int save_argc; // how many file names on cmd line
173static int cmdcnt; // repetition count
174static fd_set rfds; // use select() for small sleeps
175static struct timeval tv; // use select() for small sleeps
Eric Andersen3f980402001-04-04 17:31:15 +0000176static int rows, columns; // the terminal screen is this size
177static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset
Eric Andersen3f980402001-04-04 17:31:15 +0000178static Byte *status_buffer; // mesages to the user
Eric Andersen3f980402001-04-04 17:31:15 +0000179static Byte *cfn; // previous, current, and next file name
180static Byte *text, *end, *textend; // pointers to the user data in memory
181static Byte *screen; // pointer to the virtual screen buffer
182static int screensize; // and its size
183static Byte *screenbegin; // index into text[], of top line on the screen
184static Byte *dot; // where all the action takes place
185static int tabstop;
186static struct termios term_orig, term_vi; // remember what the cooked mode was
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000187static Byte erase_char; // the users erase character
188static Byte last_input_char; // last char read from user
189static Byte last_forward_char; // last char searched for with 'f'
Eric Andersen3f980402001-04-04 17:31:15 +0000190
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000191#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +0000192static int last_row; // where the cursor was last moved to
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000193#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
194#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000195static jmp_buf restart; // catch_sig()
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000196#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000197#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
198static int my_pid;
199#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000200#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000201static int adding2q; // are we currently adding user input to q
202static Byte *last_modifying_cmd; // last modifying cmd for "."
203static Byte *ioq, *ioq_start; // pointer to string for get_one_char to "read"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000204#endif /* CONFIG_FEATURE_VI_DOT_CMD */
205#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
Eric Andersen3f980402001-04-04 17:31:15 +0000206static Byte *modifying_cmds; // cmds that modify text[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000207#endif /* CONFIG_FEATURE_VI_DOT_CMD || CONFIG_FEATURE_VI_YANKMARK */
208#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000209static int vi_readonly, readonly;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000210#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000211#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000212static Byte *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
213static int YDreg, Ureg; // default delete register and orig line for "U"
214static Byte *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
215static Byte *context_start, *context_end;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000216#endif /* CONFIG_FEATURE_VI_YANKMARK */
217#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000218static Byte *last_search_pattern; // last pattern from a '/' or '?' search
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000219#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +0000220
221
222static void edit_file(Byte *); // edit one file
223static void do_cmd(Byte); // execute a command
224static void sync_cursor(Byte *, int *, int *); // synchronize the screen cursor to dot
225static Byte *begin_line(Byte *); // return pointer to cur line B-o-l
226static Byte *end_line(Byte *); // return pointer to cur line E-o-l
Eric Andersen3f980402001-04-04 17:31:15 +0000227static Byte *prev_line(Byte *); // return pointer to prev line B-o-l
228static Byte *next_line(Byte *); // return pointer to next line B-o-l
229static Byte *end_screen(void); // get pointer to last char on screen
230static int count_lines(Byte *, Byte *); // count line from start to stop
231static Byte *find_line(int); // find begining of line #li
232static Byte *move_to_col(Byte *, int); // move "p" to column l
233static int isblnk(Byte); // is the char a blank or tab
234static void dot_left(void); // move dot left- dont leave line
235static void dot_right(void); // move dot right- dont leave line
236static void dot_begin(void); // move dot to B-o-l
237static void dot_end(void); // move dot to E-o-l
238static void dot_next(void); // move dot to next line B-o-l
239static void dot_prev(void); // move dot to prev line B-o-l
240static void dot_scroll(int, int); // move the screen up or down
241static void dot_skip_over_ws(void); // move dot pat WS
242static void dot_delete(void); // delete the char at 'dot'
243static Byte *bound_dot(Byte *); // make sure text[0] <= P < "end"
244static Byte *new_screen(int, int); // malloc virtual screen memory
245static Byte *new_text(int); // malloc memory for text[] buffer
246static Byte *char_insert(Byte *, Byte); // insert the char c at 'p'
247static Byte *stupid_insert(Byte *, Byte); // stupidly insert the char c at 'p'
248static Byte find_range(Byte **, Byte **, Byte); // return pointers for an object
249static int st_test(Byte *, int, int, Byte *); // helper for skip_thing()
250static Byte *skip_thing(Byte *, int, int, int); // skip some object
251static Byte *find_pair(Byte *, Byte); // find matching pair () [] {}
252static Byte *text_hole_delete(Byte *, Byte *); // at "p", delete a 'size' byte hole
253static Byte *text_hole_make(Byte *, int); // at "p", make a 'size' byte hole
254static Byte *yank_delete(Byte *, Byte *, int, int); // yank text[] into register then delete
255static void show_help(void); // display some help info
Eric Andersen3f980402001-04-04 17:31:15 +0000256static void rawmode(void); // set "raw" mode on tty
257static void cookmode(void); // return to "cooked" mode on tty
258static int mysleep(int); // sleep for 'h' 1/100 seconds
259static Byte readit(void); // read (maybe cursor) key from stdin
260static Byte get_one_char(void); // read 1 char from stdin
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000261static int file_size(const Byte *); // what is the byte size of "fn"
Eric Andersen3f980402001-04-04 17:31:15 +0000262static int file_insert(Byte *, Byte *, int);
263static int file_write(Byte *, Byte *, Byte *);
Eric Andersen822c3832001-05-07 17:37:43 +0000264static void place_cursor(int, int, int);
Eric Andersenbff7a602001-11-17 07:15:43 +0000265static void screen_erase(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000266static void clear_to_eol(void);
267static void clear_to_eos(void);
268static void standout_start(void); // send "start reverse video" sequence
269static void standout_end(void); // send "end reverse video" sequence
270static void flash(int); // flash the terminal screen
Eric Andersen3f980402001-04-04 17:31:15 +0000271static void show_status_line(void); // put a message on the bottom line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000272static void psb(const char *, ...); // Print Status Buf
273static void psbs(const char *, ...); // Print Status Buf in standout mode
Eric Andersen3f980402001-04-04 17:31:15 +0000274static void ni(Byte *); // display messages
275static void edit_status(void); // show file status on status line
276static void redraw(int); // force a full screen refresh
Eric Andersen822c3832001-05-07 17:37:43 +0000277static void format_line(Byte*, Byte*, int);
Eric Andersen3f980402001-04-04 17:31:15 +0000278static void refresh(int); // update the terminal from screen[]
279
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000280static void Indicate_Error(void); // use flash or beep to indicate error
281#define indicate_error(c) Indicate_Error()
282
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000283#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000284static Byte *char_search(Byte *, Byte *, int, int); // search for pattern starting at p
285static int mycmp(Byte *, Byte *, int); // string cmp based in "ignorecase"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000286#endif /* CONFIG_FEATURE_VI_SEARCH */
287#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +0000288static void Hit_Return(void);
Eric Andersen1c0d3112001-04-16 15:46:44 +0000289static Byte *get_one_address(Byte *, int *); // get colon addr, if present
290static Byte *get_address(Byte *, int *, int *); // get two colon addrs, if present
Eric Andersen3f980402001-04-04 17:31:15 +0000291static void colon(Byte *); // execute the "colon" mode cmds
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000292#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000293#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000294static void winch_sig(int); // catch window size changes
295static void suspend_sig(int); // catch ctrl-Z
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000296static void catch_sig(int); // catch ctrl-C and alarm time-outs
Eric Andersen3f980402001-04-04 17:31:15 +0000297static void core_sig(int); // catch a core dump signal
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000298#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
299#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000300static void start_new_cmd_q(Byte); // new queue for command
Eric Andersenbff7a602001-11-17 07:15:43 +0000301static void end_cmd_q(void); // stop saving input chars
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000302#else /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000303#define end_cmd_q()
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000304#endif /* CONFIG_FEATURE_VI_DOT_CMD */
305#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000306static void window_size_get(int); // find out what size the window is
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000307#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
308#ifdef CONFIG_FEATURE_VI_SETOPTS
Eric Andersen3f980402001-04-04 17:31:15 +0000309static void showmatching(Byte *); // show the matching pair () [] {}
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000310#endif /* CONFIG_FEATURE_VI_SETOPTS */
311#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
Eric Andersen3f980402001-04-04 17:31:15 +0000312static Byte *string_insert(Byte *, Byte *); // insert the string at 'p'
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000313#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
314#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000315static Byte *text_yank(Byte *, Byte *, int); // save copy of "p" into a register
316static Byte what_reg(void); // what is letter of current YDreg
317static void check_context(Byte); // remember context for '' command
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000318#endif /* CONFIG_FEATURE_VI_YANKMARK */
319#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000320static void crash_dummy();
321static void crash_test();
322static int crashme = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000323#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000324
325
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000326static void write1(const char *out)
327{
328 fputs(out, stdout);
329}
330
Eric Andersen3f980402001-04-04 17:31:15 +0000331extern int vi_main(int argc, char **argv)
Eric Andersen3f980402001-04-04 17:31:15 +0000332{
Eric Andersend402edf2001-04-04 19:29:48 +0000333 int c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000334 RESERVE_CONFIG_BUFFER(STATUS_BUFFER, 200);
Eric Andersen3f980402001-04-04 17:31:15 +0000335
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000336#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000337 int i;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000338#endif /* CONFIG_FEATURE_VI_YANKMARK */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000339#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
340 my_pid = getpid();
341#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000342#ifdef CONFIG_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000343 (void) srand((long) my_pid);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000344#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000345
346 status_buffer = STATUS_BUFFER;
Eric Andersen0ef24c62005-07-18 10:32:59 +0000347 *status_buffer = '\0'; // clear status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000348
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000349#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000350 vi_readonly = readonly = FALSE;
Eric Andersen3f980402001-04-04 17:31:15 +0000351 if (strncmp(argv[0], "view", 4) == 0) {
352 readonly = TRUE;
Eric Andersen822c3832001-05-07 17:37:43 +0000353 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000354 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000355#endif /* CONFIG_FEATURE_VI_READONLY */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000356 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000357#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000358 for (i = 0; i < 28; i++) {
359 reg[i] = 0;
360 } // init the yank regs
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000361#endif /* CONFIG_FEATURE_VI_YANKMARK */
Tim Riker86c76a92002-04-26 07:41:22 +0000362#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
Eric Andersen3f980402001-04-04 17:31:15 +0000363 modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000364#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000365
366 // 1- process $HOME/.exrc file
367 // 2- process EXINIT variable from environment
368 // 3- process command line args
369 while ((c = getopt(argc, argv, "hCR")) != -1) {
370 switch (c) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000371#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000372 case 'C':
373 crashme = 1;
374 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000375#endif /* CONFIG_FEATURE_VI_CRASHME */
376#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +0000377 case 'R': // Read-only flag
378 readonly = TRUE;
Eric Andersen90fb65f2004-03-31 11:12:51 +0000379 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000380 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000381#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen822c3832001-05-07 17:37:43 +0000382 //case 'r': // recover flag- ignore- we don't use tmp file
383 //case 'x': // encryption flag- ignore
384 //case 'c': // execute command first
385 //case 'h': // help -- just use default
Eric Andersen3f980402001-04-04 17:31:15 +0000386 default:
387 show_help();
Eric Andersendd8500b2001-07-02 18:06:14 +0000388 return 1;
Eric Andersen3f980402001-04-04 17:31:15 +0000389 }
390 }
391
392 // The argv array can be used by the ":next" and ":rewind" commands
393 // save optind.
394 fn_start = optind; // remember first file name for :next and :rew
395 save_argc = argc;
396
397 //----- This is the main file handling loop --------------
398 if (optind >= argc) {
399 editing = 1; // 0= exit, 1= one file, 2= multiple files
400 edit_file(0);
401 } else {
402 for (; optind < argc; optind++) {
403 editing = 1; // 0=exit, 1=one file, 2+ =many files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000404 free(cfn);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000405 cfn = (Byte *) bb_xstrdup(argv[optind]);
Eric Andersen3f980402001-04-04 17:31:15 +0000406 edit_file(cfn);
407 }
408 }
409 //-----------------------------------------------------------
410
411 return (0);
412}
413
Eric Andersen8efe9672003-09-15 08:33:45 +0000414#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
415//----- See what the window size currently is --------------------
416static inline void window_size_get(int fd)
417{
418 get_terminal_width_height(fd, &columns, &rows);
419}
420#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
421
Eric Andersen3f980402001-04-04 17:31:15 +0000422static void edit_file(Byte * fn)
423{
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000424 Byte c;
Eric Andersen1c0d3112001-04-16 15:46:44 +0000425 int cnt, size, ch;
Eric Andersen3f980402001-04-04 17:31:15 +0000426
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000427#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000428 int sig;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000429#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
430#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000431 static Byte *cur_line;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000432#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000433
434 rawmode();
435 rows = 24;
436 columns = 80;
Eric Andersen822c3832001-05-07 17:37:43 +0000437 ch= -1;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000438#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000439 window_size_get(0);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000440#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
Eric Andersen3f980402001-04-04 17:31:15 +0000441 new_screen(rows, columns); // get memory for virtual screen
442
443 cnt = file_size(fn); // file size
444 size = 2 * cnt; // 200% of file size
445 new_text(size); // get a text[] buffer
446 screenbegin = dot = end = text;
447 if (fn != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000448 ch= file_insert(fn, text, cnt);
449 }
450 if (ch < 1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000451 (void) char_insert(text, '\n'); // start empty buf with dummy line
452 }
453 file_modified = FALSE;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000454#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000455 YDreg = 26; // default Yank/Delete reg
456 Ureg = 27; // hold orig line for "U" cmd
457 for (cnt = 0; cnt < 28; cnt++) {
458 mark[cnt] = 0;
459 } // init the marks
460 mark[26] = mark[27] = text; // init "previous context"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000461#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000462
Eric Andersen3f980402001-04-04 17:31:15 +0000463 last_forward_char = last_input_char = '\0';
464 crow = 0;
465 ccol = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000466
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000467#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000468 catch_sig(0);
469 core_sig(0);
Eric Andersen3f980402001-04-04 17:31:15 +0000470 signal(SIGWINCH, winch_sig);
471 signal(SIGTSTP, suspend_sig);
472 sig = setjmp(restart);
473 if (sig != 0) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000474 const char *msg = "";
475
Eric Andersen3f980402001-04-04 17:31:15 +0000476 if (sig == SIGWINCH)
477 msg = "(window resize)";
478 if (sig == SIGHUP)
479 msg = "(hangup)";
480 if (sig == SIGINT)
481 msg = "(interrupt)";
482 if (sig == SIGTERM)
483 msg = "(terminate)";
484 if (sig == SIGBUS)
485 msg = "(bus error)";
486 if (sig == SIGSEGV)
487 msg = "(I tried to touch invalid memory)";
488 if (sig == SIGALRM)
489 msg = "(alarm)";
490
491 psbs("-- caught signal %d %s--", sig, msg);
Eric Andersen1c0d3112001-04-16 15:46:44 +0000492 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000493 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000494#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +0000495
496 editing = 1;
497 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
498 cmdcnt = 0;
499 tabstop = 8;
500 offset = 0; // no horizontal offset
501 c = '\0';
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000502#ifdef CONFIG_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000503 free(last_modifying_cmd);
504 free(ioq_start);
Eric Andersen3f980402001-04-04 17:31:15 +0000505 ioq = ioq_start = last_modifying_cmd = 0;
506 adding2q = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000507#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen822c3832001-05-07 17:37:43 +0000508 redraw(FALSE); // dont force every col re-draw
Eric Andersen0ef24c62005-07-18 10:32:59 +0000509 edit_status();
Eric Andersen3f980402001-04-04 17:31:15 +0000510 show_status_line();
511
512 //------This is the main Vi cmd handling loop -----------------------
513 while (editing > 0) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000514#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000515 if (crashme > 0) {
516 if ((end - text) > 1) {
517 crash_dummy(); // generate a random command
518 } else {
519 crashme = 0;
520 dot =
521 string_insert(text, (Byte *) "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
522 refresh(FALSE);
523 }
524 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000525#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000526 last_input_char = c = get_one_char(); // get a cmd from user
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000527#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000528 // save a copy of the current line- for the 'U" command
529 if (begin_line(dot) != cur_line) {
530 cur_line = begin_line(dot);
531 text_yank(begin_line(dot), end_line(dot), Ureg);
532 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000533#endif /* CONFIG_FEATURE_VI_YANKMARK */
534#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000535 // These are commands that change text[].
536 // Remember the input for the "." command
537 if (!adding2q && ioq_start == 0
538 && strchr((char *) modifying_cmds, c) != NULL) {
539 start_new_cmd_q(c);
540 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000541#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000542 do_cmd(c); // execute the user command
543 //
544 // poll to see if there is input already waiting. if we are
545 // not able to display output fast enough to keep up, skip
546 // the display update until we catch up with input.
547 if (mysleep(0) == 0) {
548 // no input pending- so update output
549 refresh(FALSE);
550 show_status_line();
551 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000552#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000553 if (crashme > 0)
554 crash_test(); // test editor variables
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000555#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000556 }
557 //-------------------------------------------------------------------
558
Eric Andersen822c3832001-05-07 17:37:43 +0000559 place_cursor(rows, 0, FALSE); // go to bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +0000560 clear_to_eol(); // Erase to end of line
561 cookmode();
562}
563
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000564//----- The Colon commands -------------------------------------
565#ifdef CONFIG_FEATURE_VI_COLON
566static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present
567{
568 int st;
569 Byte *q;
570
571#ifdef CONFIG_FEATURE_VI_YANKMARK
572 Byte c;
573#endif /* CONFIG_FEATURE_VI_YANKMARK */
574#ifdef CONFIG_FEATURE_VI_SEARCH
575 Byte *pat, buf[BUFSIZ];
576#endif /* CONFIG_FEATURE_VI_SEARCH */
577
578 *addr = -1; // assume no addr
579 if (*p == '.') { // the current line
580 p++;
581 q = begin_line(dot);
582 *addr = count_lines(text, q);
583#ifdef CONFIG_FEATURE_VI_YANKMARK
584 } else if (*p == '\'') { // is this a mark addr
585 p++;
586 c = tolower(*p);
587 p++;
588 if (c >= 'a' && c <= 'z') {
589 // we have a mark
590 c = c - 'a';
591 q = mark[(int) c];
592 if (q != NULL) { // is mark valid
593 *addr = count_lines(text, q); // count lines
594 }
595 }
596#endif /* CONFIG_FEATURE_VI_YANKMARK */
597#ifdef CONFIG_FEATURE_VI_SEARCH
598 } else if (*p == '/') { // a search pattern
599 q = buf;
600 for (p++; *p; p++) {
601 if (*p == '/')
602 break;
603 *q++ = *p;
604 *q = '\0';
605 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000606 pat = (Byte *) bb_xstrdup((char *) buf); // save copy of pattern
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000607 if (*p == '/')
608 p++;
609 q = char_search(dot, pat, FORWARD, FULL);
610 if (q != NULL) {
611 *addr = count_lines(text, q);
612 }
613 free(pat);
614#endif /* CONFIG_FEATURE_VI_SEARCH */
615 } else if (*p == '$') { // the last line in file
616 p++;
617 q = begin_line(end - 1);
618 *addr = count_lines(text, q);
619 } else if (isdigit(*p)) { // specific line number
620 sscanf((char *) p, "%d%n", addr, &st);
621 p += st;
622 } else { // I don't reconise this
623 // unrecognised address- assume -1
624 *addr = -1;
625 }
626 return (p);
627}
628
629static Byte *get_address(Byte *p, int *b, int *e) // get two colon addrs, if present
630{
631 //----- get the address' i.e., 1,3 'a,'b -----
632 // get FIRST addr, if present
633 while (isblnk(*p))
634 p++; // skip over leading spaces
635 if (*p == '%') { // alias for 1,$
636 p++;
637 *b = 1;
638 *e = count_lines(text, end-1);
639 goto ga0;
640 }
641 p = get_one_address(p, b);
642 while (isblnk(*p))
643 p++;
Eric Andersenaff114c2004-04-14 17:51:38 +0000644 if (*p == ',') { // is there a address separator
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000645 p++;
646 while (isblnk(*p))
647 p++;
648 // get SECOND addr, if present
649 p = get_one_address(p, e);
650 }
651ga0:
652 while (isblnk(*p))
653 p++; // skip over trailing spaces
654 return (p);
655}
656
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000657#ifdef CONFIG_FEATURE_VI_SETOPTS
658static void setops(const Byte *args, const char *opname, int flg_no,
659 const char *short_opname, int opt)
660{
661 const char *a = (char *) args + flg_no;
662 int l = strlen(opname) - 1; /* opname have + ' ' */
663
664 if (strncasecmp(a, opname, l) == 0 ||
665 strncasecmp(a, short_opname, 2) == 0) {
666 if(flg_no)
667 vi_setops &= ~opt;
668 else
669 vi_setops |= opt;
670 }
671}
672#endif
673
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000674static void colon(Byte * buf)
675{
676 Byte c, *orig_buf, *buf1, *q, *r;
677 Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
678 int i, l, li, ch, st, b, e;
Eric Andersena9eb33d2004-08-19 19:15:06 +0000679 int useforce = FALSE, forced = FALSE;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000680 struct stat st_buf;
681
682 // :3154 // if (-e line 3154) goto it else stay put
683 // :4,33w! foo // write a portion of buffer to file "foo"
684 // :w // write all of buffer to current file
685 // :q // quit
686 // :q! // quit- dont care about modified file
687 // :'a,'z!sort -u // filter block through sort
688 // :'f // goto mark "f"
689 // :'fl // list literal the mark "f" line
690 // :.r bar // read file "bar" into buffer before dot
691 // :/123/,/abc/d // delete lines from "123" line to "abc" line
692 // :/xyz/ // goto the "xyz" line
693 // :s/find/replace/ // substitute pattern "find" with "replace"
694 // :!<cmd> // run <cmd> then return
695 //
Eric Andersen165e8cb2004-07-20 06:44:46 +0000696
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000697 if (strlen((char *) buf) <= 0)
698 goto vc1;
699 if (*buf == ':')
700 buf++; // move past the ':'
701
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000702 li = st = ch = i = 0;
703 b = e = -1;
704 q = text; // assume 1,$ for the range
705 r = end - 1;
706 li = count_lines(text, end - 1);
707 fn = cfn; // default to current file
708 memset(cmd, '\0', BUFSIZ); // clear cmd[]
709 memset(args, '\0', BUFSIZ); // clear args[]
710
711 // look for optional address(es) :. :1 :1,9 :'q,'a :%
712 buf = get_address(buf, &b, &e);
713
714 // remember orig command line
715 orig_buf = buf;
716
717 // get the COMMAND into cmd[]
718 buf1 = cmd;
719 while (*buf != '\0') {
720 if (isspace(*buf))
721 break;
722 *buf1++ = *buf++;
723 }
724 // get any ARGuments
725 while (isblnk(*buf))
726 buf++;
727 strcpy((char *) args, (char *) buf);
728 buf1 = last_char_is((char *)cmd, '!');
729 if (buf1) {
730 useforce = TRUE;
731 *buf1 = '\0'; // get rid of !
732 }
733 if (b >= 0) {
734 // if there is only one addr, then the addr
735 // is the line number of the single line the
736 // user wants. So, reset the end
737 // pointer to point at end of the "b" line
738 q = find_line(b); // what line is #b
739 r = end_line(q);
740 li = 1;
741 }
742 if (e >= 0) {
743 // we were given two addrs. change the
744 // end pointer to the addr given by user.
745 r = find_line(e); // what line is #e
746 r = end_line(r);
747 li = e - b + 1;
748 }
749 // ------------ now look for the command ------------
750 i = strlen((char *) cmd);
751 if (i == 0) { // :123CR goto line #123
752 if (b >= 0) {
753 dot = find_line(b); // what line is #b
754 dot_skip_over_ws();
755 }
756 } else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd
757 // :!ls run the <cmd>
758 (void) alarm(0); // wait for input- no alarms
759 place_cursor(rows - 1, 0, FALSE); // go to Status line
760 clear_to_eol(); // clear the line
761 cookmode();
762 system(orig_buf+1); // run the cmd
763 rawmode();
764 Hit_Return(); // let user see results
765 (void) alarm(3); // done waiting for input
766 } else if (strncmp((char *) cmd, "=", i) == 0) { // where is the address
767 if (b < 0) { // no addr given- use defaults
768 b = e = count_lines(text, dot);
769 }
770 psb("%d", b);
771 } else if (strncasecmp((char *) cmd, "delete", i) == 0) { // delete lines
772 if (b < 0) { // no addr given- use defaults
773 q = begin_line(dot); // assume .,. for the range
774 r = end_line(dot);
775 }
776 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
777 dot_skip_over_ws();
778 } else if (strncasecmp((char *) cmd, "edit", i) == 0) { // Edit a file
779 int sr;
780 sr= 0;
781 // don't edit, if the current file has been modified
782 if (file_modified && ! useforce) {
783 psbs("No write since last change (:edit! overrides)");
784 goto vc1;
785 }
786 if (strlen(args) > 0) {
787 // the user supplied a file name
788 fn= args;
789 } else if (cfn != 0 && strlen(cfn) > 0) {
790 // no user supplied name- use the current filename
791 fn= cfn;
792 goto vc5;
793 } else {
794 // no user file name, no current name- punt
795 psbs("No current filename");
796 goto vc1;
797 }
798
799 // see if file exists- if not, its just a new file request
800 if ((sr=stat((char*)fn, &st_buf)) < 0) {
801 // This is just a request for a new file creation.
802 // The file_insert below will fail but we get
803 // an empty buffer with a file name. Then the "write"
804 // command can do the create.
805 } else {
806 if ((st_buf.st_mode & (S_IFREG)) == 0) {
807 // This is not a regular file
808 psbs("\"%s\" is not a regular file", fn);
809 goto vc1;
810 }
811 if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
812 // dont have any read permissions
813 psbs("\"%s\" is not readable", fn);
814 goto vc1;
815 }
816 }
817
818 // There is a read-able regular file
819 // make this the current file
Manuel Novoa III cad53642003-03-19 09:13:01 +0000820 q = (Byte *) bb_xstrdup((char *) fn); // save the cfn
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000821 free(cfn); // free the old name
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000822 cfn = q; // remember new cfn
823
824 vc5:
825 // delete all the contents of text[]
826 new_text(2 * file_size(fn));
827 screenbegin = dot = end = text;
828
829 // insert new file
830 ch = file_insert(fn, text, file_size(fn));
831
832 if (ch < 1) {
833 // start empty buf with dummy line
834 (void) char_insert(text, '\n');
835 ch= 1;
836 }
837 file_modified = FALSE;
838#ifdef CONFIG_FEATURE_VI_YANKMARK
839 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
840 free(reg[Ureg]); // free orig line reg- for 'U'
841 reg[Ureg]= 0;
842 }
843 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
844 free(reg[YDreg]); // free default yank/delete register
845 reg[YDreg]= 0;
846 }
847 for (li = 0; li < 28; li++) {
848 mark[li] = 0;
849 } // init the marks
850#endif /* CONFIG_FEATURE_VI_YANKMARK */
851 // how many lines in text[]?
852 li = count_lines(text, end - 1);
853 psb("\"%s\"%s"
854#ifdef CONFIG_FEATURE_VI_READONLY
855 "%s"
856#endif /* CONFIG_FEATURE_VI_READONLY */
857 " %dL, %dC", cfn,
858 (sr < 0 ? " [New file]" : ""),
859#ifdef CONFIG_FEATURE_VI_READONLY
860 ((vi_readonly || readonly) ? " [Read only]" : ""),
861#endif /* CONFIG_FEATURE_VI_READONLY */
862 li, ch);
863 } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this
864 if (b != -1 || e != -1) {
865 ni((Byte *) "No address allowed on this command");
866 goto vc1;
867 }
868 if (strlen((char *) args) > 0) {
869 // user wants a new filename
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000870 free(cfn);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000871 cfn = (Byte *) bb_xstrdup((char *) args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000872 } else {
873 // user wants file status info
874 edit_status();
Eric Andersen0ef24c62005-07-18 10:32:59 +0000875 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000876 }
877 } else if (strncasecmp((char *) cmd, "features", i) == 0) { // what features are available
878 // print out values of all features
879 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
880 clear_to_eol(); // clear the line
881 cookmode();
882 show_help();
883 rawmode();
884 Hit_Return();
885 } else if (strncasecmp((char *) cmd, "list", i) == 0) { // literal print line
886 if (b < 0) { // no addr given- use defaults
887 q = begin_line(dot); // assume .,. for the range
888 r = end_line(dot);
889 }
890 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
891 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000892 puts("\r");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000893 for (; q <= r; q++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000894 int c_is_no_print;
895
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000896 c = *q;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000897 c_is_no_print = c > 127 && !Isprint(c);
898 if (c_is_no_print) {
899 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000900 standout_start();
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000901 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000902 if (c == '\n') {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000903 write1("$\r");
904 } else if (c < ' ' || c == 127) {
905 putchar('^');
906 if(c == 127)
907 c = '?';
908 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000909 c += '@';
910 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000911 putchar(c);
912 if (c_is_no_print)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000913 standout_end();
914 }
915#ifdef CONFIG_FEATURE_VI_SET
916 vc2:
917#endif /* CONFIG_FEATURE_VI_SET */
918 Hit_Return();
919 } else if ((strncasecmp((char *) cmd, "quit", i) == 0) || // Quit
920 (strncasecmp((char *) cmd, "next", i) == 0)) { // edit next file
921 if (useforce) {
922 // force end of argv list
923 if (*cmd == 'q') {
924 optind = save_argc;
925 }
926 editing = 0;
927 goto vc1;
928 }
929 // don't exit if the file been modified
930 if (file_modified) {
931 psbs("No write since last change (:%s! overrides)",
932 (*cmd == 'q' ? "quit" : "next"));
933 goto vc1;
934 }
935 // are there other file to edit
936 if (*cmd == 'q' && optind < save_argc - 1) {
937 psbs("%d more file to edit", (save_argc - optind - 1));
938 goto vc1;
939 }
940 if (*cmd == 'n' && optind >= save_argc - 1) {
941 psbs("No more files to edit");
942 goto vc1;
943 }
944 editing = 0;
945 } else if (strncasecmp((char *) cmd, "read", i) == 0) { // read file into text[]
946 fn = args;
947 if (strlen((char *) fn) <= 0) {
948 psbs("No filename given");
949 goto vc1;
950 }
951 if (b < 0) { // no addr given- use defaults
952 q = begin_line(dot); // assume "dot"
953 }
954 // read after current line- unless user said ":0r foo"
955 if (b != 0)
956 q = next_line(q);
957#ifdef CONFIG_FEATURE_VI_READONLY
958 l= readonly; // remember current files' status
959#endif
960 ch = file_insert(fn, q, file_size(fn));
961#ifdef CONFIG_FEATURE_VI_READONLY
962 readonly= l;
963#endif
964 if (ch < 0)
965 goto vc1; // nothing was inserted
966 // how many lines in text[]?
967 li = count_lines(q, q + ch - 1);
968 psb("\"%s\""
969#ifdef CONFIG_FEATURE_VI_READONLY
970 "%s"
971#endif /* CONFIG_FEATURE_VI_READONLY */
972 " %dL, %dC", fn,
973#ifdef CONFIG_FEATURE_VI_READONLY
974 ((vi_readonly || readonly) ? " [Read only]" : ""),
975#endif /* CONFIG_FEATURE_VI_READONLY */
976 li, ch);
977 if (ch > 0) {
978 // if the insert is before "dot" then we need to update
979 if (q <= dot)
980 dot += ch;
981 file_modified = TRUE;
982 }
983 } else if (strncasecmp((char *) cmd, "rewind", i) == 0) { // rewind cmd line args
984 if (file_modified && ! useforce) {
985 psbs("No write since last change (:rewind! overrides)");
986 } else {
987 // reset the filenames to edit
988 optind = fn_start - 1;
989 editing = 0;
990 }
991#ifdef CONFIG_FEATURE_VI_SET
992 } else if (strncasecmp((char *) cmd, "set", i) == 0) { // set or clear features
993 i = 0; // offset into args
994 if (strlen((char *) args) == 0) {
995 // print out values of all options
996 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
997 clear_to_eol(); // clear the line
998 printf("----------------------------------------\r\n");
999#ifdef CONFIG_FEATURE_VI_SETOPTS
1000 if (!autoindent)
1001 printf("no");
1002 printf("autoindent ");
1003 if (!err_method)
1004 printf("no");
1005 printf("flash ");
1006 if (!ignorecase)
1007 printf("no");
1008 printf("ignorecase ");
1009 if (!showmatch)
1010 printf("no");
1011 printf("showmatch ");
1012 printf("tabstop=%d ", tabstop);
1013#endif /* CONFIG_FEATURE_VI_SETOPTS */
1014 printf("\r\n");
1015 goto vc2;
1016 }
1017 if (strncasecmp((char *) args, "no", 2) == 0)
1018 i = 2; // ":set noautoindent"
1019#ifdef CONFIG_FEATURE_VI_SETOPTS
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001020 setops(args, "autoindent ", i, "ai", VI_AUTOINDENT);
1021 setops(args, "flash ", i, "fl", VI_ERR_METHOD);
1022 setops(args, "ignorecase ", i, "ic", VI_IGNORECASE);
1023 setops(args, "showmatch ", i, "ic", VI_SHOWMATCH);
1024 if (strncasecmp((char *) args + i, "tabstop=%d ", 7) == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001025 sscanf(strchr((char *) args + i, '='), "=%d", &ch);
1026 if (ch > 0 && ch < columns - 1)
1027 tabstop = ch;
1028 }
1029#endif /* CONFIG_FEATURE_VI_SETOPTS */
1030#endif /* CONFIG_FEATURE_VI_SET */
1031#ifdef CONFIG_FEATURE_VI_SEARCH
1032 } else if (strncasecmp((char *) cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
1033 Byte *ls, *F, *R;
1034 int gflag;
1035
1036 // F points to the "find" pattern
1037 // R points to the "replace" pattern
1038 // replace the cmd line delimiters "/" with NULLs
1039 gflag = 0; // global replace flag
1040 c = orig_buf[1]; // what is the delimiter
1041 F = orig_buf + 2; // start of "find"
1042 R = (Byte *) strchr((char *) F, c); // middle delimiter
1043 if (!R) goto colon_s_fail;
1044 *R++ = '\0'; // terminate "find"
1045 buf1 = (Byte *) strchr((char *) R, c);
1046 if (!buf1) goto colon_s_fail;
1047 *buf1++ = '\0'; // terminate "replace"
1048 if (*buf1 == 'g') { // :s/foo/bar/g
1049 buf1++;
1050 gflag++; // turn on gflag
1051 }
1052 q = begin_line(q);
1053 if (b < 0) { // maybe :s/foo/bar/
1054 q = begin_line(dot); // start with cur line
1055 b = count_lines(text, q); // cur line number
1056 }
1057 if (e < 0)
1058 e = b; // maybe :.s/foo/bar/
1059 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
1060 ls = q; // orig line start
1061 vc4:
1062 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
1063 if (buf1 != NULL) {
1064 // we found the "find" pattern- delete it
1065 (void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
1066 // inset the "replace" patern
1067 (void) string_insert(buf1, R); // insert the string
1068 // check for "global" :s/foo/bar/g
1069 if (gflag == 1) {
1070 if ((buf1 + strlen((char *) R)) < end_line(ls)) {
1071 q = buf1 + strlen((char *) R);
1072 goto vc4; // don't let q move past cur line
1073 }
1074 }
1075 }
1076 q = next_line(ls);
1077 }
1078#endif /* CONFIG_FEATURE_VI_SEARCH */
1079 } else if (strncasecmp((char *) cmd, "version", i) == 0) { // show software version
1080 psb("%s", vi_Version);
1081 } else if ((strncasecmp((char *) cmd, "write", i) == 0) || // write text to file
1082 (strncasecmp((char *) cmd, "wq", i) == 0) ||
1083 (strncasecmp((char *) cmd, "x", i) == 0)) {
1084 // is there a file name to write to?
1085 if (strlen((char *) args) > 0) {
1086 fn = args;
1087 }
1088#ifdef CONFIG_FEATURE_VI_READONLY
1089 if ((vi_readonly || readonly) && ! useforce) {
1090 psbs("\"%s\" File is read only", fn);
1091 goto vc3;
1092 }
1093#endif /* CONFIG_FEATURE_VI_READONLY */
1094 // how many lines in text[]?
1095 li = count_lines(q, r);
1096 ch = r - q + 1;
1097 // see if file exists- if not, its just a new file request
1098 if (useforce) {
1099 // if "fn" is not write-able, chmod u+w
1100 // sprintf(syscmd, "chmod u+w %s", fn);
1101 // system(syscmd);
1102 forced = TRUE;
1103 }
1104 l = file_write(fn, q, r);
1105 if (useforce && forced) {
1106 // chmod u-w
1107 // sprintf(syscmd, "chmod u-w %s", fn);
1108 // system(syscmd);
1109 forced = FALSE;
1110 }
1111 psb("\"%s\" %dL, %dC", fn, li, l);
1112 if (q == text && r == end - 1 && l == ch)
1113 file_modified = FALSE;
1114 if ((cmd[0] == 'x' || cmd[1] == 'q') && l == ch) {
1115 editing = 0;
1116 }
1117#ifdef CONFIG_FEATURE_VI_READONLY
1118 vc3:;
1119#endif /* CONFIG_FEATURE_VI_READONLY */
1120#ifdef CONFIG_FEATURE_VI_YANKMARK
1121 } else if (strncasecmp((char *) cmd, "yank", i) == 0) { // yank lines
1122 if (b < 0) { // no addr given- use defaults
1123 q = begin_line(dot); // assume .,. for the range
1124 r = end_line(dot);
1125 }
1126 text_yank(q, r, YDreg);
1127 li = count_lines(q, r);
1128 psb("Yank %d lines (%d chars) into [%c]",
1129 li, strlen((char *) reg[YDreg]), what_reg());
1130#endif /* CONFIG_FEATURE_VI_YANKMARK */
1131 } else {
1132 // cmd unknown
1133 ni((Byte *) cmd);
1134 }
1135 vc1:
1136 dot = bound_dot(dot); // make sure "dot" is valid
1137 return;
1138#ifdef CONFIG_FEATURE_VI_SEARCH
1139colon_s_fail:
1140 psb(":s expression missing delimiters");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001141#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001142}
1143
1144static void Hit_Return(void)
1145{
1146 char c;
1147
1148 standout_start(); // start reverse video
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001149 write1("[Hit return to continue]");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001150 standout_end(); // end reverse video
1151 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
1152 ;
1153 redraw(TRUE); // force redraw all
1154}
1155#endif /* CONFIG_FEATURE_VI_COLON */
1156
1157//----- Synchronize the cursor to Dot --------------------------
1158static void sync_cursor(Byte * d, int *row, int *col)
1159{
1160 Byte *beg_cur, *end_cur; // begin and end of "d" line
1161 Byte *beg_scr, *end_scr; // begin and end of screen
1162 Byte *tp;
1163 int cnt, ro, co;
1164
1165 beg_cur = begin_line(d); // first char of cur line
1166 end_cur = end_line(d); // last char of cur line
1167
1168 beg_scr = end_scr = screenbegin; // first char of screen
1169 end_scr = end_screen(); // last char of screen
1170
1171 if (beg_cur < screenbegin) {
1172 // "d" is before top line on screen
1173 // how many lines do we have to move
1174 cnt = count_lines(beg_cur, screenbegin);
1175 sc1:
1176 screenbegin = beg_cur;
1177 if (cnt > (rows - 1) / 2) {
1178 // we moved too many lines. put "dot" in middle of screen
1179 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1180 screenbegin = prev_line(screenbegin);
1181 }
1182 }
1183 } else if (beg_cur > end_scr) {
1184 // "d" is after bottom line on screen
1185 // how many lines do we have to move
1186 cnt = count_lines(end_scr, beg_cur);
1187 if (cnt > (rows - 1) / 2)
1188 goto sc1; // too many lines
1189 for (ro = 0; ro < cnt - 1; ro++) {
1190 // move screen begin the same amount
1191 screenbegin = next_line(screenbegin);
1192 // now, move the end of screen
1193 end_scr = next_line(end_scr);
1194 end_scr = end_line(end_scr);
1195 }
1196 }
1197 // "d" is on screen- find out which row
1198 tp = screenbegin;
1199 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1200 if (tp == beg_cur)
1201 break;
1202 tp = next_line(tp);
1203 }
1204
1205 // find out what col "d" is on
1206 co = 0;
1207 do { // drive "co" to correct column
1208 if (*tp == '\n' || *tp == '\0')
1209 break;
1210 if (*tp == '\t') {
1211 // 7 - (co % 8 )
1212 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001213 } else if (*tp < ' ' || *tp == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001214 co++; // display as ^X, use 2 columns
1215 }
1216 } while (tp++ < d && ++co);
1217
1218 // "co" is the column where "dot" is.
1219 // The screen has "columns" columns.
1220 // The currently displayed columns are 0+offset -- columns+ofset
1221 // |-------------------------------------------------------------|
1222 // ^ ^ ^
1223 // offset | |------- columns ----------------|
1224 //
1225 // If "co" is already in this range then we do not have to adjust offset
1226 // but, we do have to subtract the "offset" bias from "co".
1227 // If "co" is outside this range then we have to change "offset".
1228 // If the first char of a line is a tab the cursor will try to stay
1229 // in column 7, but we have to set offset to 0.
1230
1231 if (co < 0 + offset) {
1232 offset = co;
1233 }
1234 if (co >= columns + offset) {
1235 offset = co - columns + 1;
1236 }
1237 // if the first char of the line is a tab, and "dot" is sitting on it
1238 // force offset to 0.
1239 if (d == beg_cur && *d == '\t') {
1240 offset = 0;
1241 }
1242 co -= offset;
1243
1244 *row = ro;
1245 *col = co;
1246}
1247
1248//----- Text Movement Routines ---------------------------------
1249static Byte *begin_line(Byte * p) // return pointer to first char cur line
1250{
1251 while (p > text && p[-1] != '\n')
1252 p--; // go to cur line B-o-l
1253 return (p);
1254}
1255
1256static Byte *end_line(Byte * p) // return pointer to NL of cur line line
1257{
1258 while (p < end - 1 && *p != '\n')
1259 p++; // go to cur line E-o-l
1260 return (p);
1261}
1262
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001263static inline Byte *dollar_line(Byte * p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001264{
1265 while (p < end - 1 && *p != '\n')
1266 p++; // go to cur line E-o-l
1267 // Try to stay off of the Newline
1268 if (*p == '\n' && (p - begin_line(p)) > 0)
1269 p--;
1270 return (p);
1271}
1272
1273static Byte *prev_line(Byte * p) // return pointer first char prev line
1274{
1275 p = begin_line(p); // goto begining of cur line
1276 if (p[-1] == '\n' && p > text)
1277 p--; // step to prev line
1278 p = begin_line(p); // goto begining of prev line
1279 return (p);
1280}
1281
1282static Byte *next_line(Byte * p) // return pointer first char next line
1283{
1284 p = end_line(p);
1285 if (*p == '\n' && p < end - 1)
1286 p++; // step to next line
1287 return (p);
1288}
1289
1290//----- Text Information Routines ------------------------------
1291static Byte *end_screen(void)
1292{
1293 Byte *q;
1294 int cnt;
1295
1296 // find new bottom line
1297 q = screenbegin;
1298 for (cnt = 0; cnt < rows - 2; cnt++)
1299 q = next_line(q);
1300 q = end_line(q);
1301 return (q);
1302}
1303
1304static int count_lines(Byte * start, Byte * stop) // count line from start to stop
1305{
1306 Byte *q;
1307 int cnt;
1308
1309 if (stop < start) { // start and stop are backwards- reverse them
1310 q = start;
1311 start = stop;
1312 stop = q;
1313 }
1314 cnt = 0;
1315 stop = end_line(stop); // get to end of this line
1316 for (q = start; q <= stop && q <= end - 1; q++) {
1317 if (*q == '\n')
1318 cnt++;
1319 }
1320 return (cnt);
1321}
1322
1323static Byte *find_line(int li) // find begining of line #li
1324{
1325 Byte *q;
1326
1327 for (q = text; li > 1; li--) {
1328 q = next_line(q);
1329 }
1330 return (q);
1331}
1332
1333//----- Dot Movement Routines ----------------------------------
1334static void dot_left(void)
1335{
1336 if (dot > text && dot[-1] != '\n')
1337 dot--;
Eric Andersen0ef24c62005-07-18 10:32:59 +00001338 edit_status(); // show current file status
1339 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001340}
1341
1342static void dot_right(void)
1343{
1344 if (dot < end - 1 && *dot != '\n')
1345 dot++;
Eric Andersen0ef24c62005-07-18 10:32:59 +00001346 edit_status(); // show current file status
1347 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001348}
1349
1350static void dot_begin(void)
1351{
1352 dot = begin_line(dot); // return pointer to first char cur line
Eric Andersen0ef24c62005-07-18 10:32:59 +00001353 edit_status(); // show current file status
1354 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001355}
1356
1357static void dot_end(void)
1358{
1359 dot = end_line(dot); // return pointer to last char cur line
Eric Andersen0ef24c62005-07-18 10:32:59 +00001360 edit_status(); // show current file status
1361 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001362}
1363
1364static Byte *move_to_col(Byte * p, int l)
1365{
1366 int co;
1367
1368 p = begin_line(p);
1369 co = 0;
1370 do {
1371 if (*p == '\n' || *p == '\0')
1372 break;
1373 if (*p == '\t') {
1374 // 7 - (co % 8 )
1375 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001376 } else if (*p < ' ' || *p == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001377 co++; // display as ^X, use 2 columns
1378 }
1379 } while (++co <= l && p++ < end);
1380 return (p);
1381}
1382
1383static void dot_next(void)
1384{
1385 dot = next_line(dot);
Eric Andersen0ef24c62005-07-18 10:32:59 +00001386 edit_status(); // show current file status
1387 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001388}
1389
1390static void dot_prev(void)
1391{
1392 dot = prev_line(dot);
Eric Andersen0ef24c62005-07-18 10:32:59 +00001393 edit_status(); // show current file status
1394 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001395}
1396
1397static void dot_scroll(int cnt, int dir)
1398{
1399 Byte *q;
1400
1401 for (; cnt > 0; cnt--) {
1402 if (dir < 0) {
1403 // scroll Backwards
1404 // ctrl-Y scroll up one line
1405 screenbegin = prev_line(screenbegin);
1406 } else {
1407 // scroll Forwards
1408 // ctrl-E scroll down one line
1409 screenbegin = next_line(screenbegin);
1410 }
1411 }
1412 // make sure "dot" stays on the screen so we dont scroll off
1413 if (dot < screenbegin)
1414 dot = screenbegin;
1415 q = end_screen(); // find new bottom line
1416 if (dot > q)
1417 dot = begin_line(q); // is dot is below bottom line?
1418 dot_skip_over_ws();
Eric Andersen0ef24c62005-07-18 10:32:59 +00001419 edit_status(); // show current file status
1420 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001421}
1422
1423static void dot_skip_over_ws(void)
1424{
1425 // skip WS
1426 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1427 dot++;
1428}
1429
1430static void dot_delete(void) // delete the char at 'dot'
1431{
1432 (void) text_hole_delete(dot, dot);
1433}
1434
1435static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end"
1436{
1437 if (p >= end && end > text) {
1438 p = end - 1;
1439 indicate_error('1');
1440 }
1441 if (p < text) {
1442 p = text;
1443 indicate_error('2');
1444 }
1445 return (p);
1446}
1447
1448//----- Helper Utility Routines --------------------------------
1449
1450//----------------------------------------------------------------
1451//----- Char Routines --------------------------------------------
1452/* Chars that are part of a word-
1453 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1454 * Chars that are Not part of a word (stoppers)
1455 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1456 * Chars that are WhiteSpace
1457 * TAB NEWLINE VT FF RETURN SPACE
1458 * DO NOT COUNT NEWLINE AS WHITESPACE
1459 */
1460
1461static Byte *new_screen(int ro, int co)
1462{
1463 int li;
1464
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001465 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001466 screensize = ro * co + 8;
1467 screen = (Byte *) xmalloc(screensize);
1468 // initialize the new screen. assume this will be a empty file.
1469 screen_erase();
Eric Andersenaff114c2004-04-14 17:51:38 +00001470 // non-existent text[] lines start with a tilde (~).
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001471 for (li = 1; li < ro - 1; li++) {
1472 screen[(li * co) + 0] = '~';
1473 }
1474 return (screen);
1475}
1476
1477static Byte *new_text(int size)
1478{
1479 if (size < 10240)
1480 size = 10240; // have a minimum size for new files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001481 free(text);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001482 text = (Byte *) xmalloc(size + 8);
1483 memset(text, '\0', size); // clear new text[]
1484 //text += 4; // leave some room for "oops"
1485 textend = text + size - 1;
1486 //textend -= 4; // leave some root for "oops"
1487 return (text);
1488}
1489
1490#ifdef CONFIG_FEATURE_VI_SEARCH
1491static int mycmp(Byte * s1, Byte * s2, int len)
1492{
1493 int i;
1494
1495 i = strncmp((char *) s1, (char *) s2, len);
1496#ifdef CONFIG_FEATURE_VI_SETOPTS
1497 if (ignorecase) {
1498 i = strncasecmp((char *) s1, (char *) s2, len);
1499 }
1500#endif /* CONFIG_FEATURE_VI_SETOPTS */
1501 return (i);
1502}
1503
1504static Byte *char_search(Byte * p, Byte * pat, int dir, int range) // search for pattern starting at p
1505{
1506#ifndef REGEX_SEARCH
1507 Byte *start, *stop;
1508 int len;
1509
1510 len = strlen((char *) pat);
1511 if (dir == FORWARD) {
1512 stop = end - 1; // assume range is p - end-1
1513 if (range == LIMITED)
1514 stop = next_line(p); // range is to next line
1515 for (start = p; start < stop; start++) {
1516 if (mycmp(start, pat, len) == 0) {
1517 return (start);
1518 }
1519 }
1520 } else if (dir == BACK) {
1521 stop = text; // assume range is text - p
1522 if (range == LIMITED)
1523 stop = prev_line(p); // range is to prev line
1524 for (start = p - len; start >= stop; start--) {
1525 if (mycmp(start, pat, len) == 0) {
1526 return (start);
1527 }
1528 }
1529 }
1530 // pattern not found
1531 return (NULL);
1532#else /*REGEX_SEARCH */
1533 char *q;
1534 struct re_pattern_buffer preg;
1535 int i;
1536 int size, range;
1537
1538 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1539 preg.translate = 0;
1540 preg.fastmap = 0;
1541 preg.buffer = 0;
1542 preg.allocated = 0;
1543
1544 // assume a LIMITED forward search
1545 q = next_line(p);
1546 q = end_line(q);
1547 q = end - 1;
1548 if (dir == BACK) {
1549 q = prev_line(p);
1550 q = text;
1551 }
1552 // count the number of chars to search over, forward or backward
1553 size = q - p;
1554 if (size < 0)
1555 size = p - q;
1556 // RANGE could be negative if we are searching backwards
1557 range = q - p;
1558
1559 q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
1560 if (q != 0) {
1561 // The pattern was not compiled
1562 psbs("bad search pattern: \"%s\": %s", pat, q);
1563 i = 0; // return p if pattern not compiled
1564 goto cs1;
1565 }
1566
1567 q = p;
1568 if (range < 0) {
1569 q = p - size;
1570 if (q < text)
1571 q = text;
1572 }
1573 // search for the compiled pattern, preg, in p[]
1574 // range < 0- search backward
1575 // range > 0- search forward
1576 // 0 < start < size
1577 // re_search() < 0 not found or error
1578 // re_search() > 0 index of found pattern
1579 // struct pattern char int int int struct reg
1580 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1581 i = re_search(&preg, q, size, 0, range, 0);
1582 if (i == -1) {
1583 p = 0;
1584 i = 0; // return NULL if pattern not found
1585 }
1586 cs1:
1587 if (dir == FORWARD) {
1588 p = p + i;
1589 } else {
1590 p = p - i;
1591 }
1592 return (p);
1593#endif /*REGEX_SEARCH */
1594}
1595#endif /* CONFIG_FEATURE_VI_SEARCH */
1596
1597static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
1598{
1599 if (c == 22) { // Is this an ctrl-V?
1600 p = stupid_insert(p, '^'); // use ^ to indicate literal next
1601 p--; // backup onto ^
1602 refresh(FALSE); // show the ^
1603 c = get_one_char();
1604 *p = c;
1605 p++;
1606 file_modified = TRUE; // has the file been modified
1607 } else if (c == 27) { // Is this an ESC?
1608 cmd_mode = 0;
1609 cmdcnt = 0;
1610 end_cmd_q(); // stop adding to q
Eric Andersen0ef24c62005-07-18 10:32:59 +00001611 *status_buffer = '\0'; // clear the status buffer
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001612 if ((p[-1] != '\n') && (dot>text)) {
1613 p--;
1614 }
Paul Foxd13b90b2005-07-18 22:17:25 +00001615 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001616 // 123456789
1617 if ((p[-1] != '\n') && (dot>text)) {
1618 p--;
1619 p = text_hole_delete(p, p); // shrink buffer 1 char
1620#ifdef CONFIG_FEATURE_VI_DOT_CMD
1621 // also rmove char from last_modifying_cmd
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001622 if (last_modifying_cmd != 0 && strlen((char *) last_modifying_cmd) > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001623 Byte *q;
1624
1625 q = last_modifying_cmd;
1626 q[strlen((char *) q) - 1] = '\0'; // erase BS
1627 q[strlen((char *) q) - 1] = '\0'; // erase prev char
1628 }
1629#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1630 }
1631 } else {
1632 // insert a char into text[]
1633 Byte *sp; // "save p"
1634
1635 if (c == 13)
1636 c = '\n'; // translate \r to \n
1637 sp = p; // remember addr of insert
1638 p = stupid_insert(p, c); // insert the char
1639#ifdef CONFIG_FEATURE_VI_SETOPTS
1640 if (showmatch && strchr(")]}", *sp) != NULL) {
1641 showmatching(sp);
1642 }
1643 if (autoindent && c == '\n') { // auto indent the new line
1644 Byte *q;
1645
1646 q = prev_line(p); // use prev line as templet
1647 for (; isblnk(*q); q++) {
1648 p = stupid_insert(p, *q); // insert the char
1649 }
1650 }
1651#endif /* CONFIG_FEATURE_VI_SETOPTS */
1652 }
1653 return (p);
1654}
1655
1656static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
1657{
1658 p = text_hole_make(p, 1);
1659 if (p != 0) {
1660 *p = c;
1661 file_modified = TRUE; // has the file been modified
1662 p++;
1663 }
1664 return (p);
1665}
1666
1667static Byte find_range(Byte ** start, Byte ** stop, Byte c)
1668{
1669 Byte *save_dot, *p, *q;
1670 int cnt;
1671
1672 save_dot = dot;
1673 p = q = dot;
1674
1675 if (strchr("cdy><", c)) {
1676 // these cmds operate on whole lines
1677 p = q = begin_line(p);
1678 for (cnt = 1; cnt < cmdcnt; cnt++) {
1679 q = next_line(q);
1680 }
1681 q = end_line(q);
1682 } else if (strchr("^%$0bBeEft", c)) {
1683 // These cmds operate on char positions
1684 do_cmd(c); // execute movement cmd
1685 q = dot;
1686 } else if (strchr("wW", c)) {
1687 do_cmd(c); // execute movement cmd
Eric Andersenaeea32c2004-02-04 11:19:44 +00001688 // if we are at the next word's first char
1689 // step back one char
1690 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00001691 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Eric Andersenaeea32c2004-02-04 11:19:44 +00001692 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1693 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1694 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001695 if (dot > text && *dot == '\n')
1696 dot--; // stay off NL
1697 q = dot;
1698 } else if (strchr("H-k{", c)) {
1699 // these operate on multi-lines backwards
1700 q = end_line(dot); // find NL
1701 do_cmd(c); // execute movement cmd
1702 dot_begin();
1703 p = dot;
1704 } else if (strchr("L+j}\r\n", c)) {
1705 // these operate on multi-lines forwards
1706 p = begin_line(dot);
1707 do_cmd(c); // execute movement cmd
1708 dot_end(); // find NL
1709 q = dot;
1710 } else {
1711 c = 27; // error- return an ESC char
1712 //break;
1713 }
1714 *start = p;
1715 *stop = q;
1716 if (q < p) {
1717 *start = q;
1718 *stop = p;
1719 }
1720 dot = save_dot;
1721 return (c);
1722}
1723
1724static int st_test(Byte * p, int type, int dir, Byte * tested)
1725{
1726 Byte c, c0, ci;
1727 int test, inc;
1728
1729 inc = dir;
1730 c = c0 = p[0];
1731 ci = p[inc];
1732 test = 0;
1733
1734 if (type == S_BEFORE_WS) {
1735 c = ci;
1736 test = ((!isspace(c)) || c == '\n');
1737 }
1738 if (type == S_TO_WS) {
1739 c = c0;
1740 test = ((!isspace(c)) || c == '\n');
1741 }
1742 if (type == S_OVER_WS) {
1743 c = c0;
1744 test = ((isspace(c)));
1745 }
1746 if (type == S_END_PUNCT) {
1747 c = ci;
1748 test = ((ispunct(c)));
1749 }
1750 if (type == S_END_ALNUM) {
1751 c = ci;
1752 test = ((isalnum(c)) || c == '_');
1753 }
1754 *tested = c;
1755 return (test);
1756}
1757
1758static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
1759{
1760 Byte c;
1761
1762 while (st_test(p, type, dir, &c)) {
1763 // make sure we limit search to correct number of lines
1764 if (c == '\n' && --linecnt < 1)
1765 break;
1766 if (dir >= 0 && p >= end - 1)
1767 break;
1768 if (dir < 0 && p <= text)
1769 break;
1770 p += dir; // move to next char
1771 }
1772 return (p);
1773}
1774
1775// find matching char of pair () [] {}
1776static Byte *find_pair(Byte * p, Byte c)
1777{
1778 Byte match, *q;
1779 int dir, level;
1780
1781 match = ')';
1782 level = 1;
1783 dir = 1; // assume forward
1784 switch (c) {
1785 case '(':
1786 match = ')';
1787 break;
1788 case '[':
1789 match = ']';
1790 break;
1791 case '{':
1792 match = '}';
1793 break;
1794 case ')':
1795 match = '(';
1796 dir = -1;
1797 break;
1798 case ']':
1799 match = '[';
1800 dir = -1;
1801 break;
1802 case '}':
1803 match = '{';
1804 dir = -1;
1805 break;
1806 }
1807 for (q = p + dir; text <= q && q < end; q += dir) {
1808 // look for match, count levels of pairs (( ))
1809 if (*q == c)
1810 level++; // increase pair levels
1811 if (*q == match)
1812 level--; // reduce pair level
1813 if (level == 0)
1814 break; // found matching pair
1815 }
1816 if (level != 0)
1817 q = NULL; // indicate no match
1818 return (q);
1819}
1820
1821#ifdef CONFIG_FEATURE_VI_SETOPTS
1822// show the matching char of a pair, () [] {}
1823static void showmatching(Byte * p)
1824{
1825 Byte *q, *save_dot;
1826
1827 // we found half of a pair
1828 q = find_pair(p, *p); // get loc of matching char
1829 if (q == NULL) {
1830 indicate_error('3'); // no matching char
1831 } else {
1832 // "q" now points to matching pair
1833 save_dot = dot; // remember where we are
1834 dot = q; // go to new loc
1835 refresh(FALSE); // let the user see it
1836 (void) mysleep(40); // give user some time
1837 dot = save_dot; // go back to old loc
1838 refresh(FALSE);
1839 }
1840}
1841#endif /* CONFIG_FEATURE_VI_SETOPTS */
1842
1843// open a hole in text[]
1844static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
1845{
1846 Byte *src, *dest;
1847 int cnt;
1848
1849 if (size <= 0)
1850 goto thm0;
1851 src = p;
1852 dest = p + size;
1853 cnt = end - src; // the rest of buffer
1854 if (memmove(dest, src, cnt) != dest) {
1855 psbs("can't create room for new characters");
1856 }
1857 memset(p, ' ', size); // clear new hole
1858 end = end + size; // adjust the new END
1859 file_modified = TRUE; // has the file been modified
1860 thm0:
1861 return (p);
1862}
1863
1864// close a hole in text[]
1865static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
1866{
1867 Byte *src, *dest;
1868 int cnt, hole_size;
1869
1870 // move forwards, from beginning
1871 // assume p <= q
1872 src = q + 1;
1873 dest = p;
1874 if (q < p) { // they are backward- swap them
1875 src = p + 1;
1876 dest = q;
1877 }
1878 hole_size = q - p + 1;
1879 cnt = end - src;
1880 if (src < text || src > end)
1881 goto thd0;
1882 if (dest < text || dest >= end)
1883 goto thd0;
1884 if (src >= end)
1885 goto thd_atend; // just delete the end of the buffer
1886 if (memmove(dest, src, cnt) != dest) {
1887 psbs("can't delete the character");
1888 }
1889 thd_atend:
1890 end = end - hole_size; // adjust the new END
1891 if (dest >= end)
1892 dest = end - 1; // make sure dest in below end-1
1893 if (end <= text)
1894 dest = end = text; // keep pointers valid
1895 file_modified = TRUE; // has the file been modified
1896 thd0:
1897 return (dest);
1898}
1899
1900// copy text into register, then delete text.
1901// if dist <= 0, do not include, or go past, a NewLine
1902//
1903static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
1904{
1905 Byte *p;
1906
1907 // make sure start <= stop
1908 if (start > stop) {
1909 // they are backwards, reverse them
1910 p = start;
1911 start = stop;
1912 stop = p;
1913 }
1914 if (dist <= 0) {
1915 // we can not cross NL boundaries
1916 p = start;
1917 if (*p == '\n')
1918 return (p);
1919 // dont go past a NewLine
1920 for (; p + 1 <= stop; p++) {
1921 if (p[1] == '\n') {
1922 stop = p; // "stop" just before NewLine
1923 break;
1924 }
1925 }
1926 }
1927 p = start;
1928#ifdef CONFIG_FEATURE_VI_YANKMARK
1929 text_yank(start, stop, YDreg);
1930#endif /* CONFIG_FEATURE_VI_YANKMARK */
1931 if (yf == YANKDEL) {
1932 p = text_hole_delete(start, stop);
1933 } // delete lines
1934 return (p);
1935}
1936
1937static void show_help(void)
1938{
1939 puts("These features are available:"
1940#ifdef CONFIG_FEATURE_VI_SEARCH
1941 "\n\tPattern searches with / and ?"
1942#endif /* CONFIG_FEATURE_VI_SEARCH */
1943#ifdef CONFIG_FEATURE_VI_DOT_CMD
1944 "\n\tLast command repeat with \'.\'"
1945#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1946#ifdef CONFIG_FEATURE_VI_YANKMARK
1947 "\n\tLine marking with 'x"
1948 "\n\tNamed buffers with \"x"
1949#endif /* CONFIG_FEATURE_VI_YANKMARK */
1950#ifdef CONFIG_FEATURE_VI_READONLY
1951 "\n\tReadonly if vi is called as \"view\""
1952 "\n\tReadonly with -R command line arg"
1953#endif /* CONFIG_FEATURE_VI_READONLY */
1954#ifdef CONFIG_FEATURE_VI_SET
1955 "\n\tSome colon mode commands with \':\'"
1956#endif /* CONFIG_FEATURE_VI_SET */
1957#ifdef CONFIG_FEATURE_VI_SETOPTS
1958 "\n\tSettable options with \":set\""
1959#endif /* CONFIG_FEATURE_VI_SETOPTS */
1960#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
1961 "\n\tSignal catching- ^C"
1962 "\n\tJob suspend and resume with ^Z"
1963#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
1964#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
1965 "\n\tAdapt to window re-sizes"
1966#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
1967 );
1968}
1969
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001970static inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001971{
1972 Byte c, b[2];
1973
1974 b[1] = '\0';
1975 strcpy((char *) buf, ""); // init buf
1976 if (strlen((char *) s) <= 0)
1977 s = (Byte *) "(NULL)";
1978 for (; *s > '\0'; s++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001979 int c_is_no_print;
1980
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001981 c = *s;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001982 c_is_no_print = c > 127 && !Isprint(c);
1983 if (c_is_no_print) {
1984 strcat((char *) buf, SOn);
1985 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001986 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001987 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001988 strcat((char *) buf, "^");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001989 if(c == 127)
1990 c = '?';
1991 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001992 c += '@';
1993 }
1994 b[0] = c;
1995 strcat((char *) buf, (char *) b);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001996 if (c_is_no_print)
1997 strcat((char *) buf, SOs);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001998 if (*s == '\n') {
1999 strcat((char *) buf, "$");
2000 }
2001 }
2002}
2003
2004#ifdef CONFIG_FEATURE_VI_DOT_CMD
2005static void start_new_cmd_q(Byte c)
2006{
2007 // release old cmd
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002008 free(last_modifying_cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002009 // get buffer for new cmd
2010 last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
2011 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
2012 // if there is a current cmd count put it in the buffer first
2013 if (cmdcnt > 0)
2014 sprintf((char *) last_modifying_cmd, "%d", cmdcnt);
2015 // save char c onto queue
2016 last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
2017 adding2q = 1;
2018 return;
2019}
2020
2021static void end_cmd_q(void)
2022{
2023#ifdef CONFIG_FEATURE_VI_YANKMARK
2024 YDreg = 26; // go back to default Yank/Delete reg
2025#endif /* CONFIG_FEATURE_VI_YANKMARK */
2026 adding2q = 0;
2027 return;
2028}
2029#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2030
2031#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
2032static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
2033{
2034 int cnt, i;
2035
2036 i = strlen((char *) s);
2037 p = text_hole_make(p, i);
2038 strncpy((char *) p, (char *) s, i);
2039 for (cnt = 0; *s != '\0'; s++) {
2040 if (*s == '\n')
2041 cnt++;
2042 }
2043#ifdef CONFIG_FEATURE_VI_YANKMARK
2044 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2045#endif /* CONFIG_FEATURE_VI_YANKMARK */
2046 return (p);
2047}
2048#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
2049
2050#ifdef CONFIG_FEATURE_VI_YANKMARK
2051static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register
2052{
2053 Byte *t;
2054 int cnt;
2055
2056 if (q < p) { // they are backwards- reverse them
2057 t = q;
2058 q = p;
2059 p = t;
2060 }
2061 cnt = q - p + 1;
2062 t = reg[dest];
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002063 free(t); // if already a yank register, free it
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002064 t = (Byte *) xmalloc(cnt + 1); // get a new register
2065 memset(t, '\0', cnt + 1); // clear new text[]
2066 strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer
2067 reg[dest] = t;
2068 return (p);
2069}
2070
2071static Byte what_reg(void)
2072{
2073 Byte c;
2074 int i;
2075
2076 i = 0;
2077 c = 'D'; // default to D-reg
2078 if (0 <= YDreg && YDreg <= 25)
2079 c = 'a' + (Byte) YDreg;
2080 if (YDreg == 26)
2081 c = 'D';
2082 if (YDreg == 27)
2083 c = 'U';
2084 return (c);
2085}
2086
2087static void check_context(Byte cmd)
2088{
2089 // A context is defined to be "modifying text"
2090 // Any modifying command establishes a new context.
2091
2092 if (dot < context_start || dot > context_end) {
2093 if (strchr((char *) modifying_cmds, cmd) != NULL) {
2094 // we are trying to modify text[]- make this the current context
2095 mark[27] = mark[26]; // move cur to prev
2096 mark[26] = dot; // move local to cur
2097 context_start = prev_line(prev_line(dot));
2098 context_end = next_line(next_line(dot));
2099 //loiter= start_loiter= now;
2100 }
2101 }
2102 return;
2103}
2104
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002105static inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002106{
2107 Byte *tmp;
2108
2109 // the current context is in mark[26]
2110 // the previous context is in mark[27]
2111 // only swap context if other context is valid
2112 if (text <= mark[27] && mark[27] <= end - 1) {
2113 tmp = mark[27];
2114 mark[27] = mark[26];
2115 mark[26] = tmp;
2116 p = mark[26]; // where we are going- previous context
2117 context_start = prev_line(prev_line(prev_line(p)));
2118 context_end = next_line(next_line(next_line(p)));
2119 }
2120 return (p);
2121}
2122#endif /* CONFIG_FEATURE_VI_YANKMARK */
2123
2124static int isblnk(Byte c) // is the char a blank or tab
2125{
2126 return (c == ' ' || c == '\t');
2127}
2128
2129//----- Set terminal attributes --------------------------------
2130static void rawmode(void)
2131{
2132 tcgetattr(0, &term_orig);
2133 term_vi = term_orig;
2134 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2135 term_vi.c_iflag &= (~IXON & ~ICRNL);
2136 term_vi.c_oflag &= (~ONLCR);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002137 term_vi.c_cc[VMIN] = 1;
2138 term_vi.c_cc[VTIME] = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002139 erase_char = term_vi.c_cc[VERASE];
2140 tcsetattr(0, TCSANOW, &term_vi);
2141}
2142
2143static void cookmode(void)
2144{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002145 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002146 tcsetattr(0, TCSANOW, &term_orig);
2147}
2148
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002149//----- Come here when we get a window resize signal ---------
2150#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
2151static void winch_sig(int sig)
2152{
2153 signal(SIGWINCH, winch_sig);
2154#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2155 window_size_get(0);
2156#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2157 new_screen(rows, columns); // get memory for virtual screen
2158 redraw(TRUE); // re-draw the screen
2159}
2160
2161//----- Come here when we get a continue signal -------------------
2162static void cont_sig(int sig)
2163{
2164 rawmode(); // terminal to "raw"
2165 *status_buffer = '\0'; // clear the status buffer
2166 redraw(TRUE); // re-draw the screen
2167
2168 signal(SIGTSTP, suspend_sig);
2169 signal(SIGCONT, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002170 kill(my_pid, SIGCONT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002171}
2172
2173//----- Come here when we get a Suspend signal -------------------
2174static void suspend_sig(int sig)
2175{
2176 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2177 clear_to_eol(); // Erase to end of line
2178 cookmode(); // terminal to "cooked"
2179
2180 signal(SIGCONT, cont_sig);
2181 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002182 kill(my_pid, SIGTSTP);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002183}
2184
2185//----- Come here when we get a signal ---------------------------
2186static void catch_sig(int sig)
2187{
2188 signal(SIGHUP, catch_sig);
2189 signal(SIGINT, catch_sig);
2190 signal(SIGTERM, catch_sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002191 signal(SIGALRM, catch_sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002192 if(sig)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002193 longjmp(restart, sig);
2194}
2195
2196//----- Come here when we get a core dump signal -----------------
2197static void core_sig(int sig)
2198{
2199 signal(SIGQUIT, core_sig);
2200 signal(SIGILL, core_sig);
2201 signal(SIGTRAP, core_sig);
2202 signal(SIGIOT, core_sig);
2203 signal(SIGABRT, core_sig);
2204 signal(SIGFPE, core_sig);
2205 signal(SIGBUS, core_sig);
2206 signal(SIGSEGV, core_sig);
2207#ifdef SIGSYS
2208 signal(SIGSYS, core_sig);
2209#endif
2210
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002211 if(sig) { // signaled
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002212 dot = bound_dot(dot); // make sure "dot" is valid
2213
2214 longjmp(restart, sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002215 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002216}
2217#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2218
2219static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2220{
2221 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002222 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002223 FD_ZERO(&rfds);
2224 FD_SET(0, &rfds);
2225 tv.tv_sec = 0;
2226 tv.tv_usec = hund * 10000;
2227 select(1, &rfds, NULL, NULL, &tv);
2228 return (FD_ISSET(0, &rfds));
2229}
2230
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002231static Byte readbuffer[BUFSIZ];
2232static int readed_for_parse;
2233
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002234//----- IO Routines --------------------------------------------
2235static Byte readit(void) // read (maybe cursor) key from stdin
2236{
2237 Byte c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002238 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002239 struct esc_cmds {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002240 const char *seq;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002241 Byte val;
2242 };
2243
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002244 static const struct esc_cmds esccmds[] = {
2245 {"OA", (Byte) VI_K_UP}, // cursor key Up
2246 {"OB", (Byte) VI_K_DOWN}, // cursor key Down
2247 {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2248 {"OD", (Byte) VI_K_LEFT}, // cursor key Left
2249 {"OH", (Byte) VI_K_HOME}, // Cursor Key Home
2250 {"OF", (Byte) VI_K_END}, // Cursor Key End
2251 {"[A", (Byte) VI_K_UP}, // cursor key Up
2252 {"[B", (Byte) VI_K_DOWN}, // cursor key Down
2253 {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right
2254 {"[D", (Byte) VI_K_LEFT}, // cursor key Left
2255 {"[H", (Byte) VI_K_HOME}, // Cursor Key Home
2256 {"[F", (Byte) VI_K_END}, // Cursor Key End
2257 {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home
2258 {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2259 {"[4~", (Byte) VI_K_END}, // Cursor Key End
2260 {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2261 {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2262 {"OP", (Byte) VI_K_FUN1}, // Function Key F1
2263 {"OQ", (Byte) VI_K_FUN2}, // Function Key F2
2264 {"OR", (Byte) VI_K_FUN3}, // Function Key F3
2265 {"OS", (Byte) VI_K_FUN4}, // Function Key F4
2266 {"[15~", (Byte) VI_K_FUN5}, // Function Key F5
2267 {"[17~", (Byte) VI_K_FUN6}, // Function Key F6
2268 {"[18~", (Byte) VI_K_FUN7}, // Function Key F7
2269 {"[19~", (Byte) VI_K_FUN8}, // Function Key F8
2270 {"[20~", (Byte) VI_K_FUN9}, // Function Key F9
2271 {"[21~", (Byte) VI_K_FUN10}, // Function Key F10
2272 {"[23~", (Byte) VI_K_FUN11}, // Function Key F11
2273 {"[24~", (Byte) VI_K_FUN12}, // Function Key F12
2274 {"[11~", (Byte) VI_K_FUN1}, // Function Key F1
2275 {"[12~", (Byte) VI_K_FUN2}, // Function Key F2
2276 {"[13~", (Byte) VI_K_FUN3}, // Function Key F3
2277 {"[14~", (Byte) VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002278 };
2279
2280#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2281
2282 (void) alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002283 fflush(stdout);
2284 n = readed_for_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002285 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002286 if (n <= 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002287 ri0:
2288 // the Q is empty, wait for a typed char
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002289 n = read(0, readbuffer, BUFSIZ - 1);
2290 if (n < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002291 if (errno == EINTR)
2292 goto ri0; // interrupted sys call
2293 if (errno == EBADF)
2294 editing = 0;
2295 if (errno == EFAULT)
2296 editing = 0;
2297 if (errno == EINVAL)
2298 editing = 0;
2299 if (errno == EIO)
2300 editing = 0;
2301 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002302 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002303 if(n <= 0)
2304 return 0; // error
2305 if (readbuffer[0] == 27) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002306 // This is an ESC char. Is this Esc sequence?
2307 // Could be bare Esc key. See if there are any
2308 // more chars to read after the ESC. This would
2309 // be a Function or Cursor Key sequence.
2310 FD_ZERO(&rfds);
2311 FD_SET(0, &rfds);
2312 tv.tv_sec = 0;
2313 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2314
2315 // keep reading while there are input chars and room in buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002316 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002317 // read the rest of the ESC string
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002318 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2319 if (r > 0) {
2320 n += r;
2321 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002322 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002323 }
2324 readed_for_parse = n;
2325 }
2326 c = readbuffer[0];
2327 if(c == 27 && n > 1) {
2328 // Maybe cursor or function key?
2329 const struct esc_cmds *eindex;
2330
2331 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2332 int cnt = strlen(eindex->seq);
2333
2334 if(n <= cnt)
2335 continue;
2336 if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
2337 continue;
2338 // is a Cursor key- put derived value back into Q
2339 c = eindex->val;
2340 // for squeeze out the ESC sequence
2341 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002342 break;
2343 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002344 if(eindex == &esccmds[ESCCMDS_COUNT]) {
2345 /* defined ESC sequence not found, set only one ESC */
2346 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002347 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002348 } else {
2349 n = 1;
2350 }
2351 // remove key sequence from Q
2352 readed_for_parse -= n;
2353 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002354 (void) alarm(3); // we are done waiting for input, turn alarm ON
2355 return (c);
2356}
2357
2358//----- IO Routines --------------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002359static Byte get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002360{
2361 static Byte c;
2362
2363#ifdef CONFIG_FEATURE_VI_DOT_CMD
2364 // ! adding2q && ioq == 0 read()
2365 // ! adding2q && ioq != 0 *ioq
2366 // adding2q *last_modifying_cmd= read()
2367 if (!adding2q) {
2368 // we are not adding to the q.
2369 // but, we may be reading from a q
2370 if (ioq == 0) {
2371 // there is no current q, read from STDIN
2372 c = readit(); // get the users input
2373 } else {
2374 // there is a queue to get chars from first
2375 c = *ioq++;
2376 if (c == '\0') {
2377 // the end of the q, read from STDIN
2378 free(ioq_start);
2379 ioq_start = ioq = 0;
2380 c = readit(); // get the users input
2381 }
2382 }
2383 } else {
2384 // adding STDIN chars to q
2385 c = readit(); // get the users input
2386 if (last_modifying_cmd != 0) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002387 int len = strlen((char *) last_modifying_cmd);
2388 if (len + 1 >= BUFSIZ) {
2389 psbs("last_modifying_cmd overrun");
2390 } else {
2391 // add new char to q
2392 last_modifying_cmd[len] = c;
2393 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002394 }
2395 }
2396#else /* CONFIG_FEATURE_VI_DOT_CMD */
2397 c = readit(); // get the users input
2398#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2399 return (c); // return the char, where ever it came from
2400}
2401
2402static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2403{
2404 Byte buf[BUFSIZ];
2405 Byte c;
2406 int i;
2407 static Byte *obufp = NULL;
2408
2409 strcpy((char *) buf, (char *) prompt);
2410 *status_buffer = '\0'; // clear the status buffer
2411 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2412 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002413 write1(prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002414
2415 for (i = strlen((char *) buf); i < BUFSIZ;) {
2416 c = get_one_char(); // read user input
2417 if (c == '\n' || c == '\r' || c == 27)
2418 break; // is this end of input
2419 if (c == erase_char) { // user wants to erase prev char
2420 i--; // backup to prev char
2421 buf[i] = '\0'; // erase the char
2422 buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002423 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002424 if (i <= 0) { // user backs up before b-o-l, exit
2425 break;
2426 }
2427 } else {
2428 buf[i] = c; // save char in buffer
2429 buf[i + 1] = '\0'; // make sure buffer is null terminated
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002430 putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002431 i++;
2432 }
2433 }
2434 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002435 free(obufp);
Manuel Novoa III cad53642003-03-19 09:13:01 +00002436 obufp = (Byte *) bb_xstrdup((char *) buf);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002437 return (obufp);
2438}
2439
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002440static int file_size(const Byte * fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002441{
2442 struct stat st_buf;
2443 int cnt, sr;
2444
2445 if (fn == 0 || strlen(fn) <= 0)
2446 return (-1);
2447 cnt = -1;
2448 sr = stat((char *) fn, &st_buf); // see if file exists
2449 if (sr >= 0) {
2450 cnt = (int) st_buf.st_size;
2451 }
2452 return (cnt);
2453}
2454
2455static int file_insert(Byte * fn, Byte * p, int size)
2456{
2457 int fd, cnt;
2458
2459 cnt = -1;
2460#ifdef CONFIG_FEATURE_VI_READONLY
2461 readonly = FALSE;
2462#endif /* CONFIG_FEATURE_VI_READONLY */
2463 if (fn == 0 || strlen((char*) fn) <= 0) {
2464 psbs("No filename given");
2465 goto fi0;
2466 }
2467 if (size == 0) {
2468 // OK- this is just a no-op
2469 cnt = 0;
2470 goto fi0;
2471 }
2472 if (size < 0) {
2473 psbs("Trying to insert a negative number (%d) of characters", size);
2474 goto fi0;
2475 }
2476 if (p < text || p > end) {
2477 psbs("Trying to insert file outside of memory");
2478 goto fi0;
2479 }
2480
2481 // see if we can open the file
2482#ifdef CONFIG_FEATURE_VI_READONLY
2483 if (vi_readonly) goto fi1; // do not try write-mode
2484#endif
2485 fd = open((char *) fn, O_RDWR); // assume read & write
2486 if (fd < 0) {
2487 // could not open for writing- maybe file is read only
2488#ifdef CONFIG_FEATURE_VI_READONLY
2489 fi1:
2490#endif
2491 fd = open((char *) fn, O_RDONLY); // try read-only
2492 if (fd < 0) {
2493 psbs("\"%s\" %s", fn, "could not open file");
2494 goto fi0;
2495 }
2496#ifdef CONFIG_FEATURE_VI_READONLY
2497 // got the file- read-only
2498 readonly = TRUE;
2499#endif /* CONFIG_FEATURE_VI_READONLY */
2500 }
2501 p = text_hole_make(p, size);
2502 cnt = read(fd, p, size);
2503 close(fd);
2504 if (cnt < 0) {
2505 cnt = -1;
2506 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2507 psbs("could not read file \"%s\"", fn);
2508 } else if (cnt < size) {
2509 // There was a partial read, shrink unused space text[]
2510 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
2511 psbs("could not read all of file \"%s\"", fn);
2512 }
2513 if (cnt >= size)
2514 file_modified = TRUE;
2515 fi0:
2516 return (cnt);
2517}
2518
2519static int file_write(Byte * fn, Byte * first, Byte * last)
2520{
2521 int fd, cnt, charcnt;
2522
2523 if (fn == 0) {
2524 psbs("No current filename");
2525 return (-1);
2526 }
2527 charcnt = 0;
2528 // FIXIT- use the correct umask()
2529 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2530 if (fd < 0)
2531 return (-1);
2532 cnt = last - first + 1;
2533 charcnt = write(fd, first, cnt);
2534 if (charcnt == cnt) {
2535 // good write
2536 //file_modified= FALSE; // the file has not been modified
2537 } else {
2538 charcnt = 0;
2539 }
2540 close(fd);
2541 return (charcnt);
2542}
2543
2544//----- Terminal Drawing ---------------------------------------
2545// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002546// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002547// screen coordinates
2548// 0,0 ... 0,79
2549// 1,0 ... 1,79
2550// . ... .
2551// . ... .
2552// 22,0 ... 22,79
2553// 23,0 ... 23,79 status line
2554//
2555
2556//----- Move the cursor to row x col (count from 0, not 1) -------
2557static void place_cursor(int row, int col, int opti)
2558{
2559 char cm1[BUFSIZ];
2560 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002561#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2562 char cm2[BUFSIZ];
2563 Byte *screenp;
2564 // char cm3[BUFSIZ];
2565 int Rrow= last_row;
2566#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002567
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002568 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2569
2570 if (row < 0) row = 0;
2571 if (row >= rows) row = rows - 1;
2572 if (col < 0) col = 0;
2573 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002574
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002575 //----- 1. Try the standard terminal ESC sequence
2576 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2577 cm= cm1;
2578 if (! opti) goto pc0;
2579
2580#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2581 //----- find the minimum # of chars to move cursor -------------
2582 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2583 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002584
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002585 // move to the correct row
2586 while (row < Rrow) {
2587 // the cursor has to move up
2588 strcat(cm2, CMup);
2589 Rrow--;
2590 }
2591 while (row > Rrow) {
2592 // the cursor has to move down
2593 strcat(cm2, CMdown);
2594 Rrow++;
2595 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002596
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002597 // now move to the correct column
2598 strcat(cm2, "\r"); // start at col 0
2599 // just send out orignal source char to get to correct place
2600 screenp = &screen[row * columns]; // start of screen line
2601 strncat(cm2, screenp, col);
2602
2603 //----- 3. Try some other way of moving cursor
2604 //---------------------------------------------
2605
2606 // pick the shortest cursor motion to send out
2607 cm= cm1;
2608 if (strlen(cm2) < strlen(cm)) {
2609 cm= cm2;
2610 } /* else if (strlen(cm3) < strlen(cm)) {
2611 cm= cm3;
2612 } */
2613#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2614 pc0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002615 write1(cm); // move the cursor
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002616}
2617
2618//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002619static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002620{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002621 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002622}
2623
2624//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002625static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002626{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002627 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002628}
2629
2630//----- Start standout mode ------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002631static void standout_start(void) // send "start reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002632{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002633 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002634}
2635
2636//----- End standout mode --------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002637static void standout_end(void) // send "end reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002638{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002639 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002640}
2641
2642//----- Flash the screen --------------------------------------
2643static void flash(int h)
2644{
2645 standout_start(); // send "start reverse video" sequence
2646 redraw(TRUE);
2647 (void) mysleep(h);
2648 standout_end(); // send "end reverse video" sequence
2649 redraw(TRUE);
2650}
2651
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002652static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002653{
2654#ifdef CONFIG_FEATURE_VI_CRASHME
2655 if (crashme > 0)
2656 return; // generate a random command
2657#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002658 if (!err_method) {
2659 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002660 } else {
2661 flash(10);
2662 }
2663}
2664
2665//----- Screen[] Routines --------------------------------------
2666//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002667static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002668{
2669 memset(screen, ' ', screensize); // clear new screen
2670}
2671
2672//----- Draw the status line at bottom of the screen -------------
2673static void show_status_line(void)
2674{
2675 static int last_cksum;
2676 int l, cnt, cksum;
2677
Eric Andersen0ef24c62005-07-18 10:32:59 +00002678 //edit_status();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002679 cnt = strlen((char *) status_buffer);
2680 for (cksum= l= 0; l < cnt; l++) { cksum += (int)(status_buffer[l]); }
2681 // don't write the status line unless it changes
2682 if (cnt > 0 && last_cksum != cksum) {
2683 last_cksum= cksum; // remember if we have seen this line
2684 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002685 write1(status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002686 clear_to_eol();
2687 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2688 }
Eric Andersena9eb33d2004-08-19 19:15:06 +00002689 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002690}
2691
2692//----- format the status buffer, the bottom line of screen ------
2693// print status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002694static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002695{
2696 va_list args;
2697
2698 va_start(args, format);
2699 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
Eric Andersen0ef24c62005-07-18 10:32:59 +00002700 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002701 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2702 va_end(args);
Eric Andersen0ef24c62005-07-18 10:32:59 +00002703 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002704
2705 return;
2706}
2707
2708// print status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002709static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002710{
2711 va_list args;
2712
2713 va_start(args, format);
2714 vsprintf((char *) status_buffer, format, args);
2715 va_end(args);
Eric Andersen0ef24c62005-07-18 10:32:59 +00002716 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002717 return;
2718}
2719
2720static void ni(Byte * s) // display messages
2721{
2722 Byte buf[BUFSIZ];
2723
2724 print_literal(buf, s);
2725 psbs("\'%s\' is not implemented", buf);
2726}
2727
2728static void edit_status(void) // show file status on status line
2729{
2730 int cur, tot, percent;
2731
2732 cur = count_lines(text, dot);
2733 tot = count_lines(text, end - 1);
2734 // current line percent
2735 // ------------- ~~ ----------
2736 // total lines 100
2737 if (tot > 0) {
2738 percent = (100 * cur) / tot;
2739 } else {
2740 cur = tot = 0;
2741 percent = 100;
2742 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00002743
2744 sprintf((char *) status_buffer,
2745 "\"%s\""
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002746#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen0ef24c62005-07-18 10:32:59 +00002747 "%s"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002748#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen0ef24c62005-07-18 10:32:59 +00002749 "%s line %d of %d --%d%%--",
2750 (cfn != 0 ? (char *) cfn : "No file"),
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002751#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen0ef24c62005-07-18 10:32:59 +00002752 ((vi_readonly || readonly) ? " [Read only]" : ""),
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002753#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen0ef24c62005-07-18 10:32:59 +00002754 (file_modified ? " [modified]" : ""),
2755 cur, tot, percent);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002756}
2757
2758//----- Force refresh of all Lines -----------------------------
2759static void redraw(int full_screen)
2760{
2761 place_cursor(0, 0, FALSE); // put cursor in correct place
2762 clear_to_eos(); // tel terminal to erase display
2763 screen_erase(); // erase the internal screen buffer
2764 refresh(full_screen); // this will redraw the entire display
2765}
2766
2767//----- Format a text[] line into a buffer ---------------------
2768static void format_line(Byte *dest, Byte *src, int li)
2769{
2770 int co;
2771 Byte c;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002772
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002773 for (co= 0; co < MAX_SCR_COLS; co++) {
2774 c= ' '; // assume blank
2775 if (li > 0 && co == 0) {
2776 c = '~'; // not first line, assume Tilde
2777 }
2778 // are there chars in text[] and have we gone past the end
2779 if (text < end && src < end) {
2780 c = *src++;
2781 }
2782 if (c == '\n')
2783 break;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002784 if (c > 127 && !Isprint(c)) {
2785 c = '.';
2786 }
2787 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002788 if (c == '\t') {
2789 c = ' ';
2790 // co % 8 != 7
2791 for (; (co % tabstop) != (tabstop - 1); co++) {
2792 dest[co] = c;
2793 }
2794 } else {
2795 dest[co++] = '^';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002796 if(c == 127)
2797 c = '?';
2798 else
2799 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002800 }
2801 }
2802 // the co++ is done here so that the column will
2803 // not be overwritten when we blank-out the rest of line
2804 dest[co] = c;
2805 if (src >= end)
2806 break;
2807 }
2808}
2809
2810//----- Refresh the changed screen lines -----------------------
2811// Copy the source line from text[] into the buffer and note
2812// if the current screenline is different from the new buffer.
2813// If they differ then that line needs redrawing on the terminal.
2814//
2815static void refresh(int full_screen)
2816{
2817 static int old_offset;
2818 int li, changed;
2819 Byte buf[MAX_SCR_COLS];
2820 Byte *tp, *sp; // pointer into text[] and screen[]
2821#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2822 int last_li= -2; // last line that changed- for optimizing cursor movement
2823#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2824
2825#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2826 window_size_get(0);
2827#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2828 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2829 tp = screenbegin; // index into text[] of top line
2830
2831 // compare text[] to screen[] and mark screen[] lines that need updating
2832 for (li = 0; li < rows - 1; li++) {
2833 int cs, ce; // column start & end
2834 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2835 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2836 // format current text line into buf
2837 format_line(buf, tp, li);
2838
2839 // skip to the end of the current text[] line
2840 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2841
2842 // see if there are any changes between vitual screen and buf
2843 changed = FALSE; // assume no change
2844 cs= 0;
2845 ce= columns-1;
2846 sp = &screen[li * columns]; // start of screen line
2847 if (full_screen) {
2848 // force re-draw of every single column from 0 - columns-1
2849 goto re0;
2850 }
2851 // compare newly formatted buffer with virtual screen
2852 // look forward for first difference between buf and screen
2853 for ( ; cs <= ce; cs++) {
2854 if (buf[cs + offset] != sp[cs]) {
2855 changed = TRUE; // mark for redraw
2856 break;
2857 }
2858 }
2859
2860 // look backward for last difference between buf and screen
2861 for ( ; ce >= cs; ce--) {
2862 if (buf[ce + offset] != sp[ce]) {
2863 changed = TRUE; // mark for redraw
2864 break;
2865 }
2866 }
2867 // now, cs is index of first diff, and ce is index of last diff
2868
2869 // if horz offset has changed, force a redraw
2870 if (offset != old_offset) {
2871 re0:
2872 changed = TRUE;
2873 }
2874
2875 // make a sanity check of columns indexes
2876 if (cs < 0) cs= 0;
2877 if (ce > columns-1) ce= columns-1;
2878 if (cs > ce) { cs= 0; ce= columns-1; }
2879 // is there a change between vitual screen and buf
2880 if (changed) {
2881 // copy changed part of buffer to virtual screen
2882 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2883
2884 // move cursor to column of first change
2885 if (offset != old_offset) {
2886 // opti_cur_move is still too stupid
2887 // to handle offsets correctly
2888 place_cursor(li, cs, FALSE);
2889 } else {
2890#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2891 // if this just the next line
2892 // try to optimize cursor movement
2893 // otherwise, use standard ESC sequence
2894 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2895 last_li= li;
2896#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2897 place_cursor(li, cs, FALSE); // use standard ESC sequence
2898#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2899 }
2900
Eric Andersen0ef24c62005-07-18 10:32:59 +00002901 edit_status(); // show current file status
2902 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002903 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002904 {
2905 int nic = ce-cs+1;
2906 char *out = sp+cs;
2907
2908 while(nic-- > 0) {
2909 putchar(*out);
2910 out++;
2911 }
2912 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002913#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2914 last_row = li;
2915#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2916 }
2917 }
2918
2919#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2920 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2921 last_row = crow;
2922#else
2923 place_cursor(crow, ccol, FALSE);
2924#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002925
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002926 if (offset != old_offset)
2927 old_offset = offset;
2928}
2929
Eric Andersen3f980402001-04-04 17:31:15 +00002930//---------------------------------------------------------------------
2931//----- the Ascii Chart -----------------------------------------------
2932//
2933// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2934// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2935// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2936// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2937// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2938// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2939// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2940// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2941// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2942// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2943// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2944// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2945// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2946// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2947// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2948// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2949//---------------------------------------------------------------------
2950
2951//----- Execute a Vi Command -----------------------------------
2952static void do_cmd(Byte c)
2953{
2954 Byte c1, *p, *q, *msg, buf[9], *save_dot;
2955 int cnt, i, j, dir, yf;
2956
2957 c1 = c; // quiet the compiler
2958 cnt = yf = dir = 0; // quiet the compiler
2959 p = q = save_dot = msg = buf; // quiet the compiler
2960 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00002961
2962 /* if this is a cursor key, skip these checks */
2963 switch (c) {
2964 case VI_K_UP:
2965 case VI_K_DOWN:
2966 case VI_K_LEFT:
2967 case VI_K_RIGHT:
2968 case VI_K_HOME:
2969 case VI_K_END:
2970 case VI_K_PAGEUP:
2971 case VI_K_PAGEDOWN:
2972 goto key_cmd_mode;
2973 }
2974
Eric Andersen3f980402001-04-04 17:31:15 +00002975 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002976 // flip-flop Insert/Replace mode
2977 if (c == VI_K_INSERT) goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00002978 // we are 'R'eplacing the current *dot with new char
2979 if (*dot == '\n') {
2980 // don't Replace past E-o-l
2981 cmd_mode = 1; // convert to insert
2982 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002983 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00002984 if (c != 27)
2985 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
2986 dot = char_insert(dot, c); // insert new char
2987 }
2988 goto dc1;
2989 }
2990 }
2991 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00002992 // hitting "Insert" twice means "R" replace mode
2993 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00002994 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002995 if (1 <= c || Isprint(c)) {
2996 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00002997 }
2998 goto dc1;
2999 }
3000
Eric Andersenbff7a602001-11-17 07:15:43 +00003001key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00003002 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00003003 //case 0x01: // soh
3004 //case 0x09: // ht
3005 //case 0x0b: // vt
3006 //case 0x0e: // so
3007 //case 0x0f: // si
3008 //case 0x10: // dle
3009 //case 0x11: // dc1
3010 //case 0x13: // dc3
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003011#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00003012 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00003013 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003014 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003015#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen822c3832001-05-07 17:37:43 +00003016 //case 0x16: // syn
3017 //case 0x17: // etb
3018 //case 0x18: // can
3019 //case 0x1c: // fs
3020 //case 0x1d: // gs
3021 //case 0x1e: // rs
3022 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003023 //case '!': // !-
3024 //case '#': // #-
3025 //case '&': // &-
3026 //case '(': // (-
3027 //case ')': // )-
3028 //case '*': // *-
3029 //case ',': // ,-
3030 //case '=': // =-
3031 //case '@': // @-
3032 //case 'F': // F-
3033 //case 'K': // K-
3034 //case 'Q': // Q-
3035 //case 'S': // S-
3036 //case 'T': // T-
3037 //case 'V': // V-
3038 //case '[': // [-
3039 //case '\\': // \-
3040 //case ']': // ]-
3041 //case '_': // _-
3042 //case '`': // `-
3043 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003044 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003045 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00003046 default: // unrecognised command
3047 buf[0] = c;
3048 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003049 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00003050 buf[0] = '^';
3051 buf[1] = c + '@';
3052 buf[2] = '\0';
3053 }
3054 ni((Byte *) buf);
3055 end_cmd_q(); // stop adding to q
3056 case 0x00: // nul- ignore
3057 break;
3058 case 2: // ctrl-B scroll up full screen
3059 case VI_K_PAGEUP: // Cursor Key Page Up
3060 dot_scroll(rows - 2, -1);
3061 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003062#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003063 case 0x03: // ctrl-C interrupt
3064 longjmp(restart, 1);
3065 break;
3066 case 26: // ctrl-Z suspend
3067 suspend_sig(SIGTSTP);
3068 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003069#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +00003070 case 4: // ctrl-D scroll down half screen
3071 dot_scroll((rows - 2) / 2, 1);
3072 break;
3073 case 5: // ctrl-E scroll down one line
3074 dot_scroll(1, 1);
3075 break;
3076 case 6: // ctrl-F scroll down full screen
3077 case VI_K_PAGEDOWN: // Cursor Key Page Down
3078 dot_scroll(rows - 2, 1);
3079 break;
3080 case 7: // ctrl-G show current status
3081 edit_status();
Eric Andersen0ef24c62005-07-18 10:32:59 +00003082 show_status_line();
Eric Andersen3f980402001-04-04 17:31:15 +00003083 break;
3084 case 'h': // h- move left
3085 case VI_K_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003086 case 8: // ctrl-H- move left (This may be ERASE char)
3087 case 127: // DEL- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003088 if (cmdcnt-- > 1) {
3089 do_cmd(c);
3090 } // repeat cnt
3091 dot_left();
Eric Andersen0ef24c62005-07-18 10:32:59 +00003092 edit_status(); // show current file status
3093 show_status_line();
Eric Andersen3f980402001-04-04 17:31:15 +00003094 break;
3095 case 10: // Newline ^J
3096 case 'j': // j- goto next line, same col
3097 case VI_K_DOWN: // cursor key Down
3098 if (cmdcnt-- > 1) {
3099 do_cmd(c);
3100 } // repeat cnt
3101 dot_next(); // go to next B-o-l
3102 dot = move_to_col(dot, ccol + offset); // try stay in same col
3103 break;
3104 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003105 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003106 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003107 clear_to_eos(); // tel terminal to erase display
3108 (void) mysleep(10);
3109 screen_erase(); // erase the internal screen buffer
3110 refresh(TRUE); // this will redraw the entire display
3111 break;
3112 case 13: // Carriage Return ^M
3113 case '+': // +- goto next line
3114 if (cmdcnt-- > 1) {
3115 do_cmd(c);
3116 } // repeat cnt
3117 dot_next();
3118 dot_skip_over_ws();
3119 break;
3120 case 21: // ctrl-U scroll up half screen
3121 dot_scroll((rows - 2) / 2, -1);
3122 break;
3123 case 25: // ctrl-Y scroll up one line
3124 dot_scroll(1, -1);
3125 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003126 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003127 if (cmd_mode == 0)
3128 indicate_error(c);
3129 cmd_mode = 0; // stop insrting
3130 end_cmd_q();
3131 *status_buffer = '\0'; // clear status buffer
3132 break;
3133 case ' ': // move right
3134 case 'l': // move right
3135 case VI_K_RIGHT: // Cursor Key Right
3136 if (cmdcnt-- > 1) {
3137 do_cmd(c);
3138 } // repeat cnt
3139 dot_right();
3140 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003141#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003142 case '"': // "- name a register to use for Delete/Yank
3143 c1 = get_one_char();
3144 c1 = tolower(c1);
3145 if (islower(c1)) {
3146 YDreg = c1 - 'a';
3147 } else {
3148 indicate_error(c);
3149 }
3150 break;
3151 case '\'': // '- goto a specific mark
3152 c1 = get_one_char();
3153 c1 = tolower(c1);
3154 if (islower(c1)) {
3155 c1 = c1 - 'a';
3156 // get the b-o-l
3157 q = mark[(int) c1];
3158 if (text <= q && q < end) {
3159 dot = q;
3160 dot_begin(); // go to B-o-l
3161 dot_skip_over_ws();
3162 }
3163 } else if (c1 == '\'') { // goto previous context
3164 dot = swap_context(dot); // swap current and previous context
3165 dot_begin(); // go to B-o-l
3166 dot_skip_over_ws();
3167 } else {
3168 indicate_error(c);
3169 }
3170 break;
3171 case 'm': // m- Mark a line
3172 // this is really stupid. If there are any inserts or deletes
3173 // between text[0] and dot then this mark will not point to the
3174 // correct location! It could be off by many lines!
3175 // Well..., at least its quick and dirty.
3176 c1 = get_one_char();
3177 c1 = tolower(c1);
3178 if (islower(c1)) {
3179 c1 = c1 - 'a';
3180 // remember the line
3181 mark[(int) c1] = dot;
3182 } else {
3183 indicate_error(c);
3184 }
3185 break;
3186 case 'P': // P- Put register before
3187 case 'p': // p- put register after
3188 p = reg[YDreg];
3189 if (p == 0) {
3190 psbs("Nothing in register %c", what_reg());
3191 break;
3192 }
3193 // are we putting whole lines or strings
3194 if (strchr((char *) p, '\n') != NULL) {
3195 if (c == 'P') {
3196 dot_begin(); // putting lines- Put above
3197 }
3198 if (c == 'p') {
3199 // are we putting after very last line?
3200 if (end_line(dot) == (end - 1)) {
3201 dot = end; // force dot to end of text[]
3202 } else {
3203 dot_next(); // next line, then put before
3204 }
3205 }
3206 } else {
3207 if (c == 'p')
3208 dot_right(); // move to right, can move to NL
3209 }
3210 dot = string_insert(dot, p); // insert the string
3211 end_cmd_q(); // stop adding to q
3212 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003213 case 'U': // U- Undo; replace current line with original version
3214 if (reg[Ureg] != 0) {
3215 p = begin_line(dot);
3216 q = end_line(dot);
3217 p = text_hole_delete(p, q); // delete cur line
3218 p = string_insert(p, reg[Ureg]); // insert orig line
3219 dot = p;
3220 dot_skip_over_ws();
3221 }
3222 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003223#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003224 case '$': // $- goto end of line
3225 case VI_K_END: // Cursor Key End
3226 if (cmdcnt-- > 1) {
3227 do_cmd(c);
3228 } // repeat cnt
Glenn L McGrathee829062004-01-21 10:59:45 +00003229 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003230 break;
3231 case '%': // %- find matching char of pair () [] {}
3232 for (q = dot; q < end && *q != '\n'; q++) {
3233 if (strchr("()[]{}", *q) != NULL) {
3234 // we found half of a pair
3235 p = find_pair(q, *q);
3236 if (p == NULL) {
3237 indicate_error(c);
3238 } else {
3239 dot = p;
3240 }
3241 break;
3242 }
3243 }
3244 if (*q == '\n')
3245 indicate_error(c);
3246 break;
3247 case 'f': // f- forward to a user specified char
3248 last_forward_char = get_one_char(); // get the search char
3249 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003250 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003251 //
Paul Foxd13b90b2005-07-18 22:17:25 +00003252 //**** fall thru to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003253 case ';': // ;- look at rest of line for last forward char
3254 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003255 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003256 } // repeat cnt
Eric Andersen822c3832001-05-07 17:37:43 +00003257 if (last_forward_char == 0) break;
Eric Andersen3f980402001-04-04 17:31:15 +00003258 q = dot + 1;
3259 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3260 q++;
3261 }
3262 if (*q == last_forward_char)
3263 dot = q;
3264 break;
3265 case '-': // -- goto prev line
3266 if (cmdcnt-- > 1) {
3267 do_cmd(c);
3268 } // repeat cnt
3269 dot_prev();
3270 dot_skip_over_ws();
3271 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003272#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003273 case '.': // .- repeat the last modifying command
3274 // Stuff the last_modifying_cmd back into stdin
3275 // and let it be re-executed.
3276 if (last_modifying_cmd != 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +00003277 ioq = ioq_start = (Byte *) bb_xstrdup((char *) last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003278 }
3279 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003280#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3281#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003282 case '?': // /- search for a pattern
3283 case '/': // /- search for a pattern
3284 buf[0] = c;
3285 buf[1] = '\0';
3286 q = get_input_line(buf); // get input line- use "status line"
3287 if (strlen((char *) q) == 1)
3288 goto dc3; // if no pat re-use old pat
3289 if (strlen((char *) q) > 1) { // new pat- save it and find
3290 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003291 free(last_search_pattern);
Manuel Novoa III cad53642003-03-19 09:13:01 +00003292 last_search_pattern = (Byte *) bb_xstrdup((char *) q);
Eric Andersen3f980402001-04-04 17:31:15 +00003293 goto dc3; // now find the pattern
3294 }
3295 // user changed mind and erased the "/"- do nothing
3296 break;
3297 case 'N': // N- backward search for last pattern
3298 if (cmdcnt-- > 1) {
3299 do_cmd(c);
3300 } // repeat cnt
3301 dir = BACK; // assume BACKWARD search
3302 p = dot - 1;
3303 if (last_search_pattern[0] == '?') {
3304 dir = FORWARD;
3305 p = dot + 1;
3306 }
3307 goto dc4; // now search for pattern
3308 break;
3309 case 'n': // n- repeat search for last pattern
3310 // search rest of text[] starting at next char
3311 // if search fails return orignal "p" not the "p+1" address
3312 if (cmdcnt-- > 1) {
3313 do_cmd(c);
3314 } // repeat cnt
3315 dc3:
3316 if (last_search_pattern == 0) {
3317 msg = (Byte *) "No previous regular expression";
3318 goto dc2;
3319 }
3320 if (last_search_pattern[0] == '/') {
3321 dir = FORWARD; // assume FORWARD search
3322 p = dot + 1;
3323 }
3324 if (last_search_pattern[0] == '?') {
3325 dir = BACK;
3326 p = dot - 1;
3327 }
3328 dc4:
3329 q = char_search(p, last_search_pattern + 1, dir, FULL);
3330 if (q != NULL) {
3331 dot = q; // good search, update "dot"
3332 msg = (Byte *) "";
3333 goto dc2;
3334 }
3335 // no pattern found between "dot" and "end"- continue at top
3336 p = text;
3337 if (dir == BACK) {
3338 p = end - 1;
3339 }
3340 q = char_search(p, last_search_pattern + 1, dir, FULL);
3341 if (q != NULL) { // found something
3342 dot = q; // found new pattern- goto it
3343 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3344 if (dir == BACK) {
3345 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3346 }
3347 } else {
3348 msg = (Byte *) "Pattern not found";
3349 }
3350 dc2:
3351 psbs("%s", msg);
3352 break;
3353 case '{': // {- move backward paragraph
3354 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3355 if (q != NULL) { // found blank line
3356 dot = next_line(q); // move to next blank line
3357 }
3358 break;
3359 case '}': // }- move forward paragraph
3360 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3361 if (q != NULL) { // found blank line
3362 dot = next_line(q); // move to next blank line
3363 }
3364 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003365#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003366 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003367 case '1': // 1-
3368 case '2': // 2-
3369 case '3': // 3-
3370 case '4': // 4-
3371 case '5': // 5-
3372 case '6': // 6-
3373 case '7': // 7-
3374 case '8': // 8-
3375 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003376 if (c == '0' && cmdcnt < 1) {
3377 dot_begin(); // this was a standalone zero
3378 } else {
3379 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3380 }
3381 break;
3382 case ':': // :- the colon mode commands
Eric Andersen3f980402001-04-04 17:31:15 +00003383 p = get_input_line((Byte *) ":"); // get input line- use "status line"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003384#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003385 colon(p); // execute the command
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003386#else /* CONFIG_FEATURE_VI_COLON */
Eric Andersen822c3832001-05-07 17:37:43 +00003387 if (*p == ':')
3388 p++; // move past the ':'
3389 cnt = strlen((char *) p);
3390 if (cnt <= 0)
3391 break;
3392 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3393 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
Matt Kraai1f0c4362001-12-20 23:13:26 +00003394 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003395 psbs("No write since last change (:quit! overrides)");
3396 } else {
3397 editing = 0;
3398 }
Eric Andersen822c3832001-05-07 17:37:43 +00003399 } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
Robert Griebla71389b2002-07-31 21:22:21 +00003400 strncasecmp((char *) p, "wq", cnt) == 0 ||
3401 strncasecmp((char *) p, "x", cnt) == 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003402 cnt = file_write(cfn, text, end - 1);
3403 file_modified = FALSE;
3404 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
Robert Griebla71389b2002-07-31 21:22:21 +00003405 if (p[0] == 'x' || p[1] == 'q') {
Eric Andersen3f980402001-04-04 17:31:15 +00003406 editing = 0;
3407 }
Eric Andersen822c3832001-05-07 17:37:43 +00003408 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
3409 edit_status(); // show current file status
Eric Andersen0ef24c62005-07-18 10:32:59 +00003410 show_status_line();
Eric Andersen822c3832001-05-07 17:37:43 +00003411 } else if (sscanf((char *) p, "%d", &j) > 0) {
3412 dot = find_line(j); // go to line # j
3413 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003414 } else { // unrecognised cmd
Eric Andersen822c3832001-05-07 17:37:43 +00003415 ni((Byte *) p);
Eric Andersen3f980402001-04-04 17:31:15 +00003416 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003417#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003418 break;
3419 case '<': // <- Left shift something
3420 case '>': // >- Right shift something
3421 cnt = count_lines(text, dot); // remember what line we are on
3422 c1 = get_one_char(); // get the type of thing to delete
3423 find_range(&p, &q, c1);
3424 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3425 p = begin_line(p);
3426 q = end_line(q);
3427 i = count_lines(p, q); // # of lines we are shifting
3428 for ( ; i > 0; i--, p = next_line(p)) {
3429 if (c == '<') {
3430 // shift left- remove tab or 8 spaces
3431 if (*p == '\t') {
3432 // shrink buffer 1 char
3433 (void) text_hole_delete(p, p);
3434 } else if (*p == ' ') {
3435 // we should be calculating columns, not just SPACE
3436 for (j = 0; *p == ' ' && j < tabstop; j++) {
3437 (void) text_hole_delete(p, p);
3438 }
3439 }
3440 } else if (c == '>') {
3441 // shift right -- add tab or 8 spaces
3442 (void) char_insert(p, '\t');
3443 }
3444 }
3445 dot = find_line(cnt); // what line were we on
3446 dot_skip_over_ws();
3447 end_cmd_q(); // stop adding to q
3448 break;
3449 case 'A': // A- append at e-o-l
3450 dot_end(); // go to e-o-l
3451 //**** fall thru to ... 'a'
3452 case 'a': // a- append after current char
3453 if (*dot != '\n')
3454 dot++;
3455 goto dc_i;
3456 break;
3457 case 'B': // B- back a blank-delimited Word
3458 case 'E': // E- end of a blank-delimited word
3459 case 'W': // W- forward a blank-delimited word
3460 if (cmdcnt-- > 1) {
3461 do_cmd(c);
3462 } // repeat cnt
3463 dir = FORWARD;
3464 if (c == 'B')
3465 dir = BACK;
3466 if (c == 'W' || isspace(dot[dir])) {
3467 dot = skip_thing(dot, 1, dir, S_TO_WS);
3468 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3469 }
3470 if (c != 'W')
3471 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3472 break;
3473 case 'C': // C- Change to e-o-l
3474 case 'D': // D- delete to e-o-l
3475 save_dot = dot;
3476 dot = dollar_line(dot); // move to before NL
3477 // copy text into a register and delete
3478 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3479 if (c == 'C')
3480 goto dc_i; // start inserting
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003481#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003482 if (c == 'D')
3483 end_cmd_q(); // stop adding to q
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003484#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003485 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003486 case 'G': // G- goto to a line number (default= E-O-F)
3487 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003488 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003489 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003490 }
3491 dot_skip_over_ws();
3492 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003493 case 'H': // H- goto top line on screen
3494 dot = screenbegin;
3495 if (cmdcnt > (rows - 1)) {
3496 cmdcnt = (rows - 1);
3497 }
3498 if (cmdcnt-- > 1) {
3499 do_cmd('+');
3500 } // repeat cnt
3501 dot_skip_over_ws();
3502 break;
3503 case 'I': // I- insert before first non-blank
3504 dot_begin(); // 0
3505 dot_skip_over_ws();
3506 //**** fall thru to ... 'i'
3507 case 'i': // i- insert before current char
3508 case VI_K_INSERT: // Cursor Key Insert
3509 dc_i:
3510 cmd_mode = 1; // start insrting
3511 psb("-- Insert --");
3512 break;
3513 case 'J': // J- join current and next lines together
3514 if (cmdcnt-- > 2) {
3515 do_cmd(c);
3516 } // repeat cnt
3517 dot_end(); // move to NL
3518 if (dot < end - 1) { // make sure not last char in text[]
3519 *dot++ = ' '; // replace NL with space
Paul Fox18433aa2005-07-20 17:39:52 +00003520 file_modified = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +00003521 while (isblnk(*dot)) { // delete leading WS
3522 dot_delete();
3523 }
3524 }
3525 end_cmd_q(); // stop adding to q
3526 break;
3527 case 'L': // L- goto bottom line on screen
3528 dot = end_screen();
3529 if (cmdcnt > (rows - 1)) {
3530 cmdcnt = (rows - 1);
3531 }
3532 if (cmdcnt-- > 1) {
3533 do_cmd('-');
3534 } // repeat cnt
3535 dot_begin();
3536 dot_skip_over_ws();
3537 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003538 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003539 dot = screenbegin;
3540 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3541 dot = next_line(dot);
3542 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003543 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003544 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003545 p = begin_line(dot);
3546 if (p[-1] == '\n') {
3547 dot_prev();
3548 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3549 dot_end();
3550 dot = char_insert(dot, '\n');
3551 } else {
3552 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003553 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003554 dot_prev(); // -
3555 }
3556 goto dc_i;
3557 break;
3558 case 'R': // R- continuous Replace char
Eric Andersen1c0d3112001-04-16 15:46:44 +00003559 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003560 cmd_mode = 2;
3561 psb("-- Replace --");
3562 break;
3563 case 'X': // X- delete char before dot
3564 case 'x': // x- delete the current char
3565 case 's': // s- substitute the current char
3566 if (cmdcnt-- > 1) {
3567 do_cmd(c);
3568 } // repeat cnt
3569 dir = 0;
3570 if (c == 'X')
3571 dir = -1;
3572 if (dot[dir] != '\n') {
3573 if (c == 'X')
3574 dot--; // delete prev char
3575 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3576 }
3577 if (c == 's')
3578 goto dc_i; // start insrting
3579 end_cmd_q(); // stop adding to q
3580 break;
3581 case 'Z': // Z- if modified, {write}; exit
3582 // ZZ means to save file (if necessary), then exit
3583 c1 = get_one_char();
3584 if (c1 != 'Z') {
3585 indicate_error(c);
3586 break;
3587 }
Matt Kraai1f0c4362001-12-20 23:13:26 +00003588 if (file_modified
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003589#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00003590 && ! vi_readonly
3591 && ! readonly
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003592#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00003593 ) {
3594 cnt = file_write(cfn, text, end - 1);
3595 if (cnt == (end - 1 - text + 1)) {
3596 editing = 0;
3597 }
3598 } else {
3599 editing = 0;
3600 }
3601 break;
3602 case '^': // ^- move to first non-blank on line
3603 dot_begin();
3604 dot_skip_over_ws();
3605 break;
3606 case 'b': // b- back a word
3607 case 'e': // e- end of word
3608 if (cmdcnt-- > 1) {
3609 do_cmd(c);
3610 } // repeat cnt
3611 dir = FORWARD;
3612 if (c == 'b')
3613 dir = BACK;
3614 if ((dot + dir) < text || (dot + dir) > end - 1)
3615 break;
3616 dot += dir;
3617 if (isspace(*dot)) {
3618 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3619 }
3620 if (isalnum(*dot) || *dot == '_') {
3621 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3622 } else if (ispunct(*dot)) {
3623 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3624 }
3625 break;
3626 case 'c': // c- change something
3627 case 'd': // d- delete something
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003628#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003629 case 'y': // y- yank something
3630 case 'Y': // Y- Yank a line
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003631#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003632 yf = YANKDEL; // assume either "c" or "d"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003633#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003634 if (c == 'y' || c == 'Y')
3635 yf = YANKONLY;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003636#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003637 c1 = 'y';
3638 if (c != 'Y')
3639 c1 = get_one_char(); // get the type of thing to delete
3640 find_range(&p, &q, c1);
3641 if (c1 == 27) { // ESC- user changed mind and wants out
3642 c = c1 = 27; // Escape- do nothing
3643 } else if (strchr("wW", c1)) {
3644 if (c == 'c') {
3645 // don't include trailing WS as part of word
3646 while (isblnk(*q)) {
3647 if (q <= text || q[-1] == '\n')
3648 break;
3649 q--;
3650 }
3651 }
3652 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003653 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003654 // single line copy text into a register and delete
3655 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003656 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003657 // multiple line copy text into a register and delete
3658 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003659 if (c == 'c') {
3660 dot = char_insert(dot, '\n');
3661 // on the last line of file don't move to prev line
3662 if (dot != (end-1)) {
3663 dot_prev();
3664 }
3665 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003666 dot_begin();
3667 dot_skip_over_ws();
3668 }
3669 } else {
3670 // could not recognize object
3671 c = c1 = 27; // error-
3672 indicate_error(c);
3673 }
3674 if (c1 != 27) {
3675 // if CHANGING, not deleting, start inserting after the delete
3676 if (c == 'c') {
3677 strcpy((char *) buf, "Change");
3678 goto dc_i; // start inserting
3679 }
3680 if (c == 'd') {
3681 strcpy((char *) buf, "Delete");
3682 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003683#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003684 if (c == 'y' || c == 'Y') {
3685 strcpy((char *) buf, "Yank");
3686 }
3687 p = reg[YDreg];
3688 q = p + strlen((char *) p);
3689 for (cnt = 0; p <= q; p++) {
3690 if (*p == '\n')
3691 cnt++;
3692 }
3693 psb("%s %d lines (%d chars) using [%c]",
3694 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003695#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003696 end_cmd_q(); // stop adding to q
3697 }
3698 break;
3699 case 'k': // k- goto prev line, same col
3700 case VI_K_UP: // cursor key Up
3701 if (cmdcnt-- > 1) {
3702 do_cmd(c);
3703 } // repeat cnt
3704 dot_prev();
3705 dot = move_to_col(dot, ccol + offset); // try stay in same col
3706 break;
3707 case 'r': // r- replace the current char with user input
3708 c1 = get_one_char(); // get the replacement char
3709 if (*dot != '\n') {
3710 *dot = c1;
3711 file_modified = TRUE; // has the file been modified
3712 }
3713 end_cmd_q(); // stop adding to q
3714 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003715 case 't': // t- move to char prior to next x
3716 last_forward_char = get_one_char();
3717 do_cmd(';');
3718 if (*dot == last_forward_char)
3719 dot_left();
3720 last_forward_char= 0;
3721 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003722 case 'w': // w- forward a word
3723 if (cmdcnt-- > 1) {
3724 do_cmd(c);
3725 } // repeat cnt
3726 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3727 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3728 } else if (ispunct(*dot)) { // we are on PUNCT
3729 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3730 }
3731 if (dot < end - 1)
3732 dot++; // move over word
3733 if (isspace(*dot)) {
3734 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3735 }
3736 break;
3737 case 'z': // z-
3738 c1 = get_one_char(); // get the replacement char
3739 cnt = 0;
3740 if (c1 == '.')
3741 cnt = (rows - 2) / 2; // put dot at center
3742 if (c1 == '-')
3743 cnt = rows - 2; // put dot at bottom
3744 screenbegin = begin_line(dot); // start dot at top
3745 dot_scroll(cnt, -1);
3746 break;
3747 case '|': // |- move to column "cmdcnt"
3748 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3749 break;
3750 case '~': // ~- flip the case of letters a-z -> A-Z
3751 if (cmdcnt-- > 1) {
3752 do_cmd(c);
3753 } // repeat cnt
3754 if (islower(*dot)) {
3755 *dot = toupper(*dot);
3756 file_modified = TRUE; // has the file been modified
3757 } else if (isupper(*dot)) {
3758 *dot = tolower(*dot);
3759 file_modified = TRUE; // has the file been modified
3760 }
3761 dot_right();
3762 end_cmd_q(); // stop adding to q
3763 break;
3764 //----- The Cursor and Function Keys -----------------------------
3765 case VI_K_HOME: // Cursor Key Home
3766 dot_begin();
3767 break;
3768 // The Fn keys could point to do_macro which could translate them
3769 case VI_K_FUN1: // Function Key F1
3770 case VI_K_FUN2: // Function Key F2
3771 case VI_K_FUN3: // Function Key F3
3772 case VI_K_FUN4: // Function Key F4
3773 case VI_K_FUN5: // Function Key F5
3774 case VI_K_FUN6: // Function Key F6
3775 case VI_K_FUN7: // Function Key F7
3776 case VI_K_FUN8: // Function Key F8
3777 case VI_K_FUN9: // Function Key F9
3778 case VI_K_FUN10: // Function Key F10
3779 case VI_K_FUN11: // Function Key F11
3780 case VI_K_FUN12: // Function Key F12
3781 break;
3782 }
3783
3784 dc1:
3785 // if text[] just became empty, add back an empty line
3786 if (end == text) {
3787 (void) char_insert(text, '\n'); // start empty buf with dummy line
3788 dot = text;
3789 }
3790 // it is OK for dot to exactly equal to end, otherwise check dot validity
3791 if (dot != end) {
3792 dot = bound_dot(dot); // make sure "dot" is valid
3793 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003794#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003795 check_context(c); // update the current context
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003796#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003797
3798 if (!isdigit(c))
3799 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3800 cnt = dot - begin_line(dot);
3801 // Try to stay off of the Newline
3802 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3803 dot--;
3804}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003805
3806#ifdef CONFIG_FEATURE_VI_CRASHME
3807static int totalcmds = 0;
3808static int Mp = 85; // Movement command Probability
3809static int Np = 90; // Non-movement command Probability
3810static int Dp = 96; // Delete command Probability
3811static int Ip = 97; // Insert command Probability
3812static int Yp = 98; // Yank command Probability
3813static int Pp = 99; // Put command Probability
3814static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3815char chars[20] = "\t012345 abcdABCD-=.$";
3816char *words[20] = { "this", "is", "a", "test",
3817 "broadcast", "the", "emergency", "of",
3818 "system", "quick", "brown", "fox",
3819 "jumped", "over", "lazy", "dogs",
3820 "back", "January", "Febuary", "March"
3821};
3822char *lines[20] = {
3823 "You should have received a copy of the GNU General Public License\n",
3824 "char c, cm, *cmd, *cmd1;\n",
3825 "generate a command by percentages\n",
3826 "Numbers may be typed as a prefix to some commands.\n",
3827 "Quit, discarding changes!\n",
3828 "Forced write, if permission originally not valid.\n",
3829 "In general, any ex or ed command (such as substitute or delete).\n",
3830 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3831 "Please get w/ me and I will go over it with you.\n",
3832 "The following is a list of scheduled, committed changes.\n",
3833 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3834 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3835 "Any question about transactions please contact Sterling Huxley.\n",
3836 "I will try to get back to you by Friday, December 31.\n",
3837 "This Change will be implemented on Friday.\n",
3838 "Let me know if you have problems accessing this;\n",
3839 "Sterling Huxley recently added you to the access list.\n",
3840 "Would you like to go to lunch?\n",
3841 "The last command will be automatically run.\n",
3842 "This is too much english for a computer geek.\n",
3843};
3844char *multilines[20] = {
3845 "You should have received a copy of the GNU General Public License\n",
3846 "char c, cm, *cmd, *cmd1;\n",
3847 "generate a command by percentages\n",
3848 "Numbers may be typed as a prefix to some commands.\n",
3849 "Quit, discarding changes!\n",
3850 "Forced write, if permission originally not valid.\n",
3851 "In general, any ex or ed command (such as substitute or delete).\n",
3852 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3853 "Please get w/ me and I will go over it with you.\n",
3854 "The following is a list of scheduled, committed changes.\n",
3855 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3856 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3857 "Any question about transactions please contact Sterling Huxley.\n",
3858 "I will try to get back to you by Friday, December 31.\n",
3859 "This Change will be implemented on Friday.\n",
3860 "Let me know if you have problems accessing this;\n",
3861 "Sterling Huxley recently added you to the access list.\n",
3862 "Would you like to go to lunch?\n",
3863 "The last command will be automatically run.\n",
3864 "This is too much english for a computer geek.\n",
3865};
3866
3867// create a random command to execute
3868static void crash_dummy()
3869{
3870 static int sleeptime; // how long to pause between commands
3871 char c, cm, *cmd, *cmd1;
3872 int i, cnt, thing, rbi, startrbi, percent;
3873
3874 // "dot" movement commands
3875 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3876
3877 // is there already a command running?
3878 if (readed_for_parse > 0)
3879 goto cd1;
3880 cd0:
3881 startrbi = rbi = 0;
3882 sleeptime = 0; // how long to pause between commands
3883 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3884 // generate a command by percentages
3885 percent = (int) lrand48() % 100; // get a number from 0-99
3886 if (percent < Mp) { // Movement commands
3887 // available commands
3888 cmd = cmd1;
3889 M++;
3890 } else if (percent < Np) { // non-movement commands
3891 cmd = "mz<>\'\""; // available commands
3892 N++;
3893 } else if (percent < Dp) { // Delete commands
3894 cmd = "dx"; // available commands
3895 D++;
3896 } else if (percent < Ip) { // Inset commands
3897 cmd = "iIaAsrJ"; // available commands
3898 I++;
3899 } else if (percent < Yp) { // Yank commands
3900 cmd = "yY"; // available commands
3901 Y++;
3902 } else if (percent < Pp) { // Put commands
3903 cmd = "pP"; // available commands
3904 P++;
3905 } else {
3906 // We do not know how to handle this command, try again
3907 U++;
3908 goto cd0;
3909 }
3910 // randomly pick one of the available cmds from "cmd[]"
3911 i = (int) lrand48() % strlen(cmd);
3912 cm = cmd[i];
3913 if (strchr(":\024", cm))
3914 goto cd0; // dont allow colon or ctrl-T commands
3915 readbuffer[rbi++] = cm; // put cmd into input buffer
3916
3917 // now we have the command-
3918 // there are 1, 2, and multi char commands
3919 // find out which and generate the rest of command as necessary
3920 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3921 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3922 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3923 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3924 }
3925 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3926 c = cmd1[thing];
3927 readbuffer[rbi++] = c; // add movement to input buffer
3928 }
3929 if (strchr("iIaAsc", cm)) { // multi-char commands
3930 if (cm == 'c') {
3931 // change some thing
3932 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3933 c = cmd1[thing];
3934 readbuffer[rbi++] = c; // add movement to input buffer
3935 }
3936 thing = (int) lrand48() % 4; // what thing to insert
3937 cnt = (int) lrand48() % 10; // how many to insert
3938 for (i = 0; i < cnt; i++) {
3939 if (thing == 0) { // insert chars
3940 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3941 } else if (thing == 1) { // insert words
3942 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
3943 strcat((char *) readbuffer, " ");
3944 sleeptime = 0; // how fast to type
3945 } else if (thing == 2) { // insert lines
3946 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
3947 sleeptime = 0; // how fast to type
3948 } else { // insert multi-lines
3949 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
3950 sleeptime = 0; // how fast to type
3951 }
3952 }
3953 strcat((char *) readbuffer, "\033");
3954 }
3955 readed_for_parse = strlen(readbuffer);
3956 cd1:
3957 totalcmds++;
3958 if (sleeptime > 0)
3959 (void) mysleep(sleeptime); // sleep 1/100 sec
3960}
3961
3962// test to see if there are any errors
3963static void crash_test()
3964{
3965 static time_t oldtim;
3966 time_t tim;
Glenn L McGrath7127b582002-12-03 21:48:15 +00003967 char d[2], msg[BUFSIZ];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003968
3969 msg[0] = '\0';
3970 if (end < text) {
3971 strcat((char *) msg, "end<text ");
3972 }
3973 if (end > textend) {
3974 strcat((char *) msg, "end>textend ");
3975 }
3976 if (dot < text) {
3977 strcat((char *) msg, "dot<text ");
3978 }
3979 if (dot > end) {
3980 strcat((char *) msg, "dot>end ");
3981 }
3982 if (screenbegin < text) {
3983 strcat((char *) msg, "screenbegin<text ");
3984 }
3985 if (screenbegin > end - 1) {
3986 strcat((char *) msg, "screenbegin>end-1 ");
3987 }
3988
3989 if (strlen(msg) > 0) {
3990 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00003991 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003992 totalcmds, last_input_char, msg, SOs, SOn);
3993 fflush(stdout);
3994 while (read(0, d, 1) > 0) {
3995 if (d[0] == '\n' || d[0] == '\r')
3996 break;
3997 }
3998 alarm(3);
3999 }
4000 tim = (time_t) time((time_t *) 0);
4001 if (tim >= (oldtim + 3)) {
4002 sprintf((char *) status_buffer,
4003 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
4004 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
4005 oldtim = tim;
4006 }
4007 return;
4008}
4009#endif /* CONFIG_FEATURE_VI_CRASHME */