blob: e1f3724ccba4ef3eb0f97393e096eb46c6d2f32f [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 {
Cristian Ionescu-Idbohrn9a296fb2011-05-16 03:53:43 +02001691#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001692 // insert a char into text[]
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001693 char *sp; // "save p"
Cristian Ionescu-Idbohrn9a296fb2011-05-16 03:53:43 +02001694#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001695
1696 if (c == 13)
1697 c = '\n'; // translate \r to \n
Cristian Ionescu-Idbohrn9a296fb2011-05-16 03:53:43 +02001698#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001699 sp = p; // remember addr of insert
Cristian Ionescu-Idbohrn9a296fb2011-05-16 03:53:43 +02001700#endif
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001701 p += 1 + stupid_insert(p, c); // insert the char
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001702#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001703 if (showmatch && strchr(")]}", *sp) != NULL) {
1704 showmatching(sp);
1705 }
1706 if (autoindent && c == '\n') { // auto indent the new line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001707 char *q;
Denis Vlasenko00d84172008-11-24 07:34:42 +00001708 size_t len;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001709 q = prev_line(p); // use prev line as template
Denis Vlasenko00d84172008-11-24 07:34:42 +00001710 len = strspn(q, " \t"); // space or tab
1711 if (len) {
Denis Vlasenko3266aa92009-04-01 11:24:04 +00001712 uintptr_t bias;
Denis Vlasenko00d84172008-11-24 07:34:42 +00001713 bias = text_hole_make(p, len);
1714 p += bias;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001715 q += bias;
Denis Vlasenko00d84172008-11-24 07:34:42 +00001716 memcpy(p, q, len);
Denis Vlasenko3266aa92009-04-01 11:24:04 +00001717 p += len;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001718 }
1719 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001720#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001721 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001722 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001723}
1724
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001725// might reallocate text[]! use p += stupid_insert(p, ...),
1726// and be careful to not use pointers into potentially freed text[]!
1727static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001728{
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001729 uintptr_t bias;
1730 bias = text_hole_make(p, 1);
1731 p += bias;
Denis Vlasenkob1759462008-06-20 20:20:54 +00001732 *p = c;
1733 //file_modified++; - done by text_hole_make()
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001734 return bias;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001735}
1736
Denis Vlasenko33875382008-06-21 20:31:50 +00001737static int find_range(char **start, char **stop, char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001738{
Paul Foxc51fc7b2008-03-06 01:34:23 +00001739 char *save_dot, *p, *q, *t;
1740 int cnt, multiline = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001741
1742 save_dot = dot;
1743 p = q = dot;
1744
1745 if (strchr("cdy><", c)) {
1746 // these cmds operate on whole lines
1747 p = q = begin_line(p);
1748 for (cnt = 1; cnt < cmdcnt; cnt++) {
1749 q = next_line(q);
1750 }
1751 q = end_line(q);
Paul Foxc51fc7b2008-03-06 01:34:23 +00001752 } else if (strchr("^%$0bBeEfth\b\177", c)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001753 // These cmds operate on char positions
1754 do_cmd(c); // execute movement cmd
1755 q = dot;
1756 } else if (strchr("wW", c)) {
1757 do_cmd(c); // execute movement cmd
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001758 // if we are at the next word's first char
1759 // step back one char
1760 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00001761 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001762 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1763 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1764 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001765 if (dot > text && *dot == '\n')
1766 dot--; // stay off NL
1767 q = dot;
1768 } else if (strchr("H-k{", c)) {
1769 // these operate on multi-lines backwards
1770 q = end_line(dot); // find NL
1771 do_cmd(c); // execute movement cmd
1772 dot_begin();
1773 p = dot;
1774 } else if (strchr("L+j}\r\n", c)) {
1775 // these operate on multi-lines forwards
1776 p = begin_line(dot);
1777 do_cmd(c); // execute movement cmd
1778 dot_end(); // find NL
1779 q = dot;
1780 } else {
Paul Foxc51fc7b2008-03-06 01:34:23 +00001781 // nothing -- this causes any other values of c to
1782 // represent the one-character range under the
1783 // cursor. this is correct for ' ' and 'l', but
1784 // perhaps no others.
1785 //
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001786 }
Paul Foxc51fc7b2008-03-06 01:34:23 +00001787 if (q < p) {
1788 t = q;
1789 q = p;
1790 p = t;
1791 }
1792
Denis Vlasenko42cc3042008-03-24 02:05:58 +00001793 // backward char movements don't include start position
Paul Foxc51fc7b2008-03-06 01:34:23 +00001794 if (q > p && strchr("^0bBh\b\177", c)) q--;
1795
1796 multiline = 0;
1797 for (t = p; t <= q; t++) {
1798 if (*t == '\n') {
1799 multiline = 1;
1800 break;
1801 }
1802 }
1803
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001804 *start = p;
1805 *stop = q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001806 dot = save_dot;
Paul Foxc51fc7b2008-03-06 01:34:23 +00001807 return multiline;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001808}
1809
Denis Vlasenko33875382008-06-21 20:31:50 +00001810static int st_test(char *p, int type, int dir, char *tested)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001811{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001812 char c, c0, ci;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001813 int test, inc;
1814
1815 inc = dir;
1816 c = c0 = p[0];
1817 ci = p[inc];
1818 test = 0;
1819
1820 if (type == S_BEFORE_WS) {
1821 c = ci;
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_TO_WS) {
1825 c = c0;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02001826 test = (!isspace(c) || c == '\n');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001827 }
1828 if (type == S_OVER_WS) {
1829 c = c0;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02001830 test = isspace(c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001831 }
1832 if (type == S_END_PUNCT) {
1833 c = ci;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02001834 test = ispunct(c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001835 }
1836 if (type == S_END_ALNUM) {
1837 c = ci;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02001838 test = (isalnum(c) || c == '_');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001839 }
1840 *tested = c;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001841 return test;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001842}
1843
Denis Vlasenko33875382008-06-21 20:31:50 +00001844static char *skip_thing(char *p, int linecnt, int dir, int type)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001845{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001846 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001847
1848 while (st_test(p, type, dir, &c)) {
1849 // make sure we limit search to correct number of lines
1850 if (c == '\n' && --linecnt < 1)
1851 break;
1852 if (dir >= 0 && p >= end - 1)
1853 break;
1854 if (dir < 0 && p <= text)
1855 break;
1856 p += dir; // move to next char
1857 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001858 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001859}
1860
1861// find matching char of pair () [] {}
Denis Vlasenko33875382008-06-21 20:31:50 +00001862static char *find_pair(char *p, const char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001863{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001864 char match, *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001865 int dir, level;
1866
1867 match = ')';
1868 level = 1;
1869 dir = 1; // assume forward
1870 switch (c) {
Paul Foxc51fc7b2008-03-06 01:34:23 +00001871 case '(': match = ')'; break;
1872 case '[': match = ']'; break;
1873 case '{': match = '}'; break;
1874 case ')': match = '('; dir = -1; break;
1875 case ']': match = '['; dir = -1; break;
1876 case '}': match = '{'; dir = -1; break;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001877 }
1878 for (q = p + dir; text <= q && q < end; q += dir) {
1879 // look for match, count levels of pairs (( ))
1880 if (*q == c)
1881 level++; // increase pair levels
1882 if (*q == match)
1883 level--; // reduce pair level
1884 if (level == 0)
1885 break; // found matching pair
1886 }
1887 if (level != 0)
1888 q = NULL; // indicate no match
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001889 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001890}
1891
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001892#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001893// show the matching char of a pair, () [] {}
Denis Vlasenkof882f082007-12-23 02:36:51 +00001894static void showmatching(char *p)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001895{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001896 char *q, *save_dot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001897
1898 // we found half of a pair
1899 q = find_pair(p, *p); // get loc of matching char
1900 if (q == NULL) {
1901 indicate_error('3'); // no matching char
1902 } else {
1903 // "q" now points to matching pair
1904 save_dot = dot; // remember where we are
1905 dot = q; // go to new loc
1906 refresh(FALSE); // let the user see it
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001907 mysleep(40); // give user some time
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001908 dot = save_dot; // go back to old loc
1909 refresh(FALSE);
1910 }
1911}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001912#endif /* FEATURE_VI_SETOPTS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001913
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001914// open a hole in text[]
1915// might reallocate text[]! use p += text_hole_make(p, ...),
1916// and be careful to not use pointers into potentially freed text[]!
1917static uintptr_t text_hole_make(char *p, int size) // at "p", make a 'size' byte hole
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001918{
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001919 uintptr_t bias = 0;
1920
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001921 if (size <= 0)
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001922 return bias;
Denis Vlasenkob1759462008-06-20 20:20:54 +00001923 end += size; // adjust the new END
1924 if (end >= (text + text_size)) {
1925 char *new_text;
1926 text_size += end - (text + text_size) + 10240;
1927 new_text = xrealloc(text, text_size);
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001928 bias = (new_text - text);
1929 screenbegin += bias;
1930 dot += bias;
1931 end += bias;
1932 p += bias;
Denis Vlasenkob1759462008-06-20 20:20:54 +00001933 text = new_text;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001934 }
Denis Vlasenkod6995442008-06-27 04:06:13 +00001935 memmove(p + size, p, end - size - p);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001936 memset(p, ' ', size); // clear new hole
Denis Vlasenkob1759462008-06-20 20:20:54 +00001937 file_modified++;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001938 return bias;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001939}
1940
1941// close a hole in text[]
Denis Vlasenko33875382008-06-21 20:31:50 +00001942static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclusive
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001943{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001944 char *src, *dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001945 int cnt, hole_size;
1946
1947 // move forwards, from beginning
1948 // assume p <= q
1949 src = q + 1;
1950 dest = p;
1951 if (q < p) { // they are backward- swap them
1952 src = p + 1;
1953 dest = q;
1954 }
1955 hole_size = q - p + 1;
1956 cnt = end - src;
1957 if (src < text || src > end)
1958 goto thd0;
1959 if (dest < text || dest >= end)
1960 goto thd0;
1961 if (src >= end)
1962 goto thd_atend; // just delete the end of the buffer
Denis Vlasenkob1759462008-06-20 20:20:54 +00001963 memmove(dest, src, cnt);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001964 thd_atend:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001965 end = end - hole_size; // adjust the new END
1966 if (dest >= end)
1967 dest = end - 1; // make sure dest in below end-1
1968 if (end <= text)
1969 dest = end = text; // keep pointers valid
Denis Vlasenkob1759462008-06-20 20:20:54 +00001970 file_modified++;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001971 thd0:
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001972 return dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001973}
1974
1975// copy text into register, then delete text.
1976// if dist <= 0, do not include, or go past, a NewLine
1977//
Denis Vlasenko33875382008-06-21 20:31:50 +00001978static char *yank_delete(char *start, char *stop, int dist, int yf)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001979{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001980 char *p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001981
1982 // make sure start <= stop
1983 if (start > stop) {
1984 // they are backwards, reverse them
1985 p = start;
1986 start = stop;
1987 stop = p;
1988 }
1989 if (dist <= 0) {
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +00001990 // we cannot cross NL boundaries
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001991 p = start;
1992 if (*p == '\n')
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001993 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001994 // dont go past a NewLine
1995 for (; p + 1 <= stop; p++) {
1996 if (p[1] == '\n') {
1997 stop = p; // "stop" just before NewLine
1998 break;
1999 }
2000 }
2001 }
2002 p = start;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002003#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002004 text_yank(start, stop, YDreg);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002005#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002006 if (yf == YANKDEL) {
2007 p = text_hole_delete(start, stop);
2008 } // delete lines
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002009 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002010}
2011
2012static void show_help(void)
2013{
2014 puts("These features are available:"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002015#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002016 "\n\tPattern searches with / and ?"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002017#endif
2018#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002019 "\n\tLast command repeat with \'.\'"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002020#endif
2021#if ENABLE_FEATURE_VI_YANKMARK
Bernhard Reutner-Fischerd24d5c82007-10-01 18:04:42 +00002022 "\n\tLine marking with 'x"
2023 "\n\tNamed buffers with \"x"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002024#endif
2025#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002026 "\n\tReadonly if vi is called as \"view\""
2027 "\n\tReadonly with -R command line arg"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002028#endif
2029#if ENABLE_FEATURE_VI_SET
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002030 "\n\tSome colon mode commands with \':\'"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002031#endif
2032#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002033 "\n\tSettable options with \":set\""
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002034#endif
2035#if ENABLE_FEATURE_VI_USE_SIGNALS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002036 "\n\tSignal catching- ^C"
2037 "\n\tJob suspend and resume with ^Z"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002038#endif
2039#if ENABLE_FEATURE_VI_WIN_RESIZE
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002040 "\n\tAdapt to window re-sizes"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002041#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002042 );
2043}
2044
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002045#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002046static void start_new_cmd_q(char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002047{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002048 // get buffer for new cmd
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002049 // if there is a current cmd count put it in the buffer first
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002050 if (cmdcnt > 0) {
Paul Foxc51fc7b2008-03-06 01:34:23 +00002051 lmc_len = sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002052 } else { // just save char c onto queue
Paul Foxd957b952005-11-28 18:07:53 +00002053 last_modifying_cmd[0] = c;
Paul Foxc51fc7b2008-03-06 01:34:23 +00002054 lmc_len = 1;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002055 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002056 adding2q = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002057}
2058
2059static void end_cmd_q(void)
2060{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002061#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002062 YDreg = 26; // go back to default Yank/Delete reg
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002063#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002064 adding2q = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002065}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002066#endif /* FEATURE_VI_DOT_CMD */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002067
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002068#if ENABLE_FEATURE_VI_YANKMARK \
2069 || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
2070 || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002071// might reallocate text[]! use p += string_insert(p, ...),
2072// and be careful to not use pointers into potentially freed text[]!
2073static uintptr_t string_insert(char *p, const char *s) // insert the string at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002074{
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002075 uintptr_t bias;
2076 int i;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002077
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002078 i = strlen(s);
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002079 bias = text_hole_make(p, i);
2080 p += bias;
Denis Vlasenko00d84172008-11-24 07:34:42 +00002081 memcpy(p, s, i);
Denis Vlasenko33875382008-06-21 20:31:50 +00002082#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002083 {
2084 int cnt;
2085 for (cnt = 0; *s != '\0'; s++) {
2086 if (*s == '\n')
2087 cnt++;
2088 }
2089 status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2090 }
Denis Vlasenko33875382008-06-21 20:31:50 +00002091#endif
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002092 return bias;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002093}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002094#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002095
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002096#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko33875382008-06-21 20:31:50 +00002097static char *text_yank(char *p, char *q, int dest) // copy text into a register
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002098{
Denis Vlasenko00d84172008-11-24 07:34:42 +00002099 int cnt = q - p;
2100 if (cnt < 0) { // they are backwards- reverse them
2101 p = q;
2102 cnt = -cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002103 }
Denis Vlasenko00d84172008-11-24 07:34:42 +00002104 free(reg[dest]); // if already a yank register, free it
2105 reg[dest] = xstrndup(p, cnt + 1);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002106 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002107}
2108
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002109static char what_reg(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002110{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002111 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002112
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002113 c = 'D'; // default to D-reg
2114 if (0 <= YDreg && YDreg <= 25)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002115 c = 'a' + (char) YDreg;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002116 if (YDreg == 26)
2117 c = 'D';
2118 if (YDreg == 27)
2119 c = 'U';
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002120 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002121}
2122
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002123static void check_context(char cmd)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002124{
2125 // A context is defined to be "modifying text"
2126 // Any modifying command establishes a new context.
2127
2128 if (dot < context_start || dot > context_end) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002129 if (strchr(modifying_cmds, cmd) != NULL) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002130 // we are trying to modify text[]- make this the current context
2131 mark[27] = mark[26]; // move cur to prev
2132 mark[26] = dot; // move local to cur
2133 context_start = prev_line(prev_line(dot));
2134 context_end = next_line(next_line(dot));
2135 //loiter= start_loiter= now;
2136 }
2137 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002138}
2139
Denis Vlasenkof882f082007-12-23 02:36:51 +00002140static char *swap_context(char *p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002141{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002142 char *tmp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002143
2144 // the current context is in mark[26]
2145 // the previous context is in mark[27]
2146 // only swap context if other context is valid
2147 if (text <= mark[27] && mark[27] <= end - 1) {
2148 tmp = mark[27];
2149 mark[27] = mark[26];
2150 mark[26] = tmp;
2151 p = mark[26]; // where we are going- previous context
2152 context_start = prev_line(prev_line(prev_line(p)));
2153 context_end = next_line(next_line(next_line(p)));
2154 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002155 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002156}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002157#endif /* FEATURE_VI_YANKMARK */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002158
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002159//----- Set terminal attributes --------------------------------
2160static void rawmode(void)
2161{
2162 tcgetattr(0, &term_orig);
2163 term_vi = term_orig;
2164 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2165 term_vi.c_iflag &= (~IXON & ~ICRNL);
2166 term_vi.c_oflag &= (~ONLCR);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002167 term_vi.c_cc[VMIN] = 1;
2168 term_vi.c_cc[VTIME] = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002169 erase_char = term_vi.c_cc[VERASE];
Denis Vlasenko202ac502008-11-05 13:20:58 +00002170 tcsetattr_stdin_TCSANOW(&term_vi);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002171}
2172
2173static void cookmode(void)
2174{
Denys Vlasenko8131eea2009-11-02 14:19:51 +01002175 fflush_all();
Denis Vlasenko202ac502008-11-05 13:20:58 +00002176 tcsetattr_stdin_TCSANOW(&term_orig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002177}
2178
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002179#if ENABLE_FEATURE_VI_USE_SIGNALS
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002180//----- Come here when we get a window resize signal ---------
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002181static void winch_sig(int sig UNUSED_PARAM)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002182{
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002183 int save_errno = errno;
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002184 // FIXME: do it in main loop!!!
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002185 signal(SIGWINCH, winch_sig);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002186 query_screen_dimensions();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002187 new_screen(rows, columns); // get memory for virtual screen
2188 redraw(TRUE); // re-draw the screen
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002189 errno = save_errno;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002190}
2191
2192//----- Come here when we get a continue signal -------------------
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002193static void cont_sig(int sig UNUSED_PARAM)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002194{
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002195 int save_errno = errno;
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002196 rawmode(); // terminal to "raw"
2197 last_status_cksum = 0; // force status update
2198 redraw(TRUE); // re-draw the screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002199
2200 signal(SIGTSTP, suspend_sig);
2201 signal(SIGCONT, SIG_DFL);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002202 //kill(my_pid, SIGCONT); // huh? why? we are already "continued"...
2203 errno = save_errno;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002204}
2205
2206//----- Come here when we get a Suspend signal -------------------
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002207static void suspend_sig(int sig UNUSED_PARAM)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002208{
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002209 int save_errno = errno;
Denis Vlasenko267e16c2008-10-14 10:34:41 +00002210 go_bottom_and_clear_to_eol();
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002211 cookmode(); // terminal to "cooked"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002212
2213 signal(SIGCONT, cont_sig);
2214 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002215 kill(my_pid, SIGTSTP);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002216 errno = save_errno;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002217}
2218
2219//----- Come here when we get a signal ---------------------------
2220static void catch_sig(int sig)
2221{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002222 signal(SIGINT, catch_sig);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002223 siglongjmp(restart, sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002224}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002225#endif /* FEATURE_VI_USE_SIGNALS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002226
Denys Vlasenko606291b2009-09-23 23:15:43 +02002227static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002228{
Denis Vlasenko87f3b262007-09-07 13:43:28 +00002229 struct pollfd pfd[1];
Denis Vlasenkocd5c7862007-05-17 16:37:22 +00002230
Denys Vlasenko606291b2009-09-23 23:15:43 +02002231 pfd[0].fd = STDIN_FILENO;
Denis Vlasenko87f3b262007-09-07 13:43:28 +00002232 pfd[0].events = POLLIN;
Denis Vlasenko5d61e712007-09-27 10:09:59 +00002233 return safe_poll(pfd, 1, hund*10) > 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002234}
2235
2236//----- IO Routines --------------------------------------------
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002237static int readit(void) // read (maybe cursor) key from stdin
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002238{
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002239 int c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002240
Denys Vlasenko8131eea2009-11-02 14:19:51 +01002241 fflush_all();
Denys Vlasenko58f108e2010-03-11 21:17:55 +01002242 c = read_key(STDIN_FILENO, readbuffer, /*timeout off:*/ -2);
Denis Vlasenko0112ff52008-10-25 23:23:00 +00002243 if (c == -1) { // EOF/error
2244 go_bottom_and_clear_to_eol();
2245 cookmode(); // terminal to "cooked"
2246 bb_error_msg_and_die("can't read user input");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002247 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002248 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002249}
2250
2251//----- IO Routines --------------------------------------------
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002252static int get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002253{
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002254 int c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002255
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002256#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002257 if (!adding2q) {
2258 // we are not adding to the q.
2259 // but, we may be reading from a q
2260 if (ioq == 0) {
2261 // there is no current q, read from STDIN
2262 c = readit(); // get the users input
2263 } else {
2264 // there is a queue to get chars from first
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002265 // careful with correct sign expansion!
2266 c = (unsigned char)*ioq++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002267 if (c == '\0') {
2268 // the end of the q, read from STDIN
2269 free(ioq_start);
2270 ioq_start = ioq = 0;
2271 c = readit(); // get the users input
2272 }
2273 }
2274 } else {
2275 // adding STDIN chars to q
2276 c = readit(); // get the users input
Denis Vlasenkob1759462008-06-20 20:20:54 +00002277 if (lmc_len >= MAX_INPUT_LEN - 1) {
2278 status_line_bold("last_modifying_cmd overrun");
2279 } else {
2280 // add new char to q
2281 last_modifying_cmd[lmc_len++] = c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002282 }
2283 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002284#else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002285 c = readit(); // get the users input
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002286#endif /* FEATURE_VI_DOT_CMD */
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002287 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002288}
2289
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002290// Get input line (uses "status line" area)
2291static char *get_input_line(const char *prompt)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002292{
Denis Vlasenkob1759462008-06-20 20:20:54 +00002293 // char [MAX_INPUT_LEN]
2294#define buf get_input_line__buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002295
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002296 int c;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002297 int i;
2298
2299 strcpy(buf, prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002300 last_status_cksum = 0; // force status update
Denis Vlasenko267e16c2008-10-14 10:34:41 +00002301 go_bottom_and_clear_to_eol();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002302 write1(prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002303
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002304 i = strlen(buf);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002305 while (i < MAX_INPUT_LEN) {
2306 c = get_one_char();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002307 if (c == '\n' || c == '\r' || c == 27)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002308 break; // this is end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002309 if (c == erase_char || c == 8 || c == 127) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002310 // user wants to erase prev char
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002311 buf[--i] = '\0';
2312 write1("\b \b"); // erase char on screen
2313 if (i <= 0) // user backs up before b-o-l, exit
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002314 break;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002315 } else if (c > 0 && c < 256) { // exclude Unicode
2316 // (TODO: need to handle Unicode)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002317 buf[i] = c;
2318 buf[++i] = '\0';
2319 bb_putchar(c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002320 }
2321 }
2322 refresh(FALSE);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002323 return buf;
Denis Vlasenkob1759462008-06-20 20:20:54 +00002324#undef buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002325}
2326
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002327static int file_size(const char *fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002328{
2329 struct stat st_buf;
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002330 int cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002331
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002332 cnt = -1;
Denys Vlasenkodef47832010-04-18 20:39:41 -07002333 if (fn && stat(fn, &st_buf) == 0) // see if file exists
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002334 cnt = (int) st_buf.st_size;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002335 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002336}
2337
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002338// might reallocate text[]!
2339static int file_insert(const char *fn, char *p, int update_ro_status)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002340{
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002341 int cnt = -1;
2342 int fd, size;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002343 struct stat statbuf;
2344
2345 /* Validate file */
2346 if (stat(fn, &statbuf) < 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002347 status_line_bold("\"%s\" %s", fn, strerror(errno));
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002348 goto fi0;
2349 }
Denis Vlasenko33875382008-06-21 20:31:50 +00002350 if (!S_ISREG(statbuf.st_mode)) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002351 // This is not a regular file
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002352 status_line_bold("\"%s\" Not a regular file", fn);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002353 goto fi0;
2354 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002355 if (p < text || p > end) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002356 status_line_bold("Trying to insert file outside of memory");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002357 goto fi0;
2358 }
2359
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002360 // read file to buffer
2361 fd = open(fn, O_RDONLY);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002362 if (fd < 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002363 status_line_bold("\"%s\" %s", fn, strerror(errno));
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002364 goto fi0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002365 }
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002366 size = statbuf.st_size;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002367 p += text_hole_make(p, size);
Denis Vlasenko4f95e5a2007-10-11 10:10:15 +00002368 cnt = safe_read(fd, p, size);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002369 if (cnt < 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002370 status_line_bold("\"%s\" %s", fn, strerror(errno));
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002371 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002372 } else if (cnt < size) {
2373 // There was a partial read, shrink unused space text[]
2374 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
Denys Vlasenko6331cf02009-11-13 09:08:27 +01002375 status_line_bold("can't read all of file \"%s\"", fn);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002376 }
2377 if (cnt >= size)
Paul Fox8552aec2005-09-16 12:20:05 +00002378 file_modified++;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002379 close(fd);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002380 fi0:
Denis Vlasenko856be772007-08-17 08:29:48 +00002381#if ENABLE_FEATURE_VI_READONLY
2382 if (update_ro_status
2383 && ((access(fn, W_OK) < 0) ||
2384 /* root will always have access()
2385 * so we check fileperms too */
2386 !(statbuf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))
2387 )
2388 ) {
Denis Vlasenko2f6ae432007-07-19 22:50:47 +00002389 SET_READONLY_FILE(readonly_mode);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002390 }
Denis Vlasenko856be772007-08-17 08:29:48 +00002391#endif
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002392 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002393}
2394
Denis Vlasenko33875382008-06-21 20:31:50 +00002395static int file_write(char *fn, char *first, char *last)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002396{
2397 int fd, cnt, charcnt;
2398
2399 if (fn == 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002400 status_line_bold("No current filename");
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002401 return -2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002402 }
Denis Vlasenko8abae882008-05-03 11:35:59 +00002403 /* By popular request we do not open file with O_TRUNC,
2404 * but instead ftruncate() it _after_ successful write.
2405 * Might reduce amount of data lost on power fail etc.
2406 */
2407 fd = open(fn, (O_WRONLY | O_CREAT), 0666);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002408 if (fd < 0)
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002409 return -1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002410 cnt = last - first + 1;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002411 charcnt = full_write(fd, first, cnt);
Denis Vlasenko8abae882008-05-03 11:35:59 +00002412 ftruncate(fd, charcnt);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002413 if (charcnt == cnt) {
2414 // good write
Denis Vlasenkob1759462008-06-20 20:20:54 +00002415 //file_modified = FALSE;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002416 } else {
2417 charcnt = 0;
2418 }
2419 close(fd);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002420 return charcnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002421}
2422
2423//----- Terminal Drawing ---------------------------------------
2424// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002425// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002426// screen coordinates
2427// 0,0 ... 0,79
2428// 1,0 ... 1,79
2429// . ... .
2430// . ... .
2431// 22,0 ... 22,79
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002432// 23,0 ... 23,79 <- status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002433
2434//----- Move the cursor to row x col (count from 0, not 1) -------
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002435static void place_cursor(int row, int col, int optimize)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002436{
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002437 char cm1[sizeof(CMrc) + sizeof(int)*3 * 2];
Denis Vlasenko7b54dc72008-07-17 21:32:32 +00002438#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
2439 enum {
2440 SZ_UP = sizeof(CMup),
2441 SZ_DN = sizeof(CMdown),
2442 SEQ_SIZE = SZ_UP > SZ_DN ? SZ_UP : SZ_DN,
2443 };
2444 char cm2[SEQ_SIZE * 5 + 32]; // bigger than worst case size
2445#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002446 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002447
2448 if (row < 0) row = 0;
2449 if (row >= rows) row = rows - 1;
2450 if (col < 0) col = 0;
2451 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002452
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002453 //----- 1. Try the standard terminal ESC sequence
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002454 sprintf(cm1, CMrc, row + 1, col + 1);
2455 cm = cm1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002456
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002457#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002458 if (optimize && col < 16) {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002459 char *screenp;
2460 int Rrow = last_row;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002461 int diff = Rrow - row;
2462
2463 if (diff < -5 || diff > 5)
2464 goto skip;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002465
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002466 //----- find the minimum # of chars to move cursor -------------
2467 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2468 cm2[0] = '\0';
2469
2470 // move to the correct row
2471 while (row < Rrow) {
2472 // the cursor has to move up
2473 strcat(cm2, CMup);
2474 Rrow--;
2475 }
2476 while (row > Rrow) {
2477 // the cursor has to move down
2478 strcat(cm2, CMdown);
2479 Rrow++;
2480 }
2481
2482 // now move to the correct column
2483 strcat(cm2, "\r"); // start at col 0
2484 // just send out orignal source char to get to correct place
2485 screenp = &screen[row * columns]; // start of screen line
2486 strncat(cm2, screenp, col);
2487
2488 // pick the shortest cursor motion to send out
2489 if (strlen(cm2) < strlen(cm)) {
2490 cm = cm2;
2491 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002492 skip: ;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002493 }
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00002494 last_row = row;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002495#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002496 write1(cm);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002497}
2498
2499//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002500static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002501{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002502 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002503}
2504
Denis Vlasenko267e16c2008-10-14 10:34:41 +00002505static void go_bottom_and_clear_to_eol(void)
2506{
2507 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2508 clear_to_eol(); // erase to end of line
2509}
2510
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002511//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002512static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002513{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002514 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002515}
2516
2517//----- Start standout mode ------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002518static void standout_start(void) // send "start reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002519{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002520 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002521}
2522
2523//----- End standout mode --------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002524static void standout_end(void) // send "end reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002525{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002526 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002527}
2528
2529//----- Flash the screen --------------------------------------
2530static void flash(int h)
2531{
2532 standout_start(); // send "start reverse video" sequence
2533 redraw(TRUE);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002534 mysleep(h);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002535 standout_end(); // send "end reverse video" sequence
2536 redraw(TRUE);
2537}
2538
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002539static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002540{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002541#if ENABLE_FEATURE_VI_CRASHME
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002542 if (crashme > 0)
2543 return; // generate a random command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002544#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002545 if (!err_method) {
2546 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002547 } else {
2548 flash(10);
2549 }
2550}
2551
2552//----- Screen[] Routines --------------------------------------
2553//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002554static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002555{
2556 memset(screen, ' ', screensize); // clear new screen
2557}
2558
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002559static int bufsum(char *buf, int count)
Paul Fox8552aec2005-09-16 12:20:05 +00002560{
2561 int sum = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002562 char *e = buf + count;
2563
Paul Fox8552aec2005-09-16 12:20:05 +00002564 while (buf < e)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002565 sum += (unsigned char) *buf++;
Paul Fox8552aec2005-09-16 12:20:05 +00002566 return sum;
2567}
2568
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002569//----- Draw the status line at bottom of the screen -------------
2570static void show_status_line(void)
2571{
Paul Foxc3504852005-09-16 12:48:18 +00002572 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002573
Paul Fox8552aec2005-09-16 12:20:05 +00002574 // either we already have an error or status message, or we
2575 // create one.
2576 if (!have_status_msg) {
2577 cnt = format_edit_status();
2578 cksum = bufsum(status_buffer, cnt);
2579 }
2580 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002581 last_status_cksum = cksum; // remember if we have seen this line
Denis Vlasenko267e16c2008-10-14 10:34:41 +00002582 go_bottom_and_clear_to_eol();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002583 write1(status_buffer);
Paul Fox8552aec2005-09-16 12:20:05 +00002584 if (have_status_msg) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002585 if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
Paul Fox8552aec2005-09-16 12:20:05 +00002586 (columns - 1) ) {
2587 have_status_msg = 0;
2588 Hit_Return();
2589 }
2590 have_status_msg = 0;
2591 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002592 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2593 }
Denys Vlasenko8131eea2009-11-02 14:19:51 +01002594 fflush_all();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002595}
2596
2597//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00002598// format status buffer, with STANDOUT mode
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002599static void status_line_bold(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002600{
2601 va_list args;
2602
2603 va_start(args, format);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002604 strcpy(status_buffer, SOs); // Terminal standout mode on
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002605 vsprintf(status_buffer + sizeof(SOs)-1, format, args);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002606 strcat(status_buffer, SOn); // Terminal standout mode off
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002607 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002608
2609 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002610}
2611
Paul Fox8552aec2005-09-16 12:20:05 +00002612// format status buffer
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002613static void status_line(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002614{
2615 va_list args;
2616
2617 va_start(args, format);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002618 vsprintf(status_buffer, format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002619 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002620
2621 have_status_msg = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002622}
2623
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002624// copy s to buf, convert unprintable
2625static void print_literal(char *buf, const char *s)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002626{
Denys Vlasenkoc2704542009-11-20 19:14:19 +01002627 char *d;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002628 unsigned char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002629
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002630 buf[0] = '\0';
2631 if (!s[0])
2632 s = "(NULL)";
Denys Vlasenkoc2704542009-11-20 19:14:19 +01002633
2634 d = buf;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002635 for (; *s; s++) {
2636 int c_is_no_print;
2637
2638 c = *s;
2639 c_is_no_print = (c & 0x80) && !Isprint(c);
2640 if (c_is_no_print) {
Denys Vlasenkoc2704542009-11-20 19:14:19 +01002641 strcpy(d, SOn);
2642 d += sizeof(SOn)-1;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002643 c = '.';
2644 }
Denys Vlasenkoc2704542009-11-20 19:14:19 +01002645 if (c < ' ' || c == 0x7f) {
2646 *d++ = '^';
2647 c |= '@'; /* 0x40 */
2648 if (c == 0x7f)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002649 c = '?';
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002650 }
Denys Vlasenkoc2704542009-11-20 19:14:19 +01002651 *d++ = c;
2652 *d = '\0';
2653 if (c_is_no_print) {
2654 strcpy(d, SOs);
2655 d += sizeof(SOs)-1;
2656 }
2657 if (*s == '\n') {
2658 *d++ = '$';
2659 *d = '\0';
2660 }
2661 if (d - buf > MAX_INPUT_LEN - 10) // paranoia
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002662 break;
2663 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002664}
2665
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002666static void not_implemented(const char *s)
2667{
2668 char buf[MAX_INPUT_LEN];
2669
2670 print_literal(buf, s);
2671 status_line_bold("\'%s\' is not implemented", buf);
2672}
2673
2674// show file status on status line
2675static int format_edit_status(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002676{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002677 static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
Denis Vlasenkob1759462008-06-20 20:20:54 +00002678
2679#define tot format_edit_status__tot
2680
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002681 int cur, percent, ret, trunc_at;
2682
Paul Fox8552aec2005-09-16 12:20:05 +00002683 // file_modified is now a counter rather than a flag. this
2684 // helps reduce the amount of line counting we need to do.
2685 // (this will cause a mis-reporting of modified status
2686 // once every MAXINT editing operations.)
2687
2688 // it would be nice to do a similar optimization here -- if
2689 // we haven't done a motion that could have changed which line
2690 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002691 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00002692
2693 // reduce counting -- the total lines can't have
2694 // changed if we haven't done any edits.
2695 if (file_modified != last_file_modified) {
2696 tot = cur + count_lines(dot, end - 1) - 1;
2697 last_file_modified = file_modified;
2698 }
2699
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002700 // current line percent
2701 // ------------- ~~ ----------
2702 // total lines 100
2703 if (tot > 0) {
2704 percent = (100 * cur) / tot;
2705 } else {
2706 cur = tot = 0;
2707 percent = 100;
2708 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00002709
Paul Fox8552aec2005-09-16 12:20:05 +00002710 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2711 columns : STATUS_BUFFER_LEN-1;
2712
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002713 ret = snprintf(status_buffer, trunc_at+1,
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002714#if ENABLE_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002715 "%c %s%s%s %d/%d %d%%",
2716#else
2717 "%c %s%s %d/%d %d%%",
2718#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002719 cmd_mode_indicator[cmd_mode & 3],
2720 (current_filename != NULL ? current_filename : "No file"),
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002721#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002722 (readonly_mode ? " [Readonly]" : ""),
Paul Fox8552aec2005-09-16 12:20:05 +00002723#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002724 (file_modified ? " [Modified]" : ""),
Paul Fox8552aec2005-09-16 12:20:05 +00002725 cur, tot, percent);
2726
2727 if (ret >= 0 && ret < trunc_at)
2728 return ret; /* it all fit */
2729
2730 return trunc_at; /* had to truncate */
Denis Vlasenkob1759462008-06-20 20:20:54 +00002731#undef tot
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002732}
2733
2734//----- Force refresh of all Lines -----------------------------
2735static void redraw(int full_screen)
2736{
2737 place_cursor(0, 0, FALSE); // put cursor in correct place
Denis Vlasenkob1759462008-06-20 20:20:54 +00002738 clear_to_eos(); // tell terminal to erase display
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002739 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00002740 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002741 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00002742 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002743}
2744
2745//----- Format a text[] line into a buffer ---------------------
Denis Vlasenko68404f12008-03-17 09:00:54 +00002746static char* format_line(char *src /*, int li*/)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002747{
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002748 unsigned char c;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002749 int co;
2750 int ofs = offset;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002751 char *dest = scr_out_buf; // [MAX_SCR_COLS + MAX_TABSTOP * 2]
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002752
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002753 c = '~'; // char in col 0 in non-existent lines is '~'
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00002754 co = 0;
2755 while (co < columns + tabstop) {
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002756 // have we gone past the end?
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002757 if (src < end) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002758 c = *src++;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002759 if (c == '\n')
2760 break;
2761 if ((c & 0x80) && !Isprint(c)) {
2762 c = '.';
2763 }
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002764 if (c < ' ' || c == 0x7f) {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002765 if (c == '\t') {
2766 c = ' ';
2767 // co % 8 != 7
2768 while ((co % tabstop) != (tabstop - 1)) {
2769 dest[co++] = c;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002770 }
2771 } else {
2772 dest[co++] = '^';
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002773 if (c == 0x7f)
2774 c = '?';
2775 else
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002776 c += '@'; // Ctrl-X -> 'X'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002777 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002778 }
2779 }
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00002780 dest[co++] = c;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002781 // discard scrolled-off-to-the-left portion,
2782 // in tabstop-sized pieces
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002783 if (ofs >= tabstop && co >= tabstop) {
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00002784 memmove(dest, dest + tabstop, co);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002785 co -= tabstop;
2786 ofs -= tabstop;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002787 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002788 if (src >= end)
2789 break;
2790 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002791 // check "short line, gigantic offset" case
2792 if (co < ofs)
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00002793 ofs = co;
2794 // discard last scrolled off part
2795 co -= ofs;
2796 dest += ofs;
2797 // fill the rest with spaces
2798 if (co < columns)
2799 memset(&dest[co], ' ', columns - co);
2800 return dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002801}
2802
2803//----- Refresh the changed screen lines -----------------------
2804// Copy the source line from text[] into the buffer and note
2805// if the current screenline is different from the new buffer.
2806// If they differ then that line needs redrawing on the terminal.
2807//
2808static void refresh(int full_screen)
2809{
Denis Vlasenkob1759462008-06-20 20:20:54 +00002810#define old_offset refresh__old_offset
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002811
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002812 int li, changed;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002813 char *tp, *sp; // pointer into text[] and screen[]
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002814
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +02002815 if (ENABLE_FEATURE_VI_WIN_RESIZE IF_FEATURE_VI_ASK_TERMINAL(&& !G.get_rowcol_error) ) {
Denis Vlasenko55995022008-05-18 22:28:26 +00002816 unsigned c = columns, r = rows;
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002817 query_screen_dimensions();
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002818 full_screen |= (c - columns) | (r - rows);
2819 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002820 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2821 tp = screenbegin; // index into text[] of top line
2822
2823 // compare text[] to screen[] and mark screen[] lines that need updating
2824 for (li = 0; li < rows - 1; li++) {
2825 int cs, ce; // column start & end
Denis Vlasenkof882f082007-12-23 02:36:51 +00002826 char *out_buf;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002827 // format current text line
Denis Vlasenko68404f12008-03-17 09:00:54 +00002828 out_buf = format_line(tp /*, li*/);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002829
2830 // skip to the end of the current text[] line
Denis Vlasenkof882f082007-12-23 02:36:51 +00002831 if (tp < end) {
2832 char *t = memchr(tp, '\n', end - tp);
2833 if (!t) t = end - 1;
2834 tp = t + 1;
2835 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002836
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002837 // see if there are any changes between vitual screen and out_buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002838 changed = FALSE; // assume no change
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002839 cs = 0;
2840 ce = columns - 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002841 sp = &screen[li * columns]; // start of screen line
2842 if (full_screen) {
2843 // force re-draw of every single column from 0 - columns-1
2844 goto re0;
2845 }
2846 // compare newly formatted buffer with virtual screen
2847 // look forward for first difference between buf and screen
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002848 for (; cs <= ce; cs++) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002849 if (out_buf[cs] != sp[cs]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002850 changed = TRUE; // mark for redraw
2851 break;
2852 }
2853 }
2854
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002855 // look backward for last difference between out_buf and screen
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002856 for (; ce >= cs; ce--) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002857 if (out_buf[ce] != sp[ce]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002858 changed = TRUE; // mark for redraw
2859 break;
2860 }
2861 }
2862 // now, cs is index of first diff, and ce is index of last diff
2863
2864 // if horz offset has changed, force a redraw
2865 if (offset != old_offset) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002866 re0:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002867 changed = TRUE;
2868 }
2869
2870 // make a sanity check of columns indexes
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002871 if (cs < 0) cs = 0;
2872 if (ce > columns - 1) ce = columns - 1;
2873 if (cs > ce) { cs = 0; ce = columns - 1; }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002874 // is there a change between vitual screen and out_buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002875 if (changed) {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002876 // copy changed part of buffer to virtual screen
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002877 memcpy(sp+cs, out_buf+cs, ce-cs+1);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002878
2879 // move cursor to column of first change
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002880 //if (offset != old_offset) {
2881 // // place_cursor is still too stupid
2882 // // to handle offsets correctly
2883 // place_cursor(li, cs, FALSE);
2884 //} else {
2885 place_cursor(li, cs, TRUE);
2886 //}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002887
2888 // write line out to terminal
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002889 fwrite(&sp[cs], ce - cs + 1, 1, stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002890 }
2891 }
2892
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002893 place_cursor(crow, ccol, TRUE);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002894
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002895 old_offset = offset;
Denis Vlasenkob1759462008-06-20 20:20:54 +00002896#undef old_offset
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002897}
2898
Eric Andersen3f980402001-04-04 17:31:15 +00002899//---------------------------------------------------------------------
2900//----- the Ascii Chart -----------------------------------------------
2901//
2902// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2903// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2904// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2905// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2906// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2907// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2908// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2909// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2910// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2911// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2912// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2913// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2914// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2915// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2916// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2917// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2918//---------------------------------------------------------------------
2919
2920//----- Execute a Vi Command -----------------------------------
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002921static void do_cmd(int c)
Eric Andersen3f980402001-04-04 17:31:15 +00002922{
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002923 const char *msg = msg; // for compiler
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002924 char *p, *q, *save_dot;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002925 char buf[12];
Denis Vlasenkoc3a9dc82008-10-29 00:58:04 +00002926 int dir;
Paul Foxc51fc7b2008-03-06 01:34:23 +00002927 int cnt, i, j;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002928 int c1;
Eric Andersen3f980402001-04-04 17:31:15 +00002929
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002930// c1 = c; // quiet the compiler
2931// cnt = yf = 0; // quiet the compiler
2932// msg = p = q = save_dot = buf; // quiet the compiler
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002933 memset(buf, '\0', 12);
Eric Andersenbff7a602001-11-17 07:15:43 +00002934
Paul Fox8552aec2005-09-16 12:20:05 +00002935 show_status_line();
2936
Eric Andersenbff7a602001-11-17 07:15:43 +00002937 /* if this is a cursor key, skip these checks */
2938 switch (c) {
Denis Vlasenko0112ff52008-10-25 23:23:00 +00002939 case KEYCODE_UP:
2940 case KEYCODE_DOWN:
2941 case KEYCODE_LEFT:
2942 case KEYCODE_RIGHT:
2943 case KEYCODE_HOME:
2944 case KEYCODE_END:
2945 case KEYCODE_PAGEUP:
2946 case KEYCODE_PAGEDOWN:
2947 case KEYCODE_DELETE:
Eric Andersenbff7a602001-11-17 07:15:43 +00002948 goto key_cmd_mode;
2949 }
2950
Eric Andersen3f980402001-04-04 17:31:15 +00002951 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002952 // flip-flop Insert/Replace mode
Denis Vlasenko0112ff52008-10-25 23:23:00 +00002953 if (c == KEYCODE_INSERT)
Denis Vlasenko2a51af22007-03-21 22:31:24 +00002954 goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00002955 // we are 'R'eplacing the current *dot with new char
2956 if (*dot == '\n') {
2957 // don't Replace past E-o-l
2958 cmd_mode = 1; // convert to insert
2959 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002960 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00002961 if (c != 27)
2962 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
2963 dot = char_insert(dot, c); // insert new char
2964 }
2965 goto dc1;
2966 }
2967 }
2968 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00002969 // hitting "Insert" twice means "R" replace mode
Denis Vlasenko0112ff52008-10-25 23:23:00 +00002970 if (c == KEYCODE_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00002971 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002972 if (1 <= c || Isprint(c)) {
2973 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00002974 }
2975 goto dc1;
2976 }
2977
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002978 key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00002979 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00002980 //case 0x01: // soh
2981 //case 0x09: // ht
2982 //case 0x0b: // vt
2983 //case 0x0e: // so
2984 //case 0x0f: // si
2985 //case 0x10: // dle
2986 //case 0x11: // dc1
2987 //case 0x13: // dc3
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002988#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00002989 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00002990 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00002991 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002992#endif
Eric Andersen822c3832001-05-07 17:37:43 +00002993 //case 0x16: // syn
2994 //case 0x17: // etb
2995 //case 0x18: // can
2996 //case 0x1c: // fs
2997 //case 0x1d: // gs
2998 //case 0x1e: // rs
2999 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003000 //case '!': // !-
3001 //case '#': // #-
3002 //case '&': // &-
3003 //case '(': // (-
3004 //case ')': // )-
3005 //case '*': // *-
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003006 //case '=': // =-
3007 //case '@': // @-
3008 //case 'F': // F-
3009 //case 'K': // K-
3010 //case 'Q': // Q-
3011 //case 'S': // S-
3012 //case 'T': // T-
3013 //case 'V': // V-
3014 //case '[': // [-
3015 //case '\\': // \-
3016 //case ']': // ]-
3017 //case '_': // _-
3018 //case '`': // `-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003019 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003020 //case 'v': // v-
Denys Vlasenkob22bbff2009-07-04 16:50:43 +02003021 default: // unrecognized command
Eric Andersen3f980402001-04-04 17:31:15 +00003022 buf[0] = c;
3023 buf[1] = '\0';
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003024 not_implemented(buf);
Eric Andersen3f980402001-04-04 17:31:15 +00003025 end_cmd_q(); // stop adding to q
3026 case 0x00: // nul- ignore
3027 break;
3028 case 2: // ctrl-B scroll up full screen
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003029 case KEYCODE_PAGEUP: // Cursor Key Page Up
Eric Andersen3f980402001-04-04 17:31:15 +00003030 dot_scroll(rows - 2, -1);
3031 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003032 case 4: // ctrl-D scroll down half screen
3033 dot_scroll((rows - 2) / 2, 1);
3034 break;
3035 case 5: // ctrl-E scroll down one line
3036 dot_scroll(1, 1);
3037 break;
3038 case 6: // ctrl-F scroll down full screen
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003039 case KEYCODE_PAGEDOWN: // Cursor Key Page Down
Eric Andersen3f980402001-04-04 17:31:15 +00003040 dot_scroll(rows - 2, 1);
3041 break;
3042 case 7: // ctrl-G show current status
Paul Fox8552aec2005-09-16 12:20:05 +00003043 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003044 break;
3045 case 'h': // h- move left
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003046 case KEYCODE_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003047 case 8: // ctrl-H- move left (This may be ERASE char)
Denis Vlasenko2a51af22007-03-21 22:31:24 +00003048 case 0x7f: // DEL- move left (This may be ERASE char)
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003049 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003050 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003051 }
Eric Andersen3f980402001-04-04 17:31:15 +00003052 dot_left();
3053 break;
3054 case 10: // Newline ^J
3055 case 'j': // j- goto next line, same col
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003056 case KEYCODE_DOWN: // cursor key Down
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003057 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003058 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003059 }
Eric Andersen3f980402001-04-04 17:31:15 +00003060 dot_next(); // go to next B-o-l
3061 dot = move_to_col(dot, ccol + offset); // try stay in same col
3062 break;
3063 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003064 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003065 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003066 clear_to_eos(); // tel terminal to erase display
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003067 mysleep(10);
Eric Andersen3f980402001-04-04 17:31:15 +00003068 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003069 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003070 refresh(TRUE); // this will redraw the entire display
3071 break;
3072 case 13: // Carriage Return ^M
3073 case '+': // +- goto next line
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003074 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003075 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003076 }
Eric Andersen3f980402001-04-04 17:31:15 +00003077 dot_next();
3078 dot_skip_over_ws();
3079 break;
3080 case 21: // ctrl-U scroll up half screen
3081 dot_scroll((rows - 2) / 2, -1);
3082 break;
3083 case 25: // ctrl-Y scroll up one line
3084 dot_scroll(1, -1);
3085 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003086 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003087 if (cmd_mode == 0)
3088 indicate_error(c);
3089 cmd_mode = 0; // stop insrting
3090 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003091 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003092 break;
3093 case ' ': // move right
3094 case 'l': // move right
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003095 case KEYCODE_RIGHT: // Cursor Key Right
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003096 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003097 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003098 }
Eric Andersen3f980402001-04-04 17:31:15 +00003099 dot_right();
3100 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003101#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003102 case '"': // "- name a register to use for Delete/Yank
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003103 c1 = (get_one_char() | 0x20) - 'a'; // | 0x20 is tolower()
3104 if ((unsigned)c1 <= 25) { // a-z?
3105 YDreg = c1;
Eric Andersen3f980402001-04-04 17:31:15 +00003106 } else {
3107 indicate_error(c);
3108 }
3109 break;
3110 case '\'': // '- goto a specific mark
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003111 c1 = (get_one_char() | 0x20) - 'a';
3112 if ((unsigned)c1 <= 25) { // a-z?
Eric Andersen3f980402001-04-04 17:31:15 +00003113 // get the b-o-l
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003114 q = mark[c1];
Eric Andersen3f980402001-04-04 17:31:15 +00003115 if (text <= q && q < end) {
3116 dot = q;
3117 dot_begin(); // go to B-o-l
3118 dot_skip_over_ws();
3119 }
3120 } else if (c1 == '\'') { // goto previous context
3121 dot = swap_context(dot); // swap current and previous context
3122 dot_begin(); // go to B-o-l
3123 dot_skip_over_ws();
3124 } else {
3125 indicate_error(c);
3126 }
3127 break;
3128 case 'm': // m- Mark a line
3129 // this is really stupid. If there are any inserts or deletes
3130 // between text[0] and dot then this mark will not point to the
3131 // correct location! It could be off by many lines!
3132 // Well..., at least its quick and dirty.
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003133 c1 = (get_one_char() | 0x20) - 'a';
3134 if ((unsigned)c1 <= 25) { // a-z?
Eric Andersen3f980402001-04-04 17:31:15 +00003135 // remember the line
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003136 mark[c1] = dot;
Eric Andersen3f980402001-04-04 17:31:15 +00003137 } else {
3138 indicate_error(c);
3139 }
3140 break;
3141 case 'P': // P- Put register before
3142 case 'p': // p- put register after
3143 p = reg[YDreg];
Denis Vlasenko00d84172008-11-24 07:34:42 +00003144 if (p == NULL) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003145 status_line_bold("Nothing in register %c", what_reg());
Eric Andersen3f980402001-04-04 17:31:15 +00003146 break;
3147 }
3148 // are we putting whole lines or strings
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003149 if (strchr(p, '\n') != NULL) {
Eric Andersen3f980402001-04-04 17:31:15 +00003150 if (c == 'P') {
3151 dot_begin(); // putting lines- Put above
3152 }
3153 if (c == 'p') {
3154 // are we putting after very last line?
3155 if (end_line(dot) == (end - 1)) {
3156 dot = end; // force dot to end of text[]
3157 } else {
3158 dot_next(); // next line, then put before
3159 }
3160 }
3161 } else {
3162 if (c == 'p')
3163 dot_right(); // move to right, can move to NL
3164 }
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00003165 string_insert(dot, p); // insert the string
Eric Andersen3f980402001-04-04 17:31:15 +00003166 end_cmd_q(); // stop adding to q
3167 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003168 case 'U': // U- Undo; replace current line with original version
3169 if (reg[Ureg] != 0) {
3170 p = begin_line(dot);
3171 q = end_line(dot);
3172 p = text_hole_delete(p, q); // delete cur line
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00003173 p += string_insert(p, reg[Ureg]); // insert orig line
Eric Andersen3f980402001-04-04 17:31:15 +00003174 dot = p;
3175 dot_skip_over_ws();
3176 }
3177 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003178#endif /* FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003179 case '$': // $- goto end of line
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003180 case KEYCODE_END: // Cursor Key End
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003181 if (--cmdcnt > 0) {
3182 dot_next();
Eric Andersen3f980402001-04-04 17:31:15 +00003183 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003184 }
Glenn L McGrathee829062004-01-21 10:59:45 +00003185 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003186 break;
3187 case '%': // %- find matching char of pair () [] {}
3188 for (q = dot; q < end && *q != '\n'; q++) {
3189 if (strchr("()[]{}", *q) != NULL) {
3190 // we found half of a pair
3191 p = find_pair(q, *q);
3192 if (p == NULL) {
3193 indicate_error(c);
3194 } else {
3195 dot = p;
3196 }
3197 break;
3198 }
3199 }
3200 if (*q == '\n')
3201 indicate_error(c);
3202 break;
3203 case 'f': // f- forward to a user specified char
3204 last_forward_char = get_one_char(); // get the search char
3205 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003206 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003207 //
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +00003208 //**** fall through to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003209 case ';': // ;- look at rest of line for last forward char
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003210 if (--cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003211 do_cmd(';');
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003212 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003213 if (last_forward_char == 0)
3214 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003215 q = dot + 1;
3216 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3217 q++;
3218 }
3219 if (*q == last_forward_char)
3220 dot = q;
3221 break;
Paul Foxb5ee8db2008-02-14 01:17:01 +00003222 case ',': // repeat latest 'f' in opposite direction
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003223 if (--cmdcnt > 0) {
Paul Foxb5ee8db2008-02-14 01:17:01 +00003224 do_cmd(',');
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003225 }
Paul Foxb5ee8db2008-02-14 01:17:01 +00003226 if (last_forward_char == 0)
3227 break;
3228 q = dot - 1;
3229 while (q >= text && *q != '\n' && *q != last_forward_char) {
3230 q--;
3231 }
3232 if (q >= text && *q == last_forward_char)
3233 dot = q;
3234 break;
3235
Eric Andersen3f980402001-04-04 17:31:15 +00003236 case '-': // -- goto prev line
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003237 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003238 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003239 }
Eric Andersen3f980402001-04-04 17:31:15 +00003240 dot_prev();
3241 dot_skip_over_ws();
3242 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003243#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003244 case '.': // .- repeat the last modifying command
3245 // Stuff the last_modifying_cmd back into stdin
3246 // and let it be re-executed.
Denis Vlasenkob1759462008-06-20 20:20:54 +00003247 if (lmc_len > 0) {
Paul Foxc51fc7b2008-03-06 01:34:23 +00003248 last_modifying_cmd[lmc_len] = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003249 ioq = ioq_start = xstrdup(last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003250 }
3251 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003252#endif
3253#if ENABLE_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003254 case '?': // /- search for a pattern
3255 case '/': // /- search for a pattern
3256 buf[0] = c;
3257 buf[1] = '\0';
3258 q = get_input_line(buf); // get input line- use "status line"
Paul Fox4917c112008-03-05 16:44:02 +00003259 if (q[0] && !q[1]) {
3260 if (last_search_pattern[0])
Denis Vlasenkoc3a9dc82008-10-29 00:58:04 +00003261 last_search_pattern[0] = c;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003262 goto dc3; // if no pat re-use old pat
Paul Fox4917c112008-03-05 16:44:02 +00003263 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003264 if (q[0]) { // strlen(q) > 1: new pat- save it and find
Eric Andersen3f980402001-04-04 17:31:15 +00003265 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003266 free(last_search_pattern);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003267 last_search_pattern = xstrdup(q);
Eric Andersen3f980402001-04-04 17:31:15 +00003268 goto dc3; // now find the pattern
3269 }
3270 // user changed mind and erased the "/"- do nothing
3271 break;
3272 case 'N': // N- backward search for last pattern
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003273 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003274 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003275 }
Eric Andersen3f980402001-04-04 17:31:15 +00003276 dir = BACK; // assume BACKWARD search
3277 p = dot - 1;
3278 if (last_search_pattern[0] == '?') {
3279 dir = FORWARD;
3280 p = dot + 1;
3281 }
3282 goto dc4; // now search for pattern
3283 break;
3284 case 'n': // n- repeat search for last pattern
3285 // search rest of text[] starting at next char
3286 // if search fails return orignal "p" not the "p+1" address
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003287 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003288 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003289 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003290 dc3:
Denis Vlasenkoc3a9dc82008-10-29 00:58:04 +00003291 dir = FORWARD; // assume FORWARD search
3292 p = dot + 1;
Eric Andersen3f980402001-04-04 17:31:15 +00003293 if (last_search_pattern[0] == '?') {
3294 dir = BACK;
3295 p = dot - 1;
3296 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003297 dc4:
Eric Andersen3f980402001-04-04 17:31:15 +00003298 q = char_search(p, last_search_pattern + 1, dir, FULL);
3299 if (q != NULL) {
3300 dot = q; // good search, update "dot"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003301 msg = "";
Eric Andersen3f980402001-04-04 17:31:15 +00003302 goto dc2;
3303 }
3304 // no pattern found between "dot" and "end"- continue at top
3305 p = text;
3306 if (dir == BACK) {
3307 p = end - 1;
3308 }
3309 q = char_search(p, last_search_pattern + 1, dir, FULL);
3310 if (q != NULL) { // found something
3311 dot = q; // found new pattern- goto it
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003312 msg = "search hit BOTTOM, continuing at TOP";
Eric Andersen3f980402001-04-04 17:31:15 +00003313 if (dir == BACK) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003314 msg = "search hit TOP, continuing at BOTTOM";
Eric Andersen3f980402001-04-04 17:31:15 +00003315 }
3316 } else {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003317 msg = "Pattern not found";
Eric Andersen3f980402001-04-04 17:31:15 +00003318 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003319 dc2:
3320 if (*msg)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003321 status_line_bold("%s", msg);
Eric Andersen3f980402001-04-04 17:31:15 +00003322 break;
3323 case '{': // {- move backward paragraph
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003324 q = char_search(dot, "\n\n", BACK, FULL);
Eric Andersen3f980402001-04-04 17:31:15 +00003325 if (q != NULL) { // found blank line
3326 dot = next_line(q); // move to next blank line
3327 }
3328 break;
3329 case '}': // }- move forward paragraph
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003330 q = char_search(dot, "\n\n", FORWARD, FULL);
Eric Andersen3f980402001-04-04 17:31:15 +00003331 if (q != NULL) { // found blank line
3332 dot = next_line(q); // move to next blank line
3333 }
3334 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003335#endif /* FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003336 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003337 case '1': // 1-
3338 case '2': // 2-
3339 case '3': // 3-
3340 case '4': // 4-
3341 case '5': // 5-
3342 case '6': // 6-
3343 case '7': // 7-
3344 case '8': // 8-
3345 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003346 if (c == '0' && cmdcnt < 1) {
3347 dot_begin(); // this was a standalone zero
3348 } else {
3349 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3350 }
3351 break;
3352 case ':': // :- the colon mode commands
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003353 p = get_input_line(":"); // get input line- use "status line"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003354#if ENABLE_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003355 colon(p); // execute the command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003356#else
Eric Andersen822c3832001-05-07 17:37:43 +00003357 if (*p == ':')
3358 p++; // move past the ':'
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003359 cnt = strlen(p);
Eric Andersen822c3832001-05-07 17:37:43 +00003360 if (cnt <= 0)
3361 break;
Denys Vlasenko6548edd2009-06-15 12:44:11 +02003362 if (strncmp(p, "quit", cnt) == 0
3363 || strncmp(p, "q!", cnt) == 0 // delete lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003364 ) {
Matt Kraai1f0c4362001-12-20 23:13:26 +00003365 if (file_modified && p[1] != '!') {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003366 status_line_bold("No write since last change (:quit! overrides)");
Eric Andersen3f980402001-04-04 17:31:15 +00003367 } else {
3368 editing = 0;
3369 }
Denys Vlasenko6548edd2009-06-15 12:44:11 +02003370 } else if (strncmp(p, "write", cnt) == 0
3371 || strncmp(p, "wq", cnt) == 0
3372 || strncmp(p, "wn", cnt) == 0
3373 || (p[0] == 'x' && !p[1])
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003374 ) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003375 cnt = file_write(current_filename, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003376 if (cnt < 0) {
3377 if (cnt == -1)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003378 status_line_bold("Write error: %s", strerror(errno));
Paul Fox61e45db2005-10-09 14:43:22 +00003379 } else {
3380 file_modified = 0;
3381 last_file_modified = -1;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003382 status_line("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003383 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
3384 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
3385 ) {
Paul Fox61e45db2005-10-09 14:43:22 +00003386 editing = 0;
3387 }
Eric Andersen3f980402001-04-04 17:31:15 +00003388 }
Denys Vlasenko6548edd2009-06-15 12:44:11 +02003389 } else if (strncmp(p, "file", cnt) == 0) {
Paul Fox8552aec2005-09-16 12:20:05 +00003390 last_status_cksum = 0; // force status update
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003391 } else if (sscanf(p, "%d", &j) > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003392 dot = find_line(j); // go to line # j
3393 dot_skip_over_ws();
Denys Vlasenkob22bbff2009-07-04 16:50:43 +02003394 } else { // unrecognized cmd
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003395 not_implemented(p);
Eric Andersen3f980402001-04-04 17:31:15 +00003396 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003397#endif /* !FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003398 break;
3399 case '<': // <- Left shift something
3400 case '>': // >- Right shift something
3401 cnt = count_lines(text, dot); // remember what line we are on
3402 c1 = get_one_char(); // get the type of thing to delete
3403 find_range(&p, &q, c1);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003404 yank_delete(p, q, 1, YANKONLY); // save copy before change
Eric Andersen3f980402001-04-04 17:31:15 +00003405 p = begin_line(p);
3406 q = end_line(q);
3407 i = count_lines(p, q); // # of lines we are shifting
3408 for ( ; i > 0; i--, p = next_line(p)) {
3409 if (c == '<') {
3410 // shift left- remove tab or 8 spaces
3411 if (*p == '\t') {
3412 // shrink buffer 1 char
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003413 text_hole_delete(p, p);
Eric Andersen3f980402001-04-04 17:31:15 +00003414 } else if (*p == ' ') {
3415 // we should be calculating columns, not just SPACE
3416 for (j = 0; *p == ' ' && j < tabstop; j++) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003417 text_hole_delete(p, p);
Eric Andersen3f980402001-04-04 17:31:15 +00003418 }
3419 }
3420 } else if (c == '>') {
3421 // shift right -- add tab or 8 spaces
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003422 char_insert(p, '\t');
Eric Andersen3f980402001-04-04 17:31:15 +00003423 }
3424 }
3425 dot = find_line(cnt); // what line were we on
3426 dot_skip_over_ws();
3427 end_cmd_q(); // stop adding to q
3428 break;
3429 case 'A': // A- append at e-o-l
3430 dot_end(); // go to e-o-l
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +00003431 //**** fall through to ... 'a'
Eric Andersen3f980402001-04-04 17:31:15 +00003432 case 'a': // a- append after current char
3433 if (*dot != '\n')
3434 dot++;
3435 goto dc_i;
3436 break;
3437 case 'B': // B- back a blank-delimited Word
3438 case 'E': // E- end of a blank-delimited word
3439 case 'W': // W- forward a blank-delimited word
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003440 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003441 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003442 }
Eric Andersen3f980402001-04-04 17:31:15 +00003443 dir = FORWARD;
3444 if (c == 'B')
3445 dir = BACK;
3446 if (c == 'W' || isspace(dot[dir])) {
3447 dot = skip_thing(dot, 1, dir, S_TO_WS);
3448 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3449 }
3450 if (c != 'W')
3451 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3452 break;
3453 case 'C': // C- Change to e-o-l
3454 case 'D': // D- delete to e-o-l
3455 save_dot = dot;
3456 dot = dollar_line(dot); // move to before NL
3457 // copy text into a register and delete
3458 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3459 if (c == 'C')
3460 goto dc_i; // start inserting
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003461#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003462 if (c == 'D')
3463 end_cmd_q(); // stop adding to q
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003464#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003465 break;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003466 case 'g': // 'gg' goto a line number (vim) (default: very first line)
Paul Foxb5ee8db2008-02-14 01:17:01 +00003467 c1 = get_one_char();
3468 if (c1 != 'g') {
3469 buf[0] = 'g';
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003470 buf[1] = c1; // TODO: if Unicode?
Paul Foxb5ee8db2008-02-14 01:17:01 +00003471 buf[2] = '\0';
3472 not_implemented(buf);
3473 break;
3474 }
3475 if (cmdcnt == 0)
3476 cmdcnt = 1;
3477 /* fall through */
Eric Andersen822c3832001-05-07 17:37:43 +00003478 case 'G': // G- goto to a line number (default= E-O-F)
3479 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003480 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003481 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003482 }
3483 dot_skip_over_ws();
3484 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003485 case 'H': // H- goto top line on screen
3486 dot = screenbegin;
3487 if (cmdcnt > (rows - 1)) {
3488 cmdcnt = (rows - 1);
3489 }
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003490 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003491 do_cmd('+');
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003492 }
Eric Andersen3f980402001-04-04 17:31:15 +00003493 dot_skip_over_ws();
3494 break;
3495 case 'I': // I- insert before first non-blank
3496 dot_begin(); // 0
3497 dot_skip_over_ws();
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +00003498 //**** fall through to ... 'i'
Eric Andersen3f980402001-04-04 17:31:15 +00003499 case 'i': // i- insert before current char
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003500 case KEYCODE_INSERT: // Cursor Key Insert
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003501 dc_i:
Eric Andersen3f980402001-04-04 17:31:15 +00003502 cmd_mode = 1; // start insrting
Eric Andersen3f980402001-04-04 17:31:15 +00003503 break;
3504 case 'J': // J- join current and next lines together
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003505 if (--cmdcnt > 1) {
Eric Andersen3f980402001-04-04 17:31:15 +00003506 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003507 }
Eric Andersen3f980402001-04-04 17:31:15 +00003508 dot_end(); // move to NL
3509 if (dot < end - 1) { // make sure not last char in text[]
3510 *dot++ = ' '; // replace NL with space
Paul Fox8552aec2005-09-16 12:20:05 +00003511 file_modified++;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003512 while (isblank(*dot)) { // delete leading WS
Eric Andersen3f980402001-04-04 17:31:15 +00003513 dot_delete();
3514 }
3515 }
3516 end_cmd_q(); // stop adding to q
3517 break;
3518 case 'L': // L- goto bottom line on screen
3519 dot = end_screen();
3520 if (cmdcnt > (rows - 1)) {
3521 cmdcnt = (rows - 1);
3522 }
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003523 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003524 do_cmd('-');
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003525 }
Eric Andersen3f980402001-04-04 17:31:15 +00003526 dot_begin();
3527 dot_skip_over_ws();
3528 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003529 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003530 dot = screenbegin;
3531 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3532 dot = next_line(dot);
3533 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003534 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003535 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003536 p = begin_line(dot);
3537 if (p[-1] == '\n') {
3538 dot_prev();
3539 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3540 dot_end();
3541 dot = char_insert(dot, '\n');
3542 } else {
3543 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003544 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003545 dot_prev(); // -
3546 }
3547 goto dc_i;
3548 break;
3549 case 'R': // R- continuous Replace char
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003550 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003551 cmd_mode = 2;
Eric Andersen3f980402001-04-04 17:31:15 +00003552 break;
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003553 case KEYCODE_DELETE:
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003554 c = 'x';
3555 // fall through
Eric Andersen3f980402001-04-04 17:31:15 +00003556 case 'X': // X- delete char before dot
3557 case 'x': // x- delete the current char
3558 case 's': // s- substitute the current char
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003559 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003560 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003561 }
Eric Andersen3f980402001-04-04 17:31:15 +00003562 dir = 0;
3563 if (c == 'X')
3564 dir = -1;
3565 if (dot[dir] != '\n') {
3566 if (c == 'X')
3567 dot--; // delete prev char
3568 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3569 }
3570 if (c == 's')
3571 goto dc_i; // start insrting
3572 end_cmd_q(); // stop adding to q
3573 break;
3574 case 'Z': // Z- if modified, {write}; exit
3575 // ZZ means to save file (if necessary), then exit
3576 c1 = get_one_char();
3577 if (c1 != 'Z') {
3578 indicate_error(c);
3579 break;
3580 }
Paul Foxf0305b72006-03-28 14:18:21 +00003581 if (file_modified) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003582 if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003583 status_line_bold("\"%s\" File is read only", current_filename);
Denis Vlasenko92758142006-10-03 19:56:34 +00003584 break;
Paul Foxf0305b72006-03-28 14:18:21 +00003585 }
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003586 cnt = file_write(current_filename, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003587 if (cnt < 0) {
3588 if (cnt == -1)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003589 status_line_bold("Write error: %s", strerror(errno));
Paul Fox61e45db2005-10-09 14:43:22 +00003590 } else if (cnt == (end - 1 - text + 1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003591 editing = 0;
3592 }
3593 } else {
3594 editing = 0;
3595 }
3596 break;
3597 case '^': // ^- move to first non-blank on line
3598 dot_begin();
3599 dot_skip_over_ws();
3600 break;
3601 case 'b': // b- back a word
3602 case 'e': // e- end of word
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003603 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003604 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003605 }
Eric Andersen3f980402001-04-04 17:31:15 +00003606 dir = FORWARD;
3607 if (c == 'b')
3608 dir = BACK;
3609 if ((dot + dir) < text || (dot + dir) > end - 1)
3610 break;
3611 dot += dir;
3612 if (isspace(*dot)) {
3613 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3614 }
3615 if (isalnum(*dot) || *dot == '_') {
3616 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3617 } else if (ispunct(*dot)) {
3618 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3619 }
3620 break;
3621 case 'c': // c- change something
3622 case 'd': // d- delete something
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003623#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003624 case 'y': // y- yank something
3625 case 'Y': // Y- Yank a line
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003626#endif
Denis Vlasenkod44c1532008-10-14 12:59:42 +00003627 {
Paul Foxc51fc7b2008-03-06 01:34:23 +00003628 int yf, ml, whole = 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003629 yf = YANKDEL; // assume either "c" or "d"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003630#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003631 if (c == 'y' || c == 'Y')
3632 yf = YANKONLY;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003633#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003634 c1 = 'y';
3635 if (c != 'Y')
3636 c1 = get_one_char(); // get the type of thing to delete
Paul Foxc51fc7b2008-03-06 01:34:23 +00003637 // determine range, and whether it spans lines
3638 ml = find_range(&p, &q, c1);
Eric Andersen3f980402001-04-04 17:31:15 +00003639 if (c1 == 27) { // ESC- user changed mind and wants out
3640 c = c1 = 27; // Escape- do nothing
3641 } else if (strchr("wW", c1)) {
3642 if (c == 'c') {
3643 // don't include trailing WS as part of word
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003644 while (isblank(*q)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003645 if (q <= text || q[-1] == '\n')
3646 break;
3647 q--;
3648 }
3649 }
Paul Foxc51fc7b2008-03-06 01:34:23 +00003650 dot = yank_delete(p, q, ml, yf); // delete word
3651 } else if (strchr("^0bBeEft%$ lh\b\177", c1)) {
3652 // partial line copy text into a register and delete
3653 dot = yank_delete(p, q, ml, yf); // delete word
3654 } else if (strchr("cdykjHL+-{}\r\n", c1)) {
3655 // whole line copy text into a register and delete
3656 dot = yank_delete(p, q, ml, yf); // delete lines
3657 whole = 1;
3658 } else {
3659 // could not recognize object
3660 c = c1 = 27; // error-
3661 ml = 0;
3662 indicate_error(c);
3663 }
3664 if (ml && whole) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003665 if (c == 'c') {
3666 dot = char_insert(dot, '\n');
3667 // on the last line of file don't move to prev line
Paul Foxc51fc7b2008-03-06 01:34:23 +00003668 if (whole && dot != (end-1)) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003669 dot_prev();
3670 }
3671 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003672 dot_begin();
3673 dot_skip_over_ws();
3674 }
Eric Andersen3f980402001-04-04 17:31:15 +00003675 }
3676 if (c1 != 27) {
3677 // if CHANGING, not deleting, start inserting after the delete
3678 if (c == 'c') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003679 strcpy(buf, "Change");
Eric Andersen3f980402001-04-04 17:31:15 +00003680 goto dc_i; // start inserting
3681 }
3682 if (c == 'd') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003683 strcpy(buf, "Delete");
Eric Andersen3f980402001-04-04 17:31:15 +00003684 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003685#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003686 if (c == 'y' || c == 'Y') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003687 strcpy(buf, "Yank");
Eric Andersen3f980402001-04-04 17:31:15 +00003688 }
3689 p = reg[YDreg];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003690 q = p + strlen(p);
Eric Andersen3f980402001-04-04 17:31:15 +00003691 for (cnt = 0; p <= q; p++) {
3692 if (*p == '\n')
3693 cnt++;
3694 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003695 status_line("%s %d lines (%d chars) using [%c]",
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003696 buf, cnt, strlen(reg[YDreg]), what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003697#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003698 end_cmd_q(); // stop adding to q
3699 }
3700 break;
Denis Vlasenkod44c1532008-10-14 12:59:42 +00003701 }
Eric Andersen3f980402001-04-04 17:31:15 +00003702 case 'k': // k- goto prev line, same col
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003703 case KEYCODE_UP: // cursor key Up
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003704 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003705 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003706 }
Eric Andersen3f980402001-04-04 17:31:15 +00003707 dot_prev();
3708 dot = move_to_col(dot, ccol + offset); // try stay in same col
3709 break;
3710 case 'r': // r- replace the current char with user input
3711 c1 = get_one_char(); // get the replacement char
3712 if (*dot != '\n') {
3713 *dot = c1;
Denis Vlasenkob1759462008-06-20 20:20:54 +00003714 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003715 }
3716 end_cmd_q(); // stop adding to q
3717 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003718 case 't': // t- move to char prior to next x
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00003719 last_forward_char = get_one_char();
3720 do_cmd(';');
3721 if (*dot == last_forward_char)
3722 dot_left();
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003723 last_forward_char = 0;
Eric Andersen822c3832001-05-07 17:37:43 +00003724 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003725 case 'w': // w- forward a word
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003726 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003727 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003728 }
Eric Andersen3f980402001-04-04 17:31:15 +00003729 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3730 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3731 } else if (ispunct(*dot)) { // we are on PUNCT
3732 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3733 }
3734 if (dot < end - 1)
3735 dot++; // move over word
3736 if (isspace(*dot)) {
3737 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3738 }
3739 break;
3740 case 'z': // z-
3741 c1 = get_one_char(); // get the replacement char
3742 cnt = 0;
3743 if (c1 == '.')
3744 cnt = (rows - 2) / 2; // put dot at center
3745 if (c1 == '-')
3746 cnt = rows - 2; // put dot at bottom
3747 screenbegin = begin_line(dot); // start dot at top
3748 dot_scroll(cnt, -1);
3749 break;
3750 case '|': // |- move to column "cmdcnt"
3751 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3752 break;
3753 case '~': // ~- flip the case of letters a-z -> A-Z
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003754 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003755 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003756 }
Eric Andersen3f980402001-04-04 17:31:15 +00003757 if (islower(*dot)) {
3758 *dot = toupper(*dot);
Denis Vlasenkob1759462008-06-20 20:20:54 +00003759 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003760 } else if (isupper(*dot)) {
3761 *dot = tolower(*dot);
Denis Vlasenkob1759462008-06-20 20:20:54 +00003762 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003763 }
3764 dot_right();
3765 end_cmd_q(); // stop adding to q
3766 break;
3767 //----- The Cursor and Function Keys -----------------------------
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003768 case KEYCODE_HOME: // Cursor Key Home
Eric Andersen3f980402001-04-04 17:31:15 +00003769 dot_begin();
3770 break;
3771 // The Fn keys could point to do_macro which could translate them
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003772#if 0
3773 case KEYCODE_FUN1: // Function Key F1
3774 case KEYCODE_FUN2: // Function Key F2
3775 case KEYCODE_FUN3: // Function Key F3
3776 case KEYCODE_FUN4: // Function Key F4
3777 case KEYCODE_FUN5: // Function Key F5
3778 case KEYCODE_FUN6: // Function Key F6
3779 case KEYCODE_FUN7: // Function Key F7
3780 case KEYCODE_FUN8: // Function Key F8
3781 case KEYCODE_FUN9: // Function Key F9
3782 case KEYCODE_FUN10: // Function Key F10
3783 case KEYCODE_FUN11: // Function Key F11
3784 case KEYCODE_FUN12: // Function Key F12
Eric Andersen3f980402001-04-04 17:31:15 +00003785 break;
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003786#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003787 }
3788
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003789 dc1:
Eric Andersen3f980402001-04-04 17:31:15 +00003790 // if text[] just became empty, add back an empty line
3791 if (end == text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003792 char_insert(text, '\n'); // start empty buf with dummy line
Eric Andersen3f980402001-04-04 17:31:15 +00003793 dot = text;
3794 }
3795 // it is OK for dot to exactly equal to end, otherwise check dot validity
3796 if (dot != end) {
3797 dot = bound_dot(dot); // make sure "dot" is valid
3798 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003799#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003800 check_context(c); // update the current context
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003801#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003802
3803 if (!isdigit(c))
3804 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3805 cnt = dot - begin_line(dot);
3806 // Try to stay off of the Newline
3807 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3808 dot--;
3809}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003810
Paul Fox35e9c5d2008-03-06 16:26:12 +00003811/* NB! the CRASHME code is unmaintained, and doesn't currently build */
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003812#if ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003813static int totalcmds = 0;
3814static int Mp = 85; // Movement command Probability
3815static int Np = 90; // Non-movement command Probability
3816static int Dp = 96; // Delete command Probability
3817static int Ip = 97; // Insert command Probability
3818static int Yp = 98; // Yank command Probability
3819static int Pp = 99; // Put command Probability
3820static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003821static const char chars[20] = "\t012345 abcdABCD-=.$";
3822static const char *const words[20] = {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003823 "this", "is", "a", "test",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003824 "broadcast", "the", "emergency", "of",
3825 "system", "quick", "brown", "fox",
3826 "jumped", "over", "lazy", "dogs",
3827 "back", "January", "Febuary", "March"
3828};
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003829static const char *const lines[20] = {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003830 "You should have received a copy of the GNU General Public License\n",
3831 "char c, cm, *cmd, *cmd1;\n",
3832 "generate a command by percentages\n",
3833 "Numbers may be typed as a prefix to some commands.\n",
3834 "Quit, discarding changes!\n",
3835 "Forced write, if permission originally not valid.\n",
3836 "In general, any ex or ed command (such as substitute or delete).\n",
3837 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3838 "Please get w/ me and I will go over it with you.\n",
3839 "The following is a list of scheduled, committed changes.\n",
3840 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3841 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3842 "Any question about transactions please contact Sterling Huxley.\n",
3843 "I will try to get back to you by Friday, December 31.\n",
3844 "This Change will be implemented on Friday.\n",
3845 "Let me know if you have problems accessing this;\n",
3846 "Sterling Huxley recently added you to the access list.\n",
3847 "Would you like to go to lunch?\n",
3848 "The last command will be automatically run.\n",
3849 "This is too much english for a computer geek.\n",
3850};
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003851static char *multilines[20] = {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003852 "You should have received a copy of the GNU General Public License\n",
3853 "char c, cm, *cmd, *cmd1;\n",
3854 "generate a command by percentages\n",
3855 "Numbers may be typed as a prefix to some commands.\n",
3856 "Quit, discarding changes!\n",
3857 "Forced write, if permission originally not valid.\n",
3858 "In general, any ex or ed command (such as substitute or delete).\n",
3859 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3860 "Please get w/ me and I will go over it with you.\n",
3861 "The following is a list of scheduled, committed changes.\n",
3862 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3863 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3864 "Any question about transactions please contact Sterling Huxley.\n",
3865 "I will try to get back to you by Friday, December 31.\n",
3866 "This Change will be implemented on Friday.\n",
3867 "Let me know if you have problems accessing this;\n",
3868 "Sterling Huxley recently added you to the access list.\n",
3869 "Would you like to go to lunch?\n",
3870 "The last command will be automatically run.\n",
3871 "This is too much english for a computer geek.\n",
3872};
3873
3874// create a random command to execute
3875static void crash_dummy()
3876{
3877 static int sleeptime; // how long to pause between commands
3878 char c, cm, *cmd, *cmd1;
3879 int i, cnt, thing, rbi, startrbi, percent;
3880
3881 // "dot" movement commands
3882 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3883
3884 // is there already a command running?
Denys Vlasenko020f4062009-05-17 16:44:54 +02003885 if (readbuffer[0] > 0)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003886 goto cd1;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003887 cd0:
Denys Vlasenko020f4062009-05-17 16:44:54 +02003888 readbuffer[0] = 'X';
3889 startrbi = rbi = 1;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003890 sleeptime = 0; // how long to pause between commands
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003891 memset(readbuffer, '\0', sizeof(readbuffer));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003892 // generate a command by percentages
3893 percent = (int) lrand48() % 100; // get a number from 0-99
3894 if (percent < Mp) { // Movement commands
3895 // available commands
3896 cmd = cmd1;
3897 M++;
3898 } else if (percent < Np) { // non-movement commands
3899 cmd = "mz<>\'\""; // available commands
3900 N++;
3901 } else if (percent < Dp) { // Delete commands
3902 cmd = "dx"; // available commands
3903 D++;
3904 } else if (percent < Ip) { // Inset commands
3905 cmd = "iIaAsrJ"; // available commands
3906 I++;
3907 } else if (percent < Yp) { // Yank commands
3908 cmd = "yY"; // available commands
3909 Y++;
3910 } else if (percent < Pp) { // Put commands
3911 cmd = "pP"; // available commands
3912 P++;
3913 } else {
3914 // We do not know how to handle this command, try again
3915 U++;
3916 goto cd0;
3917 }
3918 // randomly pick one of the available cmds from "cmd[]"
3919 i = (int) lrand48() % strlen(cmd);
3920 cm = cmd[i];
3921 if (strchr(":\024", cm))
3922 goto cd0; // dont allow colon or ctrl-T commands
3923 readbuffer[rbi++] = cm; // put cmd into input buffer
3924
3925 // now we have the command-
3926 // there are 1, 2, and multi char commands
3927 // find out which and generate the rest of command as necessary
3928 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3929 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3930 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3931 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3932 }
3933 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3934 c = cmd1[thing];
3935 readbuffer[rbi++] = c; // add movement to input buffer
3936 }
3937 if (strchr("iIaAsc", cm)) { // multi-char commands
3938 if (cm == 'c') {
3939 // change some thing
3940 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3941 c = cmd1[thing];
3942 readbuffer[rbi++] = c; // add movement to input buffer
3943 }
3944 thing = (int) lrand48() % 4; // what thing to insert
3945 cnt = (int) lrand48() % 10; // how many to insert
3946 for (i = 0; i < cnt; i++) {
3947 if (thing == 0) { // insert chars
3948 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3949 } else if (thing == 1) { // insert words
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003950 strcat(readbuffer, words[(int) lrand48() % 20]);
3951 strcat(readbuffer, " ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003952 sleeptime = 0; // how fast to type
3953 } else if (thing == 2) { // insert lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003954 strcat(readbuffer, lines[(int) lrand48() % 20]);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003955 sleeptime = 0; // how fast to type
3956 } else { // insert multi-lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003957 strcat(readbuffer, multilines[(int) lrand48() % 20]);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003958 sleeptime = 0; // how fast to type
3959 }
3960 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003961 strcat(readbuffer, "\033");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003962 }
Denys Vlasenko020f4062009-05-17 16:44:54 +02003963 readbuffer[0] = strlen(readbuffer + 1);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003964 cd1:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003965 totalcmds++;
3966 if (sleeptime > 0)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003967 mysleep(sleeptime); // sleep 1/100 sec
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003968}
3969
3970// test to see if there are any errors
3971static void crash_test()
3972{
3973 static time_t oldtim;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003974
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003975 time_t tim;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003976 char d[2], msg[80];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003977
3978 msg[0] = '\0';
3979 if (end < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003980 strcat(msg, "end<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003981 }
3982 if (end > textend) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003983 strcat(msg, "end>textend ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003984 }
3985 if (dot < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003986 strcat(msg, "dot<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003987 }
3988 if (dot > end) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003989 strcat(msg, "dot>end ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003990 }
3991 if (screenbegin < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003992 strcat(msg, "screenbegin<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003993 }
3994 if (screenbegin > end - 1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003995 strcat(msg, "screenbegin>end-1 ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003996 }
3997
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003998 if (msg[0]) {
Glenn L McGrath7127b582002-12-03 21:48:15 +00003999 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004000 totalcmds, last_input_char, msg, SOs, SOn);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01004001 fflush_all();
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00004002 while (safe_read(STDIN_FILENO, d, 1) > 0) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004003 if (d[0] == '\n' || d[0] == '\r')
4004 break;
4005 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004006 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004007 tim = time(NULL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004008 if (tim >= (oldtim + 3)) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004009 sprintf(status_buffer,
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004010 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
4011 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
4012 oldtim = tim;
4013 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004014}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004015#endif