blob: 638b3b7336466aee185bc9139c67f1c97a679a59 [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 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02006 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
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
Pere Orga6a3e01d2011-04-01 22:56:30 +020024//usage:#define vi_trivial_usage
25//usage: "[OPTIONS] [FILE]..."
26//usage:#define vi_full_usage "\n\n"
27//usage: "Edit FILE\n"
28//usage: "\nOptions:"
29//usage: IF_FEATURE_VI_COLON(
30//usage: "\n -c Initial command to run ($EXINIT also available)"
31//usage: )
32//usage: IF_FEATURE_VI_READONLY(
33//usage: "\n -R Read-only"
34//usage: )
35//usage: "\n -H Short help regarding available features"
36
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000037#include "libbb.h"
Eric Andersen3f980402001-04-04 17:31:15 +000038
Paul Fox35e9c5d2008-03-06 16:26:12 +000039/* the CRASHME code is unmaintained, and doesn't currently build */
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +000040#define ENABLE_FEATURE_VI_CRASHME 0
41
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +000042
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +000043#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +000044
45#if ENABLE_FEATURE_VI_8BIT
Denys Vlasenkoc2704542009-11-20 19:14:19 +010046//FIXME: this does not work properly for Unicode anyway
47# define Isprint(c) (isprint)(c)
Glenn L McGrath09adaca2002-12-02 21:18:10 +000048#else
Denys Vlasenkoc2704542009-11-20 19:14:19 +010049# define Isprint(c) isprint_asciionly(c)
Glenn L McGrath09adaca2002-12-02 21:18:10 +000050#endif
51
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +000052#else
53
54/* 0x9b is Meta-ESC */
55#if ENABLE_FEATURE_VI_8BIT
56#define Isprint(c) ((unsigned char)(c) >= ' ' && (c) != 0x7f && (unsigned char)(c) != 0x9b)
57#else
58#define Isprint(c) ((unsigned char)(c) >= ' ' && (unsigned char)(c) < 0x7f)
59#endif
60
61#endif
62
63
Denis Vlasenkoe8a07882007-06-10 15:08:44 +000064enum {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +000065 MAX_TABSTOP = 32, // sanity limit
Denis Vlasenko88adfcd2007-12-22 15:40:13 +000066 // User input len. Need not be extra big.
67 // Lines in file being edited *can* be bigger than this.
68 MAX_INPUT_LEN = 128,
69 // Sanity limits. We have only one buffer of this size.
70 MAX_SCR_COLS = CONFIG_FEATURE_VI_MAX_LEN,
71 MAX_SCR_ROWS = CONFIG_FEATURE_VI_MAX_LEN,
Denis Vlasenkoe8a07882007-06-10 15:08:44 +000072};
Eric Andersen3f980402001-04-04 17:31:15 +000073
Glenn L McGrath09adaca2002-12-02 21:18:10 +000074/* vt102 typical ESC sequence */
75/* terminal standout start/normal ESC sequence */
Denys Vlasenkod9a3e892010-05-16 23:42:13 +020076#define SOs "\033[7m"
77#define SOn "\033[0m"
Glenn L McGrath09adaca2002-12-02 21:18:10 +000078/* terminal bell sequence */
Denys Vlasenkod9a3e892010-05-16 23:42:13 +020079#define bell "\007"
Glenn L McGrath09adaca2002-12-02 21:18:10 +000080/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
Denys Vlasenkod9a3e892010-05-16 23:42:13 +020081#define Ceol "\033[K"
82#define Ceos "\033[J"
Glenn L McGrath09adaca2002-12-02 21:18:10 +000083/* Cursor motion arbitrary destination ESC sequence */
Denys Vlasenkod9a3e892010-05-16 23:42:13 +020084#define CMrc "\033[%u;%uH"
Glenn L McGrath09adaca2002-12-02 21:18:10 +000085/* Cursor motion up and down ESC sequence */
Denys Vlasenkod9a3e892010-05-16 23:42:13 +020086#define CMup "\033[A"
87#define CMdown "\n"
Glenn L McGrath09adaca2002-12-02 21:18:10 +000088
Denis Vlasenkoded6ad32008-10-14 12:26:30 +000089#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
90// cmds modifying text[]
91// vda: removed "aAiIs" as they switch us into insert mode
92// and remembering input for replay after them makes no sense
93static const char modifying_cmds[] = "cCdDJoOpPrRxX<>~";
94#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +000095
Rob Landleybc68cd12006-03-10 19:22:06 +000096enum {
97 YANKONLY = FALSE,
98 YANKDEL = TRUE,
99 FORWARD = 1, // code depends on "1" for array index
100 BACK = -1, // code depends on "-1" for array index
101 LIMITED = 0, // how much of text[] in char_search
102 FULL = 1, // how much of text[] in char_search
Eric Andersen3f980402001-04-04 17:31:15 +0000103
Rob Landleybc68cd12006-03-10 19:22:06 +0000104 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
105 S_TO_WS = 2, // used in skip_thing() for moving "dot"
106 S_OVER_WS = 3, // used in skip_thing() for moving "dot"
107 S_END_PUNCT = 4, // used in skip_thing() for moving "dot"
Denis Vlasenko8e858e22007-03-07 09:35:43 +0000108 S_END_ALNUM = 5, // used in skip_thing() for moving "dot"
Rob Landleybc68cd12006-03-10 19:22:06 +0000109};
Eric Andersen3f980402001-04-04 17:31:15 +0000110
Denis Vlasenkob1759462008-06-20 20:20:54 +0000111
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000112/* vi.c expects chars to be unsigned. */
113/* busybox build system provides that, but it's better */
114/* to audit and fix the source */
Eric Andersen3f980402001-04-04 17:31:15 +0000115
Denis Vlasenkob1759462008-06-20 20:20:54 +0000116struct globals {
117 /* many references - keep near the top of globals */
118 char *text, *end; // pointers to the user data in memory
119 char *dot; // where all the action takes place
120 int text_size; // size of the allocated buffer
121
122 /* the rest */
123 smallint vi_setops;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000124#define VI_AUTOINDENT 1
125#define VI_SHOWMATCH 2
126#define VI_IGNORECASE 4
127#define VI_ERR_METHOD 8
128#define autoindent (vi_setops & VI_AUTOINDENT)
129#define showmatch (vi_setops & VI_SHOWMATCH )
130#define ignorecase (vi_setops & VI_IGNORECASE)
131/* indicate error with beep or flash */
132#define err_method (vi_setops & VI_ERR_METHOD)
133
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000134#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkob1759462008-06-20 20:20:54 +0000135 smallint readonly_mode;
Denis Vlasenko6a2f7f42007-08-16 10:35:17 +0000136#define SET_READONLY_FILE(flags) ((flags) |= 0x01)
137#define SET_READONLY_MODE(flags) ((flags) |= 0x02)
138#define UNSET_READONLY_FILE(flags) ((flags) &= 0xfe)
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000139#else
Denis Vlasenkob1759462008-06-20 20:20:54 +0000140#define SET_READONLY_FILE(flags) ((void)0)
141#define SET_READONLY_MODE(flags) ((void)0)
142#define UNSET_READONLY_FILE(flags) ((void)0)
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000143#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000144
Denis Vlasenkob1759462008-06-20 20:20:54 +0000145 smallint editing; // >0 while we are editing a file
Denis Vlasenko30cfdf92008-09-21 15:29:29 +0000146 // [code audit says "can be 0, 1 or 2 only"]
Denis Vlasenkob1759462008-06-20 20:20:54 +0000147 smallint cmd_mode; // 0=command 1=insert 2=replace
148 int file_modified; // buffer contents changed (counter, not flag!)
Denis Vlasenko30cfdf92008-09-21 15:29:29 +0000149 int last_file_modified; // = -1;
Denis Vlasenkob1759462008-06-20 20:20:54 +0000150 int fn_start; // index of first cmd line file name
151 int save_argc; // how many file names on cmd line
152 int cmdcnt; // repetition count
153 unsigned rows, columns; // the terminal screen is this size
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700154#if ENABLE_FEATURE_VI_ASK_TERMINAL
155 int get_rowcol_error;
156#endif
Denis Vlasenkob1759462008-06-20 20:20:54 +0000157 int crow, ccol; // cursor is on Crow x Ccol
158 int offset; // chars scrolled off the screen to the left
159 int have_status_msg; // is default edit status needed?
160 // [don't make smallint!]
161 int last_status_cksum; // hash of current status line
162 char *current_filename;
163 char *screenbegin; // index into text[], of top line on the screen
164 char *screen; // pointer to the virtual screen buffer
165 int screensize; // and its size
166 int tabstop;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000167 int last_forward_char; // last char searched for with 'f' (int because of Unicode)
Denis Vlasenkob1759462008-06-20 20:20:54 +0000168 char erase_char; // the users erase character
169 char last_input_char; // last char read from user
Denis Vlasenkob1759462008-06-20 20:20:54 +0000170
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000171#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkob1759462008-06-20 20:20:54 +0000172 smallint adding2q; // are we currently adding user input to q
173 int lmc_len; // length of last_modifying_cmd
174 char *ioq, *ioq_start; // pointer to string for get_one_char to "read"
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000175#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000176#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Denis Vlasenkob1759462008-06-20 20:20:54 +0000177 int last_row; // where the cursor was last moved to
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000178#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000179#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenkob1759462008-06-20 20:20:54 +0000180 int my_pid;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000181#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000182#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkob1759462008-06-20 20:20:54 +0000183 char *last_search_pattern; // last pattern from a '/' or '?' search
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000184#endif
Denis Vlasenko0112ff52008-10-25 23:23:00 +0000185
Denis Vlasenkob1759462008-06-20 20:20:54 +0000186 /* former statics */
187#if ENABLE_FEATURE_VI_YANKMARK
188 char *edit_file__cur_line;
189#endif
190 int refresh__old_offset;
191 int format_edit_status__tot;
Eric Andersen3f980402001-04-04 17:31:15 +0000192
Denis Vlasenkob1759462008-06-20 20:20:54 +0000193 /* a few references only */
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000194#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000195 int YDreg, Ureg; // default delete register and orig line for "U"
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000196 char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000197 char *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
198 char *context_start, *context_end;
199#endif
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000200#if ENABLE_FEATURE_VI_USE_SIGNALS
Denis Vlasenkob1759462008-06-20 20:20:54 +0000201 sigjmp_buf restart; // catch_sig()
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000202#endif
Denis Vlasenkob1759462008-06-20 20:20:54 +0000203 struct termios term_orig, term_vi; // remember what the cooked mode was
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000204#if ENABLE_FEATURE_VI_COLON
205 char *initial_cmds[3]; // currently 2 entries, NULL terminated
206#endif
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000207 // Should be just enough to hold a key sequence,
Denis Vlasenko25497c12008-10-14 10:25:05 +0000208 // but CRASHME mode uses it as generated command buffer too
209#if ENABLE_FEATURE_VI_CRASHME
Denis Vlasenko1dfeeeb2008-10-18 19:04:37 +0000210 char readbuffer[128];
Denis Vlasenko25497c12008-10-14 10:25:05 +0000211#else
Denis Vlasenko5f6aaf32008-10-25 23:27:29 +0000212 char readbuffer[KEYCODE_BUFFER_SIZE];
Denis Vlasenko25497c12008-10-14 10:25:05 +0000213#endif
Denis Vlasenkob1759462008-06-20 20:20:54 +0000214#define STATUS_BUFFER_LEN 200
215 char status_buffer[STATUS_BUFFER_LEN]; // messages to the user
216#if ENABLE_FEATURE_VI_DOT_CMD
217 char last_modifying_cmd[MAX_INPUT_LEN]; // last modifying cmd for "."
218#endif
219 char get_input_line__buf[MAX_INPUT_LEN]; /* former static */
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000220
221 char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2];
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000222};
223#define G (*ptr_to_globals)
224#define text (G.text )
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000225#define text_size (G.text_size )
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000226#define end (G.end )
227#define dot (G.dot )
228#define reg (G.reg )
Denis Vlasenkob1759462008-06-20 20:20:54 +0000229
230#define vi_setops (G.vi_setops )
231#define editing (G.editing )
232#define cmd_mode (G.cmd_mode )
233#define file_modified (G.file_modified )
234#define last_file_modified (G.last_file_modified )
235#define fn_start (G.fn_start )
236#define save_argc (G.save_argc )
237#define cmdcnt (G.cmdcnt )
238#define rows (G.rows )
239#define columns (G.columns )
240#define crow (G.crow )
241#define ccol (G.ccol )
242#define offset (G.offset )
243#define status_buffer (G.status_buffer )
244#define have_status_msg (G.have_status_msg )
245#define last_status_cksum (G.last_status_cksum )
246#define current_filename (G.current_filename )
247#define screen (G.screen )
248#define screensize (G.screensize )
249#define screenbegin (G.screenbegin )
250#define tabstop (G.tabstop )
Denis Vlasenko0112ff52008-10-25 23:23:00 +0000251#define last_forward_char (G.last_forward_char )
Denis Vlasenkob1759462008-06-20 20:20:54 +0000252#define erase_char (G.erase_char )
253#define last_input_char (G.last_input_char )
Denis Vlasenko2a210e52008-06-22 13:20:42 +0000254#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkob1759462008-06-20 20:20:54 +0000255#define readonly_mode (G.readonly_mode )
Denis Vlasenko2a210e52008-06-22 13:20:42 +0000256#else
257#define readonly_mode 0
258#endif
Denis Vlasenkob1759462008-06-20 20:20:54 +0000259#define adding2q (G.adding2q )
260#define lmc_len (G.lmc_len )
261#define ioq (G.ioq )
262#define ioq_start (G.ioq_start )
263#define last_row (G.last_row )
264#define my_pid (G.my_pid )
Denis Vlasenkob1759462008-06-20 20:20:54 +0000265#define last_search_pattern (G.last_search_pattern)
Denis Vlasenko2a210e52008-06-22 13:20:42 +0000266
Denis Vlasenkob1759462008-06-20 20:20:54 +0000267#define edit_file__cur_line (G.edit_file__cur_line)
268#define refresh__old_offset (G.refresh__old_offset)
269#define format_edit_status__tot (G.format_edit_status__tot)
270
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000271#define YDreg (G.YDreg )
272#define Ureg (G.Ureg )
273#define mark (G.mark )
274#define context_start (G.context_start )
275#define context_end (G.context_end )
276#define restart (G.restart )
277#define term_orig (G.term_orig )
278#define term_vi (G.term_vi )
279#define initial_cmds (G.initial_cmds )
Denis Vlasenkoa96425f2007-12-09 04:13:43 +0000280#define readbuffer (G.readbuffer )
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000281#define scr_out_buf (G.scr_out_buf )
Denis Vlasenkob1759462008-06-20 20:20:54 +0000282#define last_modifying_cmd (G.last_modifying_cmd )
283#define get_input_line__buf (G.get_input_line__buf)
284
Denis Vlasenkoa96425f2007-12-09 04:13:43 +0000285#define INIT_G() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000286 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
Denis Vlasenkob1759462008-06-20 20:20:54 +0000287 last_file_modified = -1; \
Denis Vlasenko31d58e52008-10-29 13:16:28 +0000288 /* "" but has space for 2 chars: */ \
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000289 IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \
Denis Vlasenkoa96425f2007-12-09 04:13:43 +0000290} while (0)
Eric Andersen3f980402001-04-04 17:31:15 +0000291
Denis Vlasenkob1759462008-06-20 20:20:54 +0000292
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000293static int init_text_buffer(char *); // init from file or create new
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000294static void edit_file(char *); // edit one file
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000295static void do_cmd(int); // execute a command
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000296static int next_tabstop(int);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000297static void sync_cursor(char *, int *, int *); // synchronize the screen cursor to dot
298static char *begin_line(char *); // return pointer to cur line B-o-l
299static char *end_line(char *); // return pointer to cur line E-o-l
300static char *prev_line(char *); // return pointer to prev line B-o-l
301static char *next_line(char *); // return pointer to next line B-o-l
302static char *end_screen(void); // get pointer to last char on screen
303static int count_lines(char *, char *); // count line from start to stop
304static char *find_line(int); // find begining of line #li
305static char *move_to_col(char *, int); // move "p" to column l
Eric Andersen3f980402001-04-04 17:31:15 +0000306static void dot_left(void); // move dot left- dont leave line
307static void dot_right(void); // move dot right- dont leave line
308static void dot_begin(void); // move dot to B-o-l
309static void dot_end(void); // move dot to E-o-l
310static void dot_next(void); // move dot to next line B-o-l
311static void dot_prev(void); // move dot to prev line B-o-l
312static void dot_scroll(int, int); // move the screen up or down
313static void dot_skip_over_ws(void); // move dot pat WS
314static void dot_delete(void); // delete the char at 'dot'
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000315static char *bound_dot(char *); // make sure text[0] <= P < "end"
316static char *new_screen(int, int); // malloc virtual screen memory
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000317static char *char_insert(char *, char); // insert the char c at 'p'
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000318// might reallocate text[]! use p += stupid_insert(p, ...),
319// and be careful to not use pointers into potentially freed text[]!
320static uintptr_t stupid_insert(char *, char); // stupidly insert the char c at 'p'
Paul Foxc51fc7b2008-03-06 01:34:23 +0000321static int find_range(char **, char **, char); // return pointers for an object
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000322static int st_test(char *, int, int, char *); // helper for skip_thing()
323static char *skip_thing(char *, int, int, int); // skip some object
324static char *find_pair(char *, char); // find matching pair () [] {}
325static char *text_hole_delete(char *, char *); // at "p", delete a 'size' byte hole
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000326// might reallocate text[]! use p += text_hole_make(p, ...),
327// and be careful to not use pointers into potentially freed text[]!
328static uintptr_t text_hole_make(char *, int); // at "p", make a 'size' byte hole
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000329static char *yank_delete(char *, char *, int, int); // yank text[] into register then delete
Eric Andersen3f980402001-04-04 17:31:15 +0000330static void show_help(void); // display some help info
Eric Andersen3f980402001-04-04 17:31:15 +0000331static void rawmode(void); // set "raw" mode on tty
332static void cookmode(void); // return to "cooked" mode on tty
Denis Vlasenko87f3b262007-09-07 13:43:28 +0000333// sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready)
334static int mysleep(int);
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000335static int readit(void); // read (maybe cursor) key from stdin
336static int get_one_char(void); // read 1 char from stdin
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000337static int file_size(const char *); // what is the byte size of "fn"
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000338#if !ENABLE_FEATURE_VI_READONLY
339#define file_insert(fn, p, update_ro_status) file_insert(fn, p)
Denis Vlasenko59a1f302007-07-14 22:43:10 +0000340#endif
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000341// file_insert might reallocate text[]!
342static int file_insert(const char *, char *, int);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000343static int file_write(char *, char *, char *);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +0000344#if !ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
345#define place_cursor(a, b, optimize) place_cursor(a, b)
346#endif
Eric Andersen822c3832001-05-07 17:37:43 +0000347static void place_cursor(int, int, int);
Eric Andersenbff7a602001-11-17 07:15:43 +0000348static void screen_erase(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000349static void clear_to_eol(void);
350static void clear_to_eos(void);
Denis Vlasenko267e16c2008-10-14 10:34:41 +0000351static void go_bottom_and_clear_to_eol(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000352static void standout_start(void); // send "start reverse video" sequence
353static void standout_end(void); // send "end reverse video" sequence
354static void flash(int); // flash the terminal screen
Eric Andersen3f980402001-04-04 17:31:15 +0000355static void show_status_line(void); // put a message on the bottom line
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000356static void status_line(const char *, ...); // print to status buf
357static void status_line_bold(const char *, ...);
358static void not_implemented(const char *); // display "Not implemented" message
Paul Fox8552aec2005-09-16 12:20:05 +0000359static int format_edit_status(void); // format file status on status line
Eric Andersen3f980402001-04-04 17:31:15 +0000360static void redraw(int); // force a full screen refresh
Denis Vlasenko68404f12008-03-17 09:00:54 +0000361static char* format_line(char* /*, int*/);
Eric Andersen3f980402001-04-04 17:31:15 +0000362static void refresh(int); // update the terminal from screen[]
363
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000364static void Indicate_Error(void); // use flash or beep to indicate error
365#define indicate_error(c) Indicate_Error()
Paul Fox90372ed2005-10-09 14:26:26 +0000366static void Hit_Return(void);
367
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000368#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000369static char *char_search(char *, const char *, int, int); // search for pattern starting at p
370static int mycmp(const char *, const char *, int); // string cmp based in "ignorecase"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000371#endif
372#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000373static char *get_one_address(char *, int *); // get colon addr, if present
374static char *get_address(char *, int *, int *); // get two colon addrs, if present
375static void colon(char *); // execute the "colon" mode cmds
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000376#endif
377#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000378static void winch_sig(int); // catch window size changes
379static void suspend_sig(int); // catch ctrl-Z
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000380static void catch_sig(int); // catch ctrl-C and alarm time-outs
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000381#endif
382#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000383static void start_new_cmd_q(char); // new queue for command
Eric Andersenbff7a602001-11-17 07:15:43 +0000384static void end_cmd_q(void); // stop saving input chars
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000385#else
386#define end_cmd_q() ((void)0)
387#endif
388#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000389static void showmatching(char *); // show the matching pair () [] {}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000390#endif
391#if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000392// might reallocate text[]! use p += string_insert(p, ...),
393// and be careful to not use pointers into potentially freed text[]!
394static uintptr_t string_insert(char *, const char *); // insert the string at 'p'
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000395#endif
396#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000397static char *text_yank(char *, char *, int); // save copy of "p" into a register
398static char what_reg(void); // what is letter of current YDreg
399static void check_context(char); // remember context for '' command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000400#endif
401#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000402static void crash_dummy();
403static void crash_test();
404static int crashme = 0;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000405#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000406
407
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000408static void write1(const char *out)
409{
410 fputs(out, stdout);
411}
412
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000413int vi_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Rob Landleydfba7412006-03-06 20:47:33 +0000414int vi_main(int argc, char **argv)
Eric Andersen3f980402001-04-04 17:31:15 +0000415{
Eric Andersend402edf2001-04-04 19:29:48 +0000416 int c;
Denis Vlasenkob1759462008-06-20 20:20:54 +0000417
418 INIT_G();
Eric Andersen3f980402001-04-04 17:31:15 +0000419
Denis Vlasenkocd5c7862007-05-17 16:37:22 +0000420#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000421 my_pid = getpid();
422#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000423#if ENABLE_FEATURE_VI_CRASHME
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000424 srand((long) my_pid);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000425#endif
Denis Vlasenko2414a962007-07-18 22:03:40 +0000426#ifdef NO_SUCH_APPLET_YET
427 /* If we aren't "vi", we are "view" */
428 if (ENABLE_FEATURE_VI_READONLY && applet_name[2]) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000429 SET_READONLY_MODE(readonly_mode);
Eric Andersen3f980402001-04-04 17:31:15 +0000430 }
Denis Vlasenko2414a962007-07-18 22:03:40 +0000431#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000432
Bernhard Reutner-Fischer73f56bb2007-09-22 21:18:46 +0000433 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE;
Denis Vlasenkof9234132007-03-21 00:03:42 +0000434 // 1- process $HOME/.exrc file (not inplemented yet)
Eric Andersen3f980402001-04-04 17:31:15 +0000435 // 2- process EXINIT variable from environment
436 // 3- process command line args
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000437#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000438 {
439 char *p = getenv("EXINIT");
440 if (p && *p)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000441 initial_cmds[0] = xstrndup(p, MAX_INPUT_LEN);
Denis Vlasenkof9234132007-03-21 00:03:42 +0000442 }
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000443#endif
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000444 while ((c = getopt(argc, argv, "hCRH" IF_FEATURE_VI_COLON("c:"))) != -1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000445 switch (c) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000446#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000447 case 'C':
448 crashme = 1;
449 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000450#endif
451#if ENABLE_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +0000452 case 'R': // Read-only flag
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000453 SET_READONLY_MODE(readonly_mode);
Eric Andersen3f980402001-04-04 17:31:15 +0000454 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000455#endif
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000456#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000457 case 'c': // cmd line vi command
458 if (*optarg)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000459 initial_cmds[initial_cmds[0] != 0] = xstrndup(optarg, MAX_INPUT_LEN);
Denis Vlasenkof9234132007-03-21 00:03:42 +0000460 break;
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000461#endif
Paul Fox35e9c5d2008-03-06 16:26:12 +0000462 case 'H':
Eric Andersen3f980402001-04-04 17:31:15 +0000463 show_help();
Paul Fox35e9c5d2008-03-06 16:26:12 +0000464 /* fall through */
Paul Fox35e9c5d2008-03-06 16:26:12 +0000465 default:
Denis Vlasenkoc6938402008-03-24 02:18:03 +0000466 bb_show_usage();
Eric Andersendd8500b2001-07-02 18:06:14 +0000467 return 1;
Eric Andersen3f980402001-04-04 17:31:15 +0000468 }
469 }
470
471 // The argv array can be used by the ":next" and ":rewind" commands
472 // save optind.
473 fn_start = optind; // remember first file name for :next and :rew
474 save_argc = argc;
475
476 //----- This is the main file handling loop --------------
Denys Vlasenko04cecd52010-04-16 22:13:55 -0700477 while (1) {
478 edit_file(argv[optind]); /* param might be NULL */
479 if (++optind >= argc)
480 break;
Eric Andersen3f980402001-04-04 17:31:15 +0000481 }
482 //-----------------------------------------------------------
483
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000484 return 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000485}
486
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000487/* read text from file or create an empty buf */
488/* will also update current_filename */
489static int init_text_buffer(char *fn)
490{
491 int rc;
492 int size = file_size(fn); // file size. -1 means does not exist.
493
494 /* allocate/reallocate text buffer */
495 free(text);
Denis Vlasenkob1759462008-06-20 20:20:54 +0000496 text_size = size + 10240;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000497 screenbegin = dot = end = text = xzalloc(text_size);
Denis Vlasenko2f6ae432007-07-19 22:50:47 +0000498
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000499 if (fn != current_filename) {
500 free(current_filename);
501 current_filename = xstrdup(fn);
502 }
503 if (size < 0) {
504 // file dont exist. Start empty buf with dummy line
505 char_insert(text, '\n');
506 rc = 0;
507 } else {
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000508 rc = file_insert(fn, text, 1);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000509 }
510 file_modified = 0;
511 last_file_modified = -1;
512#if ENABLE_FEATURE_VI_YANKMARK
513 /* init the marks. */
514 memset(mark, 0, sizeof(mark));
515#endif
516 return rc;
Denis Vlasenko2f6ae432007-07-19 22:50:47 +0000517}
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000518
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700519#if ENABLE_FEATURE_VI_WIN_RESIZE
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200520static int query_screen_dimensions(void)
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700521{
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200522 int err = get_terminal_width_height(STDIN_FILENO, &columns, &rows);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700523 if (rows > MAX_SCR_ROWS)
524 rows = MAX_SCR_ROWS;
525 if (columns > MAX_SCR_COLS)
526 columns = MAX_SCR_COLS;
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200527 return err;
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700528}
529#else
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200530# define query_screen_dimensions() (0)
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700531#endif
532
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000533static void edit_file(char *fn)
Eric Andersen3f980402001-04-04 17:31:15 +0000534{
Denis Vlasenkob1759462008-06-20 20:20:54 +0000535#if ENABLE_FEATURE_VI_YANKMARK
536#define cur_line edit_file__cur_line
537#endif
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000538 int c;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000539#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000540 int sig;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000541#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000542
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000543 editing = 1; // 0 = exit, 1 = one file, 2 = multiple files
Eric Andersen3f980402001-04-04 17:31:15 +0000544 rawmode();
545 rows = 24;
546 columns = 80;
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200547 IF_FEATURE_VI_ASK_TERMINAL(G.get_rowcol_error =) query_screen_dimensions();
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700548#if ENABLE_FEATURE_VI_ASK_TERMINAL
549 if (G.get_rowcol_error /* TODO? && no input on stdin */) {
550 uint64_t k;
551 write1("\033[999;999H" "\033[6n");
552 fflush_all();
553 k = read_key(STDIN_FILENO, readbuffer, /*timeout_ms:*/ 100);
554 if ((int32_t)k == KEYCODE_CURSOR_POS) {
555 uint32_t rc = (k >> 32);
556 columns = (rc & 0x7fff);
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200557 if (columns > MAX_SCR_COLS)
558 columns = MAX_SCR_COLS;
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700559 rows = ((rc >> 16) & 0x7fff);
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200560 if (rows > MAX_SCR_ROWS)
561 rows = MAX_SCR_ROWS;
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700562 }
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700563 }
564#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000565 new_screen(rows, columns); // get memory for virtual screen
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000566 init_text_buffer(fn);
Eric Andersen3f980402001-04-04 17:31:15 +0000567
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000568#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000569 YDreg = 26; // default Yank/Delete reg
570 Ureg = 27; // hold orig line for "U" cmd
Eric Andersen3f980402001-04-04 17:31:15 +0000571 mark[26] = mark[27] = text; // init "previous context"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000572#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000573
Eric Andersen3f980402001-04-04 17:31:15 +0000574 last_forward_char = last_input_char = '\0';
575 crow = 0;
576 ccol = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000577
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000578#if ENABLE_FEATURE_VI_USE_SIGNALS
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700579 signal(SIGINT, catch_sig);
Eric Andersen3f980402001-04-04 17:31:15 +0000580 signal(SIGWINCH, winch_sig);
581 signal(SIGTSTP, suspend_sig);
Paul Fox2724fa92008-03-17 15:28:07 +0000582 sig = sigsetjmp(restart, 1);
Eric Andersen3f980402001-04-04 17:31:15 +0000583 if (sig != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000584 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000585 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000586#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000587
Eric Andersen3f980402001-04-04 17:31:15 +0000588 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
589 cmdcnt = 0;
590 tabstop = 8;
591 offset = 0; // no horizontal offset
592 c = '\0';
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000593#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000594 free(ioq_start);
Paul Foxc51fc7b2008-03-06 01:34:23 +0000595 ioq = ioq_start = NULL;
596 lmc_len = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000597 adding2q = 0;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000598#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000599
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000600#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000601 {
602 char *p, *q;
603 int n = 0;
604
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700605 while ((p = initial_cmds[n]) != NULL) {
Denis Vlasenkof9234132007-03-21 00:03:42 +0000606 do {
607 q = p;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000608 p = strchr(q, '\n');
Denis Vlasenkof9234132007-03-21 00:03:42 +0000609 if (p)
Denis Vlasenko51742f42007-04-12 00:32:05 +0000610 while (*p == '\n')
Denis Vlasenkof9234132007-03-21 00:03:42 +0000611 *p++ = '\0';
612 if (*q)
613 colon(q);
614 } while (p);
615 free(initial_cmds[n]);
616 initial_cmds[n] = NULL;
617 n++;
618 }
619 }
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000620#endif
Paul Fox35e9c5d2008-03-06 16:26:12 +0000621 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000622 //------This is the main Vi cmd handling loop -----------------------
623 while (editing > 0) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000624#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000625 if (crashme > 0) {
626 if ((end - text) > 1) {
627 crash_dummy(); // generate a random command
628 } else {
629 crashme = 0;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000630 string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
631 dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000632 refresh(FALSE);
633 }
634 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000635#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000636 last_input_char = c = get_one_char(); // get a cmd from user
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000637#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000638 // save a copy of the current line- for the 'U" command
639 if (begin_line(dot) != cur_line) {
640 cur_line = begin_line(dot);
641 text_yank(begin_line(dot), end_line(dot), Ureg);
642 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000643#endif
644#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000645 // These are commands that change text[].
646 // Remember the input for the "." command
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000647 if (!adding2q && ioq_start == NULL
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000648 && cmd_mode == 0 // command mode
649 && c > '\0' // exclude NUL and non-ASCII chars
650 && c < 0x7f // (Unicode and such)
651 && strchr(modifying_cmds, c)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000652 ) {
Eric Andersen3f980402001-04-04 17:31:15 +0000653 start_new_cmd_q(c);
654 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000655#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000656 do_cmd(c); // execute the user command
Denis Vlasenko8ef801b2008-10-16 09:46:07 +0000657
Eric Andersen3f980402001-04-04 17:31:15 +0000658 // poll to see if there is input already waiting. if we are
659 // not able to display output fast enough to keep up, skip
660 // the display update until we catch up with input.
Denys Vlasenko020f4062009-05-17 16:44:54 +0200661 if (!readbuffer[0] && mysleep(0) == 0) {
Denis Vlasenko8ef801b2008-10-16 09:46:07 +0000662 // no input pending - so update output
Eric Andersen3f980402001-04-04 17:31:15 +0000663 refresh(FALSE);
664 show_status_line();
665 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000666#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000667 if (crashme > 0)
668 crash_test(); // test editor variables
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000669#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000670 }
671 //-------------------------------------------------------------------
672
Denis Vlasenko267e16c2008-10-14 10:34:41 +0000673 go_bottom_and_clear_to_eol();
Eric Andersen3f980402001-04-04 17:31:15 +0000674 cookmode();
Denis Vlasenkob1759462008-06-20 20:20:54 +0000675#undef cur_line
Eric Andersen3f980402001-04-04 17:31:15 +0000676}
677
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000678//----- The Colon commands -------------------------------------
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000679#if ENABLE_FEATURE_VI_COLON
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000680static char *get_one_address(char *p, int *addr) // get colon addr, if present
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000681{
682 int st;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000683 char *q;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000684 IF_FEATURE_VI_YANKMARK(char c;)
685 IF_FEATURE_VI_SEARCH(char *pat;)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000686
687 *addr = -1; // assume no addr
688 if (*p == '.') { // the current line
689 p++;
690 q = begin_line(dot);
691 *addr = count_lines(text, q);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000692 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000693#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000694 else if (*p == '\'') { // is this a mark addr
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000695 p++;
696 c = tolower(*p);
697 p++;
698 if (c >= 'a' && c <= 'z') {
699 // we have a mark
700 c = c - 'a';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000701 q = mark[(unsigned char) c];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000702 if (q != NULL) { // is mark valid
Denis Vlasenko00d84172008-11-24 07:34:42 +0000703 *addr = count_lines(text, q);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000704 }
705 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000706 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000707#endif
708#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000709 else if (*p == '/') { // a search pattern
710 q = strchrnul(++p, '/');
711 pat = xstrndup(p, q - p); // save copy of pattern
712 p = q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000713 if (*p == '/')
714 p++;
715 q = char_search(dot, pat, FORWARD, FULL);
716 if (q != NULL) {
717 *addr = count_lines(text, q);
718 }
719 free(pat);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000720 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000721#endif
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000722 else if (*p == '$') { // the last line in file
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000723 p++;
724 q = begin_line(end - 1);
725 *addr = count_lines(text, q);
726 } else if (isdigit(*p)) { // specific line number
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000727 sscanf(p, "%d%n", addr, &st);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000728 p += st;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000729 } else {
Denys Vlasenkob22bbff2009-07-04 16:50:43 +0200730 // unrecognized address - assume -1
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000731 *addr = -1;
732 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000733 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000734}
735
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000736static char *get_address(char *p, int *b, int *e) // get two colon addrs, if present
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000737{
738 //----- get the address' i.e., 1,3 'a,'b -----
739 // get FIRST addr, if present
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000740 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000741 p++; // skip over leading spaces
742 if (*p == '%') { // alias for 1,$
743 p++;
744 *b = 1;
745 *e = count_lines(text, end-1);
746 goto ga0;
747 }
748 p = get_one_address(p, b);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000749 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000750 p++;
Eric Andersenaff114c2004-04-14 17:51:38 +0000751 if (*p == ',') { // is there a address separator
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000752 p++;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000753 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000754 p++;
755 // get SECOND addr, if present
756 p = get_one_address(p, e);
757 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000758 ga0:
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000759 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000760 p++; // skip over trailing spaces
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000761 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000762}
763
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000764#if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000765static void setops(const char *args, const char *opname, int flg_no,
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000766 const char *short_opname, int opt)
767{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000768 const char *a = args + flg_no;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000769 int l = strlen(opname) - 1; /* opname have + ' ' */
770
Denys Vlasenko6548edd2009-06-15 12:44:11 +0200771 // maybe strncmp? we had tons of erroneous strncasecmp's...
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000772 if (strncasecmp(a, opname, l) == 0
773 || strncasecmp(a, short_opname, 2) == 0
774 ) {
775 if (flg_no)
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000776 vi_setops &= ~opt;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000777 else
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000778 vi_setops |= opt;
779 }
780}
781#endif
782
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000783// buf must be no longer than MAX_INPUT_LEN!
784static void colon(char *buf)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000785{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000786 char c, *orig_buf, *buf1, *q, *r;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000787 char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN];
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +0000788 int i, l, li, ch, b, e;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000789 int useforce, forced = FALSE;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000790
791 // :3154 // if (-e line 3154) goto it else stay put
792 // :4,33w! foo // write a portion of buffer to file "foo"
793 // :w // write all of buffer to current file
794 // :q // quit
795 // :q! // quit- dont care about modified file
796 // :'a,'z!sort -u // filter block through sort
797 // :'f // goto mark "f"
798 // :'fl // list literal the mark "f" line
799 // :.r bar // read file "bar" into buffer before dot
800 // :/123/,/abc/d // delete lines from "123" line to "abc" line
801 // :/xyz/ // goto the "xyz" line
802 // :s/find/replace/ // substitute pattern "find" with "replace"
803 // :!<cmd> // run <cmd> then return
804 //
Eric Andersen165e8cb2004-07-20 06:44:46 +0000805
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000806 if (!buf[0])
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +0200807 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000808 if (*buf == ':')
809 buf++; // move past the ':'
810
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +0000811 li = ch = i = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000812 b = e = -1;
813 q = text; // assume 1,$ for the range
814 r = end - 1;
815 li = count_lines(text, end - 1);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000816 fn = current_filename;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000817
818 // look for optional address(es) :. :1 :1,9 :'q,'a :%
819 buf = get_address(buf, &b, &e);
820
821 // remember orig command line
822 orig_buf = buf;
823
824 // get the COMMAND into cmd[]
825 buf1 = cmd;
826 while (*buf != '\0') {
827 if (isspace(*buf))
828 break;
829 *buf1++ = *buf++;
830 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000831 *buf1 = '\0';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000832 // get any ARGuments
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000833 while (isblank(*buf))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000834 buf++;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000835 strcpy(args, buf);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000836 useforce = FALSE;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000837 buf1 = last_char_is(cmd, '!');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000838 if (buf1) {
839 useforce = TRUE;
840 *buf1 = '\0'; // get rid of !
841 }
842 if (b >= 0) {
843 // if there is only one addr, then the addr
844 // is the line number of the single line the
845 // user wants. So, reset the end
846 // pointer to point at end of the "b" line
847 q = find_line(b); // what line is #b
848 r = end_line(q);
849 li = 1;
850 }
851 if (e >= 0) {
852 // we were given two addrs. change the
853 // end pointer to the addr given by user.
854 r = find_line(e); // what line is #e
855 r = end_line(r);
856 li = e - b + 1;
857 }
858 // ------------ now look for the command ------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000859 i = strlen(cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000860 if (i == 0) { // :123CR goto line #123
861 if (b >= 0) {
862 dot = find_line(b); // what line is #b
863 dot_skip_over_ws();
864 }
Denis Vlasenko249fabf2006-12-19 00:29:22 +0000865 }
866#if ENABLE_FEATURE_ALLOW_EXEC
Denys Vlasenko6548edd2009-06-15 12:44:11 +0200867 else if (cmd[0] == '!') { // run a cmd
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000868 int retcode;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000869 // :!ls run the <cmd>
Denis Vlasenko267e16c2008-10-14 10:34:41 +0000870 go_bottom_and_clear_to_eol();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000871 cookmode();
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000872 retcode = system(orig_buf + 1); // run the cmd
873 if (retcode)
874 printf("\nshell returned %i\n\n", retcode);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000875 rawmode();
876 Hit_Return(); // let user see results
Denis Vlasenko249fabf2006-12-19 00:29:22 +0000877 }
878#endif
Denys Vlasenko6548edd2009-06-15 12:44:11 +0200879 else if (cmd[0] == '=' && !cmd[1]) { // where is the address
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000880 if (b < 0) { // no addr given- use defaults
881 b = e = count_lines(text, dot);
882 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000883 status_line("%d", b);
Denys Vlasenko6548edd2009-06-15 12:44:11 +0200884 } else if (strncmp(cmd, "delete", i) == 0) { // delete lines
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000885 if (b < 0) { // no addr given- use defaults
886 q = begin_line(dot); // assume .,. for the range
887 r = end_line(dot);
888 }
889 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
890 dot_skip_over_ws();
Denys Vlasenko6548edd2009-06-15 12:44:11 +0200891 } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000892 // don't edit, if the current file has been modified
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000893 if (file_modified && !useforce) {
894 status_line_bold("No write since last change (:edit! overrides)");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +0200895 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000896 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000897 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000898 // the user supplied a file name
Denis Vlasenkoe8a07882007-06-10 15:08:44 +0000899 fn = args;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000900 } else if (current_filename && current_filename[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000901 // no user supplied name- use the current filename
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000902 // fn = current_filename; was set by default
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000903 } else {
904 // no user file name, no current name- punt
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000905 status_line_bold("No current filename");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +0200906 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000907 }
908
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000909 if (init_text_buffer(fn) < 0)
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +0200910 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000911
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000912#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000913 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
914 free(reg[Ureg]); // free orig line reg- for 'U'
915 reg[Ureg]= 0;
916 }
917 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
918 free(reg[YDreg]); // free default yank/delete register
919 reg[YDreg]= 0;
920 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000921#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000922 // how many lines in text[]?
923 li = count_lines(text, end - 1);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000924 status_line("\"%s\"%s"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000925 IF_FEATURE_VI_READONLY("%s")
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000926 " %dL, %dC", current_filename,
927 (file_size(fn) < 0 ? " [New file]" : ""),
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000928 IF_FEATURE_VI_READONLY(
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000929 ((readonly_mode) ? " [Readonly]" : ""),
930 )
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000931 li, ch);
Denys Vlasenko6548edd2009-06-15 12:44:11 +0200932 } else if (strncmp(cmd, "file", i) == 0) { // what File is this
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000933 if (b != -1 || e != -1) {
Denys Vlasenkoc2704542009-11-20 19:14:19 +0100934 status_line_bold("No address allowed on this command");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +0200935 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000936 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000937 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000938 // user wants a new filename
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000939 free(current_filename);
940 current_filename = xstrdup(args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000941 } else {
942 // user wants file status info
Paul Fox8552aec2005-09-16 12:20:05 +0000943 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000944 }
Denys Vlasenko6548edd2009-06-15 12:44:11 +0200945 } else if (strncmp(cmd, "features", i) == 0) { // what features are available
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000946 // print out values of all features
Denis Vlasenko267e16c2008-10-14 10:34:41 +0000947 go_bottom_and_clear_to_eol();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000948 cookmode();
949 show_help();
950 rawmode();
951 Hit_Return();
Denys Vlasenko6548edd2009-06-15 12:44:11 +0200952 } else if (strncmp(cmd, "list", i) == 0) { // literal print line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000953 if (b < 0) { // no addr given- use defaults
954 q = begin_line(dot); // assume .,. for the range
955 r = end_line(dot);
956 }
Denis Vlasenko267e16c2008-10-14 10:34:41 +0000957 go_bottom_and_clear_to_eol();
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000958 puts("\r");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000959 for (; q <= r; q++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000960 int c_is_no_print;
961
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000962 c = *q;
Denis Vlasenko2a51af22007-03-21 22:31:24 +0000963 c_is_no_print = (c & 0x80) && !Isprint(c);
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000964 if (c_is_no_print) {
965 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000966 standout_start();
Denis Vlasenkod3c042f2007-12-30 01:59:53 +0000967 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000968 if (c == '\n') {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000969 write1("$\r");
970 } else if (c < ' ' || c == 127) {
Denis Vlasenko4daad902007-09-27 10:20:47 +0000971 bb_putchar('^');
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000972 if (c == 127)
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000973 c = '?';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000974 else
975 c += '@';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000976 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000977 bb_putchar(c);
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000978 if (c_is_no_print)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000979 standout_end();
980 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000981 Hit_Return();
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +0200982 } else if (strncmp(cmd, "quit", i) == 0 // quit
Denys Vlasenko6548edd2009-06-15 12:44:11 +0200983 || strncmp(cmd, "next", i) == 0 // edit next file
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000984 ) {
Denys Vlasenko04cecd52010-04-16 22:13:55 -0700985 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000986 if (useforce) {
987 // force end of argv list
988 if (*cmd == 'q') {
989 optind = save_argc;
990 }
991 editing = 0;
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +0200992 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000993 }
994 // don't exit if the file been modified
995 if (file_modified) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000996 status_line_bold("No write since last change (:%s! overrides)",
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000997 (*cmd == 'q' ? "quit" : "next"));
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +0200998 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000999 }
1000 // are there other file to edit
Denys Vlasenko04cecd52010-04-16 22:13:55 -07001001 n = save_argc - optind - 1;
1002 if (*cmd == 'q' && n > 0) {
1003 status_line_bold("%d more file(s) to edit", n);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001004 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001005 }
Denys Vlasenko04cecd52010-04-16 22:13:55 -07001006 if (*cmd == 'n' && n <= 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001007 status_line_bold("No more files to edit");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001008 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001009 }
1010 editing = 0;
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001011 } else if (strncmp(cmd, "read", i) == 0) { // read file into text[]
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001012 fn = args;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001013 if (!fn[0]) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001014 status_line_bold("No filename given");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001015 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001016 }
1017 if (b < 0) { // no addr given- use defaults
1018 q = begin_line(dot); // assume "dot"
1019 }
1020 // read after current line- unless user said ":0r foo"
1021 if (b != 0)
1022 q = next_line(q);
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001023 { // dance around potentially-reallocated text[]
1024 uintptr_t ofs = q - text;
1025 ch = file_insert(fn, q, 0);
1026 q = text + ofs;
1027 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001028 if (ch < 0)
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001029 goto ret; // nothing was inserted
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001030 // how many lines in text[]?
1031 li = count_lines(q, q + ch - 1);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001032 status_line("\"%s\""
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001033 IF_FEATURE_VI_READONLY("%s")
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001034 " %dL, %dC", fn,
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001035 IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001036 li, ch);
1037 if (ch > 0) {
1038 // if the insert is before "dot" then we need to update
1039 if (q <= dot)
1040 dot += ch;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001041 /*file_modified++; - done by file_insert */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001042 }
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001043 } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001044 if (file_modified && !useforce) {
1045 status_line_bold("No write since last change (:rewind! overrides)");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001046 } else {
1047 // reset the filenames to edit
1048 optind = fn_start - 1;
1049 editing = 0;
1050 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001051#if ENABLE_FEATURE_VI_SET
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001052 } else if (strncmp(cmd, "set", i) == 0) { // set or clear features
Denis Vlasenko58875ae2007-03-22 22:22:10 +00001053#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkof9234132007-03-21 00:03:42 +00001054 char *argp;
Denis Vlasenko58875ae2007-03-22 22:22:10 +00001055#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001056 i = 0; // offset into args
Denis Vlasenkof9234132007-03-21 00:03:42 +00001057 // only blank is regarded as args delmiter. What about tab '\t' ?
1058 if (!args[0] || strcasecmp(args, "all") == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001059 // print out values of all options
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001060#if ENABLE_FEATURE_VI_SETOPTS
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001061 status_line_bold(
1062 "%sautoindent "
1063 "%sflash "
1064 "%signorecase "
1065 "%sshowmatch "
1066 "tabstop=%u",
1067 autoindent ? "" : "no",
1068 err_method ? "" : "no",
1069 ignorecase ? "" : "no",
1070 showmatch ? "" : "no",
1071 tabstop
1072 );
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001073#endif
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001074 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001075 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001076#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001077 argp = args;
Denis Vlasenkoba2fb712007-04-01 09:39:03 +00001078 while (*argp) {
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001079 if (strncmp(argp, "no", 2) == 0)
Denis Vlasenkof9234132007-03-21 00:03:42 +00001080 i = 2; // ":set noautoindent"
1081 setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001082 setops(argp, "flash " , i, "fl", VI_ERR_METHOD);
Denis Vlasenkof9234132007-03-21 00:03:42 +00001083 setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001084 setops(argp, "showmatch " , i, "sm", VI_SHOWMATCH );
1085 if (strncmp(argp + i, "tabstop=", 8) == 0) {
1086 int t = 0;
1087 sscanf(argp + i+8, "%u", &t);
1088 if (t > 0 && t <= MAX_TABSTOP)
1089 tabstop = t;
Denis Vlasenkof9234132007-03-21 00:03:42 +00001090 }
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001091 argp = skip_non_whitespace(argp);
1092 argp = skip_whitespace(argp);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001093 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001094#endif /* FEATURE_VI_SETOPTS */
1095#endif /* FEATURE_VI_SET */
1096#if ENABLE_FEATURE_VI_SEARCH
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001097 } else if (cmd[0] == 's') { // substitute a pattern with a replacement pattern
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001098 char *ls, *F, *R;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001099 int gflag;
1100
1101 // F points to the "find" pattern
1102 // R points to the "replace" pattern
1103 // replace the cmd line delimiters "/" with NULLs
1104 gflag = 0; // global replace flag
1105 c = orig_buf[1]; // what is the delimiter
1106 F = orig_buf + 2; // start of "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001107 R = strchr(F, c); // middle delimiter
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001108 if (!R)
1109 goto colon_s_fail;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001110 *R++ = '\0'; // terminate "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001111 buf1 = strchr(R, c);
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001112 if (!buf1)
1113 goto colon_s_fail;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001114 *buf1++ = '\0'; // terminate "replace"
1115 if (*buf1 == 'g') { // :s/foo/bar/g
1116 buf1++;
1117 gflag++; // turn on gflag
1118 }
1119 q = begin_line(q);
1120 if (b < 0) { // maybe :s/foo/bar/
1121 q = begin_line(dot); // start with cur line
1122 b = count_lines(text, q); // cur line number
1123 }
1124 if (e < 0)
1125 e = b; // maybe :.s/foo/bar/
1126 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
1127 ls = q; // orig line start
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001128 vc4:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001129 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001130 if (buf1) {
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001131 uintptr_t bias;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001132 // we found the "find" pattern - delete it
1133 text_hole_delete(buf1, buf1 + strlen(F) - 1);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001134 // inset the "replace" patern
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001135 bias = string_insert(buf1, R); // insert the string
1136 buf1 += bias;
1137 ls += bias;
1138 /*q += bias; - recalculated anyway */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001139 // check for "global" :s/foo/bar/g
1140 if (gflag == 1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001141 if ((buf1 + strlen(R)) < end_line(ls)) {
1142 q = buf1 + strlen(R);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001143 goto vc4; // don't let q move past cur line
1144 }
1145 }
1146 }
1147 q = next_line(ls);
1148 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001149#endif /* FEATURE_VI_SEARCH */
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001150 } else if (strncmp(cmd, "version", i) == 0) { // show software version
Denis Vlasenko33875382008-06-21 20:31:50 +00001151 status_line(BB_VER " " BB_BT);
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001152 } else if (strncmp(cmd, "write", i) == 0 // write text to file
1153 || strncmp(cmd, "wq", i) == 0
1154 || strncmp(cmd, "wn", i) == 0
1155 || (cmd[0] == 'x' && !cmd[1])
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001156 ) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001157 // is there a file name to write to?
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001158 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001159 fn = args;
1160 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001161#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001162 if (readonly_mode && !useforce) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001163 status_line_bold("\"%s\" File is read only", fn);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001164 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001165 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001166#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001167 // how many lines in text[]?
1168 li = count_lines(q, r);
1169 ch = r - q + 1;
1170 // see if file exists- if not, its just a new file request
1171 if (useforce) {
1172 // if "fn" is not write-able, chmod u+w
1173 // sprintf(syscmd, "chmod u+w %s", fn);
1174 // system(syscmd);
1175 forced = TRUE;
1176 }
1177 l = file_write(fn, q, r);
1178 if (useforce && forced) {
1179 // chmod u-w
1180 // sprintf(syscmd, "chmod u-w %s", fn);
1181 // system(syscmd);
1182 forced = FALSE;
1183 }
Paul Fox61e45db2005-10-09 14:43:22 +00001184 if (l < 0) {
1185 if (l == -1)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001186 status_line_bold("\"%s\" %s", fn, strerror(errno));
Paul Fox61e45db2005-10-09 14:43:22 +00001187 } else {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001188 status_line("\"%s\" %dL, %dC", fn, li, l);
Paul Fox61e45db2005-10-09 14:43:22 +00001189 if (q == text && r == end - 1 && l == ch) {
1190 file_modified = 0;
1191 last_file_modified = -1;
1192 }
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001193 if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n'
1194 || cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N'
1195 )
1196 && l == ch
1197 ) {
Paul Fox61e45db2005-10-09 14:43:22 +00001198 editing = 0;
1199 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001200 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001201#if ENABLE_FEATURE_VI_YANKMARK
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001202 } else if (strncmp(cmd, "yank", i) == 0) { // yank lines
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001203 if (b < 0) { // no addr given- use defaults
1204 q = begin_line(dot); // assume .,. for the range
1205 r = end_line(dot);
1206 }
1207 text_yank(q, r, YDreg);
1208 li = count_lines(q, r);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001209 status_line("Yank %d lines (%d chars) into [%c]",
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001210 li, strlen(reg[YDreg]), what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001211#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001212 } else {
1213 // cmd unknown
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001214 not_implemented(cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001215 }
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001216 ret:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001217 dot = bound_dot(dot); // make sure "dot" is valid
1218 return;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001219#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001220 colon_s_fail:
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001221 status_line(":s expression missing delimiters");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001222#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001223}
Paul Fox61e45db2005-10-09 14:43:22 +00001224
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001225#endif /* FEATURE_VI_COLON */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001226
1227static void Hit_Return(void)
1228{
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00001229 int c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001230
Denis Vlasenkof882f082007-12-23 02:36:51 +00001231 standout_start();
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001232 write1("[Hit return to continue]");
Denis Vlasenkof882f082007-12-23 02:36:51 +00001233 standout_end();
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001234 while ((c = get_one_char()) != '\n' && c != '\r')
1235 continue;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001236 redraw(TRUE); // force redraw all
1237}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001238
Denis Vlasenko91afdf82007-07-17 23:22:49 +00001239static int next_tabstop(int col)
1240{
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001241 return col + ((tabstop - 1) - (col % tabstop));
1242}
1243
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001244//----- Synchronize the cursor to Dot --------------------------
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02001245static NOINLINE void sync_cursor(char *d, int *row, int *col)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001246{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001247 char *beg_cur; // begin and end of "d" line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001248 char *tp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001249 int cnt, ro, co;
1250
1251 beg_cur = begin_line(d); // first char of cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001252
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001253 if (beg_cur < screenbegin) {
Denis Vlasenkof882f082007-12-23 02:36:51 +00001254 // "d" is before top line on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001255 // how many lines do we have to move
1256 cnt = count_lines(beg_cur, screenbegin);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001257 sc1:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001258 screenbegin = beg_cur;
1259 if (cnt > (rows - 1) / 2) {
1260 // we moved too many lines. put "dot" in middle of screen
1261 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1262 screenbegin = prev_line(screenbegin);
1263 }
1264 }
Denis Vlasenkof882f082007-12-23 02:36:51 +00001265 } else {
1266 char *end_scr; // begin and end of screen
1267 end_scr = end_screen(); // last char of screen
1268 if (beg_cur > end_scr) {
1269 // "d" is after bottom line on screen
1270 // how many lines do we have to move
1271 cnt = count_lines(end_scr, beg_cur);
1272 if (cnt > (rows - 1) / 2)
1273 goto sc1; // too many lines
1274 for (ro = 0; ro < cnt - 1; ro++) {
1275 // move screen begin the same amount
1276 screenbegin = next_line(screenbegin);
1277 // now, move the end of screen
1278 end_scr = next_line(end_scr);
1279 end_scr = end_line(end_scr);
1280 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001281 }
1282 }
1283 // "d" is on screen- find out which row
1284 tp = screenbegin;
1285 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1286 if (tp == beg_cur)
1287 break;
1288 tp = next_line(tp);
1289 }
1290
1291 // find out what col "d" is on
1292 co = 0;
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001293 while (tp < d) { // drive "co" to correct column
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001294 if (*tp == '\n') //vda || *tp == '\0')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001295 break;
1296 if (*tp == '\t') {
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001297 // handle tabs like real vi
1298 if (d == tp && cmd_mode) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001299 break;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001300 }
Denis Vlasenkoe3eae0d2008-06-22 16:38:53 +00001301 co = next_tabstop(co);
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001302 } else if ((unsigned char)*tp < ' ' || *tp == 0x7f) {
1303 co++; // display as ^X, use 2 columns
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001304 }
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001305 co++;
1306 tp++;
1307 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001308
1309 // "co" is the column where "dot" is.
1310 // The screen has "columns" columns.
1311 // The currently displayed columns are 0+offset -- columns+ofset
1312 // |-------------------------------------------------------------|
1313 // ^ ^ ^
1314 // offset | |------- columns ----------------|
1315 //
1316 // If "co" is already in this range then we do not have to adjust offset
1317 // but, we do have to subtract the "offset" bias from "co".
1318 // If "co" is outside this range then we have to change "offset".
1319 // If the first char of a line is a tab the cursor will try to stay
1320 // in column 7, but we have to set offset to 0.
1321
1322 if (co < 0 + offset) {
1323 offset = co;
1324 }
1325 if (co >= columns + offset) {
1326 offset = co - columns + 1;
1327 }
1328 // if the first char of the line is a tab, and "dot" is sitting on it
1329 // force offset to 0.
1330 if (d == beg_cur && *d == '\t') {
1331 offset = 0;
1332 }
1333 co -= offset;
1334
1335 *row = ro;
1336 *col = co;
1337}
1338
1339//----- Text Movement Routines ---------------------------------
Denis Vlasenkof882f082007-12-23 02:36:51 +00001340static char *begin_line(char *p) // return pointer to first char cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001341{
Denis Vlasenkof882f082007-12-23 02:36:51 +00001342 if (p > text) {
1343 p = memrchr(text, '\n', p - text);
1344 if (!p)
1345 return text;
1346 return p + 1;
1347 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001348 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001349}
1350
Denis Vlasenkoe3eae0d2008-06-22 16:38:53 +00001351static char *end_line(char *p) // return pointer to NL of cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001352{
Denis Vlasenkof882f082007-12-23 02:36:51 +00001353 if (p < end - 1) {
1354 p = memchr(p, '\n', end - p - 1);
1355 if (!p)
1356 return end - 1;
1357 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001358 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001359}
1360
Denis Vlasenkof882f082007-12-23 02:36:51 +00001361static char *dollar_line(char *p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001362{
Denis Vlasenkof882f082007-12-23 02:36:51 +00001363 p = end_line(p);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001364 // Try to stay off of the Newline
1365 if (*p == '\n' && (p - begin_line(p)) > 0)
1366 p--;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001367 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001368}
1369
Denis Vlasenkof882f082007-12-23 02:36:51 +00001370static char *prev_line(char *p) // return pointer first char prev line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001371{
1372 p = begin_line(p); // goto begining of cur line
Denis Vlasenkoe3eae0d2008-06-22 16:38:53 +00001373 if (p > text && p[-1] == '\n')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001374 p--; // step to prev line
1375 p = begin_line(p); // goto begining of prev line
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001376 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001377}
1378
Denis Vlasenkof882f082007-12-23 02:36:51 +00001379static char *next_line(char *p) // return pointer first char next line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001380{
1381 p = end_line(p);
Denis Vlasenkoe3eae0d2008-06-22 16:38:53 +00001382 if (p < end - 1 && *p == '\n')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001383 p++; // step to next line
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001384 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001385}
1386
1387//----- Text Information Routines ------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001388static char *end_screen(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001389{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001390 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001391 int cnt;
1392
1393 // find new bottom line
1394 q = screenbegin;
1395 for (cnt = 0; cnt < rows - 2; cnt++)
1396 q = next_line(q);
1397 q = end_line(q);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001398 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001399}
1400
Denis Vlasenkof882f082007-12-23 02:36:51 +00001401// count line from start to stop
1402static int count_lines(char *start, char *stop)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001403{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001404 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001405 int cnt;
1406
Denis Vlasenkof882f082007-12-23 02:36:51 +00001407 if (stop < start) { // start and stop are backwards- reverse them
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001408 q = start;
1409 start = stop;
1410 stop = q;
1411 }
1412 cnt = 0;
Denis Vlasenkof882f082007-12-23 02:36:51 +00001413 stop = end_line(stop);
1414 while (start <= stop && start <= end - 1) {
1415 start = end_line(start);
1416 if (*start == '\n')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001417 cnt++;
Denis Vlasenkof882f082007-12-23 02:36:51 +00001418 start++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001419 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001420 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001421}
1422
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001423static char *find_line(int li) // find begining of line #li
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001424{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001425 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001426
1427 for (q = text; li > 1; li--) {
1428 q = next_line(q);
1429 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001430 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001431}
1432
1433//----- Dot Movement Routines ----------------------------------
1434static void dot_left(void)
1435{
1436 if (dot > text && dot[-1] != '\n')
1437 dot--;
1438}
1439
1440static void dot_right(void)
1441{
1442 if (dot < end - 1 && *dot != '\n')
1443 dot++;
1444}
1445
1446static void dot_begin(void)
1447{
1448 dot = begin_line(dot); // return pointer to first char cur line
1449}
1450
1451static void dot_end(void)
1452{
1453 dot = end_line(dot); // return pointer to last char cur line
1454}
1455
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001456static char *move_to_col(char *p, int l)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001457{
1458 int co;
1459
1460 p = begin_line(p);
1461 co = 0;
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001462 while (co < l && p < end) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001463 if (*p == '\n') //vda || *p == '\0')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001464 break;
1465 if (*p == '\t') {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001466 co = next_tabstop(co);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001467 } else if (*p < ' ' || *p == 127) {
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001468 co++; // display as ^X, use 2 columns
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001469 }
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001470 co++;
1471 p++;
1472 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001473 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001474}
1475
1476static void dot_next(void)
1477{
1478 dot = next_line(dot);
1479}
1480
1481static void dot_prev(void)
1482{
1483 dot = prev_line(dot);
1484}
1485
1486static void dot_scroll(int cnt, int dir)
1487{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001488 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001489
1490 for (; cnt > 0; cnt--) {
1491 if (dir < 0) {
1492 // scroll Backwards
Denis Vlasenkof882f082007-12-23 02:36:51 +00001493 // ctrl-Y scroll up one line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001494 screenbegin = prev_line(screenbegin);
1495 } else {
1496 // scroll Forwards
Denis Vlasenkof882f082007-12-23 02:36:51 +00001497 // ctrl-E scroll down one line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001498 screenbegin = next_line(screenbegin);
1499 }
1500 }
1501 // make sure "dot" stays on the screen so we dont scroll off
1502 if (dot < screenbegin)
1503 dot = screenbegin;
1504 q = end_screen(); // find new bottom line
1505 if (dot > q)
1506 dot = begin_line(q); // is dot is below bottom line?
1507 dot_skip_over_ws();
1508}
1509
1510static void dot_skip_over_ws(void)
1511{
1512 // skip WS
1513 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1514 dot++;
1515}
1516
1517static void dot_delete(void) // delete the char at 'dot'
1518{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001519 text_hole_delete(dot, dot);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001520}
1521
Denis Vlasenkof882f082007-12-23 02:36:51 +00001522static char *bound_dot(char *p) // make sure text[0] <= P < "end"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001523{
1524 if (p >= end && end > text) {
1525 p = end - 1;
1526 indicate_error('1');
1527 }
1528 if (p < text) {
1529 p = text;
1530 indicate_error('2');
1531 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001532 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001533}
1534
1535//----- Helper Utility Routines --------------------------------
1536
1537//----------------------------------------------------------------
1538//----- Char Routines --------------------------------------------
1539/* Chars that are part of a word-
1540 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1541 * Chars that are Not part of a word (stoppers)
1542 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1543 * Chars that are WhiteSpace
1544 * TAB NEWLINE VT FF RETURN SPACE
1545 * DO NOT COUNT NEWLINE AS WHITESPACE
1546 */
1547
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001548static char *new_screen(int ro, int co)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001549{
1550 int li;
1551
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001552 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001553 screensize = ro * co + 8;
Denis Vlasenkob95636c2006-12-19 23:36:04 +00001554 screen = xmalloc(screensize);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001555 // initialize the new screen. assume this will be a empty file.
1556 screen_erase();
Eric Andersenaff114c2004-04-14 17:51:38 +00001557 // non-existent text[] lines start with a tilde (~).
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001558 for (li = 1; li < ro - 1; li++) {
1559 screen[(li * co) + 0] = '~';
1560 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001561 return screen;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001562}
1563
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001564#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenko33875382008-06-21 20:31:50 +00001565static int mycmp(const char *s1, const char *s2, int len)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001566{
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001567 if (ENABLE_FEATURE_VI_SETOPTS && ignorecase) {
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001568 return strncasecmp(s1, s2, len);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001569 }
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001570 return strncmp(s1, s2, len);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001571}
1572
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001573// search for pattern starting at p
Denis Vlasenko33875382008-06-21 20:31:50 +00001574static char *char_search(char *p, const char *pat, int dir, int range)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001575{
1576#ifndef REGEX_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001577 char *start, *stop;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001578 int len;
1579
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001580 len = strlen(pat);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001581 if (dir == FORWARD) {
1582 stop = end - 1; // assume range is p - end-1
1583 if (range == LIMITED)
1584 stop = next_line(p); // range is to next line
1585 for (start = p; start < stop; start++) {
1586 if (mycmp(start, pat, len) == 0) {
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001587 return start;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001588 }
1589 }
1590 } else if (dir == BACK) {
1591 stop = text; // assume range is text - p
1592 if (range == LIMITED)
1593 stop = prev_line(p); // range is to prev line
1594 for (start = p - len; start >= stop; start--) {
1595 if (mycmp(start, pat, len) == 0) {
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001596 return start;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001597 }
1598 }
1599 }
1600 // pattern not found
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001601 return NULL;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001602#else /* REGEX_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001603 char *q;
1604 struct re_pattern_buffer preg;
1605 int i;
1606 int size, range;
1607
1608 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1609 preg.translate = 0;
1610 preg.fastmap = 0;
1611 preg.buffer = 0;
1612 preg.allocated = 0;
1613
1614 // assume a LIMITED forward search
1615 q = next_line(p);
1616 q = end_line(q);
1617 q = end - 1;
1618 if (dir == BACK) {
1619 q = prev_line(p);
1620 q = text;
1621 }
1622 // count the number of chars to search over, forward or backward
1623 size = q - p;
1624 if (size < 0)
1625 size = p - q;
1626 // RANGE could be negative if we are searching backwards
1627 range = q - p;
1628
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001629 q = re_compile_pattern(pat, strlen(pat), &preg);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001630 if (q != 0) {
1631 // The pattern was not compiled
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001632 status_line_bold("bad search pattern: \"%s\": %s", pat, q);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001633 i = 0; // return p if pattern not compiled
1634 goto cs1;
1635 }
1636
1637 q = p;
1638 if (range < 0) {
1639 q = p - size;
1640 if (q < text)
1641 q = text;
1642 }
1643 // search for the compiled pattern, preg, in p[]
1644 // range < 0- search backward
1645 // range > 0- search forward
1646 // 0 < start < size
1647 // re_search() < 0 not found or error
1648 // re_search() > 0 index of found pattern
1649 // struct pattern char int int int struct reg
1650 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1651 i = re_search(&preg, q, size, 0, range, 0);
1652 if (i == -1) {
1653 p = 0;
1654 i = 0; // return NULL if pattern not found
1655 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001656 cs1:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001657 if (dir == FORWARD) {
1658 p = p + i;
1659 } else {
1660 p = p - i;
1661 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001662 return p;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001663#endif /* REGEX_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001664}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001665#endif /* FEATURE_VI_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001666
Denis Vlasenko33875382008-06-21 20:31:50 +00001667static char *char_insert(char *p, char c) // insert the char c at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001668{
1669 if (c == 22) { // Is this an ctrl-V?
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001670 p += stupid_insert(p, '^'); // use ^ to indicate literal next
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001671 refresh(FALSE); // show the ^
1672 c = get_one_char();
1673 *p = c;
1674 p++;
Denis Vlasenkob1759462008-06-20 20:20:54 +00001675 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001676 } else if (c == 27) { // Is this an ESC?
1677 cmd_mode = 0;
1678 cmdcnt = 0;
1679 end_cmd_q(); // stop adding to q
Paul Fox8552aec2005-09-16 12:20:05 +00001680 last_status_cksum = 0; // force status update
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +00001681 if ((p[-1] != '\n') && (dot > text)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001682 p--;
1683 }
Paul Foxd13b90b2005-07-18 22:17:25 +00001684 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001685 // 123456789
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +00001686 if ((p[-1] != '\n') && (dot>text)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001687 p--;
1688 p = text_hole_delete(p, p); // shrink buffer 1 char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001689 }
1690 } else {
1691 // insert a char into text[]
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001692 char *sp; // "save p"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001693
1694 if (c == 13)
1695 c = '\n'; // translate \r to \n
1696 sp = p; // remember addr of insert
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001697 p += 1 + stupid_insert(p, c); // insert the char
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001698#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001699 if (showmatch && strchr(")]}", *sp) != NULL) {
1700 showmatching(sp);
1701 }
1702 if (autoindent && c == '\n') { // auto indent the new line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001703 char *q;
Denis Vlasenko00d84172008-11-24 07:34:42 +00001704 size_t len;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001705 q = prev_line(p); // use prev line as template
Denis Vlasenko00d84172008-11-24 07:34:42 +00001706 len = strspn(q, " \t"); // space or tab
1707 if (len) {
Denis Vlasenko3266aa92009-04-01 11:24:04 +00001708 uintptr_t bias;
Denis Vlasenko00d84172008-11-24 07:34:42 +00001709 bias = text_hole_make(p, len);
1710 p += bias;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001711 q += bias;
Denis Vlasenko00d84172008-11-24 07:34:42 +00001712 memcpy(p, q, len);
Denis Vlasenko3266aa92009-04-01 11:24:04 +00001713 p += len;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001714 }
1715 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001716#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001717 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001718 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001719}
1720
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001721// might reallocate text[]! use p += stupid_insert(p, ...),
1722// and be careful to not use pointers into potentially freed text[]!
1723static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001724{
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001725 uintptr_t bias;
1726 bias = text_hole_make(p, 1);
1727 p += bias;
Denis Vlasenkob1759462008-06-20 20:20:54 +00001728 *p = c;
1729 //file_modified++; - done by text_hole_make()
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001730 return bias;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001731}
1732
Denis Vlasenko33875382008-06-21 20:31:50 +00001733static int find_range(char **start, char **stop, char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001734{
Paul Foxc51fc7b2008-03-06 01:34:23 +00001735 char *save_dot, *p, *q, *t;
1736 int cnt, multiline = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001737
1738 save_dot = dot;
1739 p = q = dot;
1740
1741 if (strchr("cdy><", c)) {
1742 // these cmds operate on whole lines
1743 p = q = begin_line(p);
1744 for (cnt = 1; cnt < cmdcnt; cnt++) {
1745 q = next_line(q);
1746 }
1747 q = end_line(q);
Paul Foxc51fc7b2008-03-06 01:34:23 +00001748 } else if (strchr("^%$0bBeEfth\b\177", c)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001749 // These cmds operate on char positions
1750 do_cmd(c); // execute movement cmd
1751 q = dot;
1752 } else if (strchr("wW", c)) {
1753 do_cmd(c); // execute movement cmd
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001754 // if we are at the next word's first char
1755 // step back one char
1756 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00001757 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001758 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1759 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1760 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001761 if (dot > text && *dot == '\n')
1762 dot--; // stay off NL
1763 q = dot;
1764 } else if (strchr("H-k{", c)) {
1765 // these operate on multi-lines backwards
1766 q = end_line(dot); // find NL
1767 do_cmd(c); // execute movement cmd
1768 dot_begin();
1769 p = dot;
1770 } else if (strchr("L+j}\r\n", c)) {
1771 // these operate on multi-lines forwards
1772 p = begin_line(dot);
1773 do_cmd(c); // execute movement cmd
1774 dot_end(); // find NL
1775 q = dot;
1776 } else {
Paul Foxc51fc7b2008-03-06 01:34:23 +00001777 // nothing -- this causes any other values of c to
1778 // represent the one-character range under the
1779 // cursor. this is correct for ' ' and 'l', but
1780 // perhaps no others.
1781 //
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001782 }
Paul Foxc51fc7b2008-03-06 01:34:23 +00001783 if (q < p) {
1784 t = q;
1785 q = p;
1786 p = t;
1787 }
1788
Denis Vlasenko42cc3042008-03-24 02:05:58 +00001789 // backward char movements don't include start position
Paul Foxc51fc7b2008-03-06 01:34:23 +00001790 if (q > p && strchr("^0bBh\b\177", c)) q--;
1791
1792 multiline = 0;
1793 for (t = p; t <= q; t++) {
1794 if (*t == '\n') {
1795 multiline = 1;
1796 break;
1797 }
1798 }
1799
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001800 *start = p;
1801 *stop = q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001802 dot = save_dot;
Paul Foxc51fc7b2008-03-06 01:34:23 +00001803 return multiline;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001804}
1805
Denis Vlasenko33875382008-06-21 20:31:50 +00001806static int st_test(char *p, int type, int dir, char *tested)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001807{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001808 char c, c0, ci;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001809 int test, inc;
1810
1811 inc = dir;
1812 c = c0 = p[0];
1813 ci = p[inc];
1814 test = 0;
1815
1816 if (type == S_BEFORE_WS) {
1817 c = ci;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02001818 test = (!isspace(c) || c == '\n');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001819 }
1820 if (type == S_TO_WS) {
1821 c = c0;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02001822 test = (!isspace(c) || c == '\n');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001823 }
1824 if (type == S_OVER_WS) {
1825 c = c0;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02001826 test = isspace(c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001827 }
1828 if (type == S_END_PUNCT) {
1829 c = ci;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02001830 test = ispunct(c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001831 }
1832 if (type == S_END_ALNUM) {
1833 c = ci;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02001834 test = (isalnum(c) || c == '_');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001835 }
1836 *tested = c;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001837 return test;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001838}
1839
Denis Vlasenko33875382008-06-21 20:31:50 +00001840static char *skip_thing(char *p, int linecnt, int dir, int type)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001841{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001842 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001843
1844 while (st_test(p, type, dir, &c)) {
1845 // make sure we limit search to correct number of lines
1846 if (c == '\n' && --linecnt < 1)
1847 break;
1848 if (dir >= 0 && p >= end - 1)
1849 break;
1850 if (dir < 0 && p <= text)
1851 break;
1852 p += dir; // move to next char
1853 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001854 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001855}
1856
1857// find matching char of pair () [] {}
Denis Vlasenko33875382008-06-21 20:31:50 +00001858static char *find_pair(char *p, const char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001859{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001860 char match, *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001861 int dir, level;
1862
1863 match = ')';
1864 level = 1;
1865 dir = 1; // assume forward
1866 switch (c) {
Paul Foxc51fc7b2008-03-06 01:34:23 +00001867 case '(': match = ')'; break;
1868 case '[': match = ']'; break;
1869 case '{': match = '}'; break;
1870 case ')': match = '('; dir = -1; break;
1871 case ']': match = '['; dir = -1; break;
1872 case '}': match = '{'; dir = -1; break;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001873 }
1874 for (q = p + dir; text <= q && q < end; q += dir) {
1875 // look for match, count levels of pairs (( ))
1876 if (*q == c)
1877 level++; // increase pair levels
1878 if (*q == match)
1879 level--; // reduce pair level
1880 if (level == 0)
1881 break; // found matching pair
1882 }
1883 if (level != 0)
1884 q = NULL; // indicate no match
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001885 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001886}
1887
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001888#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001889// show the matching char of a pair, () [] {}
Denis Vlasenkof882f082007-12-23 02:36:51 +00001890static void showmatching(char *p)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001891{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001892 char *q, *save_dot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001893
1894 // we found half of a pair
1895 q = find_pair(p, *p); // get loc of matching char
1896 if (q == NULL) {
1897 indicate_error('3'); // no matching char
1898 } else {
1899 // "q" now points to matching pair
1900 save_dot = dot; // remember where we are
1901 dot = q; // go to new loc
1902 refresh(FALSE); // let the user see it
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001903 mysleep(40); // give user some time
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001904 dot = save_dot; // go back to old loc
1905 refresh(FALSE);
1906 }
1907}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001908#endif /* FEATURE_VI_SETOPTS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001909
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001910// open a hole in text[]
1911// might reallocate text[]! use p += text_hole_make(p, ...),
1912// and be careful to not use pointers into potentially freed text[]!
1913static uintptr_t text_hole_make(char *p, int size) // at "p", make a 'size' byte hole
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001914{
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001915 uintptr_t bias = 0;
1916
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001917 if (size <= 0)
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001918 return bias;
Denis Vlasenkob1759462008-06-20 20:20:54 +00001919 end += size; // adjust the new END
1920 if (end >= (text + text_size)) {
1921 char *new_text;
1922 text_size += end - (text + text_size) + 10240;
1923 new_text = xrealloc(text, text_size);
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001924 bias = (new_text - text);
1925 screenbegin += bias;
1926 dot += bias;
1927 end += bias;
1928 p += bias;
Denis Vlasenkob1759462008-06-20 20:20:54 +00001929 text = new_text;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001930 }
Denis Vlasenkod6995442008-06-27 04:06:13 +00001931 memmove(p + size, p, end - size - p);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001932 memset(p, ' ', size); // clear new hole
Denis Vlasenkob1759462008-06-20 20:20:54 +00001933 file_modified++;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001934 return bias;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001935}
1936
1937// close a hole in text[]
Denis Vlasenko33875382008-06-21 20:31:50 +00001938static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclusive
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001939{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001940 char *src, *dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001941 int cnt, hole_size;
1942
1943 // move forwards, from beginning
1944 // assume p <= q
1945 src = q + 1;
1946 dest = p;
1947 if (q < p) { // they are backward- swap them
1948 src = p + 1;
1949 dest = q;
1950 }
1951 hole_size = q - p + 1;
1952 cnt = end - src;
1953 if (src < text || src > end)
1954 goto thd0;
1955 if (dest < text || dest >= end)
1956 goto thd0;
1957 if (src >= end)
1958 goto thd_atend; // just delete the end of the buffer
Denis Vlasenkob1759462008-06-20 20:20:54 +00001959 memmove(dest, src, cnt);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001960 thd_atend:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001961 end = end - hole_size; // adjust the new END
1962 if (dest >= end)
1963 dest = end - 1; // make sure dest in below end-1
1964 if (end <= text)
1965 dest = end = text; // keep pointers valid
Denis Vlasenkob1759462008-06-20 20:20:54 +00001966 file_modified++;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001967 thd0:
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001968 return dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001969}
1970
1971// copy text into register, then delete text.
1972// if dist <= 0, do not include, or go past, a NewLine
1973//
Denis Vlasenko33875382008-06-21 20:31:50 +00001974static char *yank_delete(char *start, char *stop, int dist, int yf)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001975{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001976 char *p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001977
1978 // make sure start <= stop
1979 if (start > stop) {
1980 // they are backwards, reverse them
1981 p = start;
1982 start = stop;
1983 stop = p;
1984 }
1985 if (dist <= 0) {
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +00001986 // we cannot cross NL boundaries
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001987 p = start;
1988 if (*p == '\n')
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001989 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001990 // dont go past a NewLine
1991 for (; p + 1 <= stop; p++) {
1992 if (p[1] == '\n') {
1993 stop = p; // "stop" just before NewLine
1994 break;
1995 }
1996 }
1997 }
1998 p = start;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001999#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002000 text_yank(start, stop, YDreg);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002001#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002002 if (yf == YANKDEL) {
2003 p = text_hole_delete(start, stop);
2004 } // delete lines
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002005 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002006}
2007
2008static void show_help(void)
2009{
2010 puts("These features are available:"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002011#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002012 "\n\tPattern searches with / and ?"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002013#endif
2014#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002015 "\n\tLast command repeat with \'.\'"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002016#endif
2017#if ENABLE_FEATURE_VI_YANKMARK
Bernhard Reutner-Fischerd24d5c82007-10-01 18:04:42 +00002018 "\n\tLine marking with 'x"
2019 "\n\tNamed buffers with \"x"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002020#endif
2021#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002022 "\n\tReadonly if vi is called as \"view\""
2023 "\n\tReadonly with -R command line arg"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002024#endif
2025#if ENABLE_FEATURE_VI_SET
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002026 "\n\tSome colon mode commands with \':\'"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002027#endif
2028#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002029 "\n\tSettable options with \":set\""
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002030#endif
2031#if ENABLE_FEATURE_VI_USE_SIGNALS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002032 "\n\tSignal catching- ^C"
2033 "\n\tJob suspend and resume with ^Z"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002034#endif
2035#if ENABLE_FEATURE_VI_WIN_RESIZE
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002036 "\n\tAdapt to window re-sizes"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002037#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002038 );
2039}
2040
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002041#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002042static void start_new_cmd_q(char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002043{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002044 // get buffer for new cmd
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002045 // if there is a current cmd count put it in the buffer first
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002046 if (cmdcnt > 0) {
Paul Foxc51fc7b2008-03-06 01:34:23 +00002047 lmc_len = sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002048 } else { // just save char c onto queue
Paul Foxd957b952005-11-28 18:07:53 +00002049 last_modifying_cmd[0] = c;
Paul Foxc51fc7b2008-03-06 01:34:23 +00002050 lmc_len = 1;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002051 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002052 adding2q = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002053}
2054
2055static void end_cmd_q(void)
2056{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002057#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002058 YDreg = 26; // go back to default Yank/Delete reg
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002059#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002060 adding2q = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002061}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002062#endif /* FEATURE_VI_DOT_CMD */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002063
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002064#if ENABLE_FEATURE_VI_YANKMARK \
2065 || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
2066 || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002067// might reallocate text[]! use p += string_insert(p, ...),
2068// and be careful to not use pointers into potentially freed text[]!
2069static uintptr_t string_insert(char *p, const char *s) // insert the string at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002070{
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002071 uintptr_t bias;
2072 int i;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002073
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002074 i = strlen(s);
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002075 bias = text_hole_make(p, i);
2076 p += bias;
Denis Vlasenko00d84172008-11-24 07:34:42 +00002077 memcpy(p, s, i);
Denis Vlasenko33875382008-06-21 20:31:50 +00002078#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002079 {
2080 int cnt;
2081 for (cnt = 0; *s != '\0'; s++) {
2082 if (*s == '\n')
2083 cnt++;
2084 }
2085 status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2086 }
Denis Vlasenko33875382008-06-21 20:31:50 +00002087#endif
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002088 return bias;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002089}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002090#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002091
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002092#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko33875382008-06-21 20:31:50 +00002093static char *text_yank(char *p, char *q, int dest) // copy text into a register
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002094{
Denis Vlasenko00d84172008-11-24 07:34:42 +00002095 int cnt = q - p;
2096 if (cnt < 0) { // they are backwards- reverse them
2097 p = q;
2098 cnt = -cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002099 }
Denis Vlasenko00d84172008-11-24 07:34:42 +00002100 free(reg[dest]); // if already a yank register, free it
2101 reg[dest] = xstrndup(p, cnt + 1);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002102 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002103}
2104
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002105static char what_reg(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002106{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002107 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002108
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002109 c = 'D'; // default to D-reg
2110 if (0 <= YDreg && YDreg <= 25)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002111 c = 'a' + (char) YDreg;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002112 if (YDreg == 26)
2113 c = 'D';
2114 if (YDreg == 27)
2115 c = 'U';
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002116 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002117}
2118
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002119static void check_context(char cmd)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002120{
2121 // A context is defined to be "modifying text"
2122 // Any modifying command establishes a new context.
2123
2124 if (dot < context_start || dot > context_end) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002125 if (strchr(modifying_cmds, cmd) != NULL) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002126 // we are trying to modify text[]- make this the current context
2127 mark[27] = mark[26]; // move cur to prev
2128 mark[26] = dot; // move local to cur
2129 context_start = prev_line(prev_line(dot));
2130 context_end = next_line(next_line(dot));
2131 //loiter= start_loiter= now;
2132 }
2133 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002134}
2135
Denis Vlasenkof882f082007-12-23 02:36:51 +00002136static char *swap_context(char *p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002137{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002138 char *tmp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002139
2140 // the current context is in mark[26]
2141 // the previous context is in mark[27]
2142 // only swap context if other context is valid
2143 if (text <= mark[27] && mark[27] <= end - 1) {
2144 tmp = mark[27];
2145 mark[27] = mark[26];
2146 mark[26] = tmp;
2147 p = mark[26]; // where we are going- previous context
2148 context_start = prev_line(prev_line(prev_line(p)));
2149 context_end = next_line(next_line(next_line(p)));
2150 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002151 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002152}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002153#endif /* FEATURE_VI_YANKMARK */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002154
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002155//----- Set terminal attributes --------------------------------
2156static void rawmode(void)
2157{
2158 tcgetattr(0, &term_orig);
2159 term_vi = term_orig;
2160 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2161 term_vi.c_iflag &= (~IXON & ~ICRNL);
2162 term_vi.c_oflag &= (~ONLCR);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002163 term_vi.c_cc[VMIN] = 1;
2164 term_vi.c_cc[VTIME] = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002165 erase_char = term_vi.c_cc[VERASE];
Denis Vlasenko202ac502008-11-05 13:20:58 +00002166 tcsetattr_stdin_TCSANOW(&term_vi);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002167}
2168
2169static void cookmode(void)
2170{
Denys Vlasenko8131eea2009-11-02 14:19:51 +01002171 fflush_all();
Denis Vlasenko202ac502008-11-05 13:20:58 +00002172 tcsetattr_stdin_TCSANOW(&term_orig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002173}
2174
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002175#if ENABLE_FEATURE_VI_USE_SIGNALS
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002176//----- Come here when we get a window resize signal ---------
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002177static void winch_sig(int sig UNUSED_PARAM)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002178{
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002179 int save_errno = errno;
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002180 // FIXME: do it in main loop!!!
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002181 signal(SIGWINCH, winch_sig);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002182 query_screen_dimensions();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002183 new_screen(rows, columns); // get memory for virtual screen
2184 redraw(TRUE); // re-draw the screen
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002185 errno = save_errno;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002186}
2187
2188//----- Come here when we get a continue signal -------------------
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002189static void cont_sig(int sig UNUSED_PARAM)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002190{
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002191 int save_errno = errno;
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002192 rawmode(); // terminal to "raw"
2193 last_status_cksum = 0; // force status update
2194 redraw(TRUE); // re-draw the screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002195
2196 signal(SIGTSTP, suspend_sig);
2197 signal(SIGCONT, SIG_DFL);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002198 //kill(my_pid, SIGCONT); // huh? why? we are already "continued"...
2199 errno = save_errno;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002200}
2201
2202//----- Come here when we get a Suspend signal -------------------
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002203static void suspend_sig(int sig UNUSED_PARAM)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002204{
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002205 int save_errno = errno;
Denis Vlasenko267e16c2008-10-14 10:34:41 +00002206 go_bottom_and_clear_to_eol();
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002207 cookmode(); // terminal to "cooked"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002208
2209 signal(SIGCONT, cont_sig);
2210 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002211 kill(my_pid, SIGTSTP);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002212 errno = save_errno;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002213}
2214
2215//----- Come here when we get a signal ---------------------------
2216static void catch_sig(int sig)
2217{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002218 signal(SIGINT, catch_sig);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002219 siglongjmp(restart, sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002220}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002221#endif /* FEATURE_VI_USE_SIGNALS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002222
Denys Vlasenko606291b2009-09-23 23:15:43 +02002223static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002224{
Denis Vlasenko87f3b262007-09-07 13:43:28 +00002225 struct pollfd pfd[1];
Denis Vlasenkocd5c7862007-05-17 16:37:22 +00002226
Denys Vlasenko606291b2009-09-23 23:15:43 +02002227 pfd[0].fd = STDIN_FILENO;
Denis Vlasenko87f3b262007-09-07 13:43:28 +00002228 pfd[0].events = POLLIN;
Denis Vlasenko5d61e712007-09-27 10:09:59 +00002229 return safe_poll(pfd, 1, hund*10) > 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002230}
2231
2232//----- IO Routines --------------------------------------------
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002233static int readit(void) // read (maybe cursor) key from stdin
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002234{
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002235 int c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002236
Denys Vlasenko8131eea2009-11-02 14:19:51 +01002237 fflush_all();
Denys Vlasenko58f108e2010-03-11 21:17:55 +01002238 c = read_key(STDIN_FILENO, readbuffer, /*timeout off:*/ -2);
Denis Vlasenko0112ff52008-10-25 23:23:00 +00002239 if (c == -1) { // EOF/error
2240 go_bottom_and_clear_to_eol();
2241 cookmode(); // terminal to "cooked"
2242 bb_error_msg_and_die("can't read user input");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002243 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002244 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002245}
2246
2247//----- IO Routines --------------------------------------------
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002248static int get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002249{
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002250 int c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002251
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002252#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002253 if (!adding2q) {
2254 // we are not adding to the q.
2255 // but, we may be reading from a q
2256 if (ioq == 0) {
2257 // there is no current q, read from STDIN
2258 c = readit(); // get the users input
2259 } else {
2260 // there is a queue to get chars from first
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002261 // careful with correct sign expansion!
2262 c = (unsigned char)*ioq++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002263 if (c == '\0') {
2264 // the end of the q, read from STDIN
2265 free(ioq_start);
2266 ioq_start = ioq = 0;
2267 c = readit(); // get the users input
2268 }
2269 }
2270 } else {
2271 // adding STDIN chars to q
2272 c = readit(); // get the users input
Denis Vlasenkob1759462008-06-20 20:20:54 +00002273 if (lmc_len >= MAX_INPUT_LEN - 1) {
2274 status_line_bold("last_modifying_cmd overrun");
2275 } else {
2276 // add new char to q
2277 last_modifying_cmd[lmc_len++] = c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002278 }
2279 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002280#else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002281 c = readit(); // get the users input
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002282#endif /* FEATURE_VI_DOT_CMD */
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002283 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002284}
2285
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002286// Get input line (uses "status line" area)
2287static char *get_input_line(const char *prompt)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002288{
Denis Vlasenkob1759462008-06-20 20:20:54 +00002289 // char [MAX_INPUT_LEN]
2290#define buf get_input_line__buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002291
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002292 int c;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002293 int i;
2294
2295 strcpy(buf, prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002296 last_status_cksum = 0; // force status update
Denis Vlasenko267e16c2008-10-14 10:34:41 +00002297 go_bottom_and_clear_to_eol();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002298 write1(prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002299
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002300 i = strlen(buf);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002301 while (i < MAX_INPUT_LEN) {
2302 c = get_one_char();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002303 if (c == '\n' || c == '\r' || c == 27)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002304 break; // this is end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002305 if (c == erase_char || c == 8 || c == 127) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002306 // user wants to erase prev char
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002307 buf[--i] = '\0';
2308 write1("\b \b"); // erase char on screen
2309 if (i <= 0) // user backs up before b-o-l, exit
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002310 break;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002311 } else if (c > 0 && c < 256) { // exclude Unicode
2312 // (TODO: need to handle Unicode)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002313 buf[i] = c;
2314 buf[++i] = '\0';
2315 bb_putchar(c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002316 }
2317 }
2318 refresh(FALSE);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002319 return buf;
Denis Vlasenkob1759462008-06-20 20:20:54 +00002320#undef buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002321}
2322
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002323static int file_size(const char *fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002324{
2325 struct stat st_buf;
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002326 int cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002327
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002328 cnt = -1;
Denys Vlasenkodef47832010-04-18 20:39:41 -07002329 if (fn && stat(fn, &st_buf) == 0) // see if file exists
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002330 cnt = (int) st_buf.st_size;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002331 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002332}
2333
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002334// might reallocate text[]!
2335static int file_insert(const char *fn, char *p, int update_ro_status)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002336{
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002337 int cnt = -1;
2338 int fd, size;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002339 struct stat statbuf;
2340
2341 /* Validate file */
2342 if (stat(fn, &statbuf) < 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002343 status_line_bold("\"%s\" %s", fn, strerror(errno));
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002344 goto fi0;
2345 }
Denis Vlasenko33875382008-06-21 20:31:50 +00002346 if (!S_ISREG(statbuf.st_mode)) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002347 // This is not a regular file
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002348 status_line_bold("\"%s\" Not a regular file", fn);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002349 goto fi0;
2350 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002351 if (p < text || p > end) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002352 status_line_bold("Trying to insert file outside of memory");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002353 goto fi0;
2354 }
2355
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002356 // read file to buffer
2357 fd = open(fn, O_RDONLY);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002358 if (fd < 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002359 status_line_bold("\"%s\" %s", fn, strerror(errno));
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002360 goto fi0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002361 }
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002362 size = statbuf.st_size;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002363 p += text_hole_make(p, size);
Denis Vlasenko4f95e5a2007-10-11 10:10:15 +00002364 cnt = safe_read(fd, p, size);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002365 if (cnt < 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002366 status_line_bold("\"%s\" %s", fn, strerror(errno));
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002367 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002368 } else if (cnt < size) {
2369 // There was a partial read, shrink unused space text[]
2370 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
Denys Vlasenko6331cf02009-11-13 09:08:27 +01002371 status_line_bold("can't read all of file \"%s\"", fn);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002372 }
2373 if (cnt >= size)
Paul Fox8552aec2005-09-16 12:20:05 +00002374 file_modified++;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002375 close(fd);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002376 fi0:
Denis Vlasenko856be772007-08-17 08:29:48 +00002377#if ENABLE_FEATURE_VI_READONLY
2378 if (update_ro_status
2379 && ((access(fn, W_OK) < 0) ||
2380 /* root will always have access()
2381 * so we check fileperms too */
2382 !(statbuf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))
2383 )
2384 ) {
Denis Vlasenko2f6ae432007-07-19 22:50:47 +00002385 SET_READONLY_FILE(readonly_mode);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002386 }
Denis Vlasenko856be772007-08-17 08:29:48 +00002387#endif
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002388 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002389}
2390
Denis Vlasenko33875382008-06-21 20:31:50 +00002391static int file_write(char *fn, char *first, char *last)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002392{
2393 int fd, cnt, charcnt;
2394
2395 if (fn == 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002396 status_line_bold("No current filename");
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002397 return -2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002398 }
Denis Vlasenko8abae882008-05-03 11:35:59 +00002399 /* By popular request we do not open file with O_TRUNC,
2400 * but instead ftruncate() it _after_ successful write.
2401 * Might reduce amount of data lost on power fail etc.
2402 */
2403 fd = open(fn, (O_WRONLY | O_CREAT), 0666);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002404 if (fd < 0)
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002405 return -1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002406 cnt = last - first + 1;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002407 charcnt = full_write(fd, first, cnt);
Denis Vlasenko8abae882008-05-03 11:35:59 +00002408 ftruncate(fd, charcnt);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002409 if (charcnt == cnt) {
2410 // good write
Denis Vlasenkob1759462008-06-20 20:20:54 +00002411 //file_modified = FALSE;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002412 } else {
2413 charcnt = 0;
2414 }
2415 close(fd);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002416 return charcnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002417}
2418
2419//----- Terminal Drawing ---------------------------------------
2420// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002421// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002422// screen coordinates
2423// 0,0 ... 0,79
2424// 1,0 ... 1,79
2425// . ... .
2426// . ... .
2427// 22,0 ... 22,79
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002428// 23,0 ... 23,79 <- status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002429
2430//----- Move the cursor to row x col (count from 0, not 1) -------
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002431static void place_cursor(int row, int col, int optimize)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002432{
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002433 char cm1[sizeof(CMrc) + sizeof(int)*3 * 2];
Denis Vlasenko7b54dc72008-07-17 21:32:32 +00002434#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
2435 enum {
2436 SZ_UP = sizeof(CMup),
2437 SZ_DN = sizeof(CMdown),
2438 SEQ_SIZE = SZ_UP > SZ_DN ? SZ_UP : SZ_DN,
2439 };
2440 char cm2[SEQ_SIZE * 5 + 32]; // bigger than worst case size
2441#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002442 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002443
2444 if (row < 0) row = 0;
2445 if (row >= rows) row = rows - 1;
2446 if (col < 0) col = 0;
2447 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002448
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002449 //----- 1. Try the standard terminal ESC sequence
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002450 sprintf(cm1, CMrc, row + 1, col + 1);
2451 cm = cm1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002452
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002453#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002454 if (optimize && col < 16) {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002455 char *screenp;
2456 int Rrow = last_row;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002457 int diff = Rrow - row;
2458
2459 if (diff < -5 || diff > 5)
2460 goto skip;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002461
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002462 //----- find the minimum # of chars to move cursor -------------
2463 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2464 cm2[0] = '\0';
2465
2466 // move to the correct row
2467 while (row < Rrow) {
2468 // the cursor has to move up
2469 strcat(cm2, CMup);
2470 Rrow--;
2471 }
2472 while (row > Rrow) {
2473 // the cursor has to move down
2474 strcat(cm2, CMdown);
2475 Rrow++;
2476 }
2477
2478 // now move to the correct column
2479 strcat(cm2, "\r"); // start at col 0
2480 // just send out orignal source char to get to correct place
2481 screenp = &screen[row * columns]; // start of screen line
2482 strncat(cm2, screenp, col);
2483
2484 // pick the shortest cursor motion to send out
2485 if (strlen(cm2) < strlen(cm)) {
2486 cm = cm2;
2487 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002488 skip: ;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002489 }
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00002490 last_row = row;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002491#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002492 write1(cm);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002493}
2494
2495//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002496static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002497{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002498 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002499}
2500
Denis Vlasenko267e16c2008-10-14 10:34:41 +00002501static void go_bottom_and_clear_to_eol(void)
2502{
2503 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2504 clear_to_eol(); // erase to end of line
2505}
2506
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002507//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002508static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002509{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002510 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002511}
2512
2513//----- Start standout mode ------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002514static void standout_start(void) // send "start reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002515{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002516 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002517}
2518
2519//----- End standout mode --------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002520static void standout_end(void) // send "end reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002521{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002522 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002523}
2524
2525//----- Flash the screen --------------------------------------
2526static void flash(int h)
2527{
2528 standout_start(); // send "start reverse video" sequence
2529 redraw(TRUE);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002530 mysleep(h);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002531 standout_end(); // send "end reverse video" sequence
2532 redraw(TRUE);
2533}
2534
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002535static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002536{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002537#if ENABLE_FEATURE_VI_CRASHME
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002538 if (crashme > 0)
2539 return; // generate a random command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002540#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002541 if (!err_method) {
2542 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002543 } else {
2544 flash(10);
2545 }
2546}
2547
2548//----- Screen[] Routines --------------------------------------
2549//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002550static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002551{
2552 memset(screen, ' ', screensize); // clear new screen
2553}
2554
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002555static int bufsum(char *buf, int count)
Paul Fox8552aec2005-09-16 12:20:05 +00002556{
2557 int sum = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002558 char *e = buf + count;
2559
Paul Fox8552aec2005-09-16 12:20:05 +00002560 while (buf < e)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002561 sum += (unsigned char) *buf++;
Paul Fox8552aec2005-09-16 12:20:05 +00002562 return sum;
2563}
2564
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002565//----- Draw the status line at bottom of the screen -------------
2566static void show_status_line(void)
2567{
Paul Foxc3504852005-09-16 12:48:18 +00002568 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002569
Paul Fox8552aec2005-09-16 12:20:05 +00002570 // either we already have an error or status message, or we
2571 // create one.
2572 if (!have_status_msg) {
2573 cnt = format_edit_status();
2574 cksum = bufsum(status_buffer, cnt);
2575 }
2576 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002577 last_status_cksum = cksum; // remember if we have seen this line
Denis Vlasenko267e16c2008-10-14 10:34:41 +00002578 go_bottom_and_clear_to_eol();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002579 write1(status_buffer);
Paul Fox8552aec2005-09-16 12:20:05 +00002580 if (have_status_msg) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002581 if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
Paul Fox8552aec2005-09-16 12:20:05 +00002582 (columns - 1) ) {
2583 have_status_msg = 0;
2584 Hit_Return();
2585 }
2586 have_status_msg = 0;
2587 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002588 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2589 }
Denys Vlasenko8131eea2009-11-02 14:19:51 +01002590 fflush_all();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002591}
2592
2593//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00002594// format status buffer, with STANDOUT mode
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002595static void status_line_bold(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002596{
2597 va_list args;
2598
2599 va_start(args, format);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002600 strcpy(status_buffer, SOs); // Terminal standout mode on
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002601 vsprintf(status_buffer + sizeof(SOs)-1, format, args);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002602 strcat(status_buffer, SOn); // Terminal standout mode off
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002603 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002604
2605 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002606}
2607
Paul Fox8552aec2005-09-16 12:20:05 +00002608// format status buffer
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002609static void status_line(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002610{
2611 va_list args;
2612
2613 va_start(args, format);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002614 vsprintf(status_buffer, format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002615 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002616
2617 have_status_msg = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002618}
2619
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002620// copy s to buf, convert unprintable
2621static void print_literal(char *buf, const char *s)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002622{
Denys Vlasenkoc2704542009-11-20 19:14:19 +01002623 char *d;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002624 unsigned char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002625
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002626 buf[0] = '\0';
2627 if (!s[0])
2628 s = "(NULL)";
Denys Vlasenkoc2704542009-11-20 19:14:19 +01002629
2630 d = buf;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002631 for (; *s; s++) {
2632 int c_is_no_print;
2633
2634 c = *s;
2635 c_is_no_print = (c & 0x80) && !Isprint(c);
2636 if (c_is_no_print) {
Denys Vlasenkoc2704542009-11-20 19:14:19 +01002637 strcpy(d, SOn);
2638 d += sizeof(SOn)-1;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002639 c = '.';
2640 }
Denys Vlasenkoc2704542009-11-20 19:14:19 +01002641 if (c < ' ' || c == 0x7f) {
2642 *d++ = '^';
2643 c |= '@'; /* 0x40 */
2644 if (c == 0x7f)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002645 c = '?';
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002646 }
Denys Vlasenkoc2704542009-11-20 19:14:19 +01002647 *d++ = c;
2648 *d = '\0';
2649 if (c_is_no_print) {
2650 strcpy(d, SOs);
2651 d += sizeof(SOs)-1;
2652 }
2653 if (*s == '\n') {
2654 *d++ = '$';
2655 *d = '\0';
2656 }
2657 if (d - buf > MAX_INPUT_LEN - 10) // paranoia
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002658 break;
2659 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002660}
2661
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002662static void not_implemented(const char *s)
2663{
2664 char buf[MAX_INPUT_LEN];
2665
2666 print_literal(buf, s);
2667 status_line_bold("\'%s\' is not implemented", buf);
2668}
2669
2670// show file status on status line
2671static int format_edit_status(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002672{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002673 static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
Denis Vlasenkob1759462008-06-20 20:20:54 +00002674
2675#define tot format_edit_status__tot
2676
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002677 int cur, percent, ret, trunc_at;
2678
Paul Fox8552aec2005-09-16 12:20:05 +00002679 // file_modified is now a counter rather than a flag. this
2680 // helps reduce the amount of line counting we need to do.
2681 // (this will cause a mis-reporting of modified status
2682 // once every MAXINT editing operations.)
2683
2684 // it would be nice to do a similar optimization here -- if
2685 // we haven't done a motion that could have changed which line
2686 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002687 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00002688
2689 // reduce counting -- the total lines can't have
2690 // changed if we haven't done any edits.
2691 if (file_modified != last_file_modified) {
2692 tot = cur + count_lines(dot, end - 1) - 1;
2693 last_file_modified = file_modified;
2694 }
2695
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002696 // current line percent
2697 // ------------- ~~ ----------
2698 // total lines 100
2699 if (tot > 0) {
2700 percent = (100 * cur) / tot;
2701 } else {
2702 cur = tot = 0;
2703 percent = 100;
2704 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00002705
Paul Fox8552aec2005-09-16 12:20:05 +00002706 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2707 columns : STATUS_BUFFER_LEN-1;
2708
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002709 ret = snprintf(status_buffer, trunc_at+1,
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002710#if ENABLE_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002711 "%c %s%s%s %d/%d %d%%",
2712#else
2713 "%c %s%s %d/%d %d%%",
2714#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002715 cmd_mode_indicator[cmd_mode & 3],
2716 (current_filename != NULL ? current_filename : "No file"),
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002717#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002718 (readonly_mode ? " [Readonly]" : ""),
Paul Fox8552aec2005-09-16 12:20:05 +00002719#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002720 (file_modified ? " [Modified]" : ""),
Paul Fox8552aec2005-09-16 12:20:05 +00002721 cur, tot, percent);
2722
2723 if (ret >= 0 && ret < trunc_at)
2724 return ret; /* it all fit */
2725
2726 return trunc_at; /* had to truncate */
Denis Vlasenkob1759462008-06-20 20:20:54 +00002727#undef tot
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002728}
2729
2730//----- Force refresh of all Lines -----------------------------
2731static void redraw(int full_screen)
2732{
2733 place_cursor(0, 0, FALSE); // put cursor in correct place
Denis Vlasenkob1759462008-06-20 20:20:54 +00002734 clear_to_eos(); // tell terminal to erase display
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002735 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00002736 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002737 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00002738 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002739}
2740
2741//----- Format a text[] line into a buffer ---------------------
Denis Vlasenko68404f12008-03-17 09:00:54 +00002742static char* format_line(char *src /*, int li*/)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002743{
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002744 unsigned char c;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002745 int co;
2746 int ofs = offset;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002747 char *dest = scr_out_buf; // [MAX_SCR_COLS + MAX_TABSTOP * 2]
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002748
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002749 c = '~'; // char in col 0 in non-existent lines is '~'
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00002750 co = 0;
2751 while (co < columns + tabstop) {
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002752 // have we gone past the end?
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002753 if (src < end) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002754 c = *src++;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002755 if (c == '\n')
2756 break;
2757 if ((c & 0x80) && !Isprint(c)) {
2758 c = '.';
2759 }
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002760 if (c < ' ' || c == 0x7f) {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002761 if (c == '\t') {
2762 c = ' ';
2763 // co % 8 != 7
2764 while ((co % tabstop) != (tabstop - 1)) {
2765 dest[co++] = c;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002766 }
2767 } else {
2768 dest[co++] = '^';
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002769 if (c == 0x7f)
2770 c = '?';
2771 else
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002772 c += '@'; // Ctrl-X -> 'X'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002773 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002774 }
2775 }
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00002776 dest[co++] = c;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002777 // discard scrolled-off-to-the-left portion,
2778 // in tabstop-sized pieces
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002779 if (ofs >= tabstop && co >= tabstop) {
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00002780 memmove(dest, dest + tabstop, co);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002781 co -= tabstop;
2782 ofs -= tabstop;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002783 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002784 if (src >= end)
2785 break;
2786 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002787 // check "short line, gigantic offset" case
2788 if (co < ofs)
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00002789 ofs = co;
2790 // discard last scrolled off part
2791 co -= ofs;
2792 dest += ofs;
2793 // fill the rest with spaces
2794 if (co < columns)
2795 memset(&dest[co], ' ', columns - co);
2796 return dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002797}
2798
2799//----- Refresh the changed screen lines -----------------------
2800// Copy the source line from text[] into the buffer and note
2801// if the current screenline is different from the new buffer.
2802// If they differ then that line needs redrawing on the terminal.
2803//
2804static void refresh(int full_screen)
2805{
Denis Vlasenkob1759462008-06-20 20:20:54 +00002806#define old_offset refresh__old_offset
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002807
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002808 int li, changed;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002809 char *tp, *sp; // pointer into text[] and screen[]
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002810
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +02002811 if (ENABLE_FEATURE_VI_WIN_RESIZE IF_FEATURE_VI_ASK_TERMINAL(&& !G.get_rowcol_error) ) {
Denis Vlasenko55995022008-05-18 22:28:26 +00002812 unsigned c = columns, r = rows;
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002813 query_screen_dimensions();
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002814 full_screen |= (c - columns) | (r - rows);
2815 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002816 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2817 tp = screenbegin; // index into text[] of top line
2818
2819 // compare text[] to screen[] and mark screen[] lines that need updating
2820 for (li = 0; li < rows - 1; li++) {
2821 int cs, ce; // column start & end
Denis Vlasenkof882f082007-12-23 02:36:51 +00002822 char *out_buf;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002823 // format current text line
Denis Vlasenko68404f12008-03-17 09:00:54 +00002824 out_buf = format_line(tp /*, li*/);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002825
2826 // skip to the end of the current text[] line
Denis Vlasenkof882f082007-12-23 02:36:51 +00002827 if (tp < end) {
2828 char *t = memchr(tp, '\n', end - tp);
2829 if (!t) t = end - 1;
2830 tp = t + 1;
2831 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002832
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002833 // see if there are any changes between vitual screen and out_buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002834 changed = FALSE; // assume no change
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002835 cs = 0;
2836 ce = columns - 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002837 sp = &screen[li * columns]; // start of screen line
2838 if (full_screen) {
2839 // force re-draw of every single column from 0 - columns-1
2840 goto re0;
2841 }
2842 // compare newly formatted buffer with virtual screen
2843 // look forward for first difference between buf and screen
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002844 for (; cs <= ce; cs++) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002845 if (out_buf[cs] != sp[cs]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002846 changed = TRUE; // mark for redraw
2847 break;
2848 }
2849 }
2850
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002851 // look backward for last difference between out_buf and screen
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002852 for (; ce >= cs; ce--) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002853 if (out_buf[ce] != sp[ce]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002854 changed = TRUE; // mark for redraw
2855 break;
2856 }
2857 }
2858 // now, cs is index of first diff, and ce is index of last diff
2859
2860 // if horz offset has changed, force a redraw
2861 if (offset != old_offset) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002862 re0:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002863 changed = TRUE;
2864 }
2865
2866 // make a sanity check of columns indexes
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002867 if (cs < 0) cs = 0;
2868 if (ce > columns - 1) ce = columns - 1;
2869 if (cs > ce) { cs = 0; ce = columns - 1; }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002870 // is there a change between vitual screen and out_buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002871 if (changed) {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002872 // copy changed part of buffer to virtual screen
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002873 memcpy(sp+cs, out_buf+cs, ce-cs+1);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002874
2875 // move cursor to column of first change
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002876 //if (offset != old_offset) {
2877 // // place_cursor is still too stupid
2878 // // to handle offsets correctly
2879 // place_cursor(li, cs, FALSE);
2880 //} else {
2881 place_cursor(li, cs, TRUE);
2882 //}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002883
2884 // write line out to terminal
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002885 fwrite(&sp[cs], ce - cs + 1, 1, stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002886 }
2887 }
2888
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002889 place_cursor(crow, ccol, TRUE);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002890
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002891 old_offset = offset;
Denis Vlasenkob1759462008-06-20 20:20:54 +00002892#undef old_offset
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002893}
2894
Eric Andersen3f980402001-04-04 17:31:15 +00002895//---------------------------------------------------------------------
2896//----- the Ascii Chart -----------------------------------------------
2897//
2898// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2899// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2900// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2901// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2902// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2903// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2904// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2905// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2906// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2907// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2908// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2909// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2910// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2911// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2912// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2913// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2914//---------------------------------------------------------------------
2915
2916//----- Execute a Vi Command -----------------------------------
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002917static void do_cmd(int c)
Eric Andersen3f980402001-04-04 17:31:15 +00002918{
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002919 const char *msg = msg; // for compiler
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002920 char *p, *q, *save_dot;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002921 char buf[12];
Denis Vlasenkoc3a9dc82008-10-29 00:58:04 +00002922 int dir;
Paul Foxc51fc7b2008-03-06 01:34:23 +00002923 int cnt, i, j;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002924 int c1;
Eric Andersen3f980402001-04-04 17:31:15 +00002925
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002926// c1 = c; // quiet the compiler
2927// cnt = yf = 0; // quiet the compiler
2928// msg = p = q = save_dot = buf; // quiet the compiler
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002929 memset(buf, '\0', 12);
Eric Andersenbff7a602001-11-17 07:15:43 +00002930
Paul Fox8552aec2005-09-16 12:20:05 +00002931 show_status_line();
2932
Eric Andersenbff7a602001-11-17 07:15:43 +00002933 /* if this is a cursor key, skip these checks */
2934 switch (c) {
Denis Vlasenko0112ff52008-10-25 23:23:00 +00002935 case KEYCODE_UP:
2936 case KEYCODE_DOWN:
2937 case KEYCODE_LEFT:
2938 case KEYCODE_RIGHT:
2939 case KEYCODE_HOME:
2940 case KEYCODE_END:
2941 case KEYCODE_PAGEUP:
2942 case KEYCODE_PAGEDOWN:
2943 case KEYCODE_DELETE:
Eric Andersenbff7a602001-11-17 07:15:43 +00002944 goto key_cmd_mode;
2945 }
2946
Eric Andersen3f980402001-04-04 17:31:15 +00002947 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002948 // flip-flop Insert/Replace mode
Denis Vlasenko0112ff52008-10-25 23:23:00 +00002949 if (c == KEYCODE_INSERT)
Denis Vlasenko2a51af22007-03-21 22:31:24 +00002950 goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00002951 // we are 'R'eplacing the current *dot with new char
2952 if (*dot == '\n') {
2953 // don't Replace past E-o-l
2954 cmd_mode = 1; // convert to insert
2955 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002956 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00002957 if (c != 27)
2958 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
2959 dot = char_insert(dot, c); // insert new char
2960 }
2961 goto dc1;
2962 }
2963 }
2964 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00002965 // hitting "Insert" twice means "R" replace mode
Denis Vlasenko0112ff52008-10-25 23:23:00 +00002966 if (c == KEYCODE_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00002967 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002968 if (1 <= c || Isprint(c)) {
2969 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00002970 }
2971 goto dc1;
2972 }
2973
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002974 key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00002975 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00002976 //case 0x01: // soh
2977 //case 0x09: // ht
2978 //case 0x0b: // vt
2979 //case 0x0e: // so
2980 //case 0x0f: // si
2981 //case 0x10: // dle
2982 //case 0x11: // dc1
2983 //case 0x13: // dc3
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002984#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00002985 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00002986 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00002987 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002988#endif
Eric Andersen822c3832001-05-07 17:37:43 +00002989 //case 0x16: // syn
2990 //case 0x17: // etb
2991 //case 0x18: // can
2992 //case 0x1c: // fs
2993 //case 0x1d: // gs
2994 //case 0x1e: // rs
2995 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002996 //case '!': // !-
2997 //case '#': // #-
2998 //case '&': // &-
2999 //case '(': // (-
3000 //case ')': // )-
3001 //case '*': // *-
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003002 //case '=': // =-
3003 //case '@': // @-
3004 //case 'F': // F-
3005 //case 'K': // K-
3006 //case 'Q': // Q-
3007 //case 'S': // S-
3008 //case 'T': // T-
3009 //case 'V': // V-
3010 //case '[': // [-
3011 //case '\\': // \-
3012 //case ']': // ]-
3013 //case '_': // _-
3014 //case '`': // `-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003015 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003016 //case 'v': // v-
Denys Vlasenkob22bbff2009-07-04 16:50:43 +02003017 default: // unrecognized command
Eric Andersen3f980402001-04-04 17:31:15 +00003018 buf[0] = c;
3019 buf[1] = '\0';
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003020 not_implemented(buf);
Eric Andersen3f980402001-04-04 17:31:15 +00003021 end_cmd_q(); // stop adding to q
3022 case 0x00: // nul- ignore
3023 break;
3024 case 2: // ctrl-B scroll up full screen
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003025 case KEYCODE_PAGEUP: // Cursor Key Page Up
Eric Andersen3f980402001-04-04 17:31:15 +00003026 dot_scroll(rows - 2, -1);
3027 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003028 case 4: // ctrl-D scroll down half screen
3029 dot_scroll((rows - 2) / 2, 1);
3030 break;
3031 case 5: // ctrl-E scroll down one line
3032 dot_scroll(1, 1);
3033 break;
3034 case 6: // ctrl-F scroll down full screen
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003035 case KEYCODE_PAGEDOWN: // Cursor Key Page Down
Eric Andersen3f980402001-04-04 17:31:15 +00003036 dot_scroll(rows - 2, 1);
3037 break;
3038 case 7: // ctrl-G show current status
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 'h': // h- move left
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003042 case KEYCODE_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003043 case 8: // ctrl-H- move left (This may be ERASE char)
Denis Vlasenko2a51af22007-03-21 22:31:24 +00003044 case 0x7f: // DEL- move left (This may be ERASE char)
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003045 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003046 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003047 }
Eric Andersen3f980402001-04-04 17:31:15 +00003048 dot_left();
3049 break;
3050 case 10: // Newline ^J
3051 case 'j': // j- goto next line, same col
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003052 case KEYCODE_DOWN: // cursor key Down
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003053 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003054 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003055 }
Eric Andersen3f980402001-04-04 17:31:15 +00003056 dot_next(); // go to next B-o-l
3057 dot = move_to_col(dot, ccol + offset); // try stay in same col
3058 break;
3059 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003060 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003061 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003062 clear_to_eos(); // tel terminal to erase display
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003063 mysleep(10);
Eric Andersen3f980402001-04-04 17:31:15 +00003064 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003065 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003066 refresh(TRUE); // this will redraw the entire display
3067 break;
3068 case 13: // Carriage Return ^M
3069 case '+': // +- goto next line
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003070 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003071 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003072 }
Eric Andersen3f980402001-04-04 17:31:15 +00003073 dot_next();
3074 dot_skip_over_ws();
3075 break;
3076 case 21: // ctrl-U scroll up half screen
3077 dot_scroll((rows - 2) / 2, -1);
3078 break;
3079 case 25: // ctrl-Y scroll up one line
3080 dot_scroll(1, -1);
3081 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003082 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003083 if (cmd_mode == 0)
3084 indicate_error(c);
3085 cmd_mode = 0; // stop insrting
3086 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003087 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003088 break;
3089 case ' ': // move right
3090 case 'l': // move right
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003091 case KEYCODE_RIGHT: // Cursor Key Right
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003092 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003093 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003094 }
Eric Andersen3f980402001-04-04 17:31:15 +00003095 dot_right();
3096 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003097#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003098 case '"': // "- name a register to use for Delete/Yank
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003099 c1 = (get_one_char() | 0x20) - 'a'; // | 0x20 is tolower()
3100 if ((unsigned)c1 <= 25) { // a-z?
3101 YDreg = c1;
Eric Andersen3f980402001-04-04 17:31:15 +00003102 } else {
3103 indicate_error(c);
3104 }
3105 break;
3106 case '\'': // '- goto a specific mark
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003107 c1 = (get_one_char() | 0x20) - 'a';
3108 if ((unsigned)c1 <= 25) { // a-z?
Eric Andersen3f980402001-04-04 17:31:15 +00003109 // get the b-o-l
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003110 q = mark[c1];
Eric Andersen3f980402001-04-04 17:31:15 +00003111 if (text <= q && q < end) {
3112 dot = q;
3113 dot_begin(); // go to B-o-l
3114 dot_skip_over_ws();
3115 }
3116 } else if (c1 == '\'') { // goto previous context
3117 dot = swap_context(dot); // swap current and previous context
3118 dot_begin(); // go to B-o-l
3119 dot_skip_over_ws();
3120 } else {
3121 indicate_error(c);
3122 }
3123 break;
3124 case 'm': // m- Mark a line
3125 // this is really stupid. If there are any inserts or deletes
3126 // between text[0] and dot then this mark will not point to the
3127 // correct location! It could be off by many lines!
3128 // Well..., at least its quick and dirty.
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003129 c1 = (get_one_char() | 0x20) - 'a';
3130 if ((unsigned)c1 <= 25) { // a-z?
Eric Andersen3f980402001-04-04 17:31:15 +00003131 // remember the line
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003132 mark[c1] = dot;
Eric Andersen3f980402001-04-04 17:31:15 +00003133 } else {
3134 indicate_error(c);
3135 }
3136 break;
3137 case 'P': // P- Put register before
3138 case 'p': // p- put register after
3139 p = reg[YDreg];
Denis Vlasenko00d84172008-11-24 07:34:42 +00003140 if (p == NULL) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003141 status_line_bold("Nothing in register %c", what_reg());
Eric Andersen3f980402001-04-04 17:31:15 +00003142 break;
3143 }
3144 // are we putting whole lines or strings
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003145 if (strchr(p, '\n') != NULL) {
Eric Andersen3f980402001-04-04 17:31:15 +00003146 if (c == 'P') {
3147 dot_begin(); // putting lines- Put above
3148 }
3149 if (c == 'p') {
3150 // are we putting after very last line?
3151 if (end_line(dot) == (end - 1)) {
3152 dot = end; // force dot to end of text[]
3153 } else {
3154 dot_next(); // next line, then put before
3155 }
3156 }
3157 } else {
3158 if (c == 'p')
3159 dot_right(); // move to right, can move to NL
3160 }
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00003161 string_insert(dot, p); // insert the string
Eric Andersen3f980402001-04-04 17:31:15 +00003162 end_cmd_q(); // stop adding to q
3163 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003164 case 'U': // U- Undo; replace current line with original version
3165 if (reg[Ureg] != 0) {
3166 p = begin_line(dot);
3167 q = end_line(dot);
3168 p = text_hole_delete(p, q); // delete cur line
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00003169 p += string_insert(p, reg[Ureg]); // insert orig line
Eric Andersen3f980402001-04-04 17:31:15 +00003170 dot = p;
3171 dot_skip_over_ws();
3172 }
3173 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003174#endif /* FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003175 case '$': // $- goto end of line
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003176 case KEYCODE_END: // Cursor Key End
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003177 if (--cmdcnt > 0) {
3178 dot_next();
Eric Andersen3f980402001-04-04 17:31:15 +00003179 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003180 }
Glenn L McGrathee829062004-01-21 10:59:45 +00003181 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003182 break;
3183 case '%': // %- find matching char of pair () [] {}
3184 for (q = dot; q < end && *q != '\n'; q++) {
3185 if (strchr("()[]{}", *q) != NULL) {
3186 // we found half of a pair
3187 p = find_pair(q, *q);
3188 if (p == NULL) {
3189 indicate_error(c);
3190 } else {
3191 dot = p;
3192 }
3193 break;
3194 }
3195 }
3196 if (*q == '\n')
3197 indicate_error(c);
3198 break;
3199 case 'f': // f- forward to a user specified char
3200 last_forward_char = get_one_char(); // get the search char
3201 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003202 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003203 //
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +00003204 //**** fall through to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003205 case ';': // ;- look at rest of line for last forward char
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003206 if (--cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003207 do_cmd(';');
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003208 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003209 if (last_forward_char == 0)
3210 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003211 q = dot + 1;
3212 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3213 q++;
3214 }
3215 if (*q == last_forward_char)
3216 dot = q;
3217 break;
Paul Foxb5ee8db2008-02-14 01:17:01 +00003218 case ',': // repeat latest 'f' in opposite direction
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003219 if (--cmdcnt > 0) {
Paul Foxb5ee8db2008-02-14 01:17:01 +00003220 do_cmd(',');
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003221 }
Paul Foxb5ee8db2008-02-14 01:17:01 +00003222 if (last_forward_char == 0)
3223 break;
3224 q = dot - 1;
3225 while (q >= text && *q != '\n' && *q != last_forward_char) {
3226 q--;
3227 }
3228 if (q >= text && *q == last_forward_char)
3229 dot = q;
3230 break;
3231
Eric Andersen3f980402001-04-04 17:31:15 +00003232 case '-': // -- goto prev line
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003233 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003234 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003235 }
Eric Andersen3f980402001-04-04 17:31:15 +00003236 dot_prev();
3237 dot_skip_over_ws();
3238 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003239#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003240 case '.': // .- repeat the last modifying command
3241 // Stuff the last_modifying_cmd back into stdin
3242 // and let it be re-executed.
Denis Vlasenkob1759462008-06-20 20:20:54 +00003243 if (lmc_len > 0) {
Paul Foxc51fc7b2008-03-06 01:34:23 +00003244 last_modifying_cmd[lmc_len] = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003245 ioq = ioq_start = xstrdup(last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003246 }
3247 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003248#endif
3249#if ENABLE_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003250 case '?': // /- search for a pattern
3251 case '/': // /- search for a pattern
3252 buf[0] = c;
3253 buf[1] = '\0';
3254 q = get_input_line(buf); // get input line- use "status line"
Paul Fox4917c112008-03-05 16:44:02 +00003255 if (q[0] && !q[1]) {
3256 if (last_search_pattern[0])
Denis Vlasenkoc3a9dc82008-10-29 00:58:04 +00003257 last_search_pattern[0] = c;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003258 goto dc3; // if no pat re-use old pat
Paul Fox4917c112008-03-05 16:44:02 +00003259 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003260 if (q[0]) { // strlen(q) > 1: new pat- save it and find
Eric Andersen3f980402001-04-04 17:31:15 +00003261 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003262 free(last_search_pattern);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003263 last_search_pattern = xstrdup(q);
Eric Andersen3f980402001-04-04 17:31:15 +00003264 goto dc3; // now find the pattern
3265 }
3266 // user changed mind and erased the "/"- do nothing
3267 break;
3268 case 'N': // N- backward search for last pattern
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003269 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003270 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003271 }
Eric Andersen3f980402001-04-04 17:31:15 +00003272 dir = BACK; // assume BACKWARD search
3273 p = dot - 1;
3274 if (last_search_pattern[0] == '?') {
3275 dir = FORWARD;
3276 p = dot + 1;
3277 }
3278 goto dc4; // now search for pattern
3279 break;
3280 case 'n': // n- repeat search for last pattern
3281 // search rest of text[] starting at next char
3282 // if search fails return orignal "p" not the "p+1" address
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003283 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003284 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003285 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003286 dc3:
Denis Vlasenkoc3a9dc82008-10-29 00:58:04 +00003287 dir = FORWARD; // assume FORWARD search
3288 p = dot + 1;
Eric Andersen3f980402001-04-04 17:31:15 +00003289 if (last_search_pattern[0] == '?') {
3290 dir = BACK;
3291 p = dot - 1;
3292 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003293 dc4:
Eric Andersen3f980402001-04-04 17:31:15 +00003294 q = char_search(p, last_search_pattern + 1, dir, FULL);
3295 if (q != NULL) {
3296 dot = q; // good search, update "dot"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003297 msg = "";
Eric Andersen3f980402001-04-04 17:31:15 +00003298 goto dc2;
3299 }
3300 // no pattern found between "dot" and "end"- continue at top
3301 p = text;
3302 if (dir == BACK) {
3303 p = end - 1;
3304 }
3305 q = char_search(p, last_search_pattern + 1, dir, FULL);
3306 if (q != NULL) { // found something
3307 dot = q; // found new pattern- goto it
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003308 msg = "search hit BOTTOM, continuing at TOP";
Eric Andersen3f980402001-04-04 17:31:15 +00003309 if (dir == BACK) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003310 msg = "search hit TOP, continuing at BOTTOM";
Eric Andersen3f980402001-04-04 17:31:15 +00003311 }
3312 } else {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003313 msg = "Pattern not found";
Eric Andersen3f980402001-04-04 17:31:15 +00003314 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003315 dc2:
3316 if (*msg)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003317 status_line_bold("%s", msg);
Eric Andersen3f980402001-04-04 17:31:15 +00003318 break;
3319 case '{': // {- move backward paragraph
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003320 q = char_search(dot, "\n\n", BACK, FULL);
Eric Andersen3f980402001-04-04 17:31:15 +00003321 if (q != NULL) { // found blank line
3322 dot = next_line(q); // move to next blank line
3323 }
3324 break;
3325 case '}': // }- move forward paragraph
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003326 q = char_search(dot, "\n\n", FORWARD, FULL);
Eric Andersen3f980402001-04-04 17:31:15 +00003327 if (q != NULL) { // found blank line
3328 dot = next_line(q); // move to next blank line
3329 }
3330 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003331#endif /* FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003332 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003333 case '1': // 1-
3334 case '2': // 2-
3335 case '3': // 3-
3336 case '4': // 4-
3337 case '5': // 5-
3338 case '6': // 6-
3339 case '7': // 7-
3340 case '8': // 8-
3341 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003342 if (c == '0' && cmdcnt < 1) {
3343 dot_begin(); // this was a standalone zero
3344 } else {
3345 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3346 }
3347 break;
3348 case ':': // :- the colon mode commands
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003349 p = get_input_line(":"); // get input line- use "status line"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003350#if ENABLE_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003351 colon(p); // execute the command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003352#else
Eric Andersen822c3832001-05-07 17:37:43 +00003353 if (*p == ':')
3354 p++; // move past the ':'
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003355 cnt = strlen(p);
Eric Andersen822c3832001-05-07 17:37:43 +00003356 if (cnt <= 0)
3357 break;
Denys Vlasenko6548edd2009-06-15 12:44:11 +02003358 if (strncmp(p, "quit", cnt) == 0
3359 || strncmp(p, "q!", cnt) == 0 // delete lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003360 ) {
Matt Kraai1f0c4362001-12-20 23:13:26 +00003361 if (file_modified && p[1] != '!') {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003362 status_line_bold("No write since last change (:quit! overrides)");
Eric Andersen3f980402001-04-04 17:31:15 +00003363 } else {
3364 editing = 0;
3365 }
Denys Vlasenko6548edd2009-06-15 12:44:11 +02003366 } else if (strncmp(p, "write", cnt) == 0
3367 || strncmp(p, "wq", cnt) == 0
3368 || strncmp(p, "wn", cnt) == 0
3369 || (p[0] == 'x' && !p[1])
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003370 ) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003371 cnt = file_write(current_filename, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003372 if (cnt < 0) {
3373 if (cnt == -1)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003374 status_line_bold("Write error: %s", strerror(errno));
Paul Fox61e45db2005-10-09 14:43:22 +00003375 } else {
3376 file_modified = 0;
3377 last_file_modified = -1;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003378 status_line("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003379 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
3380 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
3381 ) {
Paul Fox61e45db2005-10-09 14:43:22 +00003382 editing = 0;
3383 }
Eric Andersen3f980402001-04-04 17:31:15 +00003384 }
Denys Vlasenko6548edd2009-06-15 12:44:11 +02003385 } else if (strncmp(p, "file", cnt) == 0) {
Paul Fox8552aec2005-09-16 12:20:05 +00003386 last_status_cksum = 0; // force status update
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003387 } else if (sscanf(p, "%d", &j) > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003388 dot = find_line(j); // go to line # j
3389 dot_skip_over_ws();
Denys Vlasenkob22bbff2009-07-04 16:50:43 +02003390 } else { // unrecognized cmd
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003391 not_implemented(p);
Eric Andersen3f980402001-04-04 17:31:15 +00003392 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003393#endif /* !FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003394 break;
3395 case '<': // <- Left shift something
3396 case '>': // >- Right shift something
3397 cnt = count_lines(text, dot); // remember what line we are on
3398 c1 = get_one_char(); // get the type of thing to delete
3399 find_range(&p, &q, c1);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003400 yank_delete(p, q, 1, YANKONLY); // save copy before change
Eric Andersen3f980402001-04-04 17:31:15 +00003401 p = begin_line(p);
3402 q = end_line(q);
3403 i = count_lines(p, q); // # of lines we are shifting
3404 for ( ; i > 0; i--, p = next_line(p)) {
3405 if (c == '<') {
3406 // shift left- remove tab or 8 spaces
3407 if (*p == '\t') {
3408 // shrink buffer 1 char
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003409 text_hole_delete(p, p);
Eric Andersen3f980402001-04-04 17:31:15 +00003410 } else if (*p == ' ') {
3411 // we should be calculating columns, not just SPACE
3412 for (j = 0; *p == ' ' && j < tabstop; j++) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003413 text_hole_delete(p, p);
Eric Andersen3f980402001-04-04 17:31:15 +00003414 }
3415 }
3416 } else if (c == '>') {
3417 // shift right -- add tab or 8 spaces
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003418 char_insert(p, '\t');
Eric Andersen3f980402001-04-04 17:31:15 +00003419 }
3420 }
3421 dot = find_line(cnt); // what line were we on
3422 dot_skip_over_ws();
3423 end_cmd_q(); // stop adding to q
3424 break;
3425 case 'A': // A- append at e-o-l
3426 dot_end(); // go to e-o-l
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +00003427 //**** fall through to ... 'a'
Eric Andersen3f980402001-04-04 17:31:15 +00003428 case 'a': // a- append after current char
3429 if (*dot != '\n')
3430 dot++;
3431 goto dc_i;
3432 break;
3433 case 'B': // B- back a blank-delimited Word
3434 case 'E': // E- end of a blank-delimited word
3435 case 'W': // W- forward a blank-delimited word
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003436 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003437 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003438 }
Eric Andersen3f980402001-04-04 17:31:15 +00003439 dir = FORWARD;
3440 if (c == 'B')
3441 dir = BACK;
3442 if (c == 'W' || isspace(dot[dir])) {
3443 dot = skip_thing(dot, 1, dir, S_TO_WS);
3444 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3445 }
3446 if (c != 'W')
3447 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3448 break;
3449 case 'C': // C- Change to e-o-l
3450 case 'D': // D- delete to e-o-l
3451 save_dot = dot;
3452 dot = dollar_line(dot); // move to before NL
3453 // copy text into a register and delete
3454 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3455 if (c == 'C')
3456 goto dc_i; // start inserting
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003457#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003458 if (c == 'D')
3459 end_cmd_q(); // stop adding to q
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003460#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003461 break;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003462 case 'g': // 'gg' goto a line number (vim) (default: very first line)
Paul Foxb5ee8db2008-02-14 01:17:01 +00003463 c1 = get_one_char();
3464 if (c1 != 'g') {
3465 buf[0] = 'g';
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003466 buf[1] = c1; // TODO: if Unicode?
Paul Foxb5ee8db2008-02-14 01:17:01 +00003467 buf[2] = '\0';
3468 not_implemented(buf);
3469 break;
3470 }
3471 if (cmdcnt == 0)
3472 cmdcnt = 1;
3473 /* fall through */
Eric Andersen822c3832001-05-07 17:37:43 +00003474 case 'G': // G- goto to a line number (default= E-O-F)
3475 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003476 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003477 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003478 }
3479 dot_skip_over_ws();
3480 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003481 case 'H': // H- goto top line on screen
3482 dot = screenbegin;
3483 if (cmdcnt > (rows - 1)) {
3484 cmdcnt = (rows - 1);
3485 }
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003486 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003487 do_cmd('+');
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003488 }
Eric Andersen3f980402001-04-04 17:31:15 +00003489 dot_skip_over_ws();
3490 break;
3491 case 'I': // I- insert before first non-blank
3492 dot_begin(); // 0
3493 dot_skip_over_ws();
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +00003494 //**** fall through to ... 'i'
Eric Andersen3f980402001-04-04 17:31:15 +00003495 case 'i': // i- insert before current char
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003496 case KEYCODE_INSERT: // Cursor Key Insert
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003497 dc_i:
Eric Andersen3f980402001-04-04 17:31:15 +00003498 cmd_mode = 1; // start insrting
Eric Andersen3f980402001-04-04 17:31:15 +00003499 break;
3500 case 'J': // J- join current and next lines together
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003501 if (--cmdcnt > 1) {
Eric Andersen3f980402001-04-04 17:31:15 +00003502 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003503 }
Eric Andersen3f980402001-04-04 17:31:15 +00003504 dot_end(); // move to NL
3505 if (dot < end - 1) { // make sure not last char in text[]
3506 *dot++ = ' '; // replace NL with space
Paul Fox8552aec2005-09-16 12:20:05 +00003507 file_modified++;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003508 while (isblank(*dot)) { // delete leading WS
Eric Andersen3f980402001-04-04 17:31:15 +00003509 dot_delete();
3510 }
3511 }
3512 end_cmd_q(); // stop adding to q
3513 break;
3514 case 'L': // L- goto bottom line on screen
3515 dot = end_screen();
3516 if (cmdcnt > (rows - 1)) {
3517 cmdcnt = (rows - 1);
3518 }
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003519 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003520 do_cmd('-');
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003521 }
Eric Andersen3f980402001-04-04 17:31:15 +00003522 dot_begin();
3523 dot_skip_over_ws();
3524 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003525 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003526 dot = screenbegin;
3527 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3528 dot = next_line(dot);
3529 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003530 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003531 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003532 p = begin_line(dot);
3533 if (p[-1] == '\n') {
3534 dot_prev();
3535 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3536 dot_end();
3537 dot = char_insert(dot, '\n');
3538 } else {
3539 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003540 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003541 dot_prev(); // -
3542 }
3543 goto dc_i;
3544 break;
3545 case 'R': // R- continuous Replace char
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003546 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003547 cmd_mode = 2;
Eric Andersen3f980402001-04-04 17:31:15 +00003548 break;
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003549 case KEYCODE_DELETE:
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003550 c = 'x';
3551 // fall through
Eric Andersen3f980402001-04-04 17:31:15 +00003552 case 'X': // X- delete char before dot
3553 case 'x': // x- delete the current char
3554 case 's': // s- substitute the current char
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003555 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003556 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003557 }
Eric Andersen3f980402001-04-04 17:31:15 +00003558 dir = 0;
3559 if (c == 'X')
3560 dir = -1;
3561 if (dot[dir] != '\n') {
3562 if (c == 'X')
3563 dot--; // delete prev char
3564 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3565 }
3566 if (c == 's')
3567 goto dc_i; // start insrting
3568 end_cmd_q(); // stop adding to q
3569 break;
3570 case 'Z': // Z- if modified, {write}; exit
3571 // ZZ means to save file (if necessary), then exit
3572 c1 = get_one_char();
3573 if (c1 != 'Z') {
3574 indicate_error(c);
3575 break;
3576 }
Paul Foxf0305b72006-03-28 14:18:21 +00003577 if (file_modified) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003578 if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003579 status_line_bold("\"%s\" File is read only", current_filename);
Denis Vlasenko92758142006-10-03 19:56:34 +00003580 break;
Paul Foxf0305b72006-03-28 14:18:21 +00003581 }
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003582 cnt = file_write(current_filename, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003583 if (cnt < 0) {
3584 if (cnt == -1)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003585 status_line_bold("Write error: %s", strerror(errno));
Paul Fox61e45db2005-10-09 14:43:22 +00003586 } else if (cnt == (end - 1 - text + 1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003587 editing = 0;
3588 }
3589 } else {
3590 editing = 0;
3591 }
3592 break;
3593 case '^': // ^- move to first non-blank on line
3594 dot_begin();
3595 dot_skip_over_ws();
3596 break;
3597 case 'b': // b- back a word
3598 case 'e': // e- end of word
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003599 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003600 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003601 }
Eric Andersen3f980402001-04-04 17:31:15 +00003602 dir = FORWARD;
3603 if (c == 'b')
3604 dir = BACK;
3605 if ((dot + dir) < text || (dot + dir) > end - 1)
3606 break;
3607 dot += dir;
3608 if (isspace(*dot)) {
3609 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3610 }
3611 if (isalnum(*dot) || *dot == '_') {
3612 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3613 } else if (ispunct(*dot)) {
3614 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3615 }
3616 break;
3617 case 'c': // c- change something
3618 case 'd': // d- delete something
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003619#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003620 case 'y': // y- yank something
3621 case 'Y': // Y- Yank a line
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003622#endif
Denis Vlasenkod44c1532008-10-14 12:59:42 +00003623 {
Paul Foxc51fc7b2008-03-06 01:34:23 +00003624 int yf, ml, whole = 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003625 yf = YANKDEL; // assume either "c" or "d"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003626#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003627 if (c == 'y' || c == 'Y')
3628 yf = YANKONLY;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003629#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003630 c1 = 'y';
3631 if (c != 'Y')
3632 c1 = get_one_char(); // get the type of thing to delete
Paul Foxc51fc7b2008-03-06 01:34:23 +00003633 // determine range, and whether it spans lines
3634 ml = find_range(&p, &q, c1);
Eric Andersen3f980402001-04-04 17:31:15 +00003635 if (c1 == 27) { // ESC- user changed mind and wants out
3636 c = c1 = 27; // Escape- do nothing
3637 } else if (strchr("wW", c1)) {
3638 if (c == 'c') {
3639 // don't include trailing WS as part of word
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003640 while (isblank(*q)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003641 if (q <= text || q[-1] == '\n')
3642 break;
3643 q--;
3644 }
3645 }
Paul Foxc51fc7b2008-03-06 01:34:23 +00003646 dot = yank_delete(p, q, ml, yf); // delete word
3647 } else if (strchr("^0bBeEft%$ lh\b\177", c1)) {
3648 // partial line copy text into a register and delete
3649 dot = yank_delete(p, q, ml, yf); // delete word
3650 } else if (strchr("cdykjHL+-{}\r\n", c1)) {
3651 // whole line copy text into a register and delete
3652 dot = yank_delete(p, q, ml, yf); // delete lines
3653 whole = 1;
3654 } else {
3655 // could not recognize object
3656 c = c1 = 27; // error-
3657 ml = 0;
3658 indicate_error(c);
3659 }
3660 if (ml && whole) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003661 if (c == 'c') {
3662 dot = char_insert(dot, '\n');
3663 // on the last line of file don't move to prev line
Paul Foxc51fc7b2008-03-06 01:34:23 +00003664 if (whole && dot != (end-1)) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003665 dot_prev();
3666 }
3667 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003668 dot_begin();
3669 dot_skip_over_ws();
3670 }
Eric Andersen3f980402001-04-04 17:31:15 +00003671 }
3672 if (c1 != 27) {
3673 // if CHANGING, not deleting, start inserting after the delete
3674 if (c == 'c') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003675 strcpy(buf, "Change");
Eric Andersen3f980402001-04-04 17:31:15 +00003676 goto dc_i; // start inserting
3677 }
3678 if (c == 'd') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003679 strcpy(buf, "Delete");
Eric Andersen3f980402001-04-04 17:31:15 +00003680 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003681#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003682 if (c == 'y' || c == 'Y') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003683 strcpy(buf, "Yank");
Eric Andersen3f980402001-04-04 17:31:15 +00003684 }
3685 p = reg[YDreg];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003686 q = p + strlen(p);
Eric Andersen3f980402001-04-04 17:31:15 +00003687 for (cnt = 0; p <= q; p++) {
3688 if (*p == '\n')
3689 cnt++;
3690 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003691 status_line("%s %d lines (%d chars) using [%c]",
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003692 buf, cnt, strlen(reg[YDreg]), what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003693#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003694 end_cmd_q(); // stop adding to q
3695 }
3696 break;
Denis Vlasenkod44c1532008-10-14 12:59:42 +00003697 }
Eric Andersen3f980402001-04-04 17:31:15 +00003698 case 'k': // k- goto prev line, same col
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003699 case KEYCODE_UP: // cursor key Up
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003700 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003701 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003702 }
Eric Andersen3f980402001-04-04 17:31:15 +00003703 dot_prev();
3704 dot = move_to_col(dot, ccol + offset); // try stay in same col
3705 break;
3706 case 'r': // r- replace the current char with user input
3707 c1 = get_one_char(); // get the replacement char
3708 if (*dot != '\n') {
3709 *dot = c1;
Denis Vlasenkob1759462008-06-20 20:20:54 +00003710 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003711 }
3712 end_cmd_q(); // stop adding to q
3713 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003714 case 't': // t- move to char prior to next x
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00003715 last_forward_char = get_one_char();
3716 do_cmd(';');
3717 if (*dot == last_forward_char)
3718 dot_left();
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003719 last_forward_char = 0;
Eric Andersen822c3832001-05-07 17:37:43 +00003720 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003721 case 'w': // w- forward a word
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003722 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003723 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003724 }
Eric Andersen3f980402001-04-04 17:31:15 +00003725 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3726 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3727 } else if (ispunct(*dot)) { // we are on PUNCT
3728 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3729 }
3730 if (dot < end - 1)
3731 dot++; // move over word
3732 if (isspace(*dot)) {
3733 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3734 }
3735 break;
3736 case 'z': // z-
3737 c1 = get_one_char(); // get the replacement char
3738 cnt = 0;
3739 if (c1 == '.')
3740 cnt = (rows - 2) / 2; // put dot at center
3741 if (c1 == '-')
3742 cnt = rows - 2; // put dot at bottom
3743 screenbegin = begin_line(dot); // start dot at top
3744 dot_scroll(cnt, -1);
3745 break;
3746 case '|': // |- move to column "cmdcnt"
3747 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3748 break;
3749 case '~': // ~- flip the case of letters a-z -> A-Z
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003750 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003751 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003752 }
Eric Andersen3f980402001-04-04 17:31:15 +00003753 if (islower(*dot)) {
3754 *dot = toupper(*dot);
Denis Vlasenkob1759462008-06-20 20:20:54 +00003755 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003756 } else if (isupper(*dot)) {
3757 *dot = tolower(*dot);
Denis Vlasenkob1759462008-06-20 20:20:54 +00003758 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003759 }
3760 dot_right();
3761 end_cmd_q(); // stop adding to q
3762 break;
3763 //----- The Cursor and Function Keys -----------------------------
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003764 case KEYCODE_HOME: // Cursor Key Home
Eric Andersen3f980402001-04-04 17:31:15 +00003765 dot_begin();
3766 break;
3767 // The Fn keys could point to do_macro which could translate them
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003768#if 0
3769 case KEYCODE_FUN1: // Function Key F1
3770 case KEYCODE_FUN2: // Function Key F2
3771 case KEYCODE_FUN3: // Function Key F3
3772 case KEYCODE_FUN4: // Function Key F4
3773 case KEYCODE_FUN5: // Function Key F5
3774 case KEYCODE_FUN6: // Function Key F6
3775 case KEYCODE_FUN7: // Function Key F7
3776 case KEYCODE_FUN8: // Function Key F8
3777 case KEYCODE_FUN9: // Function Key F9
3778 case KEYCODE_FUN10: // Function Key F10
3779 case KEYCODE_FUN11: // Function Key F11
3780 case KEYCODE_FUN12: // Function Key F12
Eric Andersen3f980402001-04-04 17:31:15 +00003781 break;
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003782#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003783 }
3784
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003785 dc1:
Eric Andersen3f980402001-04-04 17:31:15 +00003786 // if text[] just became empty, add back an empty line
3787 if (end == text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003788 char_insert(text, '\n'); // start empty buf with dummy line
Eric Andersen3f980402001-04-04 17:31:15 +00003789 dot = text;
3790 }
3791 // it is OK for dot to exactly equal to end, otherwise check dot validity
3792 if (dot != end) {
3793 dot = bound_dot(dot); // make sure "dot" is valid
3794 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003795#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003796 check_context(c); // update the current context
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003797#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003798
3799 if (!isdigit(c))
3800 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3801 cnt = dot - begin_line(dot);
3802 // Try to stay off of the Newline
3803 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3804 dot--;
3805}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003806
Paul Fox35e9c5d2008-03-06 16:26:12 +00003807/* NB! the CRASHME code is unmaintained, and doesn't currently build */
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003808#if ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003809static int totalcmds = 0;
3810static int Mp = 85; // Movement command Probability
3811static int Np = 90; // Non-movement command Probability
3812static int Dp = 96; // Delete command Probability
3813static int Ip = 97; // Insert command Probability
3814static int Yp = 98; // Yank command Probability
3815static int Pp = 99; // Put command Probability
3816static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003817static const char chars[20] = "\t012345 abcdABCD-=.$";
3818static const char *const words[20] = {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003819 "this", "is", "a", "test",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003820 "broadcast", "the", "emergency", "of",
3821 "system", "quick", "brown", "fox",
3822 "jumped", "over", "lazy", "dogs",
3823 "back", "January", "Febuary", "March"
3824};
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003825static const char *const lines[20] = {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003826 "You should have received a copy of the GNU General Public License\n",
3827 "char c, cm, *cmd, *cmd1;\n",
3828 "generate a command by percentages\n",
3829 "Numbers may be typed as a prefix to some commands.\n",
3830 "Quit, discarding changes!\n",
3831 "Forced write, if permission originally not valid.\n",
3832 "In general, any ex or ed command (such as substitute or delete).\n",
3833 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3834 "Please get w/ me and I will go over it with you.\n",
3835 "The following is a list of scheduled, committed changes.\n",
3836 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3837 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3838 "Any question about transactions please contact Sterling Huxley.\n",
3839 "I will try to get back to you by Friday, December 31.\n",
3840 "This Change will be implemented on Friday.\n",
3841 "Let me know if you have problems accessing this;\n",
3842 "Sterling Huxley recently added you to the access list.\n",
3843 "Would you like to go to lunch?\n",
3844 "The last command will be automatically run.\n",
3845 "This is too much english for a computer geek.\n",
3846};
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003847static char *multilines[20] = {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003848 "You should have received a copy of the GNU General Public License\n",
3849 "char c, cm, *cmd, *cmd1;\n",
3850 "generate a command by percentages\n",
3851 "Numbers may be typed as a prefix to some commands.\n",
3852 "Quit, discarding changes!\n",
3853 "Forced write, if permission originally not valid.\n",
3854 "In general, any ex or ed command (such as substitute or delete).\n",
3855 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3856 "Please get w/ me and I will go over it with you.\n",
3857 "The following is a list of scheduled, committed changes.\n",
3858 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3859 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3860 "Any question about transactions please contact Sterling Huxley.\n",
3861 "I will try to get back to you by Friday, December 31.\n",
3862 "This Change will be implemented on Friday.\n",
3863 "Let me know if you have problems accessing this;\n",
3864 "Sterling Huxley recently added you to the access list.\n",
3865 "Would you like to go to lunch?\n",
3866 "The last command will be automatically run.\n",
3867 "This is too much english for a computer geek.\n",
3868};
3869
3870// create a random command to execute
3871static void crash_dummy()
3872{
3873 static int sleeptime; // how long to pause between commands
3874 char c, cm, *cmd, *cmd1;
3875 int i, cnt, thing, rbi, startrbi, percent;
3876
3877 // "dot" movement commands
3878 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3879
3880 // is there already a command running?
Denys Vlasenko020f4062009-05-17 16:44:54 +02003881 if (readbuffer[0] > 0)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003882 goto cd1;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003883 cd0:
Denys Vlasenko020f4062009-05-17 16:44:54 +02003884 readbuffer[0] = 'X';
3885 startrbi = rbi = 1;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003886 sleeptime = 0; // how long to pause between commands
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003887 memset(readbuffer, '\0', sizeof(readbuffer));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003888 // generate a command by percentages
3889 percent = (int) lrand48() % 100; // get a number from 0-99
3890 if (percent < Mp) { // Movement commands
3891 // available commands
3892 cmd = cmd1;
3893 M++;
3894 } else if (percent < Np) { // non-movement commands
3895 cmd = "mz<>\'\""; // available commands
3896 N++;
3897 } else if (percent < Dp) { // Delete commands
3898 cmd = "dx"; // available commands
3899 D++;
3900 } else if (percent < Ip) { // Inset commands
3901 cmd = "iIaAsrJ"; // available commands
3902 I++;
3903 } else if (percent < Yp) { // Yank commands
3904 cmd = "yY"; // available commands
3905 Y++;
3906 } else if (percent < Pp) { // Put commands
3907 cmd = "pP"; // available commands
3908 P++;
3909 } else {
3910 // We do not know how to handle this command, try again
3911 U++;
3912 goto cd0;
3913 }
3914 // randomly pick one of the available cmds from "cmd[]"
3915 i = (int) lrand48() % strlen(cmd);
3916 cm = cmd[i];
3917 if (strchr(":\024", cm))
3918 goto cd0; // dont allow colon or ctrl-T commands
3919 readbuffer[rbi++] = cm; // put cmd into input buffer
3920
3921 // now we have the command-
3922 // there are 1, 2, and multi char commands
3923 // find out which and generate the rest of command as necessary
3924 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3925 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3926 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3927 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3928 }
3929 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3930 c = cmd1[thing];
3931 readbuffer[rbi++] = c; // add movement to input buffer
3932 }
3933 if (strchr("iIaAsc", cm)) { // multi-char commands
3934 if (cm == 'c') {
3935 // change some thing
3936 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3937 c = cmd1[thing];
3938 readbuffer[rbi++] = c; // add movement to input buffer
3939 }
3940 thing = (int) lrand48() % 4; // what thing to insert
3941 cnt = (int) lrand48() % 10; // how many to insert
3942 for (i = 0; i < cnt; i++) {
3943 if (thing == 0) { // insert chars
3944 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3945 } else if (thing == 1) { // insert words
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003946 strcat(readbuffer, words[(int) lrand48() % 20]);
3947 strcat(readbuffer, " ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003948 sleeptime = 0; // how fast to type
3949 } else if (thing == 2) { // insert lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003950 strcat(readbuffer, lines[(int) lrand48() % 20]);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003951 sleeptime = 0; // how fast to type
3952 } else { // insert multi-lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003953 strcat(readbuffer, multilines[(int) lrand48() % 20]);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003954 sleeptime = 0; // how fast to type
3955 }
3956 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003957 strcat(readbuffer, "\033");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003958 }
Denys Vlasenko020f4062009-05-17 16:44:54 +02003959 readbuffer[0] = strlen(readbuffer + 1);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003960 cd1:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003961 totalcmds++;
3962 if (sleeptime > 0)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003963 mysleep(sleeptime); // sleep 1/100 sec
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003964}
3965
3966// test to see if there are any errors
3967static void crash_test()
3968{
3969 static time_t oldtim;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003970
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003971 time_t tim;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003972 char d[2], msg[80];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003973
3974 msg[0] = '\0';
3975 if (end < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003976 strcat(msg, "end<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003977 }
3978 if (end > textend) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003979 strcat(msg, "end>textend ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003980 }
3981 if (dot < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003982 strcat(msg, "dot<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003983 }
3984 if (dot > end) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003985 strcat(msg, "dot>end ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003986 }
3987 if (screenbegin < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003988 strcat(msg, "screenbegin<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003989 }
3990 if (screenbegin > end - 1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003991 strcat(msg, "screenbegin>end-1 ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003992 }
3993
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003994 if (msg[0]) {
Glenn L McGrath7127b582002-12-03 21:48:15 +00003995 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003996 totalcmds, last_input_char, msg, SOs, SOn);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01003997 fflush_all();
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00003998 while (safe_read(STDIN_FILENO, d, 1) > 0) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003999 if (d[0] == '\n' || d[0] == '\r')
4000 break;
4001 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004002 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004003 tim = time(NULL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004004 if (tim >= (oldtim + 3)) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004005 sprintf(status_buffer,
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004006 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
4007 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
4008 oldtim = tim;
4009 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004010}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004011#endif