blob: a25e96657c39aa6495239df3765e26ab4a8a5458 [file] [log] [blame]
Rob Landleye5e1a102006-06-21 01:15:36 +00001/* vi: set sw=4 ts=4: */
Eric Andersen3f980402001-04-04 17:31:15 +00002/*
3 * tiny vi.c: A small 'vi' clone
4 * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
5 *
Paul Foxdbf935d2006-03-27 20:29:33 +00006 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersen3f980402001-04-04 17:31:15 +00007 */
8
Eric Andersen3f980402001-04-04 17:31:15 +00009/*
Eric Andersen3f980402001-04-04 17:31:15 +000010 * Things To Do:
11 * EXINIT
Eric Andersen1c0d3112001-04-16 15:46:44 +000012 * $HOME/.exrc and ./.exrc
Eric Andersen3f980402001-04-04 17:31:15 +000013 * add magic to search /foo.*bar
14 * add :help command
15 * :map macros
Eric Andersen3f980402001-04-04 17:31:15 +000016 * if mark[] values were line numbers rather than pointers
17 * it would be easier to change the mark when add/delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +000018 * More intelligence in refresh()
19 * ":r !cmd" and "!cmd" to filter text through an external command
20 * A true "undo" facility
21 * An "ex" line oriented mode- maybe using "cmdedit"
Eric Andersen3f980402001-04-04 17:31:15 +000022 */
23
Eric Andersen3f980402001-04-04 17:31:15 +000024
Rob Landley2d6af162006-06-21 00:52:31 +000025#include "busybox.h"
Eric Andersen3f980402001-04-04 17:31:15 +000026
Glenn L McGrath09adaca2002-12-02 21:18:10 +000027#ifdef CONFIG_LOCALE_SUPPORT
28#define Isprint(c) isprint((c))
29#else
30#define Isprint(c) ( (c) >= ' ' && (c) != 127 && (c) != ((unsigned char)'\233') )
31#endif
32
Eric Andersen1c0d3112001-04-16 15:46:44 +000033#define MAX_SCR_COLS BUFSIZ
Eric Andersen3f980402001-04-04 17:31:15 +000034
35// Misc. non-Ascii keys that report an escape sequence
36#define VI_K_UP 128 // cursor key Up
37#define VI_K_DOWN 129 // cursor key Down
38#define VI_K_RIGHT 130 // Cursor Key Right
39#define VI_K_LEFT 131 // cursor key Left
40#define VI_K_HOME 132 // Cursor Key Home
41#define VI_K_END 133 // Cursor Key End
42#define VI_K_INSERT 134 // Cursor Key Insert
43#define VI_K_PAGEUP 135 // Cursor Key Page Up
44#define VI_K_PAGEDOWN 136 // Cursor Key Page Down
45#define VI_K_FUN1 137 // Function Key F1
46#define VI_K_FUN2 138 // Function Key F2
47#define VI_K_FUN3 139 // Function Key F3
48#define VI_K_FUN4 140 // Function Key F4
49#define VI_K_FUN5 141 // Function Key F5
50#define VI_K_FUN6 142 // Function Key F6
51#define VI_K_FUN7 143 // Function Key F7
52#define VI_K_FUN8 144 // Function Key F8
53#define VI_K_FUN9 145 // Function Key F9
54#define VI_K_FUN10 146 // Function Key F10
55#define VI_K_FUN11 147 // Function Key F11
56#define VI_K_FUN12 148 // Function Key F12
57
Glenn L McGrath09adaca2002-12-02 21:18:10 +000058/* vt102 typical ESC sequence */
59/* terminal standout start/normal ESC sequence */
60static const char SOs[] = "\033[7m";
61static const char SOn[] = "\033[0m";
62/* terminal bell sequence */
63static const char bell[] = "\007";
64/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
65static const char Ceol[] = "\033[0K";
66static const char Ceos [] = "\033[0J";
67/* Cursor motion arbitrary destination ESC sequence */
68static const char CMrc[] = "\033[%d;%dH";
69/* Cursor motion up and down ESC sequence */
70static const char CMup[] = "\033[A";
71static const char CMdown[] = "\n";
72
73
Rob Landleybc68cd12006-03-10 19:22:06 +000074enum {
75 YANKONLY = FALSE,
76 YANKDEL = TRUE,
77 FORWARD = 1, // code depends on "1" for array index
78 BACK = -1, // code depends on "-1" for array index
79 LIMITED = 0, // how much of text[] in char_search
80 FULL = 1, // how much of text[] in char_search
Eric Andersen3f980402001-04-04 17:31:15 +000081
Rob Landleybc68cd12006-03-10 19:22:06 +000082 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
83 S_TO_WS = 2, // used in skip_thing() for moving "dot"
84 S_OVER_WS = 3, // used in skip_thing() for moving "dot"
85 S_END_PUNCT = 4, // used in skip_thing() for moving "dot"
86 S_END_ALNUM = 5 // used in skip_thing() for moving "dot"
87};
Eric Andersen3f980402001-04-04 17:31:15 +000088
89typedef unsigned char Byte;
90
Glenn L McGrath09adaca2002-12-02 21:18:10 +000091static int vi_setops;
92#define VI_AUTOINDENT 1
93#define VI_SHOWMATCH 2
94#define VI_IGNORECASE 4
95#define VI_ERR_METHOD 8
96#define autoindent (vi_setops & VI_AUTOINDENT)
97#define showmatch (vi_setops & VI_SHOWMATCH )
98#define ignorecase (vi_setops & VI_IGNORECASE)
99/* indicate error with beep or flash */
100#define err_method (vi_setops & VI_ERR_METHOD)
101
Eric Andersen3f980402001-04-04 17:31:15 +0000102
103static int editing; // >0 while we are editing a file
Paul Fox8552aec2005-09-16 12:20:05 +0000104static int cmd_mode; // 0=command 1=insert 2=replace
Eric Andersen3f980402001-04-04 17:31:15 +0000105static int file_modified; // buffer contents changed
Paul Fox8552aec2005-09-16 12:20:05 +0000106static int last_file_modified = -1;
Eric Andersen3f980402001-04-04 17:31:15 +0000107static int fn_start; // index of first cmd line file name
108static int save_argc; // how many file names on cmd line
109static int cmdcnt; // repetition count
110static fd_set rfds; // use select() for small sleeps
111static struct timeval tv; // use select() for small sleeps
Eric Andersen3f980402001-04-04 17:31:15 +0000112static int rows, columns; // the terminal screen is this size
113static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset
Eric Andersen3f980402001-04-04 17:31:15 +0000114static Byte *status_buffer; // mesages to the user
Paul Fox8552aec2005-09-16 12:20:05 +0000115#define STATUS_BUFFER_LEN 200
116static int have_status_msg; // is default edit status needed?
117static int last_status_cksum; // hash of current status line
Eric Andersen3f980402001-04-04 17:31:15 +0000118static Byte *cfn; // previous, current, and next file name
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +0000119static Byte *text, *end; // pointers to the user data in memory
Eric Andersen3f980402001-04-04 17:31:15 +0000120static Byte *screen; // pointer to the virtual screen buffer
121static int screensize; // and its size
122static Byte *screenbegin; // index into text[], of top line on the screen
123static Byte *dot; // where all the action takes place
124static int tabstop;
125static struct termios term_orig, term_vi; // remember what the cooked mode was
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000126static Byte erase_char; // the users erase character
127static Byte last_input_char; // last char read from user
128static Byte last_forward_char; // last char searched for with 'f'
Eric Andersen3f980402001-04-04 17:31:15 +0000129
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000130#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +0000131static int last_row; // where the cursor was last moved to
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000132#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
133#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000134static jmp_buf restart; // catch_sig()
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000135#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000136#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
137static int my_pid;
138#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000139#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000140static int adding2q; // are we currently adding user input to q
141static Byte *last_modifying_cmd; // last modifying cmd for "."
142static Byte *ioq, *ioq_start; // pointer to string for get_one_char to "read"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000143#endif /* CONFIG_FEATURE_VI_DOT_CMD */
144#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
Eric Andersen3f980402001-04-04 17:31:15 +0000145static Byte *modifying_cmds; // cmds that modify text[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000146#endif /* CONFIG_FEATURE_VI_DOT_CMD || CONFIG_FEATURE_VI_YANKMARK */
147#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000148static int vi_readonly, readonly;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000149#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000150#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000151static Byte *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
152static int YDreg, Ureg; // default delete register and orig line for "U"
153static Byte *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
154static Byte *context_start, *context_end;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000155#endif /* CONFIG_FEATURE_VI_YANKMARK */
156#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000157static Byte *last_search_pattern; // last pattern from a '/' or '?' search
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000158#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +0000159
160
161static void edit_file(Byte *); // edit one file
162static void do_cmd(Byte); // execute a command
163static void sync_cursor(Byte *, int *, int *); // synchronize the screen cursor to dot
164static Byte *begin_line(Byte *); // return pointer to cur line B-o-l
165static Byte *end_line(Byte *); // return pointer to cur line E-o-l
Eric Andersen3f980402001-04-04 17:31:15 +0000166static Byte *prev_line(Byte *); // return pointer to prev line B-o-l
167static Byte *next_line(Byte *); // return pointer to next line B-o-l
168static Byte *end_screen(void); // get pointer to last char on screen
169static int count_lines(Byte *, Byte *); // count line from start to stop
170static Byte *find_line(int); // find begining of line #li
171static Byte *move_to_col(Byte *, int); // move "p" to column l
172static int isblnk(Byte); // is the char a blank or tab
173static void dot_left(void); // move dot left- dont leave line
174static void dot_right(void); // move dot right- dont leave line
175static void dot_begin(void); // move dot to B-o-l
176static void dot_end(void); // move dot to E-o-l
177static void dot_next(void); // move dot to next line B-o-l
178static void dot_prev(void); // move dot to prev line B-o-l
179static void dot_scroll(int, int); // move the screen up or down
180static void dot_skip_over_ws(void); // move dot pat WS
181static void dot_delete(void); // delete the char at 'dot'
182static Byte *bound_dot(Byte *); // make sure text[0] <= P < "end"
183static Byte *new_screen(int, int); // malloc virtual screen memory
184static Byte *new_text(int); // malloc memory for text[] buffer
185static Byte *char_insert(Byte *, Byte); // insert the char c at 'p'
186static Byte *stupid_insert(Byte *, Byte); // stupidly insert the char c at 'p'
187static Byte find_range(Byte **, Byte **, Byte); // return pointers for an object
188static int st_test(Byte *, int, int, Byte *); // helper for skip_thing()
189static Byte *skip_thing(Byte *, int, int, int); // skip some object
190static Byte *find_pair(Byte *, Byte); // find matching pair () [] {}
191static Byte *text_hole_delete(Byte *, Byte *); // at "p", delete a 'size' byte hole
192static Byte *text_hole_make(Byte *, int); // at "p", make a 'size' byte hole
193static Byte *yank_delete(Byte *, Byte *, int, int); // yank text[] into register then delete
194static void show_help(void); // display some help info
Eric Andersen3f980402001-04-04 17:31:15 +0000195static void rawmode(void); // set "raw" mode on tty
196static void cookmode(void); // return to "cooked" mode on tty
197static int mysleep(int); // sleep for 'h' 1/100 seconds
198static Byte readit(void); // read (maybe cursor) key from stdin
199static Byte get_one_char(void); // read 1 char from stdin
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000200static int file_size(const Byte *); // what is the byte size of "fn"
Eric Andersen3f980402001-04-04 17:31:15 +0000201static int file_insert(Byte *, Byte *, int);
202static int file_write(Byte *, Byte *, Byte *);
Eric Andersen822c3832001-05-07 17:37:43 +0000203static void place_cursor(int, int, int);
Eric Andersenbff7a602001-11-17 07:15:43 +0000204static void screen_erase(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000205static void clear_to_eol(void);
206static void clear_to_eos(void);
207static void standout_start(void); // send "start reverse video" sequence
208static void standout_end(void); // send "end reverse video" sequence
209static void flash(int); // flash the terminal screen
Eric Andersen3f980402001-04-04 17:31:15 +0000210static void show_status_line(void); // put a message on the bottom line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000211static void psb(const char *, ...); // Print Status Buf
212static void psbs(const char *, ...); // Print Status Buf in standout mode
Eric Andersen3f980402001-04-04 17:31:15 +0000213static void ni(Byte *); // display messages
Paul Fox8552aec2005-09-16 12:20:05 +0000214static int format_edit_status(void); // format file status on status line
Eric Andersen3f980402001-04-04 17:31:15 +0000215static void redraw(int); // force a full screen refresh
Eric Andersen822c3832001-05-07 17:37:43 +0000216static void format_line(Byte*, Byte*, int);
Eric Andersen3f980402001-04-04 17:31:15 +0000217static void refresh(int); // update the terminal from screen[]
218
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000219static void Indicate_Error(void); // use flash or beep to indicate error
220#define indicate_error(c) Indicate_Error()
Paul Fox90372ed2005-10-09 14:26:26 +0000221static void Hit_Return(void);
222
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000223#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000224static Byte *char_search(Byte *, Byte *, int, int); // search for pattern starting at p
225static int mycmp(Byte *, Byte *, int); // string cmp based in "ignorecase"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000226#endif /* CONFIG_FEATURE_VI_SEARCH */
227#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen1c0d3112001-04-16 15:46:44 +0000228static Byte *get_one_address(Byte *, int *); // get colon addr, if present
229static Byte *get_address(Byte *, int *, int *); // get two colon addrs, if present
Eric Andersen3f980402001-04-04 17:31:15 +0000230static void colon(Byte *); // execute the "colon" mode cmds
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000231#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000232#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000233static void winch_sig(int); // catch window size changes
234static void suspend_sig(int); // catch ctrl-Z
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000235static void catch_sig(int); // catch ctrl-C and alarm time-outs
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000236#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
237#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000238static void start_new_cmd_q(Byte); // new queue for command
Eric Andersenbff7a602001-11-17 07:15:43 +0000239static void end_cmd_q(void); // stop saving input chars
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000240#else /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000241#define end_cmd_q()
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000242#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000243#ifdef CONFIG_FEATURE_VI_SETOPTS
Eric Andersen3f980402001-04-04 17:31:15 +0000244static void showmatching(Byte *); // show the matching pair () [] {}
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000245#endif /* CONFIG_FEATURE_VI_SETOPTS */
Bernhard Reutner-Fischer1e23b6f2006-06-09 07:12:27 +0000246#if defined(CONFIG_FEATURE_VI_YANKMARK) || (defined(CONFIG_FEATURE_VI_COLON) && defined(CONFIG_FEATURE_VI_SEARCH)) || defined(CONFIG_FEATURE_VI_CRASHME)
Eric Andersen3f980402001-04-04 17:31:15 +0000247static Byte *string_insert(Byte *, Byte *); // insert the string at 'p'
Paul Foxb7b24d62006-04-05 14:17:24 +0000248#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_SEARCH || CONFIG_FEATURE_VI_CRASHME */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000249#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000250static Byte *text_yank(Byte *, Byte *, int); // save copy of "p" into a register
251static Byte what_reg(void); // what is letter of current YDreg
252static void check_context(Byte); // remember context for '' command
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000253#endif /* CONFIG_FEATURE_VI_YANKMARK */
254#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000255static void crash_dummy();
256static void crash_test();
257static int crashme = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000258#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000259
260
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000261static void write1(const char *out)
262{
263 fputs(out, stdout);
264}
265
Rob Landleydfba7412006-03-06 20:47:33 +0000266int vi_main(int argc, char **argv)
Eric Andersen3f980402001-04-04 17:31:15 +0000267{
Eric Andersend402edf2001-04-04 19:29:48 +0000268 int c;
Paul Fox8552aec2005-09-16 12:20:05 +0000269 RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN);
Eric Andersen3f980402001-04-04 17:31:15 +0000270
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000271#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000272 int i;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000273#endif /* CONFIG_FEATURE_VI_YANKMARK */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000274#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
275 my_pid = getpid();
276#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000277#ifdef CONFIG_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000278 (void) srand((long) my_pid);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000279#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000280
Eric Andersena68ea1c2006-01-30 22:48:39 +0000281 status_buffer = (Byte *)STATUS_BUFFER;
Paul Fox8552aec2005-09-16 12:20:05 +0000282 last_status_cksum = 0;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000283
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000284#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000285 vi_readonly = readonly = FALSE;
Eric Andersen3f980402001-04-04 17:31:15 +0000286 if (strncmp(argv[0], "view", 4) == 0) {
287 readonly = TRUE;
Eric Andersen822c3832001-05-07 17:37:43 +0000288 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000289 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000290#endif /* CONFIG_FEATURE_VI_READONLY */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000291 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000292#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000293 for (i = 0; i < 28; i++) {
294 reg[i] = 0;
295 } // init the yank regs
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000296#endif /* CONFIG_FEATURE_VI_YANKMARK */
Tim Riker86c76a92002-04-26 07:41:22 +0000297#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
Eric Andersen3f980402001-04-04 17:31:15 +0000298 modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000299#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000300
301 // 1- process $HOME/.exrc file
302 // 2- process EXINIT variable from environment
303 // 3- process command line args
304 while ((c = getopt(argc, argv, "hCR")) != -1) {
305 switch (c) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000306#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000307 case 'C':
308 crashme = 1;
309 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000310#endif /* CONFIG_FEATURE_VI_CRASHME */
311#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +0000312 case 'R': // Read-only flag
313 readonly = TRUE;
Eric Andersen90fb65f2004-03-31 11:12:51 +0000314 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000315 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000316#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen822c3832001-05-07 17:37:43 +0000317 //case 'r': // recover flag- ignore- we don't use tmp file
318 //case 'x': // encryption flag- ignore
319 //case 'c': // execute command first
320 //case 'h': // help -- just use default
Eric Andersen3f980402001-04-04 17:31:15 +0000321 default:
322 show_help();
Eric Andersendd8500b2001-07-02 18:06:14 +0000323 return 1;
Eric Andersen3f980402001-04-04 17:31:15 +0000324 }
325 }
326
327 // The argv array can be used by the ":next" and ":rewind" commands
328 // save optind.
329 fn_start = optind; // remember first file name for :next and :rew
330 save_argc = argc;
331
332 //----- This is the main file handling loop --------------
333 if (optind >= argc) {
334 editing = 1; // 0= exit, 1= one file, 2= multiple files
335 edit_file(0);
336 } else {
337 for (; optind < argc; optind++) {
338 editing = 1; // 0=exit, 1=one file, 2+ =many files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000339 free(cfn);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000340 cfn = (Byte *) xstrdup(argv[optind]);
Eric Andersen3f980402001-04-04 17:31:15 +0000341 edit_file(cfn);
342 }
343 }
344 //-----------------------------------------------------------
345
346 return (0);
347}
348
349static void edit_file(Byte * fn)
350{
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000351 Byte c;
Eric Andersen1c0d3112001-04-16 15:46:44 +0000352 int cnt, size, ch;
Eric Andersen3f980402001-04-04 17:31:15 +0000353
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000354#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000355 int sig;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000356#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
357#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000358 static Byte *cur_line;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000359#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000360
361 rawmode();
362 rows = 24;
363 columns = 80;
Eric Andersen822c3832001-05-07 17:37:43 +0000364 ch= -1;
Rob Landleye5e1a102006-06-21 01:15:36 +0000365 if (ENABLE_FEATURE_VI_WIN_RESIZE)
366 get_terminal_width_height(0, &columns, &rows);
Eric Andersen3f980402001-04-04 17:31:15 +0000367 new_screen(rows, columns); // get memory for virtual screen
368
369 cnt = file_size(fn); // file size
370 size = 2 * cnt; // 200% of file size
371 new_text(size); // get a text[] buffer
372 screenbegin = dot = end = text;
373 if (fn != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000374 ch= file_insert(fn, text, cnt);
375 }
376 if (ch < 1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000377 (void) char_insert(text, '\n'); // start empty buf with dummy line
378 }
Paul Fox8552aec2005-09-16 12:20:05 +0000379 file_modified = 0;
380 last_file_modified = -1;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000381#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000382 YDreg = 26; // default Yank/Delete reg
383 Ureg = 27; // hold orig line for "U" cmd
384 for (cnt = 0; cnt < 28; cnt++) {
385 mark[cnt] = 0;
386 } // init the marks
387 mark[26] = mark[27] = text; // init "previous context"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000388#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000389
Eric Andersen3f980402001-04-04 17:31:15 +0000390 last_forward_char = last_input_char = '\0';
391 crow = 0;
392 ccol = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000393
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000394#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000395 catch_sig(0);
Eric Andersen3f980402001-04-04 17:31:15 +0000396 signal(SIGWINCH, winch_sig);
397 signal(SIGTSTP, suspend_sig);
398 sig = setjmp(restart);
399 if (sig != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000400 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000401 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000402#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +0000403
404 editing = 1;
405 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
406 cmdcnt = 0;
407 tabstop = 8;
408 offset = 0; // no horizontal offset
409 c = '\0';
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000410#ifdef CONFIG_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000411 free(last_modifying_cmd);
412 free(ioq_start);
Eric Andersen3f980402001-04-04 17:31:15 +0000413 ioq = ioq_start = last_modifying_cmd = 0;
414 adding2q = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000415#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen822c3832001-05-07 17:37:43 +0000416 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000417 show_status_line();
418
419 //------This is the main Vi cmd handling loop -----------------------
420 while (editing > 0) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000421#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000422 if (crashme > 0) {
423 if ((end - text) > 1) {
424 crash_dummy(); // generate a random command
425 } else {
426 crashme = 0;
427 dot =
428 string_insert(text, (Byte *) "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
429 refresh(FALSE);
430 }
431 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000432#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000433 last_input_char = c = get_one_char(); // get a cmd from user
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000434#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000435 // save a copy of the current line- for the 'U" command
436 if (begin_line(dot) != cur_line) {
437 cur_line = begin_line(dot);
438 text_yank(begin_line(dot), end_line(dot), Ureg);
439 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000440#endif /* CONFIG_FEATURE_VI_YANKMARK */
441#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000442 // These are commands that change text[].
443 // Remember the input for the "." command
444 if (!adding2q && ioq_start == 0
445 && strchr((char *) modifying_cmds, c) != NULL) {
446 start_new_cmd_q(c);
447 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000448#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000449 do_cmd(c); // execute the user command
450 //
451 // poll to see if there is input already waiting. if we are
452 // not able to display output fast enough to keep up, skip
453 // the display update until we catch up with input.
454 if (mysleep(0) == 0) {
455 // no input pending- so update output
456 refresh(FALSE);
457 show_status_line();
458 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000459#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000460 if (crashme > 0)
461 crash_test(); // test editor variables
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000462#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000463 }
464 //-------------------------------------------------------------------
465
Eric Andersen822c3832001-05-07 17:37:43 +0000466 place_cursor(rows, 0, FALSE); // go to bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +0000467 clear_to_eol(); // Erase to end of line
468 cookmode();
469}
470
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000471//----- The Colon commands -------------------------------------
472#ifdef CONFIG_FEATURE_VI_COLON
473static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present
474{
475 int st;
476 Byte *q;
477
478#ifdef CONFIG_FEATURE_VI_YANKMARK
479 Byte c;
480#endif /* CONFIG_FEATURE_VI_YANKMARK */
481#ifdef CONFIG_FEATURE_VI_SEARCH
482 Byte *pat, buf[BUFSIZ];
483#endif /* CONFIG_FEATURE_VI_SEARCH */
484
485 *addr = -1; // assume no addr
486 if (*p == '.') { // the current line
487 p++;
488 q = begin_line(dot);
489 *addr = count_lines(text, q);
490#ifdef CONFIG_FEATURE_VI_YANKMARK
491 } else if (*p == '\'') { // is this a mark addr
492 p++;
493 c = tolower(*p);
494 p++;
495 if (c >= 'a' && c <= 'z') {
496 // we have a mark
497 c = c - 'a';
498 q = mark[(int) c];
499 if (q != NULL) { // is mark valid
500 *addr = count_lines(text, q); // count lines
501 }
502 }
503#endif /* CONFIG_FEATURE_VI_YANKMARK */
504#ifdef CONFIG_FEATURE_VI_SEARCH
505 } else if (*p == '/') { // a search pattern
506 q = buf;
507 for (p++; *p; p++) {
508 if (*p == '/')
509 break;
510 *q++ = *p;
511 *q = '\0';
512 }
Rob Landleyd921b2e2006-08-03 15:41:12 +0000513 pat = (Byte *) xstrdup((char *) buf); // save copy of pattern
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000514 if (*p == '/')
515 p++;
516 q = char_search(dot, pat, FORWARD, FULL);
517 if (q != NULL) {
518 *addr = count_lines(text, q);
519 }
520 free(pat);
521#endif /* CONFIG_FEATURE_VI_SEARCH */
522 } else if (*p == '$') { // the last line in file
523 p++;
524 q = begin_line(end - 1);
525 *addr = count_lines(text, q);
526 } else if (isdigit(*p)) { // specific line number
527 sscanf((char *) p, "%d%n", addr, &st);
528 p += st;
529 } else { // I don't reconise this
530 // unrecognised address- assume -1
531 *addr = -1;
532 }
533 return (p);
534}
535
536static Byte *get_address(Byte *p, int *b, int *e) // get two colon addrs, if present
537{
538 //----- get the address' i.e., 1,3 'a,'b -----
539 // get FIRST addr, if present
540 while (isblnk(*p))
541 p++; // skip over leading spaces
542 if (*p == '%') { // alias for 1,$
543 p++;
544 *b = 1;
545 *e = count_lines(text, end-1);
546 goto ga0;
547 }
548 p = get_one_address(p, b);
549 while (isblnk(*p))
550 p++;
Eric Andersenaff114c2004-04-14 17:51:38 +0000551 if (*p == ',') { // is there a address separator
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000552 p++;
553 while (isblnk(*p))
554 p++;
555 // get SECOND addr, if present
556 p = get_one_address(p, e);
557 }
558ga0:
559 while (isblnk(*p))
560 p++; // skip over trailing spaces
561 return (p);
562}
563
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000564#ifdef CONFIG_FEATURE_VI_SETOPTS
565static void setops(const Byte *args, const char *opname, int flg_no,
566 const char *short_opname, int opt)
567{
568 const char *a = (char *) args + flg_no;
569 int l = strlen(opname) - 1; /* opname have + ' ' */
570
571 if (strncasecmp(a, opname, l) == 0 ||
572 strncasecmp(a, short_opname, 2) == 0) {
573 if(flg_no)
574 vi_setops &= ~opt;
575 else
576 vi_setops |= opt;
577 }
578}
579#endif
580
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000581static void colon(Byte * buf)
582{
583 Byte c, *orig_buf, *buf1, *q, *r;
584 Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +0000585 int i, l, li, ch, b, e;
Eric Andersena9eb33d2004-08-19 19:15:06 +0000586 int useforce = FALSE, forced = FALSE;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000587 struct stat st_buf;
588
589 // :3154 // if (-e line 3154) goto it else stay put
590 // :4,33w! foo // write a portion of buffer to file "foo"
591 // :w // write all of buffer to current file
592 // :q // quit
593 // :q! // quit- dont care about modified file
594 // :'a,'z!sort -u // filter block through sort
595 // :'f // goto mark "f"
596 // :'fl // list literal the mark "f" line
597 // :.r bar // read file "bar" into buffer before dot
598 // :/123/,/abc/d // delete lines from "123" line to "abc" line
599 // :/xyz/ // goto the "xyz" line
600 // :s/find/replace/ // substitute pattern "find" with "replace"
601 // :!<cmd> // run <cmd> then return
602 //
Eric Andersen165e8cb2004-07-20 06:44:46 +0000603
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000604 if (strlen((char *) buf) <= 0)
605 goto vc1;
606 if (*buf == ':')
607 buf++; // move past the ':'
608
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +0000609 li = ch = i = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000610 b = e = -1;
611 q = text; // assume 1,$ for the range
612 r = end - 1;
613 li = count_lines(text, end - 1);
614 fn = cfn; // default to current file
615 memset(cmd, '\0', BUFSIZ); // clear cmd[]
616 memset(args, '\0', BUFSIZ); // clear args[]
617
618 // look for optional address(es) :. :1 :1,9 :'q,'a :%
619 buf = get_address(buf, &b, &e);
620
621 // remember orig command line
622 orig_buf = buf;
623
624 // get the COMMAND into cmd[]
625 buf1 = cmd;
626 while (*buf != '\0') {
627 if (isspace(*buf))
628 break;
629 *buf1++ = *buf++;
630 }
631 // get any ARGuments
632 while (isblnk(*buf))
633 buf++;
634 strcpy((char *) args, (char *) buf);
Eric Andersena68ea1c2006-01-30 22:48:39 +0000635 buf1 = (Byte*)last_char_is((char *)cmd, '!');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000636 if (buf1) {
637 useforce = TRUE;
638 *buf1 = '\0'; // get rid of !
639 }
640 if (b >= 0) {
641 // if there is only one addr, then the addr
642 // is the line number of the single line the
643 // user wants. So, reset the end
644 // pointer to point at end of the "b" line
645 q = find_line(b); // what line is #b
646 r = end_line(q);
647 li = 1;
648 }
649 if (e >= 0) {
650 // we were given two addrs. change the
651 // end pointer to the addr given by user.
652 r = find_line(e); // what line is #e
653 r = end_line(r);
654 li = e - b + 1;
655 }
656 // ------------ now look for the command ------------
657 i = strlen((char *) cmd);
658 if (i == 0) { // :123CR goto line #123
659 if (b >= 0) {
660 dot = find_line(b); // what line is #b
661 dot_skip_over_ws();
662 }
663 } else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd
664 // :!ls run the <cmd>
665 (void) alarm(0); // wait for input- no alarms
666 place_cursor(rows - 1, 0, FALSE); // go to Status line
667 clear_to_eol(); // clear the line
668 cookmode();
Eric Andersena68ea1c2006-01-30 22:48:39 +0000669 system((char*)(orig_buf+1)); // run the cmd
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000670 rawmode();
671 Hit_Return(); // let user see results
672 (void) alarm(3); // done waiting for input
673 } else if (strncmp((char *) cmd, "=", i) == 0) { // where is the address
674 if (b < 0) { // no addr given- use defaults
675 b = e = count_lines(text, dot);
676 }
677 psb("%d", b);
678 } else if (strncasecmp((char *) cmd, "delete", i) == 0) { // delete lines
679 if (b < 0) { // no addr given- use defaults
680 q = begin_line(dot); // assume .,. for the range
681 r = end_line(dot);
682 }
683 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
684 dot_skip_over_ws();
685 } else if (strncasecmp((char *) cmd, "edit", i) == 0) { // Edit a file
686 int sr;
687 sr= 0;
688 // don't edit, if the current file has been modified
689 if (file_modified && ! useforce) {
690 psbs("No write since last change (:edit! overrides)");
691 goto vc1;
692 }
Eric Andersena68ea1c2006-01-30 22:48:39 +0000693 if (strlen((char*)args) > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000694 // the user supplied a file name
695 fn= args;
Eric Andersena68ea1c2006-01-30 22:48:39 +0000696 } else if (cfn != 0 && strlen((char*)cfn) > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000697 // no user supplied name- use the current filename
698 fn= cfn;
699 goto vc5;
700 } else {
701 // no user file name, no current name- punt
702 psbs("No current filename");
703 goto vc1;
704 }
705
706 // see if file exists- if not, its just a new file request
707 if ((sr=stat((char*)fn, &st_buf)) < 0) {
708 // This is just a request for a new file creation.
709 // The file_insert below will fail but we get
710 // an empty buffer with a file name. Then the "write"
711 // command can do the create.
712 } else {
713 if ((st_buf.st_mode & (S_IFREG)) == 0) {
714 // This is not a regular file
715 psbs("\"%s\" is not a regular file", fn);
716 goto vc1;
717 }
718 if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
719 // dont have any read permissions
720 psbs("\"%s\" is not readable", fn);
721 goto vc1;
722 }
723 }
724
725 // There is a read-able regular file
726 // make this the current file
Rob Landleyd921b2e2006-08-03 15:41:12 +0000727 q = (Byte *) xstrdup((char *) fn); // save the cfn
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000728 free(cfn); // free the old name
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000729 cfn = q; // remember new cfn
730
731 vc5:
732 // delete all the contents of text[]
733 new_text(2 * file_size(fn));
734 screenbegin = dot = end = text;
735
736 // insert new file
737 ch = file_insert(fn, text, file_size(fn));
738
739 if (ch < 1) {
740 // start empty buf with dummy line
741 (void) char_insert(text, '\n');
742 ch= 1;
743 }
Paul Fox8552aec2005-09-16 12:20:05 +0000744 file_modified = 0;
745 last_file_modified = -1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000746#ifdef CONFIG_FEATURE_VI_YANKMARK
747 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
748 free(reg[Ureg]); // free orig line reg- for 'U'
749 reg[Ureg]= 0;
750 }
751 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
752 free(reg[YDreg]); // free default yank/delete register
753 reg[YDreg]= 0;
754 }
755 for (li = 0; li < 28; li++) {
756 mark[li] = 0;
757 } // init the marks
758#endif /* CONFIG_FEATURE_VI_YANKMARK */
759 // how many lines in text[]?
760 li = count_lines(text, end - 1);
761 psb("\"%s\"%s"
762#ifdef CONFIG_FEATURE_VI_READONLY
763 "%s"
764#endif /* CONFIG_FEATURE_VI_READONLY */
765 " %dL, %dC", cfn,
766 (sr < 0 ? " [New file]" : ""),
767#ifdef CONFIG_FEATURE_VI_READONLY
768 ((vi_readonly || readonly) ? " [Read only]" : ""),
769#endif /* CONFIG_FEATURE_VI_READONLY */
770 li, ch);
771 } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this
772 if (b != -1 || e != -1) {
773 ni((Byte *) "No address allowed on this command");
774 goto vc1;
775 }
776 if (strlen((char *) args) > 0) {
777 // user wants a new filename
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000778 free(cfn);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000779 cfn = (Byte *) xstrdup((char *) args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000780 } else {
781 // user wants file status info
Paul Fox8552aec2005-09-16 12:20:05 +0000782 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000783 }
784 } else if (strncasecmp((char *) cmd, "features", i) == 0) { // what features are available
785 // print out values of all features
786 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
787 clear_to_eol(); // clear the line
788 cookmode();
789 show_help();
790 rawmode();
791 Hit_Return();
792 } else if (strncasecmp((char *) cmd, "list", i) == 0) { // literal print line
793 if (b < 0) { // no addr given- use defaults
794 q = begin_line(dot); // assume .,. for the range
795 r = end_line(dot);
796 }
797 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
798 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000799 puts("\r");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000800 for (; q <= r; q++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000801 int c_is_no_print;
802
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000803 c = *q;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000804 c_is_no_print = c > 127 && !Isprint(c);
805 if (c_is_no_print) {
806 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000807 standout_start();
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000808 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000809 if (c == '\n') {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000810 write1("$\r");
811 } else if (c < ' ' || c == 127) {
812 putchar('^');
813 if(c == 127)
814 c = '?';
815 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000816 c += '@';
817 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000818 putchar(c);
819 if (c_is_no_print)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000820 standout_end();
821 }
822#ifdef CONFIG_FEATURE_VI_SET
823 vc2:
824#endif /* CONFIG_FEATURE_VI_SET */
825 Hit_Return();
826 } else if ((strncasecmp((char *) cmd, "quit", i) == 0) || // Quit
827 (strncasecmp((char *) cmd, "next", i) == 0)) { // edit next file
828 if (useforce) {
829 // force end of argv list
830 if (*cmd == 'q') {
831 optind = save_argc;
832 }
833 editing = 0;
834 goto vc1;
835 }
836 // don't exit if the file been modified
837 if (file_modified) {
838 psbs("No write since last change (:%s! overrides)",
839 (*cmd == 'q' ? "quit" : "next"));
840 goto vc1;
841 }
842 // are there other file to edit
843 if (*cmd == 'q' && optind < save_argc - 1) {
844 psbs("%d more file to edit", (save_argc - optind - 1));
845 goto vc1;
846 }
847 if (*cmd == 'n' && optind >= save_argc - 1) {
848 psbs("No more files to edit");
849 goto vc1;
850 }
851 editing = 0;
852 } else if (strncasecmp((char *) cmd, "read", i) == 0) { // read file into text[]
853 fn = args;
854 if (strlen((char *) fn) <= 0) {
855 psbs("No filename given");
856 goto vc1;
857 }
858 if (b < 0) { // no addr given- use defaults
859 q = begin_line(dot); // assume "dot"
860 }
861 // read after current line- unless user said ":0r foo"
862 if (b != 0)
863 q = next_line(q);
864#ifdef CONFIG_FEATURE_VI_READONLY
865 l= readonly; // remember current files' status
866#endif
867 ch = file_insert(fn, q, file_size(fn));
868#ifdef CONFIG_FEATURE_VI_READONLY
869 readonly= l;
870#endif
871 if (ch < 0)
872 goto vc1; // nothing was inserted
873 // how many lines in text[]?
874 li = count_lines(q, q + ch - 1);
875 psb("\"%s\""
876#ifdef CONFIG_FEATURE_VI_READONLY
877 "%s"
878#endif /* CONFIG_FEATURE_VI_READONLY */
879 " %dL, %dC", fn,
880#ifdef CONFIG_FEATURE_VI_READONLY
881 ((vi_readonly || readonly) ? " [Read only]" : ""),
882#endif /* CONFIG_FEATURE_VI_READONLY */
883 li, ch);
884 if (ch > 0) {
885 // if the insert is before "dot" then we need to update
886 if (q <= dot)
887 dot += ch;
Paul Fox8552aec2005-09-16 12:20:05 +0000888 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000889 }
890 } else if (strncasecmp((char *) cmd, "rewind", i) == 0) { // rewind cmd line args
891 if (file_modified && ! useforce) {
892 psbs("No write since last change (:rewind! overrides)");
893 } else {
894 // reset the filenames to edit
895 optind = fn_start - 1;
896 editing = 0;
897 }
898#ifdef CONFIG_FEATURE_VI_SET
899 } else if (strncasecmp((char *) cmd, "set", i) == 0) { // set or clear features
900 i = 0; // offset into args
901 if (strlen((char *) args) == 0) {
902 // print out values of all options
903 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
904 clear_to_eol(); // clear the line
905 printf("----------------------------------------\r\n");
906#ifdef CONFIG_FEATURE_VI_SETOPTS
907 if (!autoindent)
908 printf("no");
909 printf("autoindent ");
910 if (!err_method)
911 printf("no");
912 printf("flash ");
913 if (!ignorecase)
914 printf("no");
915 printf("ignorecase ");
916 if (!showmatch)
917 printf("no");
918 printf("showmatch ");
919 printf("tabstop=%d ", tabstop);
920#endif /* CONFIG_FEATURE_VI_SETOPTS */
921 printf("\r\n");
922 goto vc2;
923 }
924 if (strncasecmp((char *) args, "no", 2) == 0)
925 i = 2; // ":set noautoindent"
926#ifdef CONFIG_FEATURE_VI_SETOPTS
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000927 setops(args, "autoindent ", i, "ai", VI_AUTOINDENT);
928 setops(args, "flash ", i, "fl", VI_ERR_METHOD);
929 setops(args, "ignorecase ", i, "ic", VI_IGNORECASE);
930 setops(args, "showmatch ", i, "ic", VI_SHOWMATCH);
931 if (strncasecmp((char *) args + i, "tabstop=%d ", 7) == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000932 sscanf(strchr((char *) args + i, '='), "=%d", &ch);
933 if (ch > 0 && ch < columns - 1)
934 tabstop = ch;
935 }
936#endif /* CONFIG_FEATURE_VI_SETOPTS */
937#endif /* CONFIG_FEATURE_VI_SET */
938#ifdef CONFIG_FEATURE_VI_SEARCH
939 } else if (strncasecmp((char *) cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
940 Byte *ls, *F, *R;
941 int gflag;
942
943 // F points to the "find" pattern
944 // R points to the "replace" pattern
945 // replace the cmd line delimiters "/" with NULLs
946 gflag = 0; // global replace flag
947 c = orig_buf[1]; // what is the delimiter
948 F = orig_buf + 2; // start of "find"
949 R = (Byte *) strchr((char *) F, c); // middle delimiter
950 if (!R) goto colon_s_fail;
951 *R++ = '\0'; // terminate "find"
952 buf1 = (Byte *) strchr((char *) R, c);
953 if (!buf1) goto colon_s_fail;
954 *buf1++ = '\0'; // terminate "replace"
955 if (*buf1 == 'g') { // :s/foo/bar/g
956 buf1++;
957 gflag++; // turn on gflag
958 }
959 q = begin_line(q);
960 if (b < 0) { // maybe :s/foo/bar/
961 q = begin_line(dot); // start with cur line
962 b = count_lines(text, q); // cur line number
963 }
964 if (e < 0)
965 e = b; // maybe :.s/foo/bar/
966 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
967 ls = q; // orig line start
968 vc4:
969 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
970 if (buf1 != NULL) {
971 // we found the "find" pattern- delete it
972 (void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
973 // inset the "replace" patern
974 (void) string_insert(buf1, R); // insert the string
975 // check for "global" :s/foo/bar/g
976 if (gflag == 1) {
977 if ((buf1 + strlen((char *) R)) < end_line(ls)) {
978 q = buf1 + strlen((char *) R);
979 goto vc4; // don't let q move past cur line
980 }
981 }
982 }
983 q = next_line(ls);
984 }
985#endif /* CONFIG_FEATURE_VI_SEARCH */
986 } else if (strncasecmp((char *) cmd, "version", i) == 0) { // show software version
Rob Landleyd921b2e2006-08-03 15:41:12 +0000987 psb("%s", BB_VER " " BB_BT);
Paul Fox9360f422006-03-27 21:51:16 +0000988 } else if (strncasecmp((char *) cmd, "write", i) == 0 // write text to file
989 || strncasecmp((char *) cmd, "wq", i) == 0
990 || strncasecmp((char *) cmd, "wn", i) == 0
991 || strncasecmp((char *) cmd, "x", i) == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000992 // is there a file name to write to?
993 if (strlen((char *) args) > 0) {
994 fn = args;
995 }
996#ifdef CONFIG_FEATURE_VI_READONLY
997 if ((vi_readonly || readonly) && ! useforce) {
998 psbs("\"%s\" File is read only", fn);
999 goto vc3;
1000 }
1001#endif /* CONFIG_FEATURE_VI_READONLY */
1002 // how many lines in text[]?
1003 li = count_lines(q, r);
1004 ch = r - q + 1;
1005 // see if file exists- if not, its just a new file request
1006 if (useforce) {
1007 // if "fn" is not write-able, chmod u+w
1008 // sprintf(syscmd, "chmod u+w %s", fn);
1009 // system(syscmd);
1010 forced = TRUE;
1011 }
1012 l = file_write(fn, q, r);
1013 if (useforce && forced) {
1014 // chmod u-w
1015 // sprintf(syscmd, "chmod u-w %s", fn);
1016 // system(syscmd);
1017 forced = FALSE;
1018 }
Paul Fox61e45db2005-10-09 14:43:22 +00001019 if (l < 0) {
1020 if (l == -1)
1021 psbs("Write error: %s", strerror(errno));
1022 } else {
1023 psb("\"%s\" %dL, %dC", fn, li, l);
1024 if (q == text && r == end - 1 && l == ch) {
1025 file_modified = 0;
1026 last_file_modified = -1;
1027 }
Paul Fox9360f422006-03-27 21:51:16 +00001028 if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' ||
1029 cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N')
1030 && l == ch) {
Paul Fox61e45db2005-10-09 14:43:22 +00001031 editing = 0;
1032 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001033 }
1034#ifdef CONFIG_FEATURE_VI_READONLY
1035 vc3:;
1036#endif /* CONFIG_FEATURE_VI_READONLY */
1037#ifdef CONFIG_FEATURE_VI_YANKMARK
1038 } else if (strncasecmp((char *) cmd, "yank", i) == 0) { // yank lines
1039 if (b < 0) { // no addr given- use defaults
1040 q = begin_line(dot); // assume .,. for the range
1041 r = end_line(dot);
1042 }
1043 text_yank(q, r, YDreg);
1044 li = count_lines(q, r);
1045 psb("Yank %d lines (%d chars) into [%c]",
1046 li, strlen((char *) reg[YDreg]), what_reg());
1047#endif /* CONFIG_FEATURE_VI_YANKMARK */
1048 } else {
1049 // cmd unknown
1050 ni((Byte *) cmd);
1051 }
1052 vc1:
1053 dot = bound_dot(dot); // make sure "dot" is valid
1054 return;
1055#ifdef CONFIG_FEATURE_VI_SEARCH
1056colon_s_fail:
1057 psb(":s expression missing delimiters");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001058#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001059}
Paul Fox61e45db2005-10-09 14:43:22 +00001060
Paul Fox90372ed2005-10-09 14:26:26 +00001061#endif /* CONFIG_FEATURE_VI_COLON */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001062
1063static void Hit_Return(void)
1064{
1065 char c;
1066
1067 standout_start(); // start reverse video
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001068 write1("[Hit return to continue]");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001069 standout_end(); // end reverse video
1070 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
1071 ;
1072 redraw(TRUE); // force redraw all
1073}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001074
1075//----- Synchronize the cursor to Dot --------------------------
1076static void sync_cursor(Byte * d, int *row, int *col)
1077{
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +00001078 Byte *beg_cur; // begin and end of "d" line
1079 Byte *end_scr; // begin and end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001080 Byte *tp;
1081 int cnt, ro, co;
1082
1083 beg_cur = begin_line(d); // first char of cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001084
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001085 end_scr = end_screen(); // last char of screen
1086
1087 if (beg_cur < screenbegin) {
1088 // "d" is before top line on screen
1089 // how many lines do we have to move
1090 cnt = count_lines(beg_cur, screenbegin);
1091 sc1:
1092 screenbegin = beg_cur;
1093 if (cnt > (rows - 1) / 2) {
1094 // we moved too many lines. put "dot" in middle of screen
1095 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1096 screenbegin = prev_line(screenbegin);
1097 }
1098 }
1099 } else if (beg_cur > end_scr) {
1100 // "d" is after bottom line on screen
1101 // how many lines do we have to move
1102 cnt = count_lines(end_scr, beg_cur);
1103 if (cnt > (rows - 1) / 2)
1104 goto sc1; // too many lines
1105 for (ro = 0; ro < cnt - 1; ro++) {
1106 // move screen begin the same amount
1107 screenbegin = next_line(screenbegin);
1108 // now, move the end of screen
1109 end_scr = next_line(end_scr);
1110 end_scr = end_line(end_scr);
1111 }
1112 }
1113 // "d" is on screen- find out which row
1114 tp = screenbegin;
1115 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1116 if (tp == beg_cur)
1117 break;
1118 tp = next_line(tp);
1119 }
1120
1121 // find out what col "d" is on
1122 co = 0;
1123 do { // drive "co" to correct column
1124 if (*tp == '\n' || *tp == '\0')
1125 break;
1126 if (*tp == '\t') {
1127 // 7 - (co % 8 )
1128 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001129 } else if (*tp < ' ' || *tp == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001130 co++; // display as ^X, use 2 columns
1131 }
1132 } while (tp++ < d && ++co);
1133
1134 // "co" is the column where "dot" is.
1135 // The screen has "columns" columns.
1136 // The currently displayed columns are 0+offset -- columns+ofset
1137 // |-------------------------------------------------------------|
1138 // ^ ^ ^
1139 // offset | |------- columns ----------------|
1140 //
1141 // If "co" is already in this range then we do not have to adjust offset
1142 // but, we do have to subtract the "offset" bias from "co".
1143 // If "co" is outside this range then we have to change "offset".
1144 // If the first char of a line is a tab the cursor will try to stay
1145 // in column 7, but we have to set offset to 0.
1146
1147 if (co < 0 + offset) {
1148 offset = co;
1149 }
1150 if (co >= columns + offset) {
1151 offset = co - columns + 1;
1152 }
1153 // if the first char of the line is a tab, and "dot" is sitting on it
1154 // force offset to 0.
1155 if (d == beg_cur && *d == '\t') {
1156 offset = 0;
1157 }
1158 co -= offset;
1159
1160 *row = ro;
1161 *col = co;
1162}
1163
1164//----- Text Movement Routines ---------------------------------
1165static Byte *begin_line(Byte * p) // return pointer to first char cur line
1166{
1167 while (p > text && p[-1] != '\n')
1168 p--; // go to cur line B-o-l
1169 return (p);
1170}
1171
1172static Byte *end_line(Byte * p) // return pointer to NL of cur line line
1173{
1174 while (p < end - 1 && *p != '\n')
1175 p++; // go to cur line E-o-l
1176 return (p);
1177}
1178
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001179static inline Byte *dollar_line(Byte * p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001180{
1181 while (p < end - 1 && *p != '\n')
1182 p++; // go to cur line E-o-l
1183 // Try to stay off of the Newline
1184 if (*p == '\n' && (p - begin_line(p)) > 0)
1185 p--;
1186 return (p);
1187}
1188
1189static Byte *prev_line(Byte * p) // return pointer first char prev line
1190{
1191 p = begin_line(p); // goto begining of cur line
1192 if (p[-1] == '\n' && p > text)
1193 p--; // step to prev line
1194 p = begin_line(p); // goto begining of prev line
1195 return (p);
1196}
1197
1198static Byte *next_line(Byte * p) // return pointer first char next line
1199{
1200 p = end_line(p);
1201 if (*p == '\n' && p < end - 1)
1202 p++; // step to next line
1203 return (p);
1204}
1205
1206//----- Text Information Routines ------------------------------
1207static Byte *end_screen(void)
1208{
1209 Byte *q;
1210 int cnt;
1211
1212 // find new bottom line
1213 q = screenbegin;
1214 for (cnt = 0; cnt < rows - 2; cnt++)
1215 q = next_line(q);
1216 q = end_line(q);
1217 return (q);
1218}
1219
1220static int count_lines(Byte * start, Byte * stop) // count line from start to stop
1221{
1222 Byte *q;
1223 int cnt;
1224
1225 if (stop < start) { // start and stop are backwards- reverse them
1226 q = start;
1227 start = stop;
1228 stop = q;
1229 }
1230 cnt = 0;
1231 stop = end_line(stop); // get to end of this line
1232 for (q = start; q <= stop && q <= end - 1; q++) {
1233 if (*q == '\n')
1234 cnt++;
1235 }
1236 return (cnt);
1237}
1238
1239static Byte *find_line(int li) // find begining of line #li
1240{
1241 Byte *q;
1242
1243 for (q = text; li > 1; li--) {
1244 q = next_line(q);
1245 }
1246 return (q);
1247}
1248
1249//----- Dot Movement Routines ----------------------------------
1250static void dot_left(void)
1251{
1252 if (dot > text && dot[-1] != '\n')
1253 dot--;
1254}
1255
1256static void dot_right(void)
1257{
1258 if (dot < end - 1 && *dot != '\n')
1259 dot++;
1260}
1261
1262static void dot_begin(void)
1263{
1264 dot = begin_line(dot); // return pointer to first char cur line
1265}
1266
1267static void dot_end(void)
1268{
1269 dot = end_line(dot); // return pointer to last char cur line
1270}
1271
1272static Byte *move_to_col(Byte * p, int l)
1273{
1274 int co;
1275
1276 p = begin_line(p);
1277 co = 0;
1278 do {
1279 if (*p == '\n' || *p == '\0')
1280 break;
1281 if (*p == '\t') {
1282 // 7 - (co % 8 )
1283 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001284 } else if (*p < ' ' || *p == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001285 co++; // display as ^X, use 2 columns
1286 }
1287 } while (++co <= l && p++ < end);
1288 return (p);
1289}
1290
1291static void dot_next(void)
1292{
1293 dot = next_line(dot);
1294}
1295
1296static void dot_prev(void)
1297{
1298 dot = prev_line(dot);
1299}
1300
1301static void dot_scroll(int cnt, int dir)
1302{
1303 Byte *q;
1304
1305 for (; cnt > 0; cnt--) {
1306 if (dir < 0) {
1307 // scroll Backwards
1308 // ctrl-Y scroll up one line
1309 screenbegin = prev_line(screenbegin);
1310 } else {
1311 // scroll Forwards
1312 // ctrl-E scroll down one line
1313 screenbegin = next_line(screenbegin);
1314 }
1315 }
1316 // make sure "dot" stays on the screen so we dont scroll off
1317 if (dot < screenbegin)
1318 dot = screenbegin;
1319 q = end_screen(); // find new bottom line
1320 if (dot > q)
1321 dot = begin_line(q); // is dot is below bottom line?
1322 dot_skip_over_ws();
1323}
1324
1325static void dot_skip_over_ws(void)
1326{
1327 // skip WS
1328 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1329 dot++;
1330}
1331
1332static void dot_delete(void) // delete the char at 'dot'
1333{
1334 (void) text_hole_delete(dot, dot);
1335}
1336
1337static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end"
1338{
1339 if (p >= end && end > text) {
1340 p = end - 1;
1341 indicate_error('1');
1342 }
1343 if (p < text) {
1344 p = text;
1345 indicate_error('2');
1346 }
1347 return (p);
1348}
1349
1350//----- Helper Utility Routines --------------------------------
1351
1352//----------------------------------------------------------------
1353//----- Char Routines --------------------------------------------
1354/* Chars that are part of a word-
1355 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1356 * Chars that are Not part of a word (stoppers)
1357 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1358 * Chars that are WhiteSpace
1359 * TAB NEWLINE VT FF RETURN SPACE
1360 * DO NOT COUNT NEWLINE AS WHITESPACE
1361 */
1362
1363static Byte *new_screen(int ro, int co)
1364{
1365 int li;
1366
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001367 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001368 screensize = ro * co + 8;
1369 screen = (Byte *) xmalloc(screensize);
1370 // initialize the new screen. assume this will be a empty file.
1371 screen_erase();
Eric Andersenaff114c2004-04-14 17:51:38 +00001372 // non-existent text[] lines start with a tilde (~).
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001373 for (li = 1; li < ro - 1; li++) {
1374 screen[(li * co) + 0] = '~';
1375 }
1376 return (screen);
1377}
1378
1379static Byte *new_text(int size)
1380{
1381 if (size < 10240)
1382 size = 10240; // have a minimum size for new files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001383 free(text);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001384 text = (Byte *) xmalloc(size + 8);
1385 memset(text, '\0', size); // clear new text[]
1386 //text += 4; // leave some room for "oops"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001387 return (text);
1388}
1389
1390#ifdef CONFIG_FEATURE_VI_SEARCH
1391static int mycmp(Byte * s1, Byte * s2, int len)
1392{
1393 int i;
1394
1395 i = strncmp((char *) s1, (char *) s2, len);
1396#ifdef CONFIG_FEATURE_VI_SETOPTS
1397 if (ignorecase) {
1398 i = strncasecmp((char *) s1, (char *) s2, len);
1399 }
1400#endif /* CONFIG_FEATURE_VI_SETOPTS */
1401 return (i);
1402}
1403
1404static Byte *char_search(Byte * p, Byte * pat, int dir, int range) // search for pattern starting at p
1405{
1406#ifndef REGEX_SEARCH
1407 Byte *start, *stop;
1408 int len;
1409
1410 len = strlen((char *) pat);
1411 if (dir == FORWARD) {
1412 stop = end - 1; // assume range is p - end-1
1413 if (range == LIMITED)
1414 stop = next_line(p); // range is to next line
1415 for (start = p; start < stop; start++) {
1416 if (mycmp(start, pat, len) == 0) {
1417 return (start);
1418 }
1419 }
1420 } else if (dir == BACK) {
1421 stop = text; // assume range is text - p
1422 if (range == LIMITED)
1423 stop = prev_line(p); // range is to prev line
1424 for (start = p - len; start >= stop; start--) {
1425 if (mycmp(start, pat, len) == 0) {
1426 return (start);
1427 }
1428 }
1429 }
1430 // pattern not found
1431 return (NULL);
1432#else /*REGEX_SEARCH */
1433 char *q;
1434 struct re_pattern_buffer preg;
1435 int i;
1436 int size, range;
1437
1438 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1439 preg.translate = 0;
1440 preg.fastmap = 0;
1441 preg.buffer = 0;
1442 preg.allocated = 0;
1443
1444 // assume a LIMITED forward search
1445 q = next_line(p);
1446 q = end_line(q);
1447 q = end - 1;
1448 if (dir == BACK) {
1449 q = prev_line(p);
1450 q = text;
1451 }
1452 // count the number of chars to search over, forward or backward
1453 size = q - p;
1454 if (size < 0)
1455 size = p - q;
1456 // RANGE could be negative if we are searching backwards
1457 range = q - p;
1458
1459 q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
1460 if (q != 0) {
1461 // The pattern was not compiled
1462 psbs("bad search pattern: \"%s\": %s", pat, q);
1463 i = 0; // return p if pattern not compiled
1464 goto cs1;
1465 }
1466
1467 q = p;
1468 if (range < 0) {
1469 q = p - size;
1470 if (q < text)
1471 q = text;
1472 }
1473 // search for the compiled pattern, preg, in p[]
1474 // range < 0- search backward
1475 // range > 0- search forward
1476 // 0 < start < size
1477 // re_search() < 0 not found or error
1478 // re_search() > 0 index of found pattern
1479 // struct pattern char int int int struct reg
1480 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1481 i = re_search(&preg, q, size, 0, range, 0);
1482 if (i == -1) {
1483 p = 0;
1484 i = 0; // return NULL if pattern not found
1485 }
1486 cs1:
1487 if (dir == FORWARD) {
1488 p = p + i;
1489 } else {
1490 p = p - i;
1491 }
1492 return (p);
1493#endif /*REGEX_SEARCH */
1494}
1495#endif /* CONFIG_FEATURE_VI_SEARCH */
1496
1497static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
1498{
1499 if (c == 22) { // Is this an ctrl-V?
1500 p = stupid_insert(p, '^'); // use ^ to indicate literal next
1501 p--; // backup onto ^
1502 refresh(FALSE); // show the ^
1503 c = get_one_char();
1504 *p = c;
1505 p++;
Paul Fox8552aec2005-09-16 12:20:05 +00001506 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001507 } else if (c == 27) { // Is this an ESC?
1508 cmd_mode = 0;
1509 cmdcnt = 0;
1510 end_cmd_q(); // stop adding to q
Paul Fox8552aec2005-09-16 12:20:05 +00001511 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001512 if ((p[-1] != '\n') && (dot>text)) {
1513 p--;
1514 }
Paul Foxd13b90b2005-07-18 22:17:25 +00001515 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001516 // 123456789
1517 if ((p[-1] != '\n') && (dot>text)) {
1518 p--;
1519 p = text_hole_delete(p, p); // shrink buffer 1 char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001520 }
1521 } else {
1522 // insert a char into text[]
1523 Byte *sp; // "save p"
1524
1525 if (c == 13)
1526 c = '\n'; // translate \r to \n
1527 sp = p; // remember addr of insert
1528 p = stupid_insert(p, c); // insert the char
1529#ifdef CONFIG_FEATURE_VI_SETOPTS
1530 if (showmatch && strchr(")]}", *sp) != NULL) {
1531 showmatching(sp);
1532 }
1533 if (autoindent && c == '\n') { // auto indent the new line
1534 Byte *q;
1535
1536 q = prev_line(p); // use prev line as templet
1537 for (; isblnk(*q); q++) {
1538 p = stupid_insert(p, *q); // insert the char
1539 }
1540 }
1541#endif /* CONFIG_FEATURE_VI_SETOPTS */
1542 }
1543 return (p);
1544}
1545
1546static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
1547{
1548 p = text_hole_make(p, 1);
1549 if (p != 0) {
1550 *p = c;
Paul Fox8552aec2005-09-16 12:20:05 +00001551 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001552 p++;
1553 }
1554 return (p);
1555}
1556
1557static Byte find_range(Byte ** start, Byte ** stop, Byte c)
1558{
1559 Byte *save_dot, *p, *q;
1560 int cnt;
1561
1562 save_dot = dot;
1563 p = q = dot;
1564
1565 if (strchr("cdy><", c)) {
1566 // these cmds operate on whole lines
1567 p = q = begin_line(p);
1568 for (cnt = 1; cnt < cmdcnt; cnt++) {
1569 q = next_line(q);
1570 }
1571 q = end_line(q);
1572 } else if (strchr("^%$0bBeEft", c)) {
1573 // These cmds operate on char positions
1574 do_cmd(c); // execute movement cmd
1575 q = dot;
1576 } else if (strchr("wW", c)) {
1577 do_cmd(c); // execute movement cmd
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001578 // if we are at the next word's first char
1579 // step back one char
1580 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00001581 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001582 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1583 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1584 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001585 if (dot > text && *dot == '\n')
1586 dot--; // stay off NL
1587 q = dot;
1588 } else if (strchr("H-k{", c)) {
1589 // these operate on multi-lines backwards
1590 q = end_line(dot); // find NL
1591 do_cmd(c); // execute movement cmd
1592 dot_begin();
1593 p = dot;
1594 } else if (strchr("L+j}\r\n", c)) {
1595 // these operate on multi-lines forwards
1596 p = begin_line(dot);
1597 do_cmd(c); // execute movement cmd
1598 dot_end(); // find NL
1599 q = dot;
1600 } else {
1601 c = 27; // error- return an ESC char
1602 //break;
1603 }
1604 *start = p;
1605 *stop = q;
1606 if (q < p) {
1607 *start = q;
1608 *stop = p;
1609 }
1610 dot = save_dot;
1611 return (c);
1612}
1613
1614static int st_test(Byte * p, int type, int dir, Byte * tested)
1615{
1616 Byte c, c0, ci;
1617 int test, inc;
1618
1619 inc = dir;
1620 c = c0 = p[0];
1621 ci = p[inc];
1622 test = 0;
1623
1624 if (type == S_BEFORE_WS) {
1625 c = ci;
1626 test = ((!isspace(c)) || c == '\n');
1627 }
1628 if (type == S_TO_WS) {
1629 c = c0;
1630 test = ((!isspace(c)) || c == '\n');
1631 }
1632 if (type == S_OVER_WS) {
1633 c = c0;
1634 test = ((isspace(c)));
1635 }
1636 if (type == S_END_PUNCT) {
1637 c = ci;
1638 test = ((ispunct(c)));
1639 }
1640 if (type == S_END_ALNUM) {
1641 c = ci;
1642 test = ((isalnum(c)) || c == '_');
1643 }
1644 *tested = c;
1645 return (test);
1646}
1647
1648static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
1649{
1650 Byte c;
1651
1652 while (st_test(p, type, dir, &c)) {
1653 // make sure we limit search to correct number of lines
1654 if (c == '\n' && --linecnt < 1)
1655 break;
1656 if (dir >= 0 && p >= end - 1)
1657 break;
1658 if (dir < 0 && p <= text)
1659 break;
1660 p += dir; // move to next char
1661 }
1662 return (p);
1663}
1664
1665// find matching char of pair () [] {}
1666static Byte *find_pair(Byte * p, Byte c)
1667{
1668 Byte match, *q;
1669 int dir, level;
1670
1671 match = ')';
1672 level = 1;
1673 dir = 1; // assume forward
1674 switch (c) {
1675 case '(':
1676 match = ')';
1677 break;
1678 case '[':
1679 match = ']';
1680 break;
1681 case '{':
1682 match = '}';
1683 break;
1684 case ')':
1685 match = '(';
1686 dir = -1;
1687 break;
1688 case ']':
1689 match = '[';
1690 dir = -1;
1691 break;
1692 case '}':
1693 match = '{';
1694 dir = -1;
1695 break;
1696 }
1697 for (q = p + dir; text <= q && q < end; q += dir) {
1698 // look for match, count levels of pairs (( ))
1699 if (*q == c)
1700 level++; // increase pair levels
1701 if (*q == match)
1702 level--; // reduce pair level
1703 if (level == 0)
1704 break; // found matching pair
1705 }
1706 if (level != 0)
1707 q = NULL; // indicate no match
1708 return (q);
1709}
1710
1711#ifdef CONFIG_FEATURE_VI_SETOPTS
1712// show the matching char of a pair, () [] {}
1713static void showmatching(Byte * p)
1714{
1715 Byte *q, *save_dot;
1716
1717 // we found half of a pair
1718 q = find_pair(p, *p); // get loc of matching char
1719 if (q == NULL) {
1720 indicate_error('3'); // no matching char
1721 } else {
1722 // "q" now points to matching pair
1723 save_dot = dot; // remember where we are
1724 dot = q; // go to new loc
1725 refresh(FALSE); // let the user see it
1726 (void) mysleep(40); // give user some time
1727 dot = save_dot; // go back to old loc
1728 refresh(FALSE);
1729 }
1730}
1731#endif /* CONFIG_FEATURE_VI_SETOPTS */
1732
1733// open a hole in text[]
1734static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
1735{
1736 Byte *src, *dest;
1737 int cnt;
1738
1739 if (size <= 0)
1740 goto thm0;
1741 src = p;
1742 dest = p + size;
1743 cnt = end - src; // the rest of buffer
1744 if (memmove(dest, src, cnt) != dest) {
1745 psbs("can't create room for new characters");
1746 }
1747 memset(p, ' ', size); // clear new hole
1748 end = end + size; // adjust the new END
Paul Fox8552aec2005-09-16 12:20:05 +00001749 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001750 thm0:
1751 return (p);
1752}
1753
1754// close a hole in text[]
1755static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
1756{
1757 Byte *src, *dest;
1758 int cnt, hole_size;
1759
1760 // move forwards, from beginning
1761 // assume p <= q
1762 src = q + 1;
1763 dest = p;
1764 if (q < p) { // they are backward- swap them
1765 src = p + 1;
1766 dest = q;
1767 }
1768 hole_size = q - p + 1;
1769 cnt = end - src;
1770 if (src < text || src > end)
1771 goto thd0;
1772 if (dest < text || dest >= end)
1773 goto thd0;
1774 if (src >= end)
1775 goto thd_atend; // just delete the end of the buffer
1776 if (memmove(dest, src, cnt) != dest) {
1777 psbs("can't delete the character");
1778 }
1779 thd_atend:
1780 end = end - hole_size; // adjust the new END
1781 if (dest >= end)
1782 dest = end - 1; // make sure dest in below end-1
1783 if (end <= text)
1784 dest = end = text; // keep pointers valid
Paul Fox8552aec2005-09-16 12:20:05 +00001785 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001786 thd0:
1787 return (dest);
1788}
1789
1790// copy text into register, then delete text.
1791// if dist <= 0, do not include, or go past, a NewLine
1792//
1793static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
1794{
1795 Byte *p;
1796
1797 // make sure start <= stop
1798 if (start > stop) {
1799 // they are backwards, reverse them
1800 p = start;
1801 start = stop;
1802 stop = p;
1803 }
1804 if (dist <= 0) {
1805 // we can not cross NL boundaries
1806 p = start;
1807 if (*p == '\n')
1808 return (p);
1809 // dont go past a NewLine
1810 for (; p + 1 <= stop; p++) {
1811 if (p[1] == '\n') {
1812 stop = p; // "stop" just before NewLine
1813 break;
1814 }
1815 }
1816 }
1817 p = start;
1818#ifdef CONFIG_FEATURE_VI_YANKMARK
1819 text_yank(start, stop, YDreg);
1820#endif /* CONFIG_FEATURE_VI_YANKMARK */
1821 if (yf == YANKDEL) {
1822 p = text_hole_delete(start, stop);
1823 } // delete lines
1824 return (p);
1825}
1826
1827static void show_help(void)
1828{
1829 puts("These features are available:"
1830#ifdef CONFIG_FEATURE_VI_SEARCH
1831 "\n\tPattern searches with / and ?"
1832#endif /* CONFIG_FEATURE_VI_SEARCH */
1833#ifdef CONFIG_FEATURE_VI_DOT_CMD
1834 "\n\tLast command repeat with \'.\'"
1835#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1836#ifdef CONFIG_FEATURE_VI_YANKMARK
1837 "\n\tLine marking with 'x"
1838 "\n\tNamed buffers with \"x"
1839#endif /* CONFIG_FEATURE_VI_YANKMARK */
1840#ifdef CONFIG_FEATURE_VI_READONLY
1841 "\n\tReadonly if vi is called as \"view\""
1842 "\n\tReadonly with -R command line arg"
1843#endif /* CONFIG_FEATURE_VI_READONLY */
1844#ifdef CONFIG_FEATURE_VI_SET
1845 "\n\tSome colon mode commands with \':\'"
1846#endif /* CONFIG_FEATURE_VI_SET */
1847#ifdef CONFIG_FEATURE_VI_SETOPTS
1848 "\n\tSettable options with \":set\""
1849#endif /* CONFIG_FEATURE_VI_SETOPTS */
1850#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
1851 "\n\tSignal catching- ^C"
1852 "\n\tJob suspend and resume with ^Z"
1853#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
1854#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
1855 "\n\tAdapt to window re-sizes"
1856#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
1857 );
1858}
1859
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001860static inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001861{
1862 Byte c, b[2];
1863
1864 b[1] = '\0';
1865 strcpy((char *) buf, ""); // init buf
1866 if (strlen((char *) s) <= 0)
1867 s = (Byte *) "(NULL)";
1868 for (; *s > '\0'; s++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001869 int c_is_no_print;
1870
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001871 c = *s;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001872 c_is_no_print = c > 127 && !Isprint(c);
1873 if (c_is_no_print) {
1874 strcat((char *) buf, SOn);
1875 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001876 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001877 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001878 strcat((char *) buf, "^");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001879 if(c == 127)
1880 c = '?';
1881 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001882 c += '@';
1883 }
1884 b[0] = c;
1885 strcat((char *) buf, (char *) b);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001886 if (c_is_no_print)
1887 strcat((char *) buf, SOs);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001888 if (*s == '\n') {
1889 strcat((char *) buf, "$");
1890 }
1891 }
1892}
1893
1894#ifdef CONFIG_FEATURE_VI_DOT_CMD
1895static void start_new_cmd_q(Byte c)
1896{
1897 // release old cmd
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001898 free(last_modifying_cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001899 // get buffer for new cmd
1900 last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
1901 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
1902 // if there is a current cmd count put it in the buffer first
1903 if (cmdcnt > 0)
Paul Foxd957b952005-11-28 18:07:53 +00001904 sprintf((char *) last_modifying_cmd, "%d%c", cmdcnt, c);
1905 else // just save char c onto queue
1906 last_modifying_cmd[0] = c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001907 adding2q = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001908}
1909
1910static void end_cmd_q(void)
1911{
1912#ifdef CONFIG_FEATURE_VI_YANKMARK
1913 YDreg = 26; // go back to default Yank/Delete reg
1914#endif /* CONFIG_FEATURE_VI_YANKMARK */
1915 adding2q = 0;
1916 return;
1917}
1918#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1919
Bernhard Reutner-Fischer1e23b6f2006-06-09 07:12:27 +00001920#if defined(CONFIG_FEATURE_VI_YANKMARK) || (defined(CONFIG_FEATURE_VI_COLON) && defined(CONFIG_FEATURE_VI_SEARCH)) || defined(CONFIG_FEATURE_VI_CRASHME)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001921static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
1922{
1923 int cnt, i;
1924
1925 i = strlen((char *) s);
1926 p = text_hole_make(p, i);
1927 strncpy((char *) p, (char *) s, i);
1928 for (cnt = 0; *s != '\0'; s++) {
1929 if (*s == '\n')
1930 cnt++;
1931 }
1932#ifdef CONFIG_FEATURE_VI_YANKMARK
1933 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
1934#endif /* CONFIG_FEATURE_VI_YANKMARK */
1935 return (p);
1936}
1937#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
1938
1939#ifdef CONFIG_FEATURE_VI_YANKMARK
1940static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register
1941{
1942 Byte *t;
1943 int cnt;
1944
1945 if (q < p) { // they are backwards- reverse them
1946 t = q;
1947 q = p;
1948 p = t;
1949 }
1950 cnt = q - p + 1;
1951 t = reg[dest];
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001952 free(t); // if already a yank register, free it
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001953 t = (Byte *) xmalloc(cnt + 1); // get a new register
1954 memset(t, '\0', cnt + 1); // clear new text[]
1955 strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer
1956 reg[dest] = t;
1957 return (p);
1958}
1959
1960static Byte what_reg(void)
1961{
1962 Byte c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001963
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001964 c = 'D'; // default to D-reg
1965 if (0 <= YDreg && YDreg <= 25)
1966 c = 'a' + (Byte) YDreg;
1967 if (YDreg == 26)
1968 c = 'D';
1969 if (YDreg == 27)
1970 c = 'U';
1971 return (c);
1972}
1973
1974static void check_context(Byte cmd)
1975{
1976 // A context is defined to be "modifying text"
1977 // Any modifying command establishes a new context.
1978
1979 if (dot < context_start || dot > context_end) {
1980 if (strchr((char *) modifying_cmds, cmd) != NULL) {
1981 // we are trying to modify text[]- make this the current context
1982 mark[27] = mark[26]; // move cur to prev
1983 mark[26] = dot; // move local to cur
1984 context_start = prev_line(prev_line(dot));
1985 context_end = next_line(next_line(dot));
1986 //loiter= start_loiter= now;
1987 }
1988 }
1989 return;
1990}
1991
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001992static inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001993{
1994 Byte *tmp;
1995
1996 // the current context is in mark[26]
1997 // the previous context is in mark[27]
1998 // only swap context if other context is valid
1999 if (text <= mark[27] && mark[27] <= end - 1) {
2000 tmp = mark[27];
2001 mark[27] = mark[26];
2002 mark[26] = tmp;
2003 p = mark[26]; // where we are going- previous context
2004 context_start = prev_line(prev_line(prev_line(p)));
2005 context_end = next_line(next_line(next_line(p)));
2006 }
2007 return (p);
2008}
2009#endif /* CONFIG_FEATURE_VI_YANKMARK */
2010
2011static int isblnk(Byte c) // is the char a blank or tab
2012{
2013 return (c == ' ' || c == '\t');
2014}
2015
2016//----- Set terminal attributes --------------------------------
2017static void rawmode(void)
2018{
2019 tcgetattr(0, &term_orig);
2020 term_vi = term_orig;
2021 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2022 term_vi.c_iflag &= (~IXON & ~ICRNL);
2023 term_vi.c_oflag &= (~ONLCR);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002024 term_vi.c_cc[VMIN] = 1;
2025 term_vi.c_cc[VTIME] = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002026 erase_char = term_vi.c_cc[VERASE];
2027 tcsetattr(0, TCSANOW, &term_vi);
2028}
2029
2030static void cookmode(void)
2031{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002032 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002033 tcsetattr(0, TCSANOW, &term_orig);
2034}
2035
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002036//----- Come here when we get a window resize signal ---------
2037#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002038static void winch_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002039{
2040 signal(SIGWINCH, winch_sig);
Rob Landleye5e1a102006-06-21 01:15:36 +00002041 if (ENABLE_FEATURE_VI_WIN_RESIZE)
2042 get_terminal_width_height(0, &columns, &rows);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002043 new_screen(rows, columns); // get memory for virtual screen
2044 redraw(TRUE); // re-draw the screen
2045}
2046
2047//----- Come here when we get a continue signal -------------------
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002048static void cont_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002049{
2050 rawmode(); // terminal to "raw"
Paul Fox8552aec2005-09-16 12:20:05 +00002051 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002052 redraw(TRUE); // re-draw the screen
2053
2054 signal(SIGTSTP, suspend_sig);
2055 signal(SIGCONT, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002056 kill(my_pid, SIGCONT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002057}
2058
2059//----- Come here when we get a Suspend signal -------------------
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002060static void suspend_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002061{
2062 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2063 clear_to_eol(); // Erase to end of line
2064 cookmode(); // terminal to "cooked"
2065
2066 signal(SIGCONT, cont_sig);
2067 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002068 kill(my_pid, SIGTSTP);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002069}
2070
2071//----- Come here when we get a signal ---------------------------
2072static void catch_sig(int sig)
2073{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002074 signal(SIGINT, catch_sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002075 if(sig)
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002076 longjmp(restart, sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002077}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002078#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2079
2080static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2081{
2082 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002083 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002084 FD_ZERO(&rfds);
2085 FD_SET(0, &rfds);
2086 tv.tv_sec = 0;
2087 tv.tv_usec = hund * 10000;
2088 select(1, &rfds, NULL, NULL, &tv);
2089 return (FD_ISSET(0, &rfds));
2090}
2091
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002092#define readbuffer bb_common_bufsiz1
2093
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002094static int readed_for_parse;
2095
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002096//----- IO Routines --------------------------------------------
2097static Byte readit(void) // read (maybe cursor) key from stdin
2098{
2099 Byte c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002100 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002101 struct esc_cmds {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002102 const char *seq;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002103 Byte val;
2104 };
2105
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002106 static const struct esc_cmds esccmds[] = {
2107 {"OA", (Byte) VI_K_UP}, // cursor key Up
2108 {"OB", (Byte) VI_K_DOWN}, // cursor key Down
2109 {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2110 {"OD", (Byte) VI_K_LEFT}, // cursor key Left
2111 {"OH", (Byte) VI_K_HOME}, // Cursor Key Home
2112 {"OF", (Byte) VI_K_END}, // Cursor Key End
2113 {"[A", (Byte) VI_K_UP}, // cursor key Up
2114 {"[B", (Byte) VI_K_DOWN}, // cursor key Down
2115 {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right
2116 {"[D", (Byte) VI_K_LEFT}, // cursor key Left
2117 {"[H", (Byte) VI_K_HOME}, // Cursor Key Home
2118 {"[F", (Byte) VI_K_END}, // Cursor Key End
2119 {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home
2120 {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2121 {"[4~", (Byte) VI_K_END}, // Cursor Key End
2122 {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2123 {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2124 {"OP", (Byte) VI_K_FUN1}, // Function Key F1
2125 {"OQ", (Byte) VI_K_FUN2}, // Function Key F2
2126 {"OR", (Byte) VI_K_FUN3}, // Function Key F3
2127 {"OS", (Byte) VI_K_FUN4}, // Function Key F4
2128 {"[15~", (Byte) VI_K_FUN5}, // Function Key F5
2129 {"[17~", (Byte) VI_K_FUN6}, // Function Key F6
2130 {"[18~", (Byte) VI_K_FUN7}, // Function Key F7
2131 {"[19~", (Byte) VI_K_FUN8}, // Function Key F8
2132 {"[20~", (Byte) VI_K_FUN9}, // Function Key F9
2133 {"[21~", (Byte) VI_K_FUN10}, // Function Key F10
2134 {"[23~", (Byte) VI_K_FUN11}, // Function Key F11
2135 {"[24~", (Byte) VI_K_FUN12}, // Function Key F12
2136 {"[11~", (Byte) VI_K_FUN1}, // Function Key F1
2137 {"[12~", (Byte) VI_K_FUN2}, // Function Key F2
2138 {"[13~", (Byte) VI_K_FUN3}, // Function Key F3
2139 {"[14~", (Byte) VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002140 };
2141
2142#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2143
2144 (void) alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002145 fflush(stdout);
2146 n = readed_for_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002147 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002148 if (n <= 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002149 ri0:
2150 // the Q is empty, wait for a typed char
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002151 n = read(0, readbuffer, BUFSIZ - 1);
2152 if (n < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002153 if (errno == EINTR)
2154 goto ri0; // interrupted sys call
2155 if (errno == EBADF)
2156 editing = 0;
2157 if (errno == EFAULT)
2158 editing = 0;
2159 if (errno == EINVAL)
2160 editing = 0;
2161 if (errno == EIO)
2162 editing = 0;
2163 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002164 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002165 if(n <= 0)
2166 return 0; // error
2167 if (readbuffer[0] == 27) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002168 // This is an ESC char. Is this Esc sequence?
2169 // Could be bare Esc key. See if there are any
2170 // more chars to read after the ESC. This would
2171 // be a Function or Cursor Key sequence.
2172 FD_ZERO(&rfds);
2173 FD_SET(0, &rfds);
2174 tv.tv_sec = 0;
2175 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2176
2177 // keep reading while there are input chars and room in buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002178 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002179 // read the rest of the ESC string
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002180 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2181 if (r > 0) {
2182 n += r;
2183 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002184 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002185 }
2186 readed_for_parse = n;
2187 }
2188 c = readbuffer[0];
2189 if(c == 27 && n > 1) {
2190 // Maybe cursor or function key?
2191 const struct esc_cmds *eindex;
2192
2193 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2194 int cnt = strlen(eindex->seq);
2195
2196 if(n <= cnt)
2197 continue;
2198 if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
2199 continue;
2200 // is a Cursor key- put derived value back into Q
2201 c = eindex->val;
2202 // for squeeze out the ESC sequence
2203 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002204 break;
2205 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002206 if(eindex == &esccmds[ESCCMDS_COUNT]) {
2207 /* defined ESC sequence not found, set only one ESC */
2208 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002209 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002210 } else {
2211 n = 1;
2212 }
2213 // remove key sequence from Q
2214 readed_for_parse -= n;
2215 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002216 (void) alarm(3); // we are done waiting for input, turn alarm ON
2217 return (c);
2218}
2219
2220//----- IO Routines --------------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002221static Byte get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002222{
2223 static Byte c;
2224
2225#ifdef CONFIG_FEATURE_VI_DOT_CMD
2226 // ! adding2q && ioq == 0 read()
2227 // ! adding2q && ioq != 0 *ioq
2228 // adding2q *last_modifying_cmd= read()
2229 if (!adding2q) {
2230 // we are not adding to the q.
2231 // but, we may be reading from a q
2232 if (ioq == 0) {
2233 // there is no current q, read from STDIN
2234 c = readit(); // get the users input
2235 } else {
2236 // there is a queue to get chars from first
2237 c = *ioq++;
2238 if (c == '\0') {
2239 // the end of the q, read from STDIN
2240 free(ioq_start);
2241 ioq_start = ioq = 0;
2242 c = readit(); // get the users input
2243 }
2244 }
2245 } else {
2246 // adding STDIN chars to q
2247 c = readit(); // get the users input
2248 if (last_modifying_cmd != 0) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002249 int len = strlen((char *) last_modifying_cmd);
2250 if (len + 1 >= BUFSIZ) {
2251 psbs("last_modifying_cmd overrun");
2252 } else {
2253 // add new char to q
2254 last_modifying_cmd[len] = c;
2255 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002256 }
2257 }
2258#else /* CONFIG_FEATURE_VI_DOT_CMD */
2259 c = readit(); // get the users input
2260#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2261 return (c); // return the char, where ever it came from
2262}
2263
2264static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2265{
2266 Byte buf[BUFSIZ];
2267 Byte c;
2268 int i;
2269 static Byte *obufp = NULL;
2270
2271 strcpy((char *) buf, (char *) prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002272 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002273 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2274 clear_to_eol(); // clear the line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002275 write1((char *) prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002276
2277 for (i = strlen((char *) buf); i < BUFSIZ;) {
2278 c = get_one_char(); // read user input
2279 if (c == '\n' || c == '\r' || c == 27)
2280 break; // is this end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002281 if (c == erase_char || c == 8 || c == 127) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002282 // user wants to erase prev char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002283 i--; // backup to prev char
2284 buf[i] = '\0'; // erase the char
2285 buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002286 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002287 if (i <= 0) { // user backs up before b-o-l, exit
2288 break;
2289 }
2290 } else {
2291 buf[i] = c; // save char in buffer
2292 buf[i + 1] = '\0'; // make sure buffer is null terminated
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002293 putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002294 i++;
2295 }
2296 }
2297 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002298 free(obufp);
Rob Landleyd921b2e2006-08-03 15:41:12 +00002299 obufp = (Byte *) xstrdup((char *) buf);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002300 return (obufp);
2301}
2302
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002303static int file_size(const Byte * fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002304{
2305 struct stat st_buf;
2306 int cnt, sr;
2307
Eric Andersena68ea1c2006-01-30 22:48:39 +00002308 if (fn == 0 || strlen((char *)fn) <= 0)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002309 return (-1);
2310 cnt = -1;
2311 sr = stat((char *) fn, &st_buf); // see if file exists
2312 if (sr >= 0) {
2313 cnt = (int) st_buf.st_size;
2314 }
2315 return (cnt);
2316}
2317
2318static int file_insert(Byte * fn, Byte * p, int size)
2319{
2320 int fd, cnt;
2321
2322 cnt = -1;
2323#ifdef CONFIG_FEATURE_VI_READONLY
2324 readonly = FALSE;
2325#endif /* CONFIG_FEATURE_VI_READONLY */
2326 if (fn == 0 || strlen((char*) fn) <= 0) {
2327 psbs("No filename given");
2328 goto fi0;
2329 }
2330 if (size == 0) {
2331 // OK- this is just a no-op
2332 cnt = 0;
2333 goto fi0;
2334 }
2335 if (size < 0) {
2336 psbs("Trying to insert a negative number (%d) of characters", size);
2337 goto fi0;
2338 }
2339 if (p < text || p > end) {
2340 psbs("Trying to insert file outside of memory");
2341 goto fi0;
2342 }
2343
2344 // see if we can open the file
2345#ifdef CONFIG_FEATURE_VI_READONLY
2346 if (vi_readonly) goto fi1; // do not try write-mode
2347#endif
2348 fd = open((char *) fn, O_RDWR); // assume read & write
2349 if (fd < 0) {
2350 // could not open for writing- maybe file is read only
2351#ifdef CONFIG_FEATURE_VI_READONLY
2352 fi1:
2353#endif
2354 fd = open((char *) fn, O_RDONLY); // try read-only
2355 if (fd < 0) {
Denis Vlasenkoea620772006-10-14 02:23:43 +00002356 psbs("\"%s\" %s", fn, "cannot open file");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002357 goto fi0;
2358 }
2359#ifdef CONFIG_FEATURE_VI_READONLY
2360 // got the file- read-only
2361 readonly = TRUE;
2362#endif /* CONFIG_FEATURE_VI_READONLY */
2363 }
2364 p = text_hole_make(p, size);
2365 cnt = read(fd, p, size);
2366 close(fd);
2367 if (cnt < 0) {
2368 cnt = -1;
2369 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
Denis Vlasenkoea620772006-10-14 02:23:43 +00002370 psbs("cannot read file \"%s\"", fn);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002371 } else if (cnt < size) {
2372 // There was a partial read, shrink unused space text[]
2373 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
Denis Vlasenkoea620772006-10-14 02:23:43 +00002374 psbs("cannot read all of file \"%s\"", fn);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002375 }
2376 if (cnt >= size)
Paul Fox8552aec2005-09-16 12:20:05 +00002377 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002378 fi0:
2379 return (cnt);
2380}
2381
2382static int file_write(Byte * fn, Byte * first, Byte * last)
2383{
2384 int fd, cnt, charcnt;
2385
2386 if (fn == 0) {
2387 psbs("No current filename");
Paul Fox61e45db2005-10-09 14:43:22 +00002388 return (-2);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002389 }
2390 charcnt = 0;
2391 // FIXIT- use the correct umask()
2392 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2393 if (fd < 0)
2394 return (-1);
2395 cnt = last - first + 1;
2396 charcnt = write(fd, first, cnt);
2397 if (charcnt == cnt) {
2398 // good write
2399 //file_modified= FALSE; // the file has not been modified
2400 } else {
2401 charcnt = 0;
2402 }
2403 close(fd);
2404 return (charcnt);
2405}
2406
2407//----- Terminal Drawing ---------------------------------------
2408// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002409// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002410// screen coordinates
2411// 0,0 ... 0,79
2412// 1,0 ... 1,79
2413// . ... .
2414// . ... .
2415// 22,0 ... 22,79
2416// 23,0 ... 23,79 status line
2417//
2418
2419//----- Move the cursor to row x col (count from 0, not 1) -------
2420static void place_cursor(int row, int col, int opti)
2421{
2422 char cm1[BUFSIZ];
2423 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002424#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2425 char cm2[BUFSIZ];
2426 Byte *screenp;
2427 // char cm3[BUFSIZ];
2428 int Rrow= last_row;
2429#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002430
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002431 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2432
2433 if (row < 0) row = 0;
2434 if (row >= rows) row = rows - 1;
2435 if (col < 0) col = 0;
2436 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002437
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002438 //----- 1. Try the standard terminal ESC sequence
2439 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2440 cm= cm1;
2441 if (! opti) goto pc0;
2442
2443#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2444 //----- find the minimum # of chars to move cursor -------------
2445 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2446 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002447
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002448 // move to the correct row
2449 while (row < Rrow) {
2450 // the cursor has to move up
2451 strcat(cm2, CMup);
2452 Rrow--;
2453 }
2454 while (row > Rrow) {
2455 // the cursor has to move down
2456 strcat(cm2, CMdown);
2457 Rrow++;
2458 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002459
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002460 // now move to the correct column
2461 strcat(cm2, "\r"); // start at col 0
2462 // just send out orignal source char to get to correct place
2463 screenp = &screen[row * columns]; // start of screen line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002464 strncat(cm2, (char* )screenp, col);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002465
2466 //----- 3. Try some other way of moving cursor
2467 //---------------------------------------------
2468
2469 // pick the shortest cursor motion to send out
2470 cm= cm1;
2471 if (strlen(cm2) < strlen(cm)) {
2472 cm= cm2;
2473 } /* else if (strlen(cm3) < strlen(cm)) {
2474 cm= cm3;
2475 } */
2476#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2477 pc0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002478 write1(cm); // move the cursor
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002479}
2480
2481//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002482static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002483{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002484 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002485}
2486
2487//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002488static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002489{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002490 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002491}
2492
2493//----- Start standout mode ------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002494static void standout_start(void) // send "start reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002495{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002496 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002497}
2498
2499//----- End standout mode --------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002500static void standout_end(void) // send "end reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002501{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002502 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002503}
2504
2505//----- Flash the screen --------------------------------------
2506static void flash(int h)
2507{
2508 standout_start(); // send "start reverse video" sequence
2509 redraw(TRUE);
2510 (void) mysleep(h);
2511 standout_end(); // send "end reverse video" sequence
2512 redraw(TRUE);
2513}
2514
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002515static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002516{
2517#ifdef CONFIG_FEATURE_VI_CRASHME
2518 if (crashme > 0)
2519 return; // generate a random command
2520#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002521 if (!err_method) {
2522 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002523 } else {
2524 flash(10);
2525 }
2526}
2527
2528//----- Screen[] Routines --------------------------------------
2529//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002530static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002531{
2532 memset(screen, ' ', screensize); // clear new screen
2533}
2534
Eric Andersena68ea1c2006-01-30 22:48:39 +00002535static int bufsum(unsigned char *buf, int count)
Paul Fox8552aec2005-09-16 12:20:05 +00002536{
2537 int sum = 0;
Eric Andersena68ea1c2006-01-30 22:48:39 +00002538 unsigned char *e = buf + count;
Paul Fox8552aec2005-09-16 12:20:05 +00002539 while (buf < e)
2540 sum += *buf++;
2541 return sum;
2542}
2543
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002544//----- Draw the status line at bottom of the screen -------------
2545static void show_status_line(void)
2546{
Paul Foxc3504852005-09-16 12:48:18 +00002547 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002548
Paul Fox8552aec2005-09-16 12:20:05 +00002549 // either we already have an error or status message, or we
2550 // create one.
2551 if (!have_status_msg) {
2552 cnt = format_edit_status();
2553 cksum = bufsum(status_buffer, cnt);
2554 }
2555 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
2556 last_status_cksum= cksum; // remember if we have seen this line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002557 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002558 write1((char*)status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002559 clear_to_eol();
Paul Fox8552aec2005-09-16 12:20:05 +00002560 if (have_status_msg) {
Eric Andersena68ea1c2006-01-30 22:48:39 +00002561 if (((int)strlen((char*)status_buffer) - (have_status_msg - 1)) >
Paul Fox8552aec2005-09-16 12:20:05 +00002562 (columns - 1) ) {
2563 have_status_msg = 0;
2564 Hit_Return();
2565 }
2566 have_status_msg = 0;
2567 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002568 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2569 }
Eric Andersena9eb33d2004-08-19 19:15:06 +00002570 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002571}
2572
2573//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00002574// format status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002575static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002576{
2577 va_list args;
2578
2579 va_start(args, format);
2580 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
Eric Andersen0ef24c62005-07-18 10:32:59 +00002581 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002582 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2583 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002584
2585 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002586
2587 return;
2588}
2589
Paul Fox8552aec2005-09-16 12:20:05 +00002590// format status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002591static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002592{
2593 va_list args;
2594
2595 va_start(args, format);
2596 vsprintf((char *) status_buffer, format, args);
2597 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002598
2599 have_status_msg = 1;
2600
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002601 return;
2602}
2603
2604static void ni(Byte * s) // display messages
2605{
2606 Byte buf[BUFSIZ];
2607
2608 print_literal(buf, s);
2609 psbs("\'%s\' is not implemented", buf);
2610}
2611
Paul Fox8552aec2005-09-16 12:20:05 +00002612static int format_edit_status(void) // show file status on status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002613{
Paul Fox8552aec2005-09-16 12:20:05 +00002614 int cur, percent, ret, trunc_at;
2615 static int tot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002616
Paul Fox8552aec2005-09-16 12:20:05 +00002617 // file_modified is now a counter rather than a flag. this
2618 // helps reduce the amount of line counting we need to do.
2619 // (this will cause a mis-reporting of modified status
2620 // once every MAXINT editing operations.)
2621
2622 // it would be nice to do a similar optimization here -- if
2623 // we haven't done a motion that could have changed which line
2624 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002625 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00002626
2627 // reduce counting -- the total lines can't have
2628 // changed if we haven't done any edits.
2629 if (file_modified != last_file_modified) {
2630 tot = cur + count_lines(dot, end - 1) - 1;
2631 last_file_modified = file_modified;
2632 }
2633
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002634 // current line percent
2635 // ------------- ~~ ----------
2636 // total lines 100
2637 if (tot > 0) {
2638 percent = (100 * cur) / tot;
2639 } else {
2640 cur = tot = 0;
2641 percent = 100;
2642 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00002643
Paul Fox8552aec2005-09-16 12:20:05 +00002644 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2645 columns : STATUS_BUFFER_LEN-1;
2646
2647 ret = snprintf((char *) status_buffer, trunc_at+1,
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002648#ifdef CONFIG_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002649 "%c %s%s%s %d/%d %d%%",
2650#else
2651 "%c %s%s %d/%d %d%%",
2652#endif
2653 (cmd_mode ? (cmd_mode == 2 ? 'R':'I'):'-'),
2654 (cfn != 0 ? (char *) cfn : "No file"),
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002655#ifdef CONFIG_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002656 ((vi_readonly || readonly) ? " [Read-only]" : ""),
2657#endif
2658 (file_modified ? " [modified]" : ""),
2659 cur, tot, percent);
2660
2661 if (ret >= 0 && ret < trunc_at)
2662 return ret; /* it all fit */
2663
2664 return trunc_at; /* had to truncate */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002665}
2666
2667//----- Force refresh of all Lines -----------------------------
2668static void redraw(int full_screen)
2669{
2670 place_cursor(0, 0, FALSE); // put cursor in correct place
2671 clear_to_eos(); // tel terminal to erase display
2672 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00002673 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002674 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00002675 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002676}
2677
2678//----- Format a text[] line into a buffer ---------------------
2679static void format_line(Byte *dest, Byte *src, int li)
2680{
2681 int co;
2682 Byte c;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002683
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002684 for (co= 0; co < MAX_SCR_COLS; co++) {
2685 c= ' '; // assume blank
2686 if (li > 0 && co == 0) {
2687 c = '~'; // not first line, assume Tilde
2688 }
2689 // are there chars in text[] and have we gone past the end
2690 if (text < end && src < end) {
2691 c = *src++;
2692 }
2693 if (c == '\n')
2694 break;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002695 if (c > 127 && !Isprint(c)) {
2696 c = '.';
2697 }
2698 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002699 if (c == '\t') {
2700 c = ' ';
2701 // co % 8 != 7
2702 for (; (co % tabstop) != (tabstop - 1); co++) {
2703 dest[co] = c;
2704 }
2705 } else {
2706 dest[co++] = '^';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002707 if(c == 127)
2708 c = '?';
2709 else
2710 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002711 }
2712 }
2713 // the co++ is done here so that the column will
2714 // not be overwritten when we blank-out the rest of line
2715 dest[co] = c;
2716 if (src >= end)
2717 break;
2718 }
2719}
2720
2721//----- Refresh the changed screen lines -----------------------
2722// Copy the source line from text[] into the buffer and note
2723// if the current screenline is different from the new buffer.
2724// If they differ then that line needs redrawing on the terminal.
2725//
2726static void refresh(int full_screen)
2727{
2728 static int old_offset;
2729 int li, changed;
2730 Byte buf[MAX_SCR_COLS];
2731 Byte *tp, *sp; // pointer into text[] and screen[]
2732#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2733 int last_li= -2; // last line that changed- for optimizing cursor movement
2734#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2735
Rob Landleye5e1a102006-06-21 01:15:36 +00002736 if (ENABLE_FEATURE_VI_WIN_RESIZE)
2737 get_terminal_width_height(0, &columns, &rows);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002738 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2739 tp = screenbegin; // index into text[] of top line
2740
2741 // compare text[] to screen[] and mark screen[] lines that need updating
2742 for (li = 0; li < rows - 1; li++) {
2743 int cs, ce; // column start & end
2744 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2745 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2746 // format current text line into buf
2747 format_line(buf, tp, li);
2748
2749 // skip to the end of the current text[] line
2750 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2751
2752 // see if there are any changes between vitual screen and buf
2753 changed = FALSE; // assume no change
2754 cs= 0;
2755 ce= columns-1;
2756 sp = &screen[li * columns]; // start of screen line
2757 if (full_screen) {
2758 // force re-draw of every single column from 0 - columns-1
2759 goto re0;
2760 }
2761 // compare newly formatted buffer with virtual screen
2762 // look forward for first difference between buf and screen
2763 for ( ; cs <= ce; cs++) {
2764 if (buf[cs + offset] != sp[cs]) {
2765 changed = TRUE; // mark for redraw
2766 break;
2767 }
2768 }
2769
2770 // look backward for last difference between buf and screen
2771 for ( ; ce >= cs; ce--) {
2772 if (buf[ce + offset] != sp[ce]) {
2773 changed = TRUE; // mark for redraw
2774 break;
2775 }
2776 }
2777 // now, cs is index of first diff, and ce is index of last diff
2778
2779 // if horz offset has changed, force a redraw
2780 if (offset != old_offset) {
2781 re0:
2782 changed = TRUE;
2783 }
2784
2785 // make a sanity check of columns indexes
2786 if (cs < 0) cs= 0;
2787 if (ce > columns-1) ce= columns-1;
2788 if (cs > ce) { cs= 0; ce= columns-1; }
2789 // is there a change between vitual screen and buf
2790 if (changed) {
2791 // copy changed part of buffer to virtual screen
2792 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2793
2794 // move cursor to column of first change
2795 if (offset != old_offset) {
2796 // opti_cur_move is still too stupid
2797 // to handle offsets correctly
2798 place_cursor(li, cs, FALSE);
2799 } else {
2800#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2801 // if this just the next line
2802 // try to optimize cursor movement
2803 // otherwise, use standard ESC sequence
2804 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2805 last_li= li;
2806#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2807 place_cursor(li, cs, FALSE); // use standard ESC sequence
2808#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2809 }
2810
2811 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002812 {
2813 int nic = ce-cs+1;
Eric Andersena68ea1c2006-01-30 22:48:39 +00002814 char *out = (char*)sp+cs;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002815
2816 while(nic-- > 0) {
2817 putchar(*out);
2818 out++;
2819 }
2820 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002821#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2822 last_row = li;
2823#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2824 }
2825 }
2826
2827#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2828 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2829 last_row = crow;
2830#else
2831 place_cursor(crow, ccol, FALSE);
2832#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002833
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002834 if (offset != old_offset)
2835 old_offset = offset;
2836}
2837
Eric Andersen3f980402001-04-04 17:31:15 +00002838//---------------------------------------------------------------------
2839//----- the Ascii Chart -----------------------------------------------
2840//
2841// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2842// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2843// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2844// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2845// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2846// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2847// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2848// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2849// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2850// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2851// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2852// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2853// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2854// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2855// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2856// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2857//---------------------------------------------------------------------
2858
2859//----- Execute a Vi Command -----------------------------------
2860static void do_cmd(Byte c)
2861{
2862 Byte c1, *p, *q, *msg, buf[9], *save_dot;
2863 int cnt, i, j, dir, yf;
2864
2865 c1 = c; // quiet the compiler
2866 cnt = yf = dir = 0; // quiet the compiler
2867 p = q = save_dot = msg = buf; // quiet the compiler
2868 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00002869
Paul Fox8552aec2005-09-16 12:20:05 +00002870 show_status_line();
2871
Eric Andersenbff7a602001-11-17 07:15:43 +00002872 /* if this is a cursor key, skip these checks */
2873 switch (c) {
2874 case VI_K_UP:
2875 case VI_K_DOWN:
2876 case VI_K_LEFT:
2877 case VI_K_RIGHT:
2878 case VI_K_HOME:
2879 case VI_K_END:
2880 case VI_K_PAGEUP:
2881 case VI_K_PAGEDOWN:
2882 goto key_cmd_mode;
2883 }
2884
Eric Andersen3f980402001-04-04 17:31:15 +00002885 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002886 // flip-flop Insert/Replace mode
2887 if (c == VI_K_INSERT) goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00002888 // we are 'R'eplacing the current *dot with new char
2889 if (*dot == '\n') {
2890 // don't Replace past E-o-l
2891 cmd_mode = 1; // convert to insert
2892 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002893 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00002894 if (c != 27)
2895 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
2896 dot = char_insert(dot, c); // insert new char
2897 }
2898 goto dc1;
2899 }
2900 }
2901 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00002902 // hitting "Insert" twice means "R" replace mode
2903 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00002904 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002905 if (1 <= c || Isprint(c)) {
2906 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00002907 }
2908 goto dc1;
2909 }
2910
Eric Andersenbff7a602001-11-17 07:15:43 +00002911key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00002912 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00002913 //case 0x01: // soh
2914 //case 0x09: // ht
2915 //case 0x0b: // vt
2916 //case 0x0e: // so
2917 //case 0x0f: // si
2918 //case 0x10: // dle
2919 //case 0x11: // dc1
2920 //case 0x13: // dc3
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002921#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00002922 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00002923 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00002924 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002925#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen822c3832001-05-07 17:37:43 +00002926 //case 0x16: // syn
2927 //case 0x17: // etb
2928 //case 0x18: // can
2929 //case 0x1c: // fs
2930 //case 0x1d: // gs
2931 //case 0x1e: // rs
2932 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002933 //case '!': // !-
2934 //case '#': // #-
2935 //case '&': // &-
2936 //case '(': // (-
2937 //case ')': // )-
2938 //case '*': // *-
2939 //case ',': // ,-
2940 //case '=': // =-
2941 //case '@': // @-
2942 //case 'F': // F-
2943 //case 'K': // K-
2944 //case 'Q': // Q-
2945 //case 'S': // S-
2946 //case 'T': // T-
2947 //case 'V': // V-
2948 //case '[': // [-
2949 //case '\\': // \-
2950 //case ']': // ]-
2951 //case '_': // _-
2952 //case '`': // `-
2953 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00002954 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002955 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00002956 default: // unrecognised command
2957 buf[0] = c;
2958 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002959 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00002960 buf[0] = '^';
2961 buf[1] = c + '@';
2962 buf[2] = '\0';
2963 }
2964 ni((Byte *) buf);
2965 end_cmd_q(); // stop adding to q
2966 case 0x00: // nul- ignore
2967 break;
2968 case 2: // ctrl-B scroll up full screen
2969 case VI_K_PAGEUP: // Cursor Key Page Up
2970 dot_scroll(rows - 2, -1);
2971 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002972#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00002973 case 0x03: // ctrl-C interrupt
2974 longjmp(restart, 1);
2975 break;
2976 case 26: // ctrl-Z suspend
2977 suspend_sig(SIGTSTP);
2978 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00002979#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +00002980 case 4: // ctrl-D scroll down half screen
2981 dot_scroll((rows - 2) / 2, 1);
2982 break;
2983 case 5: // ctrl-E scroll down one line
2984 dot_scroll(1, 1);
2985 break;
2986 case 6: // ctrl-F scroll down full screen
2987 case VI_K_PAGEDOWN: // Cursor Key Page Down
2988 dot_scroll(rows - 2, 1);
2989 break;
2990 case 7: // ctrl-G show current status
Paul Fox8552aec2005-09-16 12:20:05 +00002991 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00002992 break;
2993 case 'h': // h- move left
2994 case VI_K_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00002995 case 8: // ctrl-H- move left (This may be ERASE char)
2996 case 127: // DEL- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00002997 if (cmdcnt-- > 1) {
2998 do_cmd(c);
2999 } // repeat cnt
3000 dot_left();
3001 break;
3002 case 10: // Newline ^J
3003 case 'j': // j- goto next line, same col
3004 case VI_K_DOWN: // cursor key Down
3005 if (cmdcnt-- > 1) {
3006 do_cmd(c);
3007 } // repeat cnt
3008 dot_next(); // go to next B-o-l
3009 dot = move_to_col(dot, ccol + offset); // try stay in same col
3010 break;
3011 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003012 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003013 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003014 clear_to_eos(); // tel terminal to erase display
3015 (void) mysleep(10);
3016 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003017 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003018 refresh(TRUE); // this will redraw the entire display
3019 break;
3020 case 13: // Carriage Return ^M
3021 case '+': // +- goto next line
3022 if (cmdcnt-- > 1) {
3023 do_cmd(c);
3024 } // repeat cnt
3025 dot_next();
3026 dot_skip_over_ws();
3027 break;
3028 case 21: // ctrl-U scroll up half screen
3029 dot_scroll((rows - 2) / 2, -1);
3030 break;
3031 case 25: // ctrl-Y scroll up one line
3032 dot_scroll(1, -1);
3033 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003034 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003035 if (cmd_mode == 0)
3036 indicate_error(c);
3037 cmd_mode = 0; // stop insrting
3038 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003039 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003040 break;
3041 case ' ': // move right
3042 case 'l': // move right
3043 case VI_K_RIGHT: // Cursor Key Right
3044 if (cmdcnt-- > 1) {
3045 do_cmd(c);
3046 } // repeat cnt
3047 dot_right();
3048 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003049#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003050 case '"': // "- name a register to use for Delete/Yank
3051 c1 = get_one_char();
3052 c1 = tolower(c1);
3053 if (islower(c1)) {
3054 YDreg = c1 - 'a';
3055 } else {
3056 indicate_error(c);
3057 }
3058 break;
3059 case '\'': // '- goto a specific mark
3060 c1 = get_one_char();
3061 c1 = tolower(c1);
3062 if (islower(c1)) {
3063 c1 = c1 - 'a';
3064 // get the b-o-l
3065 q = mark[(int) c1];
3066 if (text <= q && q < end) {
3067 dot = q;
3068 dot_begin(); // go to B-o-l
3069 dot_skip_over_ws();
3070 }
3071 } else if (c1 == '\'') { // goto previous context
3072 dot = swap_context(dot); // swap current and previous context
3073 dot_begin(); // go to B-o-l
3074 dot_skip_over_ws();
3075 } else {
3076 indicate_error(c);
3077 }
3078 break;
3079 case 'm': // m- Mark a line
3080 // this is really stupid. If there are any inserts or deletes
3081 // between text[0] and dot then this mark will not point to the
3082 // correct location! It could be off by many lines!
3083 // Well..., at least its quick and dirty.
3084 c1 = get_one_char();
3085 c1 = tolower(c1);
3086 if (islower(c1)) {
3087 c1 = c1 - 'a';
3088 // remember the line
3089 mark[(int) c1] = dot;
3090 } else {
3091 indicate_error(c);
3092 }
3093 break;
3094 case 'P': // P- Put register before
3095 case 'p': // p- put register after
3096 p = reg[YDreg];
3097 if (p == 0) {
3098 psbs("Nothing in register %c", what_reg());
3099 break;
3100 }
3101 // are we putting whole lines or strings
3102 if (strchr((char *) p, '\n') != NULL) {
3103 if (c == 'P') {
3104 dot_begin(); // putting lines- Put above
3105 }
3106 if (c == 'p') {
3107 // are we putting after very last line?
3108 if (end_line(dot) == (end - 1)) {
3109 dot = end; // force dot to end of text[]
3110 } else {
3111 dot_next(); // next line, then put before
3112 }
3113 }
3114 } else {
3115 if (c == 'p')
3116 dot_right(); // move to right, can move to NL
3117 }
3118 dot = string_insert(dot, p); // insert the string
3119 end_cmd_q(); // stop adding to q
3120 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003121 case 'U': // U- Undo; replace current line with original version
3122 if (reg[Ureg] != 0) {
3123 p = begin_line(dot);
3124 q = end_line(dot);
3125 p = text_hole_delete(p, q); // delete cur line
3126 p = string_insert(p, reg[Ureg]); // insert orig line
3127 dot = p;
3128 dot_skip_over_ws();
3129 }
3130 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003131#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003132 case '$': // $- goto end of line
3133 case VI_K_END: // Cursor Key End
3134 if (cmdcnt-- > 1) {
3135 do_cmd(c);
3136 } // repeat cnt
Glenn L McGrathee829062004-01-21 10:59:45 +00003137 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003138 break;
3139 case '%': // %- find matching char of pair () [] {}
3140 for (q = dot; q < end && *q != '\n'; q++) {
3141 if (strchr("()[]{}", *q) != NULL) {
3142 // we found half of a pair
3143 p = find_pair(q, *q);
3144 if (p == NULL) {
3145 indicate_error(c);
3146 } else {
3147 dot = p;
3148 }
3149 break;
3150 }
3151 }
3152 if (*q == '\n')
3153 indicate_error(c);
3154 break;
3155 case 'f': // f- forward to a user specified char
3156 last_forward_char = get_one_char(); // get the search char
3157 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003158 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003159 //
Paul Foxd13b90b2005-07-18 22:17:25 +00003160 //**** fall thru to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003161 case ';': // ;- look at rest of line for last forward char
3162 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003163 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003164 } // repeat cnt
Eric Andersen822c3832001-05-07 17:37:43 +00003165 if (last_forward_char == 0) break;
Eric Andersen3f980402001-04-04 17:31:15 +00003166 q = dot + 1;
3167 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3168 q++;
3169 }
3170 if (*q == last_forward_char)
3171 dot = q;
3172 break;
3173 case '-': // -- goto prev line
3174 if (cmdcnt-- > 1) {
3175 do_cmd(c);
3176 } // repeat cnt
3177 dot_prev();
3178 dot_skip_over_ws();
3179 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003180#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003181 case '.': // .- repeat the last modifying command
3182 // Stuff the last_modifying_cmd back into stdin
3183 // and let it be re-executed.
3184 if (last_modifying_cmd != 0) {
Rob Landleyd921b2e2006-08-03 15:41:12 +00003185 ioq = ioq_start = (Byte *) xstrdup((char *) last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003186 }
3187 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003188#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3189#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003190 case '?': // /- search for a pattern
3191 case '/': // /- search for a pattern
3192 buf[0] = c;
3193 buf[1] = '\0';
3194 q = get_input_line(buf); // get input line- use "status line"
3195 if (strlen((char *) q) == 1)
3196 goto dc3; // if no pat re-use old pat
3197 if (strlen((char *) q) > 1) { // new pat- save it and find
3198 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003199 free(last_search_pattern);
Rob Landleyd921b2e2006-08-03 15:41:12 +00003200 last_search_pattern = (Byte *) xstrdup((char *) q);
Eric Andersen3f980402001-04-04 17:31:15 +00003201 goto dc3; // now find the pattern
3202 }
3203 // user changed mind and erased the "/"- do nothing
3204 break;
3205 case 'N': // N- backward search for last pattern
3206 if (cmdcnt-- > 1) {
3207 do_cmd(c);
3208 } // repeat cnt
3209 dir = BACK; // assume BACKWARD search
3210 p = dot - 1;
3211 if (last_search_pattern[0] == '?') {
3212 dir = FORWARD;
3213 p = dot + 1;
3214 }
3215 goto dc4; // now search for pattern
3216 break;
3217 case 'n': // n- repeat search for last pattern
3218 // search rest of text[] starting at next char
3219 // if search fails return orignal "p" not the "p+1" address
3220 if (cmdcnt-- > 1) {
3221 do_cmd(c);
3222 } // repeat cnt
3223 dc3:
3224 if (last_search_pattern == 0) {
3225 msg = (Byte *) "No previous regular expression";
3226 goto dc2;
3227 }
3228 if (last_search_pattern[0] == '/') {
3229 dir = FORWARD; // assume FORWARD search
3230 p = dot + 1;
3231 }
3232 if (last_search_pattern[0] == '?') {
3233 dir = BACK;
3234 p = dot - 1;
3235 }
3236 dc4:
3237 q = char_search(p, last_search_pattern + 1, dir, FULL);
3238 if (q != NULL) {
3239 dot = q; // good search, update "dot"
3240 msg = (Byte *) "";
3241 goto dc2;
3242 }
3243 // no pattern found between "dot" and "end"- continue at top
3244 p = text;
3245 if (dir == BACK) {
3246 p = end - 1;
3247 }
3248 q = char_search(p, last_search_pattern + 1, dir, FULL);
3249 if (q != NULL) { // found something
3250 dot = q; // found new pattern- goto it
3251 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3252 if (dir == BACK) {
3253 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3254 }
3255 } else {
3256 msg = (Byte *) "Pattern not found";
3257 }
3258 dc2:
Paul Fox8552aec2005-09-16 12:20:05 +00003259 if (*msg) psbs("%s", msg);
Eric Andersen3f980402001-04-04 17:31:15 +00003260 break;
3261 case '{': // {- move backward paragraph
3262 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3263 if (q != NULL) { // found blank line
3264 dot = next_line(q); // move to next blank line
3265 }
3266 break;
3267 case '}': // }- move forward paragraph
3268 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3269 if (q != NULL) { // found blank line
3270 dot = next_line(q); // move to next blank line
3271 }
3272 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003273#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003274 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003275 case '1': // 1-
3276 case '2': // 2-
3277 case '3': // 3-
3278 case '4': // 4-
3279 case '5': // 5-
3280 case '6': // 6-
3281 case '7': // 7-
3282 case '8': // 8-
3283 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003284 if (c == '0' && cmdcnt < 1) {
3285 dot_begin(); // this was a standalone zero
3286 } else {
3287 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3288 }
3289 break;
3290 case ':': // :- the colon mode commands
Eric Andersen3f980402001-04-04 17:31:15 +00003291 p = get_input_line((Byte *) ":"); // get input line- use "status line"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003292#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003293 colon(p); // execute the command
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003294#else /* CONFIG_FEATURE_VI_COLON */
Eric Andersen822c3832001-05-07 17:37:43 +00003295 if (*p == ':')
3296 p++; // move past the ':'
3297 cnt = strlen((char *) p);
3298 if (cnt <= 0)
3299 break;
3300 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3301 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
Matt Kraai1f0c4362001-12-20 23:13:26 +00003302 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003303 psbs("No write since last change (:quit! overrides)");
3304 } else {
3305 editing = 0;
3306 }
Paul Fox9360f422006-03-27 21:51:16 +00003307 } else if (strncasecmp((char *) p, "write", cnt) == 0
3308 || strncasecmp((char *) p, "wq", cnt) == 0
3309 || strncasecmp((char *) p, "wn", cnt) == 0
3310 || strncasecmp((char *) p, "x", cnt) == 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003311 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003312 if (cnt < 0) {
3313 if (cnt == -1)
3314 psbs("Write error: %s", strerror(errno));
3315 } else {
3316 file_modified = 0;
3317 last_file_modified = -1;
3318 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
Paul Fox9360f422006-03-27 21:51:16 +00003319 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n' ||
3320 p[0] == 'X' || p[1] == 'Q' || p[1] == 'N') {
Paul Fox61e45db2005-10-09 14:43:22 +00003321 editing = 0;
3322 }
Eric Andersen3f980402001-04-04 17:31:15 +00003323 }
Eric Andersen822c3832001-05-07 17:37:43 +00003324 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
Paul Fox8552aec2005-09-16 12:20:05 +00003325 last_status_cksum = 0; // force status update
Eric Andersen822c3832001-05-07 17:37:43 +00003326 } else if (sscanf((char *) p, "%d", &j) > 0) {
3327 dot = find_line(j); // go to line # j
3328 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003329 } else { // unrecognised cmd
Eric Andersen822c3832001-05-07 17:37:43 +00003330 ni((Byte *) p);
Eric Andersen3f980402001-04-04 17:31:15 +00003331 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003332#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003333 break;
3334 case '<': // <- Left shift something
3335 case '>': // >- Right shift something
3336 cnt = count_lines(text, dot); // remember what line we are on
3337 c1 = get_one_char(); // get the type of thing to delete
3338 find_range(&p, &q, c1);
3339 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3340 p = begin_line(p);
3341 q = end_line(q);
3342 i = count_lines(p, q); // # of lines we are shifting
3343 for ( ; i > 0; i--, p = next_line(p)) {
3344 if (c == '<') {
3345 // shift left- remove tab or 8 spaces
3346 if (*p == '\t') {
3347 // shrink buffer 1 char
3348 (void) text_hole_delete(p, p);
3349 } else if (*p == ' ') {
3350 // we should be calculating columns, not just SPACE
3351 for (j = 0; *p == ' ' && j < tabstop; j++) {
3352 (void) text_hole_delete(p, p);
3353 }
3354 }
3355 } else if (c == '>') {
3356 // shift right -- add tab or 8 spaces
3357 (void) char_insert(p, '\t');
3358 }
3359 }
3360 dot = find_line(cnt); // what line were we on
3361 dot_skip_over_ws();
3362 end_cmd_q(); // stop adding to q
3363 break;
3364 case 'A': // A- append at e-o-l
3365 dot_end(); // go to e-o-l
3366 //**** fall thru to ... 'a'
3367 case 'a': // a- append after current char
3368 if (*dot != '\n')
3369 dot++;
3370 goto dc_i;
3371 break;
3372 case 'B': // B- back a blank-delimited Word
3373 case 'E': // E- end of a blank-delimited word
3374 case 'W': // W- forward a blank-delimited word
3375 if (cmdcnt-- > 1) {
3376 do_cmd(c);
3377 } // repeat cnt
3378 dir = FORWARD;
3379 if (c == 'B')
3380 dir = BACK;
3381 if (c == 'W' || isspace(dot[dir])) {
3382 dot = skip_thing(dot, 1, dir, S_TO_WS);
3383 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3384 }
3385 if (c != 'W')
3386 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3387 break;
3388 case 'C': // C- Change to e-o-l
3389 case 'D': // D- delete to e-o-l
3390 save_dot = dot;
3391 dot = dollar_line(dot); // move to before NL
3392 // copy text into a register and delete
3393 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3394 if (c == 'C')
3395 goto dc_i; // start inserting
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003396#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003397 if (c == 'D')
3398 end_cmd_q(); // stop adding to q
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003399#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003400 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003401 case 'G': // G- goto to a line number (default= E-O-F)
3402 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003403 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003404 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003405 }
3406 dot_skip_over_ws();
3407 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003408 case 'H': // H- goto top line on screen
3409 dot = screenbegin;
3410 if (cmdcnt > (rows - 1)) {
3411 cmdcnt = (rows - 1);
3412 }
3413 if (cmdcnt-- > 1) {
3414 do_cmd('+');
3415 } // repeat cnt
3416 dot_skip_over_ws();
3417 break;
3418 case 'I': // I- insert before first non-blank
3419 dot_begin(); // 0
3420 dot_skip_over_ws();
3421 //**** fall thru to ... 'i'
3422 case 'i': // i- insert before current char
3423 case VI_K_INSERT: // Cursor Key Insert
3424 dc_i:
3425 cmd_mode = 1; // start insrting
Eric Andersen3f980402001-04-04 17:31:15 +00003426 break;
3427 case 'J': // J- join current and next lines together
3428 if (cmdcnt-- > 2) {
3429 do_cmd(c);
3430 } // repeat cnt
3431 dot_end(); // move to NL
3432 if (dot < end - 1) { // make sure not last char in text[]
3433 *dot++ = ' '; // replace NL with space
Paul Fox8552aec2005-09-16 12:20:05 +00003434 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003435 while (isblnk(*dot)) { // delete leading WS
3436 dot_delete();
3437 }
3438 }
3439 end_cmd_q(); // stop adding to q
3440 break;
3441 case 'L': // L- goto bottom line on screen
3442 dot = end_screen();
3443 if (cmdcnt > (rows - 1)) {
3444 cmdcnt = (rows - 1);
3445 }
3446 if (cmdcnt-- > 1) {
3447 do_cmd('-');
3448 } // repeat cnt
3449 dot_begin();
3450 dot_skip_over_ws();
3451 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003452 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003453 dot = screenbegin;
3454 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3455 dot = next_line(dot);
3456 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003457 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003458 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003459 p = begin_line(dot);
3460 if (p[-1] == '\n') {
3461 dot_prev();
3462 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3463 dot_end();
3464 dot = char_insert(dot, '\n');
3465 } else {
3466 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003467 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003468 dot_prev(); // -
3469 }
3470 goto dc_i;
3471 break;
3472 case 'R': // R- continuous Replace char
Eric Andersen1c0d3112001-04-16 15:46:44 +00003473 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003474 cmd_mode = 2;
Eric Andersen3f980402001-04-04 17:31:15 +00003475 break;
3476 case 'X': // X- delete char before dot
3477 case 'x': // x- delete the current char
3478 case 's': // s- substitute the current char
3479 if (cmdcnt-- > 1) {
3480 do_cmd(c);
3481 } // repeat cnt
3482 dir = 0;
3483 if (c == 'X')
3484 dir = -1;
3485 if (dot[dir] != '\n') {
3486 if (c == 'X')
3487 dot--; // delete prev char
3488 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3489 }
3490 if (c == 's')
3491 goto dc_i; // start insrting
3492 end_cmd_q(); // stop adding to q
3493 break;
3494 case 'Z': // Z- if modified, {write}; exit
3495 // ZZ means to save file (if necessary), then exit
3496 c1 = get_one_char();
3497 if (c1 != 'Z') {
3498 indicate_error(c);
3499 break;
3500 }
Paul Foxf0305b72006-03-28 14:18:21 +00003501 if (file_modified) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003502#ifdef CONFIG_FEATURE_VI_READONLY
Paul Foxf0305b72006-03-28 14:18:21 +00003503 if (vi_readonly || readonly) {
Denis Vlasenko92758142006-10-03 19:56:34 +00003504 psbs("\"%s\" File is read only", cfn);
3505 break;
Paul Foxf0305b72006-03-28 14:18:21 +00003506 }
3507#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00003508 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003509 if (cnt < 0) {
3510 if (cnt == -1)
3511 psbs("Write error: %s", strerror(errno));
3512 } else if (cnt == (end - 1 - text + 1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003513 editing = 0;
3514 }
3515 } else {
3516 editing = 0;
3517 }
3518 break;
3519 case '^': // ^- move to first non-blank on line
3520 dot_begin();
3521 dot_skip_over_ws();
3522 break;
3523 case 'b': // b- back a word
3524 case 'e': // e- end of word
3525 if (cmdcnt-- > 1) {
3526 do_cmd(c);
3527 } // repeat cnt
3528 dir = FORWARD;
3529 if (c == 'b')
3530 dir = BACK;
3531 if ((dot + dir) < text || (dot + dir) > end - 1)
3532 break;
3533 dot += dir;
3534 if (isspace(*dot)) {
3535 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3536 }
3537 if (isalnum(*dot) || *dot == '_') {
3538 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3539 } else if (ispunct(*dot)) {
3540 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3541 }
3542 break;
3543 case 'c': // c- change something
3544 case 'd': // d- delete something
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003545#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003546 case 'y': // y- yank something
3547 case 'Y': // Y- Yank a line
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003548#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003549 yf = YANKDEL; // assume either "c" or "d"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003550#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003551 if (c == 'y' || c == 'Y')
3552 yf = YANKONLY;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003553#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003554 c1 = 'y';
3555 if (c != 'Y')
3556 c1 = get_one_char(); // get the type of thing to delete
3557 find_range(&p, &q, c1);
3558 if (c1 == 27) { // ESC- user changed mind and wants out
3559 c = c1 = 27; // Escape- do nothing
3560 } else if (strchr("wW", c1)) {
3561 if (c == 'c') {
3562 // don't include trailing WS as part of word
3563 while (isblnk(*q)) {
3564 if (q <= text || q[-1] == '\n')
3565 break;
3566 q--;
3567 }
3568 }
3569 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003570 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003571 // single line copy text into a register and delete
3572 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003573 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003574 // multiple line copy text into a register and delete
3575 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003576 if (c == 'c') {
3577 dot = char_insert(dot, '\n');
3578 // on the last line of file don't move to prev line
3579 if (dot != (end-1)) {
3580 dot_prev();
3581 }
3582 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003583 dot_begin();
3584 dot_skip_over_ws();
3585 }
3586 } else {
3587 // could not recognize object
3588 c = c1 = 27; // error-
3589 indicate_error(c);
3590 }
3591 if (c1 != 27) {
3592 // if CHANGING, not deleting, start inserting after the delete
3593 if (c == 'c') {
3594 strcpy((char *) buf, "Change");
3595 goto dc_i; // start inserting
3596 }
3597 if (c == 'd') {
3598 strcpy((char *) buf, "Delete");
3599 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003600#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003601 if (c == 'y' || c == 'Y') {
3602 strcpy((char *) buf, "Yank");
3603 }
3604 p = reg[YDreg];
3605 q = p + strlen((char *) p);
3606 for (cnt = 0; p <= q; p++) {
3607 if (*p == '\n')
3608 cnt++;
3609 }
3610 psb("%s %d lines (%d chars) using [%c]",
3611 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003612#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003613 end_cmd_q(); // stop adding to q
3614 }
3615 break;
3616 case 'k': // k- goto prev line, same col
3617 case VI_K_UP: // cursor key Up
3618 if (cmdcnt-- > 1) {
3619 do_cmd(c);
3620 } // repeat cnt
3621 dot_prev();
3622 dot = move_to_col(dot, ccol + offset); // try stay in same col
3623 break;
3624 case 'r': // r- replace the current char with user input
3625 c1 = get_one_char(); // get the replacement char
3626 if (*dot != '\n') {
3627 *dot = c1;
Paul Fox8552aec2005-09-16 12:20:05 +00003628 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003629 }
3630 end_cmd_q(); // stop adding to q
3631 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003632 case 't': // t- move to char prior to next x
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00003633 last_forward_char = get_one_char();
3634 do_cmd(';');
3635 if (*dot == last_forward_char)
3636 dot_left();
3637 last_forward_char= 0;
Eric Andersen822c3832001-05-07 17:37:43 +00003638 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003639 case 'w': // w- forward a word
3640 if (cmdcnt-- > 1) {
3641 do_cmd(c);
3642 } // repeat cnt
3643 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3644 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3645 } else if (ispunct(*dot)) { // we are on PUNCT
3646 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3647 }
3648 if (dot < end - 1)
3649 dot++; // move over word
3650 if (isspace(*dot)) {
3651 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3652 }
3653 break;
3654 case 'z': // z-
3655 c1 = get_one_char(); // get the replacement char
3656 cnt = 0;
3657 if (c1 == '.')
3658 cnt = (rows - 2) / 2; // put dot at center
3659 if (c1 == '-')
3660 cnt = rows - 2; // put dot at bottom
3661 screenbegin = begin_line(dot); // start dot at top
3662 dot_scroll(cnt, -1);
3663 break;
3664 case '|': // |- move to column "cmdcnt"
3665 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3666 break;
3667 case '~': // ~- flip the case of letters a-z -> A-Z
3668 if (cmdcnt-- > 1) {
3669 do_cmd(c);
3670 } // repeat cnt
3671 if (islower(*dot)) {
3672 *dot = toupper(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003673 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003674 } else if (isupper(*dot)) {
3675 *dot = tolower(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003676 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003677 }
3678 dot_right();
3679 end_cmd_q(); // stop adding to q
3680 break;
3681 //----- The Cursor and Function Keys -----------------------------
3682 case VI_K_HOME: // Cursor Key Home
3683 dot_begin();
3684 break;
3685 // The Fn keys could point to do_macro which could translate them
3686 case VI_K_FUN1: // Function Key F1
3687 case VI_K_FUN2: // Function Key F2
3688 case VI_K_FUN3: // Function Key F3
3689 case VI_K_FUN4: // Function Key F4
3690 case VI_K_FUN5: // Function Key F5
3691 case VI_K_FUN6: // Function Key F6
3692 case VI_K_FUN7: // Function Key F7
3693 case VI_K_FUN8: // Function Key F8
3694 case VI_K_FUN9: // Function Key F9
3695 case VI_K_FUN10: // Function Key F10
3696 case VI_K_FUN11: // Function Key F11
3697 case VI_K_FUN12: // Function Key F12
3698 break;
3699 }
3700
3701 dc1:
3702 // if text[] just became empty, add back an empty line
3703 if (end == text) {
3704 (void) char_insert(text, '\n'); // start empty buf with dummy line
3705 dot = text;
3706 }
3707 // it is OK for dot to exactly equal to end, otherwise check dot validity
3708 if (dot != end) {
3709 dot = bound_dot(dot); // make sure "dot" is valid
3710 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003711#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003712 check_context(c); // update the current context
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003713#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003714
3715 if (!isdigit(c))
3716 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3717 cnt = dot - begin_line(dot);
3718 // Try to stay off of the Newline
3719 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3720 dot--;
3721}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003722
3723#ifdef CONFIG_FEATURE_VI_CRASHME
3724static int totalcmds = 0;
3725static int Mp = 85; // Movement command Probability
3726static int Np = 90; // Non-movement command Probability
3727static int Dp = 96; // Delete command Probability
3728static int Ip = 97; // Insert command Probability
3729static int Yp = 98; // Yank command Probability
3730static int Pp = 99; // Put command Probability
3731static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3732char chars[20] = "\t012345 abcdABCD-=.$";
3733char *words[20] = { "this", "is", "a", "test",
3734 "broadcast", "the", "emergency", "of",
3735 "system", "quick", "brown", "fox",
3736 "jumped", "over", "lazy", "dogs",
3737 "back", "January", "Febuary", "March"
3738};
3739char *lines[20] = {
3740 "You should have received a copy of the GNU General Public License\n",
3741 "char c, cm, *cmd, *cmd1;\n",
3742 "generate a command by percentages\n",
3743 "Numbers may be typed as a prefix to some commands.\n",
3744 "Quit, discarding changes!\n",
3745 "Forced write, if permission originally not valid.\n",
3746 "In general, any ex or ed command (such as substitute or delete).\n",
3747 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3748 "Please get w/ me and I will go over it with you.\n",
3749 "The following is a list of scheduled, committed changes.\n",
3750 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3751 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3752 "Any question about transactions please contact Sterling Huxley.\n",
3753 "I will try to get back to you by Friday, December 31.\n",
3754 "This Change will be implemented on Friday.\n",
3755 "Let me know if you have problems accessing this;\n",
3756 "Sterling Huxley recently added you to the access list.\n",
3757 "Would you like to go to lunch?\n",
3758 "The last command will be automatically run.\n",
3759 "This is too much english for a computer geek.\n",
3760};
3761char *multilines[20] = {
3762 "You should have received a copy of the GNU General Public License\n",
3763 "char c, cm, *cmd, *cmd1;\n",
3764 "generate a command by percentages\n",
3765 "Numbers may be typed as a prefix to some commands.\n",
3766 "Quit, discarding changes!\n",
3767 "Forced write, if permission originally not valid.\n",
3768 "In general, any ex or ed command (such as substitute or delete).\n",
3769 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3770 "Please get w/ me and I will go over it with you.\n",
3771 "The following is a list of scheduled, committed changes.\n",
3772 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3773 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3774 "Any question about transactions please contact Sterling Huxley.\n",
3775 "I will try to get back to you by Friday, December 31.\n",
3776 "This Change will be implemented on Friday.\n",
3777 "Let me know if you have problems accessing this;\n",
3778 "Sterling Huxley recently added you to the access list.\n",
3779 "Would you like to go to lunch?\n",
3780 "The last command will be automatically run.\n",
3781 "This is too much english for a computer geek.\n",
3782};
3783
3784// create a random command to execute
3785static void crash_dummy()
3786{
3787 static int sleeptime; // how long to pause between commands
3788 char c, cm, *cmd, *cmd1;
3789 int i, cnt, thing, rbi, startrbi, percent;
3790
3791 // "dot" movement commands
3792 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3793
3794 // is there already a command running?
3795 if (readed_for_parse > 0)
3796 goto cd1;
3797 cd0:
3798 startrbi = rbi = 0;
3799 sleeptime = 0; // how long to pause between commands
3800 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3801 // generate a command by percentages
3802 percent = (int) lrand48() % 100; // get a number from 0-99
3803 if (percent < Mp) { // Movement commands
3804 // available commands
3805 cmd = cmd1;
3806 M++;
3807 } else if (percent < Np) { // non-movement commands
3808 cmd = "mz<>\'\""; // available commands
3809 N++;
3810 } else if (percent < Dp) { // Delete commands
3811 cmd = "dx"; // available commands
3812 D++;
3813 } else if (percent < Ip) { // Inset commands
3814 cmd = "iIaAsrJ"; // available commands
3815 I++;
3816 } else if (percent < Yp) { // Yank commands
3817 cmd = "yY"; // available commands
3818 Y++;
3819 } else if (percent < Pp) { // Put commands
3820 cmd = "pP"; // available commands
3821 P++;
3822 } else {
3823 // We do not know how to handle this command, try again
3824 U++;
3825 goto cd0;
3826 }
3827 // randomly pick one of the available cmds from "cmd[]"
3828 i = (int) lrand48() % strlen(cmd);
3829 cm = cmd[i];
3830 if (strchr(":\024", cm))
3831 goto cd0; // dont allow colon or ctrl-T commands
3832 readbuffer[rbi++] = cm; // put cmd into input buffer
3833
3834 // now we have the command-
3835 // there are 1, 2, and multi char commands
3836 // find out which and generate the rest of command as necessary
3837 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3838 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3839 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3840 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3841 }
3842 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3843 c = cmd1[thing];
3844 readbuffer[rbi++] = c; // add movement to input buffer
3845 }
3846 if (strchr("iIaAsc", cm)) { // multi-char commands
3847 if (cm == 'c') {
3848 // change some thing
3849 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3850 c = cmd1[thing];
3851 readbuffer[rbi++] = c; // add movement to input buffer
3852 }
3853 thing = (int) lrand48() % 4; // what thing to insert
3854 cnt = (int) lrand48() % 10; // how many to insert
3855 for (i = 0; i < cnt; i++) {
3856 if (thing == 0) { // insert chars
3857 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3858 } else if (thing == 1) { // insert words
3859 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
3860 strcat((char *) readbuffer, " ");
3861 sleeptime = 0; // how fast to type
3862 } else if (thing == 2) { // insert lines
3863 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
3864 sleeptime = 0; // how fast to type
3865 } else { // insert multi-lines
3866 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
3867 sleeptime = 0; // how fast to type
3868 }
3869 }
3870 strcat((char *) readbuffer, "\033");
3871 }
3872 readed_for_parse = strlen(readbuffer);
3873 cd1:
3874 totalcmds++;
3875 if (sleeptime > 0)
3876 (void) mysleep(sleeptime); // sleep 1/100 sec
3877}
3878
3879// test to see if there are any errors
3880static void crash_test()
3881{
3882 static time_t oldtim;
3883 time_t tim;
Glenn L McGrath7127b582002-12-03 21:48:15 +00003884 char d[2], msg[BUFSIZ];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003885
3886 msg[0] = '\0';
3887 if (end < text) {
3888 strcat((char *) msg, "end<text ");
3889 }
3890 if (end > textend) {
3891 strcat((char *) msg, "end>textend ");
3892 }
3893 if (dot < text) {
3894 strcat((char *) msg, "dot<text ");
3895 }
3896 if (dot > end) {
3897 strcat((char *) msg, "dot>end ");
3898 }
3899 if (screenbegin < text) {
3900 strcat((char *) msg, "screenbegin<text ");
3901 }
3902 if (screenbegin > end - 1) {
3903 strcat((char *) msg, "screenbegin>end-1 ");
3904 }
3905
3906 if (strlen(msg) > 0) {
3907 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00003908 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003909 totalcmds, last_input_char, msg, SOs, SOn);
3910 fflush(stdout);
3911 while (read(0, d, 1) > 0) {
3912 if (d[0] == '\n' || d[0] == '\r')
3913 break;
3914 }
3915 alarm(3);
3916 }
3917 tim = (time_t) time((time_t *) 0);
3918 if (tim >= (oldtim + 3)) {
3919 sprintf((char *) status_buffer,
3920 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
3921 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
3922 oldtim = tim;
3923 }
3924 return;
3925}
Denis Vlasenko9213a9e2006-09-17 16:28:10 +00003926#endif /* CONFIG_FEATURE_VI_CRASHME */