blob: 3f4ccdcddfb548ed84408b17b8edc5d978502c59 [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
Walter Harmsb9ba5802011-06-27 02:59:37 +020024//config:config VI
25//config: bool "vi"
26//config: default y
27//config: help
28//config: 'vi' is a text editor. More specifically, it is the One True
29//config: text editor <grin>. It does, however, have a rather steep
30//config: learning curve. If you are not already comfortable with 'vi'
31//config: you may wish to use something else.
32//config:
33//config:config FEATURE_VI_MAX_LEN
34//config: int "Maximum screen width in vi"
35//config: range 256 16384
36//config: default 4096
37//config: depends on VI
38//config: help
39//config: Contrary to what you may think, this is not eating much.
40//config: Make it smaller than 4k only if you are very limited on memory.
41//config:
42//config:config FEATURE_VI_8BIT
43//config: bool "Allow vi to display 8-bit chars (otherwise shows dots)"
44//config: default n
45//config: depends on VI
46//config: help
47//config: If your terminal can display characters with high bit set,
48//config: you may want to enable this. Note: vi is not Unicode-capable.
49//config: If your terminal combines several 8-bit bytes into one character
50//config: (as in Unicode mode), this will not work properly.
51//config:
52//config:config FEATURE_VI_COLON
53//config: bool "Enable \":\" colon commands (no \"ex\" mode)"
54//config: default y
55//config: depends on VI
56//config: help
57//config: Enable a limited set of colon commands for vi. This does not
58//config: provide an "ex" mode.
59//config:
60//config:config FEATURE_VI_YANKMARK
61//config: bool "Enable yank/put commands and mark cmds"
62//config: default y
63//config: depends on VI
64//config: help
65//config: This will enable you to use yank and put, as well as mark in
66//config: busybox vi.
67//config:
68//config:config FEATURE_VI_SEARCH
69//config: bool "Enable search and replace cmds"
70//config: default y
71//config: depends on VI
72//config: help
73//config: Select this if you wish to be able to do search and replace in
74//config: busybox vi.
75//config:
76//config:config FEATURE_VI_REGEX_SEARCH
77//config: bool "Enable regex in search and replace"
78//config: default n # Uses GNU regex, which may be unavailable. FIXME
79//config: depends on FEATURE_VI_SEARCH
80//config: help
81//config: Use extended regex search.
82//config:
83//config:config FEATURE_VI_USE_SIGNALS
84//config: bool "Catch signals"
85//config: default y
86//config: depends on VI
87//config: help
88//config: Selecting this option will make busybox vi signal aware. This will
89//config: make busybox vi support SIGWINCH to deal with Window Changes, catch
90//config: Ctrl-Z and Ctrl-C and alarms.
91//config:
92//config:config FEATURE_VI_DOT_CMD
93//config: bool "Remember previous cmd and \".\" cmd"
94//config: default y
95//config: depends on VI
96//config: help
97//config: Make busybox vi remember the last command and be able to repeat it.
98//config:
99//config:config FEATURE_VI_READONLY
100//config: bool "Enable -R option and \"view\" mode"
101//config: default y
102//config: depends on VI
103//config: help
104//config: Enable the read-only command line option, which allows the user to
105//config: open a file in read-only mode.
106//config:
107//config:config FEATURE_VI_SETOPTS
108//config: bool "Enable set-able options, ai ic showmatch"
109//config: default y
110//config: depends on VI
111//config: help
112//config: Enable the editor to set some (ai, ic, showmatch) options.
113//config:
114//config:config FEATURE_VI_SET
115//config: bool "Support for :set"
116//config: default y
117//config: depends on VI
118//config: help
119//config: Support for ":set".
120//config:
121//config:config FEATURE_VI_WIN_RESIZE
122//config: bool "Handle window resize"
123//config: default y
124//config: depends on VI
125//config: help
126//config: Make busybox vi behave nicely with terminals that get resized.
127//config:
128//config:config FEATURE_VI_ASK_TERMINAL
129//config: bool "Use 'tell me cursor position' ESC sequence to measure window"
130//config: default y
131//config: depends on VI
132//config: help
133//config: If terminal size can't be retrieved and $LINES/$COLUMNS are not set,
134//config: this option makes vi perform a last-ditch effort to find it:
135//config: vi positions cursor to 999,999 and asks terminal to report real
136//config: cursor position using "ESC [ 6 n" escape sequence, then reads stdin.
137//config:
138//config: This is not clean but helps a lot on serial lines and such.
139//config:
140//config:config FEATURE_VI_OPTIMIZE_CURSOR
141//config: bool "Optimize cursor movement"
142//config: default y
143//config: depends on VI
144//config: help
145//config: This will make the cursor movement faster, but requires more memory
146//config: and it makes the applet a tiny bit larger.
147
148//applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP))
149
150//kbuild:lib-$(CONFIG_VI) += vi.o
151
Pere Orga6a3e01d2011-04-01 22:56:30 +0200152//usage:#define vi_trivial_usage
153//usage: "[OPTIONS] [FILE]..."
154//usage:#define vi_full_usage "\n\n"
155//usage: "Edit FILE\n"
Pere Orga6a3e01d2011-04-01 22:56:30 +0200156//usage: IF_FEATURE_VI_COLON(
157//usage: "\n -c Initial command to run ($EXINIT also available)"
158//usage: )
159//usage: IF_FEATURE_VI_READONLY(
160//usage: "\n -R Read-only"
161//usage: )
162//usage: "\n -H Short help regarding available features"
163
Walter Harmsb9ba5802011-06-27 02:59:37 +0200164#include <regex.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000165#include "libbb.h"
Eric Andersen3f980402001-04-04 17:31:15 +0000166
Paul Fox35e9c5d2008-03-06 16:26:12 +0000167/* the CRASHME code is unmaintained, and doesn't currently build */
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000168#define ENABLE_FEATURE_VI_CRASHME 0
169
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +0000170
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000171#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +0000172
173#if ENABLE_FEATURE_VI_8BIT
Denys Vlasenkoc2704542009-11-20 19:14:19 +0100174//FIXME: this does not work properly for Unicode anyway
175# define Isprint(c) (isprint)(c)
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000176#else
Denys Vlasenkoc2704542009-11-20 19:14:19 +0100177# define Isprint(c) isprint_asciionly(c)
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000178#endif
179
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +0000180#else
181
182/* 0x9b is Meta-ESC */
183#if ENABLE_FEATURE_VI_8BIT
184#define Isprint(c) ((unsigned char)(c) >= ' ' && (c) != 0x7f && (unsigned char)(c) != 0x9b)
185#else
186#define Isprint(c) ((unsigned char)(c) >= ' ' && (unsigned char)(c) < 0x7f)
187#endif
188
189#endif
190
191
Denis Vlasenkoe8a07882007-06-10 15:08:44 +0000192enum {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +0000193 MAX_TABSTOP = 32, // sanity limit
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000194 // User input len. Need not be extra big.
195 // Lines in file being edited *can* be bigger than this.
196 MAX_INPUT_LEN = 128,
197 // Sanity limits. We have only one buffer of this size.
198 MAX_SCR_COLS = CONFIG_FEATURE_VI_MAX_LEN,
199 MAX_SCR_ROWS = CONFIG_FEATURE_VI_MAX_LEN,
Denis Vlasenkoe8a07882007-06-10 15:08:44 +0000200};
Eric Andersen3f980402001-04-04 17:31:15 +0000201
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000202/* vt102 typical ESC sequence */
203/* terminal standout start/normal ESC sequence */
Denys Vlasenkod9a3e892010-05-16 23:42:13 +0200204#define SOs "\033[7m"
205#define SOn "\033[0m"
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000206/* terminal bell sequence */
Denys Vlasenkod9a3e892010-05-16 23:42:13 +0200207#define bell "\007"
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000208/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
Denys Vlasenkod9a3e892010-05-16 23:42:13 +0200209#define Ceol "\033[K"
210#define Ceos "\033[J"
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000211/* Cursor motion arbitrary destination ESC sequence */
Denys Vlasenkod9a3e892010-05-16 23:42:13 +0200212#define CMrc "\033[%u;%uH"
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000213/* Cursor motion up and down ESC sequence */
Denys Vlasenkod9a3e892010-05-16 23:42:13 +0200214#define CMup "\033[A"
215#define CMdown "\n"
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000216
Denis Vlasenkoded6ad32008-10-14 12:26:30 +0000217#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
218// cmds modifying text[]
219// vda: removed "aAiIs" as they switch us into insert mode
220// and remembering input for replay after them makes no sense
221static const char modifying_cmds[] = "cCdDJoOpPrRxX<>~";
222#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000223
Rob Landleybc68cd12006-03-10 19:22:06 +0000224enum {
225 YANKONLY = FALSE,
226 YANKDEL = TRUE,
227 FORWARD = 1, // code depends on "1" for array index
228 BACK = -1, // code depends on "-1" for array index
229 LIMITED = 0, // how much of text[] in char_search
230 FULL = 1, // how much of text[] in char_search
Eric Andersen3f980402001-04-04 17:31:15 +0000231
Rob Landleybc68cd12006-03-10 19:22:06 +0000232 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
233 S_TO_WS = 2, // used in skip_thing() for moving "dot"
234 S_OVER_WS = 3, // used in skip_thing() for moving "dot"
235 S_END_PUNCT = 4, // used in skip_thing() for moving "dot"
Denis Vlasenko8e858e22007-03-07 09:35:43 +0000236 S_END_ALNUM = 5, // used in skip_thing() for moving "dot"
Rob Landleybc68cd12006-03-10 19:22:06 +0000237};
Eric Andersen3f980402001-04-04 17:31:15 +0000238
Denis Vlasenkob1759462008-06-20 20:20:54 +0000239
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000240/* vi.c expects chars to be unsigned. */
241/* busybox build system provides that, but it's better */
242/* to audit and fix the source */
Eric Andersen3f980402001-04-04 17:31:15 +0000243
Denis Vlasenkob1759462008-06-20 20:20:54 +0000244struct globals {
245 /* many references - keep near the top of globals */
246 char *text, *end; // pointers to the user data in memory
247 char *dot; // where all the action takes place
248 int text_size; // size of the allocated buffer
249
250 /* the rest */
251 smallint vi_setops;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000252#define VI_AUTOINDENT 1
253#define VI_SHOWMATCH 2
254#define VI_IGNORECASE 4
255#define VI_ERR_METHOD 8
256#define autoindent (vi_setops & VI_AUTOINDENT)
257#define showmatch (vi_setops & VI_SHOWMATCH )
258#define ignorecase (vi_setops & VI_IGNORECASE)
259/* indicate error with beep or flash */
260#define err_method (vi_setops & VI_ERR_METHOD)
261
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000262#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkob1759462008-06-20 20:20:54 +0000263 smallint readonly_mode;
Denis Vlasenko6a2f7f42007-08-16 10:35:17 +0000264#define SET_READONLY_FILE(flags) ((flags) |= 0x01)
265#define SET_READONLY_MODE(flags) ((flags) |= 0x02)
266#define UNSET_READONLY_FILE(flags) ((flags) &= 0xfe)
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000267#else
Denis Vlasenkob1759462008-06-20 20:20:54 +0000268#define SET_READONLY_FILE(flags) ((void)0)
269#define SET_READONLY_MODE(flags) ((void)0)
270#define UNSET_READONLY_FILE(flags) ((void)0)
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000271#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000272
Denis Vlasenkob1759462008-06-20 20:20:54 +0000273 smallint editing; // >0 while we are editing a file
Denis Vlasenko30cfdf92008-09-21 15:29:29 +0000274 // [code audit says "can be 0, 1 or 2 only"]
Denis Vlasenkob1759462008-06-20 20:20:54 +0000275 smallint cmd_mode; // 0=command 1=insert 2=replace
276 int file_modified; // buffer contents changed (counter, not flag!)
Denis Vlasenko30cfdf92008-09-21 15:29:29 +0000277 int last_file_modified; // = -1;
Denis Vlasenkob1759462008-06-20 20:20:54 +0000278 int fn_start; // index of first cmd line file name
279 int save_argc; // how many file names on cmd line
280 int cmdcnt; // repetition count
281 unsigned rows, columns; // the terminal screen is this size
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700282#if ENABLE_FEATURE_VI_ASK_TERMINAL
283 int get_rowcol_error;
284#endif
Denis Vlasenkob1759462008-06-20 20:20:54 +0000285 int crow, ccol; // cursor is on Crow x Ccol
286 int offset; // chars scrolled off the screen to the left
287 int have_status_msg; // is default edit status needed?
288 // [don't make smallint!]
289 int last_status_cksum; // hash of current status line
290 char *current_filename;
291 char *screenbegin; // index into text[], of top line on the screen
292 char *screen; // pointer to the virtual screen buffer
293 int screensize; // and its size
294 int tabstop;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000295 int last_forward_char; // last char searched for with 'f' (int because of Unicode)
Denis Vlasenkob1759462008-06-20 20:20:54 +0000296 char erase_char; // the users erase character
297 char last_input_char; // last char read from user
Denis Vlasenkob1759462008-06-20 20:20:54 +0000298
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000299#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkob1759462008-06-20 20:20:54 +0000300 smallint adding2q; // are we currently adding user input to q
301 int lmc_len; // length of last_modifying_cmd
302 char *ioq, *ioq_start; // pointer to string for get_one_char to "read"
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000303#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000304#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Denis Vlasenkob1759462008-06-20 20:20:54 +0000305 int last_row; // where the cursor was last moved to
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000306#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000307#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenkob1759462008-06-20 20:20:54 +0000308 int my_pid;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000309#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000310#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkob1759462008-06-20 20:20:54 +0000311 char *last_search_pattern; // last pattern from a '/' or '?' search
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000312#endif
Denis Vlasenko0112ff52008-10-25 23:23:00 +0000313
Denis Vlasenkob1759462008-06-20 20:20:54 +0000314 /* former statics */
315#if ENABLE_FEATURE_VI_YANKMARK
316 char *edit_file__cur_line;
317#endif
318 int refresh__old_offset;
319 int format_edit_status__tot;
Eric Andersen3f980402001-04-04 17:31:15 +0000320
Denis Vlasenkob1759462008-06-20 20:20:54 +0000321 /* a few references only */
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000322#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000323 int YDreg, Ureg; // default delete register and orig line for "U"
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000324 char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000325 char *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
326 char *context_start, *context_end;
327#endif
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000328#if ENABLE_FEATURE_VI_USE_SIGNALS
Denis Vlasenkob1759462008-06-20 20:20:54 +0000329 sigjmp_buf restart; // catch_sig()
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000330#endif
Denis Vlasenkob1759462008-06-20 20:20:54 +0000331 struct termios term_orig, term_vi; // remember what the cooked mode was
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000332#if ENABLE_FEATURE_VI_COLON
333 char *initial_cmds[3]; // currently 2 entries, NULL terminated
334#endif
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000335 // Should be just enough to hold a key sequence,
Denis Vlasenko25497c12008-10-14 10:25:05 +0000336 // but CRASHME mode uses it as generated command buffer too
337#if ENABLE_FEATURE_VI_CRASHME
Denis Vlasenko1dfeeeb2008-10-18 19:04:37 +0000338 char readbuffer[128];
Denis Vlasenko25497c12008-10-14 10:25:05 +0000339#else
Denis Vlasenko5f6aaf32008-10-25 23:27:29 +0000340 char readbuffer[KEYCODE_BUFFER_SIZE];
Denis Vlasenko25497c12008-10-14 10:25:05 +0000341#endif
Denis Vlasenkob1759462008-06-20 20:20:54 +0000342#define STATUS_BUFFER_LEN 200
343 char status_buffer[STATUS_BUFFER_LEN]; // messages to the user
344#if ENABLE_FEATURE_VI_DOT_CMD
345 char last_modifying_cmd[MAX_INPUT_LEN]; // last modifying cmd for "."
346#endif
347 char get_input_line__buf[MAX_INPUT_LEN]; /* former static */
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000348
349 char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2];
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000350};
351#define G (*ptr_to_globals)
352#define text (G.text )
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000353#define text_size (G.text_size )
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000354#define end (G.end )
355#define dot (G.dot )
356#define reg (G.reg )
Denis Vlasenkob1759462008-06-20 20:20:54 +0000357
358#define vi_setops (G.vi_setops )
359#define editing (G.editing )
360#define cmd_mode (G.cmd_mode )
361#define file_modified (G.file_modified )
362#define last_file_modified (G.last_file_modified )
363#define fn_start (G.fn_start )
364#define save_argc (G.save_argc )
365#define cmdcnt (G.cmdcnt )
366#define rows (G.rows )
367#define columns (G.columns )
368#define crow (G.crow )
369#define ccol (G.ccol )
370#define offset (G.offset )
371#define status_buffer (G.status_buffer )
372#define have_status_msg (G.have_status_msg )
373#define last_status_cksum (G.last_status_cksum )
374#define current_filename (G.current_filename )
375#define screen (G.screen )
376#define screensize (G.screensize )
377#define screenbegin (G.screenbegin )
378#define tabstop (G.tabstop )
Denis Vlasenko0112ff52008-10-25 23:23:00 +0000379#define last_forward_char (G.last_forward_char )
Denis Vlasenkob1759462008-06-20 20:20:54 +0000380#define erase_char (G.erase_char )
381#define last_input_char (G.last_input_char )
Denis Vlasenko2a210e52008-06-22 13:20:42 +0000382#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkob1759462008-06-20 20:20:54 +0000383#define readonly_mode (G.readonly_mode )
Denis Vlasenko2a210e52008-06-22 13:20:42 +0000384#else
385#define readonly_mode 0
386#endif
Denis Vlasenkob1759462008-06-20 20:20:54 +0000387#define adding2q (G.adding2q )
388#define lmc_len (G.lmc_len )
389#define ioq (G.ioq )
390#define ioq_start (G.ioq_start )
391#define last_row (G.last_row )
392#define my_pid (G.my_pid )
Denis Vlasenkob1759462008-06-20 20:20:54 +0000393#define last_search_pattern (G.last_search_pattern)
Denis Vlasenko2a210e52008-06-22 13:20:42 +0000394
Denis Vlasenkob1759462008-06-20 20:20:54 +0000395#define edit_file__cur_line (G.edit_file__cur_line)
396#define refresh__old_offset (G.refresh__old_offset)
397#define format_edit_status__tot (G.format_edit_status__tot)
398
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000399#define YDreg (G.YDreg )
400#define Ureg (G.Ureg )
401#define mark (G.mark )
402#define context_start (G.context_start )
403#define context_end (G.context_end )
404#define restart (G.restart )
405#define term_orig (G.term_orig )
406#define term_vi (G.term_vi )
407#define initial_cmds (G.initial_cmds )
Denis Vlasenkoa96425f2007-12-09 04:13:43 +0000408#define readbuffer (G.readbuffer )
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000409#define scr_out_buf (G.scr_out_buf )
Denis Vlasenkob1759462008-06-20 20:20:54 +0000410#define last_modifying_cmd (G.last_modifying_cmd )
411#define get_input_line__buf (G.get_input_line__buf)
412
Denis Vlasenkoa96425f2007-12-09 04:13:43 +0000413#define INIT_G() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000414 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
Denis Vlasenkob1759462008-06-20 20:20:54 +0000415 last_file_modified = -1; \
Denis Vlasenko31d58e52008-10-29 13:16:28 +0000416 /* "" but has space for 2 chars: */ \
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000417 IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \
Denis Vlasenkoa96425f2007-12-09 04:13:43 +0000418} while (0)
Eric Andersen3f980402001-04-04 17:31:15 +0000419
Denis Vlasenkob1759462008-06-20 20:20:54 +0000420
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000421static int init_text_buffer(char *); // init from file or create new
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000422static void edit_file(char *); // edit one file
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000423static void do_cmd(int); // execute a command
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000424static int next_tabstop(int);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000425static void sync_cursor(char *, int *, int *); // synchronize the screen cursor to dot
426static char *begin_line(char *); // return pointer to cur line B-o-l
427static char *end_line(char *); // return pointer to cur line E-o-l
428static char *prev_line(char *); // return pointer to prev line B-o-l
429static char *next_line(char *); // return pointer to next line B-o-l
430static char *end_screen(void); // get pointer to last char on screen
431static int count_lines(char *, char *); // count line from start to stop
432static char *find_line(int); // find begining of line #li
433static char *move_to_col(char *, int); // move "p" to column l
Eric Andersen3f980402001-04-04 17:31:15 +0000434static void dot_left(void); // move dot left- dont leave line
435static void dot_right(void); // move dot right- dont leave line
436static void dot_begin(void); // move dot to B-o-l
437static void dot_end(void); // move dot to E-o-l
438static void dot_next(void); // move dot to next line B-o-l
439static void dot_prev(void); // move dot to prev line B-o-l
440static void dot_scroll(int, int); // move the screen up or down
441static void dot_skip_over_ws(void); // move dot pat WS
442static void dot_delete(void); // delete the char at 'dot'
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000443static char *bound_dot(char *); // make sure text[0] <= P < "end"
444static char *new_screen(int, int); // malloc virtual screen memory
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000445static char *char_insert(char *, char); // insert the char c at 'p'
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000446// might reallocate text[]! use p += stupid_insert(p, ...),
447// and be careful to not use pointers into potentially freed text[]!
448static uintptr_t stupid_insert(char *, char); // stupidly insert the char c at 'p'
Paul Foxc51fc7b2008-03-06 01:34:23 +0000449static int find_range(char **, char **, char); // return pointers for an object
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000450static int st_test(char *, int, int, char *); // helper for skip_thing()
451static char *skip_thing(char *, int, int, int); // skip some object
452static char *find_pair(char *, char); // find matching pair () [] {}
453static char *text_hole_delete(char *, char *); // at "p", delete a 'size' byte hole
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000454// might reallocate text[]! use p += text_hole_make(p, ...),
455// and be careful to not use pointers into potentially freed text[]!
456static uintptr_t text_hole_make(char *, int); // at "p", make a 'size' byte hole
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000457static char *yank_delete(char *, char *, int, int); // yank text[] into register then delete
Eric Andersen3f980402001-04-04 17:31:15 +0000458static void show_help(void); // display some help info
Eric Andersen3f980402001-04-04 17:31:15 +0000459static void rawmode(void); // set "raw" mode on tty
460static void cookmode(void); // return to "cooked" mode on tty
Denis Vlasenko87f3b262007-09-07 13:43:28 +0000461// sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready)
462static int mysleep(int);
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000463static int readit(void); // read (maybe cursor) key from stdin
464static int get_one_char(void); // read 1 char from stdin
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000465static int file_size(const char *); // what is the byte size of "fn"
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000466#if !ENABLE_FEATURE_VI_READONLY
467#define file_insert(fn, p, update_ro_status) file_insert(fn, p)
Denis Vlasenko59a1f302007-07-14 22:43:10 +0000468#endif
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000469// file_insert might reallocate text[]!
470static int file_insert(const char *, char *, int);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000471static int file_write(char *, char *, char *);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +0000472#if !ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
473#define place_cursor(a, b, optimize) place_cursor(a, b)
474#endif
Eric Andersen822c3832001-05-07 17:37:43 +0000475static void place_cursor(int, int, int);
Eric Andersenbff7a602001-11-17 07:15:43 +0000476static void screen_erase(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000477static void clear_to_eol(void);
478static void clear_to_eos(void);
Denis Vlasenko267e16c2008-10-14 10:34:41 +0000479static void go_bottom_and_clear_to_eol(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000480static void standout_start(void); // send "start reverse video" sequence
481static void standout_end(void); // send "end reverse video" sequence
482static void flash(int); // flash the terminal screen
Eric Andersen3f980402001-04-04 17:31:15 +0000483static void show_status_line(void); // put a message on the bottom line
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000484static void status_line(const char *, ...); // print to status buf
485static void status_line_bold(const char *, ...);
486static void not_implemented(const char *); // display "Not implemented" message
Paul Fox8552aec2005-09-16 12:20:05 +0000487static int format_edit_status(void); // format file status on status line
Eric Andersen3f980402001-04-04 17:31:15 +0000488static void redraw(int); // force a full screen refresh
Denis Vlasenko68404f12008-03-17 09:00:54 +0000489static char* format_line(char* /*, int*/);
Eric Andersen3f980402001-04-04 17:31:15 +0000490static void refresh(int); // update the terminal from screen[]
491
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000492static void Indicate_Error(void); // use flash or beep to indicate error
493#define indicate_error(c) Indicate_Error()
Paul Fox90372ed2005-10-09 14:26:26 +0000494static void Hit_Return(void);
495
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000496#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000497static char *char_search(char *, const char *, int, int); // search for pattern starting at p
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000498#endif
499#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000500static char *get_one_address(char *, int *); // get colon addr, if present
501static char *get_address(char *, int *, int *); // get two colon addrs, if present
502static void colon(char *); // execute the "colon" mode cmds
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000503#endif
504#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000505static void winch_sig(int); // catch window size changes
506static void suspend_sig(int); // catch ctrl-Z
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000507static void catch_sig(int); // catch ctrl-C and alarm time-outs
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000508#endif
509#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000510static void start_new_cmd_q(char); // new queue for command
Eric Andersenbff7a602001-11-17 07:15:43 +0000511static void end_cmd_q(void); // stop saving input chars
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000512#else
513#define end_cmd_q() ((void)0)
514#endif
515#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000516static void showmatching(char *); // show the matching pair () [] {}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000517#endif
518#if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000519// might reallocate text[]! use p += string_insert(p, ...),
520// and be careful to not use pointers into potentially freed text[]!
521static uintptr_t string_insert(char *, const char *); // insert the string at 'p'
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000522#endif
523#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000524static char *text_yank(char *, char *, int); // save copy of "p" into a register
525static char what_reg(void); // what is letter of current YDreg
526static void check_context(char); // remember context for '' command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000527#endif
528#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000529static void crash_dummy();
530static void crash_test();
531static int crashme = 0;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000532#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000533
534
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000535static void write1(const char *out)
536{
537 fputs(out, stdout);
538}
539
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000540int vi_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Rob Landleydfba7412006-03-06 20:47:33 +0000541int vi_main(int argc, char **argv)
Eric Andersen3f980402001-04-04 17:31:15 +0000542{
Eric Andersend402edf2001-04-04 19:29:48 +0000543 int c;
Denis Vlasenkob1759462008-06-20 20:20:54 +0000544
545 INIT_G();
Eric Andersen3f980402001-04-04 17:31:15 +0000546
Denis Vlasenkocd5c7862007-05-17 16:37:22 +0000547#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000548 my_pid = getpid();
549#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000550#if ENABLE_FEATURE_VI_CRASHME
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000551 srand((long) my_pid);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000552#endif
Denis Vlasenko2414a962007-07-18 22:03:40 +0000553#ifdef NO_SUCH_APPLET_YET
554 /* If we aren't "vi", we are "view" */
555 if (ENABLE_FEATURE_VI_READONLY && applet_name[2]) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000556 SET_READONLY_MODE(readonly_mode);
Eric Andersen3f980402001-04-04 17:31:15 +0000557 }
Denis Vlasenko2414a962007-07-18 22:03:40 +0000558#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000559
Bernhard Reutner-Fischer73f56bb2007-09-22 21:18:46 +0000560 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE;
Denis Vlasenkof9234132007-03-21 00:03:42 +0000561 // 1- process $HOME/.exrc file (not inplemented yet)
Eric Andersen3f980402001-04-04 17:31:15 +0000562 // 2- process EXINIT variable from environment
563 // 3- process command line args
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000564#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000565 {
566 char *p = getenv("EXINIT");
567 if (p && *p)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000568 initial_cmds[0] = xstrndup(p, MAX_INPUT_LEN);
Denis Vlasenkof9234132007-03-21 00:03:42 +0000569 }
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000570#endif
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000571 while ((c = getopt(argc, argv, "hCRH" IF_FEATURE_VI_COLON("c:"))) != -1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000572 switch (c) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000573#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000574 case 'C':
575 crashme = 1;
576 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000577#endif
578#if ENABLE_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +0000579 case 'R': // Read-only flag
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000580 SET_READONLY_MODE(readonly_mode);
Eric Andersen3f980402001-04-04 17:31:15 +0000581 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000582#endif
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000583#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000584 case 'c': // cmd line vi command
585 if (*optarg)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000586 initial_cmds[initial_cmds[0] != 0] = xstrndup(optarg, MAX_INPUT_LEN);
Denis Vlasenkof9234132007-03-21 00:03:42 +0000587 break;
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000588#endif
Paul Fox35e9c5d2008-03-06 16:26:12 +0000589 case 'H':
Eric Andersen3f980402001-04-04 17:31:15 +0000590 show_help();
Paul Fox35e9c5d2008-03-06 16:26:12 +0000591 /* fall through */
Paul Fox35e9c5d2008-03-06 16:26:12 +0000592 default:
Denis Vlasenkoc6938402008-03-24 02:18:03 +0000593 bb_show_usage();
Eric Andersendd8500b2001-07-02 18:06:14 +0000594 return 1;
Eric Andersen3f980402001-04-04 17:31:15 +0000595 }
596 }
597
598 // The argv array can be used by the ":next" and ":rewind" commands
599 // save optind.
600 fn_start = optind; // remember first file name for :next and :rew
601 save_argc = argc;
602
603 //----- This is the main file handling loop --------------
Denys Vlasenko04cecd52010-04-16 22:13:55 -0700604 while (1) {
605 edit_file(argv[optind]); /* param might be NULL */
606 if (++optind >= argc)
607 break;
Eric Andersen3f980402001-04-04 17:31:15 +0000608 }
609 //-----------------------------------------------------------
610
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000611 return 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000612}
613
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000614/* read text from file or create an empty buf */
615/* will also update current_filename */
616static int init_text_buffer(char *fn)
617{
618 int rc;
619 int size = file_size(fn); // file size. -1 means does not exist.
620
621 /* allocate/reallocate text buffer */
622 free(text);
Denis Vlasenkob1759462008-06-20 20:20:54 +0000623 text_size = size + 10240;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000624 screenbegin = dot = end = text = xzalloc(text_size);
Denis Vlasenko2f6ae432007-07-19 22:50:47 +0000625
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000626 if (fn != current_filename) {
627 free(current_filename);
628 current_filename = xstrdup(fn);
629 }
630 if (size < 0) {
631 // file dont exist. Start empty buf with dummy line
632 char_insert(text, '\n');
633 rc = 0;
634 } else {
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000635 rc = file_insert(fn, text, 1);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000636 }
637 file_modified = 0;
638 last_file_modified = -1;
639#if ENABLE_FEATURE_VI_YANKMARK
640 /* init the marks. */
641 memset(mark, 0, sizeof(mark));
642#endif
643 return rc;
Denis Vlasenko2f6ae432007-07-19 22:50:47 +0000644}
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000645
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700646#if ENABLE_FEATURE_VI_WIN_RESIZE
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200647static int query_screen_dimensions(void)
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700648{
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200649 int err = get_terminal_width_height(STDIN_FILENO, &columns, &rows);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700650 if (rows > MAX_SCR_ROWS)
651 rows = MAX_SCR_ROWS;
652 if (columns > MAX_SCR_COLS)
653 columns = MAX_SCR_COLS;
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200654 return err;
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700655}
656#else
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200657# define query_screen_dimensions() (0)
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700658#endif
659
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000660static void edit_file(char *fn)
Eric Andersen3f980402001-04-04 17:31:15 +0000661{
Denis Vlasenkob1759462008-06-20 20:20:54 +0000662#if ENABLE_FEATURE_VI_YANKMARK
663#define cur_line edit_file__cur_line
664#endif
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000665 int c;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000666#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000667 int sig;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000668#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000669
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000670 editing = 1; // 0 = exit, 1 = one file, 2 = multiple files
Eric Andersen3f980402001-04-04 17:31:15 +0000671 rawmode();
672 rows = 24;
673 columns = 80;
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200674 IF_FEATURE_VI_ASK_TERMINAL(G.get_rowcol_error =) query_screen_dimensions();
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700675#if ENABLE_FEATURE_VI_ASK_TERMINAL
676 if (G.get_rowcol_error /* TODO? && no input on stdin */) {
677 uint64_t k;
678 write1("\033[999;999H" "\033[6n");
679 fflush_all();
680 k = read_key(STDIN_FILENO, readbuffer, /*timeout_ms:*/ 100);
681 if ((int32_t)k == KEYCODE_CURSOR_POS) {
682 uint32_t rc = (k >> 32);
683 columns = (rc & 0x7fff);
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200684 if (columns > MAX_SCR_COLS)
685 columns = MAX_SCR_COLS;
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700686 rows = ((rc >> 16) & 0x7fff);
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200687 if (rows > MAX_SCR_ROWS)
688 rows = MAX_SCR_ROWS;
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700689 }
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700690 }
691#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000692 new_screen(rows, columns); // get memory for virtual screen
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000693 init_text_buffer(fn);
Eric Andersen3f980402001-04-04 17:31:15 +0000694
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000695#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000696 YDreg = 26; // default Yank/Delete reg
697 Ureg = 27; // hold orig line for "U" cmd
Eric Andersen3f980402001-04-04 17:31:15 +0000698 mark[26] = mark[27] = text; // init "previous context"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000699#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000700
Eric Andersen3f980402001-04-04 17:31:15 +0000701 last_forward_char = last_input_char = '\0';
702 crow = 0;
703 ccol = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000704
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000705#if ENABLE_FEATURE_VI_USE_SIGNALS
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700706 signal(SIGINT, catch_sig);
Eric Andersen3f980402001-04-04 17:31:15 +0000707 signal(SIGWINCH, winch_sig);
708 signal(SIGTSTP, suspend_sig);
Paul Fox2724fa92008-03-17 15:28:07 +0000709 sig = sigsetjmp(restart, 1);
Eric Andersen3f980402001-04-04 17:31:15 +0000710 if (sig != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000711 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000712 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000713#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000714
Eric Andersen3f980402001-04-04 17:31:15 +0000715 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
716 cmdcnt = 0;
717 tabstop = 8;
718 offset = 0; // no horizontal offset
719 c = '\0';
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000720#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000721 free(ioq_start);
Paul Foxc51fc7b2008-03-06 01:34:23 +0000722 ioq = ioq_start = NULL;
723 lmc_len = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000724 adding2q = 0;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000725#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000726
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000727#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000728 {
729 char *p, *q;
730 int n = 0;
731
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700732 while ((p = initial_cmds[n]) != NULL) {
Denis Vlasenkof9234132007-03-21 00:03:42 +0000733 do {
734 q = p;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000735 p = strchr(q, '\n');
Denis Vlasenkof9234132007-03-21 00:03:42 +0000736 if (p)
Denis Vlasenko51742f42007-04-12 00:32:05 +0000737 while (*p == '\n')
Denis Vlasenkof9234132007-03-21 00:03:42 +0000738 *p++ = '\0';
739 if (*q)
740 colon(q);
741 } while (p);
742 free(initial_cmds[n]);
743 initial_cmds[n] = NULL;
744 n++;
745 }
746 }
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000747#endif
Paul Fox35e9c5d2008-03-06 16:26:12 +0000748 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000749 //------This is the main Vi cmd handling loop -----------------------
750 while (editing > 0) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000751#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000752 if (crashme > 0) {
753 if ((end - text) > 1) {
754 crash_dummy(); // generate a random command
755 } else {
756 crashme = 0;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000757 string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
758 dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000759 refresh(FALSE);
760 }
761 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000762#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000763 last_input_char = c = get_one_char(); // get a cmd from user
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000764#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000765 // save a copy of the current line- for the 'U" command
766 if (begin_line(dot) != cur_line) {
767 cur_line = begin_line(dot);
768 text_yank(begin_line(dot), end_line(dot), Ureg);
769 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000770#endif
771#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000772 // These are commands that change text[].
773 // Remember the input for the "." command
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000774 if (!adding2q && ioq_start == NULL
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000775 && cmd_mode == 0 // command mode
776 && c > '\0' // exclude NUL and non-ASCII chars
777 && c < 0x7f // (Unicode and such)
778 && strchr(modifying_cmds, c)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000779 ) {
Eric Andersen3f980402001-04-04 17:31:15 +0000780 start_new_cmd_q(c);
781 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000782#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000783 do_cmd(c); // execute the user command
Denis Vlasenko8ef801b2008-10-16 09:46:07 +0000784
Eric Andersen3f980402001-04-04 17:31:15 +0000785 // poll to see if there is input already waiting. if we are
786 // not able to display output fast enough to keep up, skip
787 // the display update until we catch up with input.
Denys Vlasenko020f4062009-05-17 16:44:54 +0200788 if (!readbuffer[0] && mysleep(0) == 0) {
Denis Vlasenko8ef801b2008-10-16 09:46:07 +0000789 // no input pending - so update output
Eric Andersen3f980402001-04-04 17:31:15 +0000790 refresh(FALSE);
791 show_status_line();
792 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000793#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000794 if (crashme > 0)
795 crash_test(); // test editor variables
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000796#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000797 }
798 //-------------------------------------------------------------------
799
Denis Vlasenko267e16c2008-10-14 10:34:41 +0000800 go_bottom_and_clear_to_eol();
Eric Andersen3f980402001-04-04 17:31:15 +0000801 cookmode();
Denis Vlasenkob1759462008-06-20 20:20:54 +0000802#undef cur_line
Eric Andersen3f980402001-04-04 17:31:15 +0000803}
804
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000805//----- The Colon commands -------------------------------------
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000806#if ENABLE_FEATURE_VI_COLON
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000807static char *get_one_address(char *p, int *addr) // get colon addr, if present
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000808{
809 int st;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000810 char *q;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000811 IF_FEATURE_VI_YANKMARK(char c;)
812 IF_FEATURE_VI_SEARCH(char *pat;)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000813
814 *addr = -1; // assume no addr
815 if (*p == '.') { // the current line
816 p++;
817 q = begin_line(dot);
818 *addr = count_lines(text, q);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000819 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000820#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000821 else if (*p == '\'') { // is this a mark addr
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000822 p++;
823 c = tolower(*p);
824 p++;
825 if (c >= 'a' && c <= 'z') {
826 // we have a mark
827 c = c - 'a';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000828 q = mark[(unsigned char) c];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000829 if (q != NULL) { // is mark valid
Denis Vlasenko00d84172008-11-24 07:34:42 +0000830 *addr = count_lines(text, q);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000831 }
832 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000833 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000834#endif
835#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000836 else if (*p == '/') { // a search pattern
837 q = strchrnul(++p, '/');
838 pat = xstrndup(p, q - p); // save copy of pattern
839 p = q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000840 if (*p == '/')
841 p++;
842 q = char_search(dot, pat, FORWARD, FULL);
843 if (q != NULL) {
844 *addr = count_lines(text, q);
845 }
846 free(pat);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000847 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000848#endif
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000849 else if (*p == '$') { // the last line in file
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000850 p++;
851 q = begin_line(end - 1);
852 *addr = count_lines(text, q);
853 } else if (isdigit(*p)) { // specific line number
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000854 sscanf(p, "%d%n", addr, &st);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000855 p += st;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000856 } else {
Denys Vlasenkob22bbff2009-07-04 16:50:43 +0200857 // unrecognized address - assume -1
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000858 *addr = -1;
859 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000860 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000861}
862
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000863static char *get_address(char *p, int *b, int *e) // get two colon addrs, if present
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000864{
865 //----- get the address' i.e., 1,3 'a,'b -----
866 // get FIRST addr, if present
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000867 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000868 p++; // skip over leading spaces
869 if (*p == '%') { // alias for 1,$
870 p++;
871 *b = 1;
872 *e = count_lines(text, end-1);
873 goto ga0;
874 }
875 p = get_one_address(p, b);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000876 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000877 p++;
Eric Andersenaff114c2004-04-14 17:51:38 +0000878 if (*p == ',') { // is there a address separator
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000879 p++;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000880 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000881 p++;
882 // get SECOND addr, if present
883 p = get_one_address(p, e);
884 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000885 ga0:
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000886 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000887 p++; // skip over trailing spaces
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000888 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000889}
890
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000891#if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000892static void setops(const char *args, const char *opname, int flg_no,
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000893 const char *short_opname, int opt)
894{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000895 const char *a = args + flg_no;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000896 int l = strlen(opname) - 1; /* opname have + ' ' */
897
Denys Vlasenko6548edd2009-06-15 12:44:11 +0200898 // maybe strncmp? we had tons of erroneous strncasecmp's...
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000899 if (strncasecmp(a, opname, l) == 0
900 || strncasecmp(a, short_opname, 2) == 0
901 ) {
902 if (flg_no)
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000903 vi_setops &= ~opt;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000904 else
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000905 vi_setops |= opt;
906 }
907}
908#endif
909
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000910// buf must be no longer than MAX_INPUT_LEN!
911static void colon(char *buf)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000912{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000913 char c, *orig_buf, *buf1, *q, *r;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000914 char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN];
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +0000915 int i, l, li, ch, b, e;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000916 int useforce, forced = FALSE;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000917
918 // :3154 // if (-e line 3154) goto it else stay put
919 // :4,33w! foo // write a portion of buffer to file "foo"
920 // :w // write all of buffer to current file
921 // :q // quit
922 // :q! // quit- dont care about modified file
923 // :'a,'z!sort -u // filter block through sort
924 // :'f // goto mark "f"
925 // :'fl // list literal the mark "f" line
926 // :.r bar // read file "bar" into buffer before dot
927 // :/123/,/abc/d // delete lines from "123" line to "abc" line
928 // :/xyz/ // goto the "xyz" line
929 // :s/find/replace/ // substitute pattern "find" with "replace"
930 // :!<cmd> // run <cmd> then return
931 //
Eric Andersen165e8cb2004-07-20 06:44:46 +0000932
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000933 if (!buf[0])
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +0200934 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000935 if (*buf == ':')
936 buf++; // move past the ':'
937
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +0000938 li = ch = i = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000939 b = e = -1;
940 q = text; // assume 1,$ for the range
941 r = end - 1;
942 li = count_lines(text, end - 1);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000943 fn = current_filename;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000944
945 // look for optional address(es) :. :1 :1,9 :'q,'a :%
946 buf = get_address(buf, &b, &e);
947
948 // remember orig command line
949 orig_buf = buf;
950
951 // get the COMMAND into cmd[]
952 buf1 = cmd;
953 while (*buf != '\0') {
954 if (isspace(*buf))
955 break;
956 *buf1++ = *buf++;
957 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000958 *buf1 = '\0';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000959 // get any ARGuments
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000960 while (isblank(*buf))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000961 buf++;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000962 strcpy(args, buf);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000963 useforce = FALSE;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000964 buf1 = last_char_is(cmd, '!');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000965 if (buf1) {
966 useforce = TRUE;
967 *buf1 = '\0'; // get rid of !
968 }
969 if (b >= 0) {
970 // if there is only one addr, then the addr
971 // is the line number of the single line the
972 // user wants. So, reset the end
973 // pointer to point at end of the "b" line
974 q = find_line(b); // what line is #b
975 r = end_line(q);
976 li = 1;
977 }
978 if (e >= 0) {
979 // we were given two addrs. change the
980 // end pointer to the addr given by user.
981 r = find_line(e); // what line is #e
982 r = end_line(r);
983 li = e - b + 1;
984 }
985 // ------------ now look for the command ------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000986 i = strlen(cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000987 if (i == 0) { // :123CR goto line #123
988 if (b >= 0) {
989 dot = find_line(b); // what line is #b
990 dot_skip_over_ws();
991 }
Denis Vlasenko249fabf2006-12-19 00:29:22 +0000992 }
993#if ENABLE_FEATURE_ALLOW_EXEC
Denys Vlasenko6548edd2009-06-15 12:44:11 +0200994 else if (cmd[0] == '!') { // run a cmd
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000995 int retcode;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000996 // :!ls run the <cmd>
Denis Vlasenko267e16c2008-10-14 10:34:41 +0000997 go_bottom_and_clear_to_eol();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000998 cookmode();
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000999 retcode = system(orig_buf + 1); // run the cmd
1000 if (retcode)
1001 printf("\nshell returned %i\n\n", retcode);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001002 rawmode();
1003 Hit_Return(); // let user see results
Denis Vlasenko249fabf2006-12-19 00:29:22 +00001004 }
1005#endif
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001006 else if (cmd[0] == '=' && !cmd[1]) { // where is the address
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001007 if (b < 0) { // no addr given- use defaults
1008 b = e = count_lines(text, dot);
1009 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001010 status_line("%d", b);
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001011 } else if (strncmp(cmd, "delete", i) == 0) { // delete lines
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001012 if (b < 0) { // no addr given- use defaults
1013 q = begin_line(dot); // assume .,. for the range
1014 r = end_line(dot);
1015 }
1016 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
1017 dot_skip_over_ws();
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001018 } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001019 // don't edit, if the current file has been modified
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001020 if (file_modified && !useforce) {
1021 status_line_bold("No write since last change (:edit! overrides)");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001022 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001023 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001024 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001025 // the user supplied a file name
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00001026 fn = args;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001027 } else if (current_filename && current_filename[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001028 // no user supplied name- use the current filename
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001029 // fn = current_filename; was set by default
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001030 } else {
1031 // no user file name, no current name- punt
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001032 status_line_bold("No current filename");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001033 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001034 }
1035
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001036 if (init_text_buffer(fn) < 0)
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001037 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001038
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001039#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001040 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
1041 free(reg[Ureg]); // free orig line reg- for 'U'
1042 reg[Ureg]= 0;
1043 }
1044 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
1045 free(reg[YDreg]); // free default yank/delete register
1046 reg[YDreg]= 0;
1047 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001048#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001049 // how many lines in text[]?
1050 li = count_lines(text, end - 1);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001051 status_line("\"%s\"%s"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001052 IF_FEATURE_VI_READONLY("%s")
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001053 " %dL, %dC", current_filename,
1054 (file_size(fn) < 0 ? " [New file]" : ""),
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001055 IF_FEATURE_VI_READONLY(
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001056 ((readonly_mode) ? " [Readonly]" : ""),
1057 )
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001058 li, ch);
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001059 } else if (strncmp(cmd, "file", i) == 0) { // what File is this
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001060 if (b != -1 || e != -1) {
Denys Vlasenkoc2704542009-11-20 19:14:19 +01001061 status_line_bold("No address allowed on this command");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001062 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001063 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001064 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001065 // user wants a new filename
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001066 free(current_filename);
1067 current_filename = xstrdup(args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001068 } else {
1069 // user wants file status info
Paul Fox8552aec2005-09-16 12:20:05 +00001070 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001071 }
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001072 } else if (strncmp(cmd, "features", i) == 0) { // what features are available
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001073 // print out values of all features
Denis Vlasenko267e16c2008-10-14 10:34:41 +00001074 go_bottom_and_clear_to_eol();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001075 cookmode();
1076 show_help();
1077 rawmode();
1078 Hit_Return();
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001079 } else if (strncmp(cmd, "list", i) == 0) { // literal print line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001080 if (b < 0) { // no addr given- use defaults
1081 q = begin_line(dot); // assume .,. for the range
1082 r = end_line(dot);
1083 }
Denis Vlasenko267e16c2008-10-14 10:34:41 +00001084 go_bottom_and_clear_to_eol();
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001085 puts("\r");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001086 for (; q <= r; q++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001087 int c_is_no_print;
1088
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001089 c = *q;
Denis Vlasenko2a51af22007-03-21 22:31:24 +00001090 c_is_no_print = (c & 0x80) && !Isprint(c);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001091 if (c_is_no_print) {
1092 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001093 standout_start();
Denis Vlasenkod3c042f2007-12-30 01:59:53 +00001094 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001095 if (c == '\n') {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001096 write1("$\r");
1097 } else if (c < ' ' || c == 127) {
Denis Vlasenko4daad902007-09-27 10:20:47 +00001098 bb_putchar('^');
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001099 if (c == 127)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001100 c = '?';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001101 else
1102 c += '@';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001103 }
Denis Vlasenko4daad902007-09-27 10:20:47 +00001104 bb_putchar(c);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001105 if (c_is_no_print)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001106 standout_end();
1107 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001108 Hit_Return();
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001109 } else if (strncmp(cmd, "quit", i) == 0 // quit
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001110 || strncmp(cmd, "next", i) == 0 // edit next file
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001111 ) {
Denys Vlasenko04cecd52010-04-16 22:13:55 -07001112 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001113 if (useforce) {
1114 // force end of argv list
1115 if (*cmd == 'q') {
1116 optind = save_argc;
1117 }
1118 editing = 0;
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001119 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001120 }
1121 // don't exit if the file been modified
1122 if (file_modified) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001123 status_line_bold("No write since last change (:%s! overrides)",
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001124 (*cmd == 'q' ? "quit" : "next"));
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001125 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001126 }
1127 // are there other file to edit
Denys Vlasenko04cecd52010-04-16 22:13:55 -07001128 n = save_argc - optind - 1;
1129 if (*cmd == 'q' && n > 0) {
1130 status_line_bold("%d more file(s) to edit", n);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001131 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001132 }
Denys Vlasenko04cecd52010-04-16 22:13:55 -07001133 if (*cmd == 'n' && n <= 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001134 status_line_bold("No more files to edit");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001135 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001136 }
1137 editing = 0;
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001138 } else if (strncmp(cmd, "read", i) == 0) { // read file into text[]
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001139 fn = args;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001140 if (!fn[0]) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001141 status_line_bold("No filename given");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001142 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001143 }
1144 if (b < 0) { // no addr given- use defaults
1145 q = begin_line(dot); // assume "dot"
1146 }
1147 // read after current line- unless user said ":0r foo"
1148 if (b != 0)
1149 q = next_line(q);
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001150 { // dance around potentially-reallocated text[]
1151 uintptr_t ofs = q - text;
1152 ch = file_insert(fn, q, 0);
1153 q = text + ofs;
1154 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001155 if (ch < 0)
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001156 goto ret; // nothing was inserted
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001157 // how many lines in text[]?
1158 li = count_lines(q, q + ch - 1);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001159 status_line("\"%s\""
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001160 IF_FEATURE_VI_READONLY("%s")
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001161 " %dL, %dC", fn,
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001162 IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001163 li, ch);
1164 if (ch > 0) {
1165 // if the insert is before "dot" then we need to update
1166 if (q <= dot)
1167 dot += ch;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001168 /*file_modified++; - done by file_insert */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001169 }
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001170 } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001171 if (file_modified && !useforce) {
1172 status_line_bold("No write since last change (:rewind! overrides)");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001173 } else {
1174 // reset the filenames to edit
1175 optind = fn_start - 1;
1176 editing = 0;
1177 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001178#if ENABLE_FEATURE_VI_SET
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001179 } else if (strncmp(cmd, "set", i) == 0) { // set or clear features
Denis Vlasenko58875ae2007-03-22 22:22:10 +00001180#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkof9234132007-03-21 00:03:42 +00001181 char *argp;
Denis Vlasenko58875ae2007-03-22 22:22:10 +00001182#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001183 i = 0; // offset into args
Denis Vlasenkof9234132007-03-21 00:03:42 +00001184 // only blank is regarded as args delmiter. What about tab '\t' ?
1185 if (!args[0] || strcasecmp(args, "all") == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001186 // print out values of all options
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001187#if ENABLE_FEATURE_VI_SETOPTS
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001188 status_line_bold(
1189 "%sautoindent "
1190 "%sflash "
1191 "%signorecase "
1192 "%sshowmatch "
1193 "tabstop=%u",
1194 autoindent ? "" : "no",
1195 err_method ? "" : "no",
1196 ignorecase ? "" : "no",
1197 showmatch ? "" : "no",
1198 tabstop
1199 );
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001200#endif
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001201 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001202 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001203#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001204 argp = args;
Denis Vlasenkoba2fb712007-04-01 09:39:03 +00001205 while (*argp) {
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001206 if (strncmp(argp, "no", 2) == 0)
Denis Vlasenkof9234132007-03-21 00:03:42 +00001207 i = 2; // ":set noautoindent"
1208 setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001209 setops(argp, "flash " , i, "fl", VI_ERR_METHOD);
Denis Vlasenkof9234132007-03-21 00:03:42 +00001210 setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001211 setops(argp, "showmatch " , i, "sm", VI_SHOWMATCH );
1212 if (strncmp(argp + i, "tabstop=", 8) == 0) {
1213 int t = 0;
1214 sscanf(argp + i+8, "%u", &t);
1215 if (t > 0 && t <= MAX_TABSTOP)
1216 tabstop = t;
Denis Vlasenkof9234132007-03-21 00:03:42 +00001217 }
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001218 argp = skip_non_whitespace(argp);
1219 argp = skip_whitespace(argp);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001220 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001221#endif /* FEATURE_VI_SETOPTS */
1222#endif /* FEATURE_VI_SET */
1223#if ENABLE_FEATURE_VI_SEARCH
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001224 } else if (cmd[0] == 's') { // substitute a pattern with a replacement pattern
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001225 char *ls, *F, *R;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001226 int gflag;
1227
1228 // F points to the "find" pattern
1229 // R points to the "replace" pattern
1230 // replace the cmd line delimiters "/" with NULLs
1231 gflag = 0; // global replace flag
1232 c = orig_buf[1]; // what is the delimiter
1233 F = orig_buf + 2; // start of "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001234 R = strchr(F, c); // middle delimiter
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001235 if (!R)
1236 goto colon_s_fail;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001237 *R++ = '\0'; // terminate "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001238 buf1 = strchr(R, c);
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001239 if (!buf1)
1240 goto colon_s_fail;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001241 *buf1++ = '\0'; // terminate "replace"
1242 if (*buf1 == 'g') { // :s/foo/bar/g
1243 buf1++;
1244 gflag++; // turn on gflag
1245 }
1246 q = begin_line(q);
1247 if (b < 0) { // maybe :s/foo/bar/
1248 q = begin_line(dot); // start with cur line
1249 b = count_lines(text, q); // cur line number
1250 }
1251 if (e < 0)
1252 e = b; // maybe :.s/foo/bar/
1253 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
1254 ls = q; // orig line start
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001255 vc4:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001256 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001257 if (buf1) {
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001258 uintptr_t bias;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001259 // we found the "find" pattern - delete it
1260 text_hole_delete(buf1, buf1 + strlen(F) - 1);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001261 // inset the "replace" patern
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001262 bias = string_insert(buf1, R); // insert the string
1263 buf1 += bias;
1264 ls += bias;
1265 /*q += bias; - recalculated anyway */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001266 // check for "global" :s/foo/bar/g
1267 if (gflag == 1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001268 if ((buf1 + strlen(R)) < end_line(ls)) {
1269 q = buf1 + strlen(R);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001270 goto vc4; // don't let q move past cur line
1271 }
1272 }
1273 }
1274 q = next_line(ls);
1275 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001276#endif /* FEATURE_VI_SEARCH */
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001277 } else if (strncmp(cmd, "version", i) == 0) { // show software version
Denis Vlasenko33875382008-06-21 20:31:50 +00001278 status_line(BB_VER " " BB_BT);
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001279 } else if (strncmp(cmd, "write", i) == 0 // write text to file
1280 || strncmp(cmd, "wq", i) == 0
1281 || strncmp(cmd, "wn", i) == 0
1282 || (cmd[0] == 'x' && !cmd[1])
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001283 ) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001284 // is there a file name to write to?
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001285 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001286 fn = args;
1287 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001288#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001289 if (readonly_mode && !useforce) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001290 status_line_bold("\"%s\" File is read only", fn);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001291 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001292 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001293#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001294 // how many lines in text[]?
1295 li = count_lines(q, r);
1296 ch = r - q + 1;
1297 // see if file exists- if not, its just a new file request
1298 if (useforce) {
1299 // if "fn" is not write-able, chmod u+w
1300 // sprintf(syscmd, "chmod u+w %s", fn);
1301 // system(syscmd);
1302 forced = TRUE;
1303 }
1304 l = file_write(fn, q, r);
1305 if (useforce && forced) {
1306 // chmod u-w
1307 // sprintf(syscmd, "chmod u-w %s", fn);
1308 // system(syscmd);
1309 forced = FALSE;
1310 }
Paul Fox61e45db2005-10-09 14:43:22 +00001311 if (l < 0) {
1312 if (l == -1)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001313 status_line_bold("\"%s\" %s", fn, strerror(errno));
Paul Fox61e45db2005-10-09 14:43:22 +00001314 } else {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001315 status_line("\"%s\" %dL, %dC", fn, li, l);
Paul Fox61e45db2005-10-09 14:43:22 +00001316 if (q == text && r == end - 1 && l == ch) {
1317 file_modified = 0;
1318 last_file_modified = -1;
1319 }
Denys Vlasenko6b9f1632010-01-28 02:24:24 +01001320 if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n'
1321 || cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N'
1322 )
1323 && l == ch
1324 ) {
Paul Fox61e45db2005-10-09 14:43:22 +00001325 editing = 0;
1326 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001327 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001328#if ENABLE_FEATURE_VI_YANKMARK
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001329 } else if (strncmp(cmd, "yank", i) == 0) { // yank lines
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001330 if (b < 0) { // no addr given- use defaults
1331 q = begin_line(dot); // assume .,. for the range
1332 r = end_line(dot);
1333 }
1334 text_yank(q, r, YDreg);
1335 li = count_lines(q, r);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001336 status_line("Yank %d lines (%d chars) into [%c]",
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001337 li, strlen(reg[YDreg]), what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001338#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001339 } else {
1340 // cmd unknown
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001341 not_implemented(cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001342 }
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001343 ret:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001344 dot = bound_dot(dot); // make sure "dot" is valid
1345 return;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001346#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001347 colon_s_fail:
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001348 status_line(":s expression missing delimiters");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001349#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001350}
Paul Fox61e45db2005-10-09 14:43:22 +00001351
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001352#endif /* FEATURE_VI_COLON */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001353
1354static void Hit_Return(void)
1355{
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00001356 int c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001357
Denis Vlasenkof882f082007-12-23 02:36:51 +00001358 standout_start();
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001359 write1("[Hit return to continue]");
Denis Vlasenkof882f082007-12-23 02:36:51 +00001360 standout_end();
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001361 while ((c = get_one_char()) != '\n' && c != '\r')
1362 continue;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001363 redraw(TRUE); // force redraw all
1364}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001365
Denis Vlasenko91afdf82007-07-17 23:22:49 +00001366static int next_tabstop(int col)
1367{
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001368 return col + ((tabstop - 1) - (col % tabstop));
1369}
1370
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001371//----- Synchronize the cursor to Dot --------------------------
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02001372static NOINLINE void sync_cursor(char *d, int *row, int *col)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001373{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001374 char *beg_cur; // begin and end of "d" line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001375 char *tp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001376 int cnt, ro, co;
1377
1378 beg_cur = begin_line(d); // first char of cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001379
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001380 if (beg_cur < screenbegin) {
Denis Vlasenkof882f082007-12-23 02:36:51 +00001381 // "d" is before top line on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001382 // how many lines do we have to move
1383 cnt = count_lines(beg_cur, screenbegin);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001384 sc1:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001385 screenbegin = beg_cur;
1386 if (cnt > (rows - 1) / 2) {
1387 // we moved too many lines. put "dot" in middle of screen
1388 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1389 screenbegin = prev_line(screenbegin);
1390 }
1391 }
Denis Vlasenkof882f082007-12-23 02:36:51 +00001392 } else {
1393 char *end_scr; // begin and end of screen
1394 end_scr = end_screen(); // last char of screen
1395 if (beg_cur > end_scr) {
1396 // "d" is after bottom line on screen
1397 // how many lines do we have to move
1398 cnt = count_lines(end_scr, beg_cur);
1399 if (cnt > (rows - 1) / 2)
1400 goto sc1; // too many lines
1401 for (ro = 0; ro < cnt - 1; ro++) {
1402 // move screen begin the same amount
1403 screenbegin = next_line(screenbegin);
1404 // now, move the end of screen
1405 end_scr = next_line(end_scr);
1406 end_scr = end_line(end_scr);
1407 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001408 }
1409 }
1410 // "d" is on screen- find out which row
1411 tp = screenbegin;
1412 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1413 if (tp == beg_cur)
1414 break;
1415 tp = next_line(tp);
1416 }
1417
1418 // find out what col "d" is on
1419 co = 0;
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001420 while (tp < d) { // drive "co" to correct column
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001421 if (*tp == '\n') //vda || *tp == '\0')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001422 break;
1423 if (*tp == '\t') {
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001424 // handle tabs like real vi
1425 if (d == tp && cmd_mode) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001426 break;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001427 }
Denis Vlasenkoe3eae0d2008-06-22 16:38:53 +00001428 co = next_tabstop(co);
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001429 } else if ((unsigned char)*tp < ' ' || *tp == 0x7f) {
1430 co++; // display as ^X, use 2 columns
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001431 }
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001432 co++;
1433 tp++;
1434 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001435
1436 // "co" is the column where "dot" is.
1437 // The screen has "columns" columns.
1438 // The currently displayed columns are 0+offset -- columns+ofset
1439 // |-------------------------------------------------------------|
1440 // ^ ^ ^
1441 // offset | |------- columns ----------------|
1442 //
1443 // If "co" is already in this range then we do not have to adjust offset
1444 // but, we do have to subtract the "offset" bias from "co".
1445 // If "co" is outside this range then we have to change "offset".
1446 // If the first char of a line is a tab the cursor will try to stay
1447 // in column 7, but we have to set offset to 0.
1448
1449 if (co < 0 + offset) {
1450 offset = co;
1451 }
1452 if (co >= columns + offset) {
1453 offset = co - columns + 1;
1454 }
1455 // if the first char of the line is a tab, and "dot" is sitting on it
1456 // force offset to 0.
1457 if (d == beg_cur && *d == '\t') {
1458 offset = 0;
1459 }
1460 co -= offset;
1461
1462 *row = ro;
1463 *col = co;
1464}
1465
1466//----- Text Movement Routines ---------------------------------
Denis Vlasenkof882f082007-12-23 02:36:51 +00001467static char *begin_line(char *p) // return pointer to first char cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001468{
Denis Vlasenkof882f082007-12-23 02:36:51 +00001469 if (p > text) {
1470 p = memrchr(text, '\n', p - text);
1471 if (!p)
1472 return text;
1473 return p + 1;
1474 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001475 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001476}
1477
Denis Vlasenkoe3eae0d2008-06-22 16:38:53 +00001478static char *end_line(char *p) // return pointer to NL of cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001479{
Denis Vlasenkof882f082007-12-23 02:36:51 +00001480 if (p < end - 1) {
1481 p = memchr(p, '\n', end - p - 1);
1482 if (!p)
1483 return end - 1;
1484 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001485 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001486}
1487
Denis Vlasenkof882f082007-12-23 02:36:51 +00001488static char *dollar_line(char *p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001489{
Denis Vlasenkof882f082007-12-23 02:36:51 +00001490 p = end_line(p);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001491 // Try to stay off of the Newline
1492 if (*p == '\n' && (p - begin_line(p)) > 0)
1493 p--;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001494 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001495}
1496
Denis Vlasenkof882f082007-12-23 02:36:51 +00001497static char *prev_line(char *p) // return pointer first char prev line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001498{
1499 p = begin_line(p); // goto begining of cur line
Denis Vlasenkoe3eae0d2008-06-22 16:38:53 +00001500 if (p > text && p[-1] == '\n')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001501 p--; // step to prev line
1502 p = begin_line(p); // goto begining of prev line
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001503 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001504}
1505
Denis Vlasenkof882f082007-12-23 02:36:51 +00001506static char *next_line(char *p) // return pointer first char next line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001507{
1508 p = end_line(p);
Denis Vlasenkoe3eae0d2008-06-22 16:38:53 +00001509 if (p < end - 1 && *p == '\n')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001510 p++; // step to next line
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001511 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001512}
1513
1514//----- Text Information Routines ------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001515static char *end_screen(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001516{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001517 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001518 int cnt;
1519
1520 // find new bottom line
1521 q = screenbegin;
1522 for (cnt = 0; cnt < rows - 2; cnt++)
1523 q = next_line(q);
1524 q = end_line(q);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001525 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001526}
1527
Denis Vlasenkof882f082007-12-23 02:36:51 +00001528// count line from start to stop
1529static int count_lines(char *start, char *stop)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001530{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001531 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001532 int cnt;
1533
Denis Vlasenkof882f082007-12-23 02:36:51 +00001534 if (stop < start) { // start and stop are backwards- reverse them
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001535 q = start;
1536 start = stop;
1537 stop = q;
1538 }
1539 cnt = 0;
Denis Vlasenkof882f082007-12-23 02:36:51 +00001540 stop = end_line(stop);
1541 while (start <= stop && start <= end - 1) {
1542 start = end_line(start);
1543 if (*start == '\n')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001544 cnt++;
Denis Vlasenkof882f082007-12-23 02:36:51 +00001545 start++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001546 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001547 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001548}
1549
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001550static char *find_line(int li) // find begining of line #li
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001551{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001552 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001553
1554 for (q = text; li > 1; li--) {
1555 q = next_line(q);
1556 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001557 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001558}
1559
1560//----- Dot Movement Routines ----------------------------------
1561static void dot_left(void)
1562{
1563 if (dot > text && dot[-1] != '\n')
1564 dot--;
1565}
1566
1567static void dot_right(void)
1568{
1569 if (dot < end - 1 && *dot != '\n')
1570 dot++;
1571}
1572
1573static void dot_begin(void)
1574{
1575 dot = begin_line(dot); // return pointer to first char cur line
1576}
1577
1578static void dot_end(void)
1579{
1580 dot = end_line(dot); // return pointer to last char cur line
1581}
1582
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001583static char *move_to_col(char *p, int l)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001584{
1585 int co;
1586
1587 p = begin_line(p);
1588 co = 0;
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001589 while (co < l && p < end) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001590 if (*p == '\n') //vda || *p == '\0')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001591 break;
1592 if (*p == '\t') {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001593 co = next_tabstop(co);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001594 } else if (*p < ' ' || *p == 127) {
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001595 co++; // display as ^X, use 2 columns
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001596 }
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001597 co++;
1598 p++;
1599 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001600 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001601}
1602
1603static void dot_next(void)
1604{
1605 dot = next_line(dot);
1606}
1607
1608static void dot_prev(void)
1609{
1610 dot = prev_line(dot);
1611}
1612
1613static void dot_scroll(int cnt, int dir)
1614{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001615 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001616
1617 for (; cnt > 0; cnt--) {
1618 if (dir < 0) {
1619 // scroll Backwards
Denis Vlasenkof882f082007-12-23 02:36:51 +00001620 // ctrl-Y scroll up one line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001621 screenbegin = prev_line(screenbegin);
1622 } else {
1623 // scroll Forwards
Denis Vlasenkof882f082007-12-23 02:36:51 +00001624 // ctrl-E scroll down one line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001625 screenbegin = next_line(screenbegin);
1626 }
1627 }
1628 // make sure "dot" stays on the screen so we dont scroll off
1629 if (dot < screenbegin)
1630 dot = screenbegin;
1631 q = end_screen(); // find new bottom line
1632 if (dot > q)
1633 dot = begin_line(q); // is dot is below bottom line?
1634 dot_skip_over_ws();
1635}
1636
1637static void dot_skip_over_ws(void)
1638{
1639 // skip WS
1640 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1641 dot++;
1642}
1643
1644static void dot_delete(void) // delete the char at 'dot'
1645{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001646 text_hole_delete(dot, dot);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001647}
1648
Denis Vlasenkof882f082007-12-23 02:36:51 +00001649static char *bound_dot(char *p) // make sure text[0] <= P < "end"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001650{
1651 if (p >= end && end > text) {
1652 p = end - 1;
1653 indicate_error('1');
1654 }
1655 if (p < text) {
1656 p = text;
1657 indicate_error('2');
1658 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001659 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001660}
1661
1662//----- Helper Utility Routines --------------------------------
1663
1664//----------------------------------------------------------------
1665//----- Char Routines --------------------------------------------
1666/* Chars that are part of a word-
1667 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1668 * Chars that are Not part of a word (stoppers)
1669 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1670 * Chars that are WhiteSpace
1671 * TAB NEWLINE VT FF RETURN SPACE
1672 * DO NOT COUNT NEWLINE AS WHITESPACE
1673 */
1674
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001675static char *new_screen(int ro, int co)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001676{
1677 int li;
1678
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001679 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001680 screensize = ro * co + 8;
Denis Vlasenkob95636c2006-12-19 23:36:04 +00001681 screen = xmalloc(screensize);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001682 // initialize the new screen. assume this will be a empty file.
1683 screen_erase();
Eric Andersenaff114c2004-04-14 17:51:38 +00001684 // non-existent text[] lines start with a tilde (~).
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001685 for (li = 1; li < ro - 1; li++) {
1686 screen[(li * co) + 0] = '~';
1687 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001688 return screen;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001689}
1690
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001691#if ENABLE_FEATURE_VI_SEARCH
Walter Harmsb9ba5802011-06-27 02:59:37 +02001692
1693# if ENABLE_FEATURE_VI_REGEX_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001694
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001695// search for pattern starting at p
Denis Vlasenko33875382008-06-21 20:31:50 +00001696static char *char_search(char *p, const char *pat, int dir, int range)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001697{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001698 char *q;
1699 struct re_pattern_buffer preg;
1700 int i;
Walter Harmsb9ba5802011-06-27 02:59:37 +02001701 int size;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001702
1703 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1704 preg.translate = 0;
1705 preg.fastmap = 0;
1706 preg.buffer = 0;
1707 preg.allocated = 0;
1708
1709 // assume a LIMITED forward search
1710 q = next_line(p);
1711 q = end_line(q);
1712 q = end - 1;
1713 if (dir == BACK) {
1714 q = prev_line(p);
1715 q = text;
1716 }
1717 // count the number of chars to search over, forward or backward
1718 size = q - p;
1719 if (size < 0)
1720 size = p - q;
1721 // RANGE could be negative if we are searching backwards
1722 range = q - p;
1723
Walter Harmsb9ba5802011-06-27 02:59:37 +02001724 q = (char *)re_compile_pattern(pat, strlen(pat), (struct re_pattern_buffer *)&preg);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001725 if (q != 0) {
1726 // The pattern was not compiled
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001727 status_line_bold("bad search pattern: \"%s\": %s", pat, q);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001728 i = 0; // return p if pattern not compiled
1729 goto cs1;
1730 }
1731
1732 q = p;
1733 if (range < 0) {
1734 q = p - size;
1735 if (q < text)
1736 q = text;
1737 }
1738 // search for the compiled pattern, preg, in p[]
1739 // range < 0- search backward
1740 // range > 0- search forward
1741 // 0 < start < size
1742 // re_search() < 0 not found or error
1743 // re_search() > 0 index of found pattern
1744 // struct pattern char int int int struct reg
1745 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1746 i = re_search(&preg, q, size, 0, range, 0);
1747 if (i == -1) {
1748 p = 0;
1749 i = 0; // return NULL if pattern not found
1750 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001751 cs1:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001752 if (dir == FORWARD) {
1753 p = p + i;
1754 } else {
1755 p = p - i;
1756 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001757 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001758}
Walter Harmsb9ba5802011-06-27 02:59:37 +02001759
1760# else
1761
1762# if ENABLE_FEATURE_VI_SETOPTS
1763static int mycmp(const char *s1, const char *s2, int len)
1764{
1765 if (ignorecase) {
1766 return strncasecmp(s1, s2, len);
1767 }
1768 return strncmp(s1, s2, len);
1769}
1770# else
1771# define mycmp strncmp
1772# endif
1773
1774static char *char_search(char *p, const char *pat, int dir, int range)
1775{
1776 char *start, *stop;
1777 int len;
1778
1779 len = strlen(pat);
1780 if (dir == FORWARD) {
1781 stop = end - 1; // assume range is p - end-1
1782 if (range == LIMITED)
1783 stop = next_line(p); // range is to next line
1784 for (start = p; start < stop; start++) {
1785 if (mycmp(start, pat, len) == 0) {
1786 return start;
1787 }
1788 }
1789 } else if (dir == BACK) {
1790 stop = text; // assume range is text - p
1791 if (range == LIMITED)
1792 stop = prev_line(p); // range is to prev line
1793 for (start = p - len; start >= stop; start--) {
1794 if (mycmp(start, pat, len) == 0) {
1795 return start;
1796 }
1797 }
1798 }
1799 // pattern not found
1800 return NULL;
1801}
1802
1803# endif
1804
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001805#endif /* FEATURE_VI_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001806
Denis Vlasenko33875382008-06-21 20:31:50 +00001807static char *char_insert(char *p, char c) // insert the char c at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001808{
1809 if (c == 22) { // Is this an ctrl-V?
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001810 p += stupid_insert(p, '^'); // use ^ to indicate literal next
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001811 refresh(FALSE); // show the ^
1812 c = get_one_char();
1813 *p = c;
1814 p++;
Denis Vlasenkob1759462008-06-20 20:20:54 +00001815 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001816 } else if (c == 27) { // Is this an ESC?
1817 cmd_mode = 0;
1818 cmdcnt = 0;
1819 end_cmd_q(); // stop adding to q
Paul Fox8552aec2005-09-16 12:20:05 +00001820 last_status_cksum = 0; // force status update
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +00001821 if ((p[-1] != '\n') && (dot > text)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001822 p--;
1823 }
Paul Foxd13b90b2005-07-18 22:17:25 +00001824 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001825 // 123456789
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +00001826 if ((p[-1] != '\n') && (dot>text)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001827 p--;
1828 p = text_hole_delete(p, p); // shrink buffer 1 char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001829 }
1830 } else {
Cristian Ionescu-Idbohrn9a296fb2011-05-16 03:53:43 +02001831#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001832 // insert a char into text[]
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001833 char *sp; // "save p"
Cristian Ionescu-Idbohrn9a296fb2011-05-16 03:53:43 +02001834#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001835
1836 if (c == 13)
1837 c = '\n'; // translate \r to \n
Cristian Ionescu-Idbohrn9a296fb2011-05-16 03:53:43 +02001838#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001839 sp = p; // remember addr of insert
Cristian Ionescu-Idbohrn9a296fb2011-05-16 03:53:43 +02001840#endif
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001841 p += 1 + stupid_insert(p, c); // insert the char
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001842#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001843 if (showmatch && strchr(")]}", *sp) != NULL) {
1844 showmatching(sp);
1845 }
1846 if (autoindent && c == '\n') { // auto indent the new line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001847 char *q;
Denis Vlasenko00d84172008-11-24 07:34:42 +00001848 size_t len;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001849 q = prev_line(p); // use prev line as template
Denis Vlasenko00d84172008-11-24 07:34:42 +00001850 len = strspn(q, " \t"); // space or tab
1851 if (len) {
Denis Vlasenko3266aa92009-04-01 11:24:04 +00001852 uintptr_t bias;
Denis Vlasenko00d84172008-11-24 07:34:42 +00001853 bias = text_hole_make(p, len);
1854 p += bias;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001855 q += bias;
Denis Vlasenko00d84172008-11-24 07:34:42 +00001856 memcpy(p, q, len);
Denis Vlasenko3266aa92009-04-01 11:24:04 +00001857 p += len;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001858 }
1859 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001860#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001861 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001862 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001863}
1864
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001865// might reallocate text[]! use p += stupid_insert(p, ...),
1866// and be careful to not use pointers into potentially freed text[]!
1867static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001868{
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001869 uintptr_t bias;
1870 bias = text_hole_make(p, 1);
1871 p += bias;
Denis Vlasenkob1759462008-06-20 20:20:54 +00001872 *p = c;
1873 //file_modified++; - done by text_hole_make()
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001874 return bias;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001875}
1876
Denis Vlasenko33875382008-06-21 20:31:50 +00001877static int find_range(char **start, char **stop, char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001878{
Paul Foxc51fc7b2008-03-06 01:34:23 +00001879 char *save_dot, *p, *q, *t;
1880 int cnt, multiline = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001881
1882 save_dot = dot;
1883 p = q = dot;
1884
1885 if (strchr("cdy><", c)) {
1886 // these cmds operate on whole lines
1887 p = q = begin_line(p);
1888 for (cnt = 1; cnt < cmdcnt; cnt++) {
1889 q = next_line(q);
1890 }
1891 q = end_line(q);
Paul Foxc51fc7b2008-03-06 01:34:23 +00001892 } else if (strchr("^%$0bBeEfth\b\177", c)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001893 // These cmds operate on char positions
1894 do_cmd(c); // execute movement cmd
1895 q = dot;
1896 } else if (strchr("wW", c)) {
1897 do_cmd(c); // execute movement cmd
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001898 // if we are at the next word's first char
1899 // step back one char
1900 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00001901 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001902 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1903 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1904 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001905 if (dot > text && *dot == '\n')
1906 dot--; // stay off NL
1907 q = dot;
1908 } else if (strchr("H-k{", c)) {
1909 // these operate on multi-lines backwards
1910 q = end_line(dot); // find NL
1911 do_cmd(c); // execute movement cmd
1912 dot_begin();
1913 p = dot;
1914 } else if (strchr("L+j}\r\n", c)) {
1915 // these operate on multi-lines forwards
1916 p = begin_line(dot);
1917 do_cmd(c); // execute movement cmd
1918 dot_end(); // find NL
1919 q = dot;
1920 } else {
Paul Foxc51fc7b2008-03-06 01:34:23 +00001921 // nothing -- this causes any other values of c to
1922 // represent the one-character range under the
1923 // cursor. this is correct for ' ' and 'l', but
1924 // perhaps no others.
1925 //
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001926 }
Paul Foxc51fc7b2008-03-06 01:34:23 +00001927 if (q < p) {
1928 t = q;
1929 q = p;
1930 p = t;
1931 }
1932
Denis Vlasenko42cc3042008-03-24 02:05:58 +00001933 // backward char movements don't include start position
Paul Foxc51fc7b2008-03-06 01:34:23 +00001934 if (q > p && strchr("^0bBh\b\177", c)) q--;
1935
1936 multiline = 0;
1937 for (t = p; t <= q; t++) {
1938 if (*t == '\n') {
1939 multiline = 1;
1940 break;
1941 }
1942 }
1943
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001944 *start = p;
1945 *stop = q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001946 dot = save_dot;
Paul Foxc51fc7b2008-03-06 01:34:23 +00001947 return multiline;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001948}
1949
Denis Vlasenko33875382008-06-21 20:31:50 +00001950static int st_test(char *p, int type, int dir, char *tested)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001951{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001952 char c, c0, ci;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001953 int test, inc;
1954
1955 inc = dir;
1956 c = c0 = p[0];
1957 ci = p[inc];
1958 test = 0;
1959
1960 if (type == S_BEFORE_WS) {
1961 c = ci;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02001962 test = (!isspace(c) || c == '\n');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001963 }
1964 if (type == S_TO_WS) {
1965 c = c0;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02001966 test = (!isspace(c) || c == '\n');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001967 }
1968 if (type == S_OVER_WS) {
1969 c = c0;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02001970 test = isspace(c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001971 }
1972 if (type == S_END_PUNCT) {
1973 c = ci;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02001974 test = ispunct(c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001975 }
1976 if (type == S_END_ALNUM) {
1977 c = ci;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02001978 test = (isalnum(c) || c == '_');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001979 }
1980 *tested = c;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001981 return test;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001982}
1983
Denis Vlasenko33875382008-06-21 20:31:50 +00001984static char *skip_thing(char *p, int linecnt, int dir, int type)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001985{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001986 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001987
1988 while (st_test(p, type, dir, &c)) {
1989 // make sure we limit search to correct number of lines
1990 if (c == '\n' && --linecnt < 1)
1991 break;
1992 if (dir >= 0 && p >= end - 1)
1993 break;
1994 if (dir < 0 && p <= text)
1995 break;
1996 p += dir; // move to next char
1997 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001998 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001999}
2000
2001// find matching char of pair () [] {}
Denis Vlasenko33875382008-06-21 20:31:50 +00002002static char *find_pair(char *p, const char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002003{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002004 char match, *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002005 int dir, level;
2006
2007 match = ')';
2008 level = 1;
2009 dir = 1; // assume forward
2010 switch (c) {
Paul Foxc51fc7b2008-03-06 01:34:23 +00002011 case '(': match = ')'; break;
2012 case '[': match = ']'; break;
2013 case '{': match = '}'; break;
2014 case ')': match = '('; dir = -1; break;
2015 case ']': match = '['; dir = -1; break;
2016 case '}': match = '{'; dir = -1; break;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002017 }
2018 for (q = p + dir; text <= q && q < end; q += dir) {
2019 // look for match, count levels of pairs (( ))
2020 if (*q == c)
2021 level++; // increase pair levels
2022 if (*q == match)
2023 level--; // reduce pair level
2024 if (level == 0)
2025 break; // found matching pair
2026 }
2027 if (level != 0)
2028 q = NULL; // indicate no match
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002029 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002030}
2031
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002032#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002033// show the matching char of a pair, () [] {}
Denis Vlasenkof882f082007-12-23 02:36:51 +00002034static void showmatching(char *p)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002035{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002036 char *q, *save_dot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002037
2038 // we found half of a pair
2039 q = find_pair(p, *p); // get loc of matching char
2040 if (q == NULL) {
2041 indicate_error('3'); // no matching char
2042 } else {
2043 // "q" now points to matching pair
2044 save_dot = dot; // remember where we are
2045 dot = q; // go to new loc
2046 refresh(FALSE); // let the user see it
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002047 mysleep(40); // give user some time
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002048 dot = save_dot; // go back to old loc
2049 refresh(FALSE);
2050 }
2051}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002052#endif /* FEATURE_VI_SETOPTS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002053
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002054// open a hole in text[]
2055// might reallocate text[]! use p += text_hole_make(p, ...),
2056// and be careful to not use pointers into potentially freed text[]!
2057static uintptr_t text_hole_make(char *p, int size) // at "p", make a 'size' byte hole
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002058{
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002059 uintptr_t bias = 0;
2060
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002061 if (size <= 0)
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002062 return bias;
Denis Vlasenkob1759462008-06-20 20:20:54 +00002063 end += size; // adjust the new END
2064 if (end >= (text + text_size)) {
2065 char *new_text;
2066 text_size += end - (text + text_size) + 10240;
2067 new_text = xrealloc(text, text_size);
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002068 bias = (new_text - text);
2069 screenbegin += bias;
2070 dot += bias;
2071 end += bias;
2072 p += bias;
Denis Vlasenkob1759462008-06-20 20:20:54 +00002073 text = new_text;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002074 }
Denis Vlasenkod6995442008-06-27 04:06:13 +00002075 memmove(p + size, p, end - size - p);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002076 memset(p, ' ', size); // clear new hole
Denis Vlasenkob1759462008-06-20 20:20:54 +00002077 file_modified++;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002078 return bias;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002079}
2080
2081// close a hole in text[]
Denis Vlasenko33875382008-06-21 20:31:50 +00002082static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclusive
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002083{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002084 char *src, *dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002085 int cnt, hole_size;
2086
2087 // move forwards, from beginning
2088 // assume p <= q
2089 src = q + 1;
2090 dest = p;
2091 if (q < p) { // they are backward- swap them
2092 src = p + 1;
2093 dest = q;
2094 }
2095 hole_size = q - p + 1;
2096 cnt = end - src;
2097 if (src < text || src > end)
2098 goto thd0;
2099 if (dest < text || dest >= end)
2100 goto thd0;
2101 if (src >= end)
2102 goto thd_atend; // just delete the end of the buffer
Denis Vlasenkob1759462008-06-20 20:20:54 +00002103 memmove(dest, src, cnt);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002104 thd_atend:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002105 end = end - hole_size; // adjust the new END
2106 if (dest >= end)
2107 dest = end - 1; // make sure dest in below end-1
2108 if (end <= text)
2109 dest = end = text; // keep pointers valid
Denis Vlasenkob1759462008-06-20 20:20:54 +00002110 file_modified++;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002111 thd0:
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002112 return dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002113}
2114
2115// copy text into register, then delete text.
2116// if dist <= 0, do not include, or go past, a NewLine
2117//
Denis Vlasenko33875382008-06-21 20:31:50 +00002118static char *yank_delete(char *start, char *stop, int dist, int yf)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002119{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002120 char *p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002121
2122 // make sure start <= stop
2123 if (start > stop) {
2124 // they are backwards, reverse them
2125 p = start;
2126 start = stop;
2127 stop = p;
2128 }
2129 if (dist <= 0) {
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +00002130 // we cannot cross NL boundaries
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002131 p = start;
2132 if (*p == '\n')
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002133 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002134 // dont go past a NewLine
2135 for (; p + 1 <= stop; p++) {
2136 if (p[1] == '\n') {
2137 stop = p; // "stop" just before NewLine
2138 break;
2139 }
2140 }
2141 }
2142 p = start;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002143#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002144 text_yank(start, stop, YDreg);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002145#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002146 if (yf == YANKDEL) {
2147 p = text_hole_delete(start, stop);
2148 } // delete lines
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002149 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002150}
2151
2152static void show_help(void)
2153{
2154 puts("These features are available:"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002155#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002156 "\n\tPattern searches with / and ?"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002157#endif
2158#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002159 "\n\tLast command repeat with \'.\'"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002160#endif
2161#if ENABLE_FEATURE_VI_YANKMARK
Bernhard Reutner-Fischerd24d5c82007-10-01 18:04:42 +00002162 "\n\tLine marking with 'x"
2163 "\n\tNamed buffers with \"x"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002164#endif
2165#if ENABLE_FEATURE_VI_READONLY
Walter Harmsb9ba5802011-06-27 02:59:37 +02002166 //not implemented: "\n\tReadonly if vi is called as \"view\""
2167 //redundant: usage text says this too: "\n\tReadonly with -R command line arg"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002168#endif
2169#if ENABLE_FEATURE_VI_SET
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002170 "\n\tSome colon mode commands with \':\'"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002171#endif
2172#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002173 "\n\tSettable options with \":set\""
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002174#endif
2175#if ENABLE_FEATURE_VI_USE_SIGNALS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002176 "\n\tSignal catching- ^C"
2177 "\n\tJob suspend and resume with ^Z"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002178#endif
2179#if ENABLE_FEATURE_VI_WIN_RESIZE
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002180 "\n\tAdapt to window re-sizes"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002181#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002182 );
2183}
2184
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002185#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002186static void start_new_cmd_q(char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002187{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002188 // get buffer for new cmd
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002189 // if there is a current cmd count put it in the buffer first
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002190 if (cmdcnt > 0) {
Paul Foxc51fc7b2008-03-06 01:34:23 +00002191 lmc_len = sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002192 } else { // just save char c onto queue
Paul Foxd957b952005-11-28 18:07:53 +00002193 last_modifying_cmd[0] = c;
Paul Foxc51fc7b2008-03-06 01:34:23 +00002194 lmc_len = 1;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002195 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002196 adding2q = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002197}
2198
2199static void end_cmd_q(void)
2200{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002201#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002202 YDreg = 26; // go back to default Yank/Delete reg
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002203#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002204 adding2q = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002205}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002206#endif /* FEATURE_VI_DOT_CMD */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002207
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002208#if ENABLE_FEATURE_VI_YANKMARK \
2209 || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
2210 || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002211// might reallocate text[]! use p += string_insert(p, ...),
2212// and be careful to not use pointers into potentially freed text[]!
2213static uintptr_t string_insert(char *p, const char *s) // insert the string at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002214{
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002215 uintptr_t bias;
2216 int i;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002217
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002218 i = strlen(s);
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002219 bias = text_hole_make(p, i);
2220 p += bias;
Denis Vlasenko00d84172008-11-24 07:34:42 +00002221 memcpy(p, s, i);
Denis Vlasenko33875382008-06-21 20:31:50 +00002222#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002223 {
2224 int cnt;
2225 for (cnt = 0; *s != '\0'; s++) {
2226 if (*s == '\n')
2227 cnt++;
2228 }
2229 status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2230 }
Denis Vlasenko33875382008-06-21 20:31:50 +00002231#endif
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002232 return bias;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002233}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002234#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002235
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002236#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko33875382008-06-21 20:31:50 +00002237static char *text_yank(char *p, char *q, int dest) // copy text into a register
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002238{
Denis Vlasenko00d84172008-11-24 07:34:42 +00002239 int cnt = q - p;
2240 if (cnt < 0) { // they are backwards- reverse them
2241 p = q;
2242 cnt = -cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002243 }
Denis Vlasenko00d84172008-11-24 07:34:42 +00002244 free(reg[dest]); // if already a yank register, free it
2245 reg[dest] = xstrndup(p, cnt + 1);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002246 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002247}
2248
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002249static char what_reg(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002250{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002251 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002252
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002253 c = 'D'; // default to D-reg
2254 if (0 <= YDreg && YDreg <= 25)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002255 c = 'a' + (char) YDreg;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002256 if (YDreg == 26)
2257 c = 'D';
2258 if (YDreg == 27)
2259 c = 'U';
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002260 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002261}
2262
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002263static void check_context(char cmd)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002264{
2265 // A context is defined to be "modifying text"
2266 // Any modifying command establishes a new context.
2267
2268 if (dot < context_start || dot > context_end) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002269 if (strchr(modifying_cmds, cmd) != NULL) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002270 // we are trying to modify text[]- make this the current context
2271 mark[27] = mark[26]; // move cur to prev
2272 mark[26] = dot; // move local to cur
2273 context_start = prev_line(prev_line(dot));
2274 context_end = next_line(next_line(dot));
2275 //loiter= start_loiter= now;
2276 }
2277 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002278}
2279
Denis Vlasenkof882f082007-12-23 02:36:51 +00002280static char *swap_context(char *p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002281{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002282 char *tmp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002283
2284 // the current context is in mark[26]
2285 // the previous context is in mark[27]
2286 // only swap context if other context is valid
2287 if (text <= mark[27] && mark[27] <= end - 1) {
2288 tmp = mark[27];
2289 mark[27] = mark[26];
2290 mark[26] = tmp;
2291 p = mark[26]; // where we are going- previous context
2292 context_start = prev_line(prev_line(prev_line(p)));
2293 context_end = next_line(next_line(next_line(p)));
2294 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002295 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002296}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002297#endif /* FEATURE_VI_YANKMARK */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002298
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002299//----- Set terminal attributes --------------------------------
2300static void rawmode(void)
2301{
2302 tcgetattr(0, &term_orig);
2303 term_vi = term_orig;
2304 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2305 term_vi.c_iflag &= (~IXON & ~ICRNL);
2306 term_vi.c_oflag &= (~ONLCR);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002307 term_vi.c_cc[VMIN] = 1;
2308 term_vi.c_cc[VTIME] = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002309 erase_char = term_vi.c_cc[VERASE];
Denis Vlasenko202ac502008-11-05 13:20:58 +00002310 tcsetattr_stdin_TCSANOW(&term_vi);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002311}
2312
2313static void cookmode(void)
2314{
Denys Vlasenko8131eea2009-11-02 14:19:51 +01002315 fflush_all();
Denis Vlasenko202ac502008-11-05 13:20:58 +00002316 tcsetattr_stdin_TCSANOW(&term_orig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002317}
2318
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002319#if ENABLE_FEATURE_VI_USE_SIGNALS
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002320//----- Come here when we get a window resize signal ---------
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002321static void winch_sig(int sig UNUSED_PARAM)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002322{
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002323 int save_errno = errno;
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002324 // FIXME: do it in main loop!!!
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002325 signal(SIGWINCH, winch_sig);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002326 query_screen_dimensions();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002327 new_screen(rows, columns); // get memory for virtual screen
2328 redraw(TRUE); // re-draw the screen
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002329 errno = save_errno;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002330}
2331
2332//----- Come here when we get a continue signal -------------------
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002333static void cont_sig(int sig UNUSED_PARAM)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002334{
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002335 int save_errno = errno;
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002336 rawmode(); // terminal to "raw"
2337 last_status_cksum = 0; // force status update
2338 redraw(TRUE); // re-draw the screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002339
2340 signal(SIGTSTP, suspend_sig);
2341 signal(SIGCONT, SIG_DFL);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002342 //kill(my_pid, SIGCONT); // huh? why? we are already "continued"...
2343 errno = save_errno;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002344}
2345
2346//----- Come here when we get a Suspend signal -------------------
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002347static void suspend_sig(int sig UNUSED_PARAM)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002348{
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002349 int save_errno = errno;
Denis Vlasenko267e16c2008-10-14 10:34:41 +00002350 go_bottom_and_clear_to_eol();
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002351 cookmode(); // terminal to "cooked"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002352
2353 signal(SIGCONT, cont_sig);
2354 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002355 kill(my_pid, SIGTSTP);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002356 errno = save_errno;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002357}
2358
2359//----- Come here when we get a signal ---------------------------
2360static void catch_sig(int sig)
2361{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002362 signal(SIGINT, catch_sig);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002363 siglongjmp(restart, sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002364}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002365#endif /* FEATURE_VI_USE_SIGNALS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002366
Denys Vlasenko606291b2009-09-23 23:15:43 +02002367static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002368{
Denis Vlasenko87f3b262007-09-07 13:43:28 +00002369 struct pollfd pfd[1];
Denis Vlasenkocd5c7862007-05-17 16:37:22 +00002370
Denys Vlasenko606291b2009-09-23 23:15:43 +02002371 pfd[0].fd = STDIN_FILENO;
Denis Vlasenko87f3b262007-09-07 13:43:28 +00002372 pfd[0].events = POLLIN;
Denis Vlasenko5d61e712007-09-27 10:09:59 +00002373 return safe_poll(pfd, 1, hund*10) > 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002374}
2375
2376//----- IO Routines --------------------------------------------
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002377static int readit(void) // read (maybe cursor) key from stdin
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002378{
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002379 int c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002380
Denys Vlasenko8131eea2009-11-02 14:19:51 +01002381 fflush_all();
Denys Vlasenko58f108e2010-03-11 21:17:55 +01002382 c = read_key(STDIN_FILENO, readbuffer, /*timeout off:*/ -2);
Denis Vlasenko0112ff52008-10-25 23:23:00 +00002383 if (c == -1) { // EOF/error
2384 go_bottom_and_clear_to_eol();
2385 cookmode(); // terminal to "cooked"
2386 bb_error_msg_and_die("can't read user input");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002387 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002388 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002389}
2390
2391//----- IO Routines --------------------------------------------
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002392static int get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002393{
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002394 int c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002395
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002396#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002397 if (!adding2q) {
2398 // we are not adding to the q.
2399 // but, we may be reading from a q
2400 if (ioq == 0) {
2401 // there is no current q, read from STDIN
2402 c = readit(); // get the users input
2403 } else {
2404 // there is a queue to get chars from first
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002405 // careful with correct sign expansion!
2406 c = (unsigned char)*ioq++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002407 if (c == '\0') {
2408 // the end of the q, read from STDIN
2409 free(ioq_start);
2410 ioq_start = ioq = 0;
2411 c = readit(); // get the users input
2412 }
2413 }
2414 } else {
2415 // adding STDIN chars to q
2416 c = readit(); // get the users input
Denis Vlasenkob1759462008-06-20 20:20:54 +00002417 if (lmc_len >= MAX_INPUT_LEN - 1) {
2418 status_line_bold("last_modifying_cmd overrun");
2419 } else {
2420 // add new char to q
2421 last_modifying_cmd[lmc_len++] = c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002422 }
2423 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002424#else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002425 c = readit(); // get the users input
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002426#endif /* FEATURE_VI_DOT_CMD */
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002427 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002428}
2429
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002430// Get input line (uses "status line" area)
2431static char *get_input_line(const char *prompt)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002432{
Denis Vlasenkob1759462008-06-20 20:20:54 +00002433 // char [MAX_INPUT_LEN]
2434#define buf get_input_line__buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002435
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002436 int c;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002437 int i;
2438
2439 strcpy(buf, prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002440 last_status_cksum = 0; // force status update
Denis Vlasenko267e16c2008-10-14 10:34:41 +00002441 go_bottom_and_clear_to_eol();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002442 write1(prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002443
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002444 i = strlen(buf);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002445 while (i < MAX_INPUT_LEN) {
2446 c = get_one_char();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002447 if (c == '\n' || c == '\r' || c == 27)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002448 break; // this is end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002449 if (c == erase_char || c == 8 || c == 127) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002450 // user wants to erase prev char
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002451 buf[--i] = '\0';
2452 write1("\b \b"); // erase char on screen
2453 if (i <= 0) // user backs up before b-o-l, exit
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002454 break;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002455 } else if (c > 0 && c < 256) { // exclude Unicode
2456 // (TODO: need to handle Unicode)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002457 buf[i] = c;
2458 buf[++i] = '\0';
2459 bb_putchar(c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002460 }
2461 }
2462 refresh(FALSE);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002463 return buf;
Denis Vlasenkob1759462008-06-20 20:20:54 +00002464#undef buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002465}
2466
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002467static int file_size(const char *fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002468{
2469 struct stat st_buf;
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002470 int cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002471
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002472 cnt = -1;
Denys Vlasenkodef47832010-04-18 20:39:41 -07002473 if (fn && stat(fn, &st_buf) == 0) // see if file exists
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002474 cnt = (int) st_buf.st_size;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002475 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002476}
2477
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002478// might reallocate text[]!
2479static int file_insert(const char *fn, char *p, int update_ro_status)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002480{
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002481 int cnt = -1;
2482 int fd, size;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002483 struct stat statbuf;
2484
2485 /* Validate file */
2486 if (stat(fn, &statbuf) < 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002487 status_line_bold("\"%s\" %s", fn, strerror(errno));
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002488 goto fi0;
2489 }
Denis Vlasenko33875382008-06-21 20:31:50 +00002490 if (!S_ISREG(statbuf.st_mode)) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002491 // This is not a regular file
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002492 status_line_bold("\"%s\" Not a regular file", fn);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002493 goto fi0;
2494 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002495 if (p < text || p > end) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002496 status_line_bold("Trying to insert file outside of memory");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002497 goto fi0;
2498 }
2499
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002500 // read file to buffer
2501 fd = open(fn, O_RDONLY);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002502 if (fd < 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002503 status_line_bold("\"%s\" %s", fn, strerror(errno));
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002504 goto fi0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002505 }
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002506 size = statbuf.st_size;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002507 p += text_hole_make(p, size);
Denis Vlasenko4f95e5a2007-10-11 10:10:15 +00002508 cnt = safe_read(fd, p, size);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002509 if (cnt < 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002510 status_line_bold("\"%s\" %s", fn, strerror(errno));
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002511 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002512 } else if (cnt < size) {
2513 // There was a partial read, shrink unused space text[]
2514 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
Denys Vlasenko6331cf02009-11-13 09:08:27 +01002515 status_line_bold("can't read all of file \"%s\"", fn);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002516 }
2517 if (cnt >= size)
Paul Fox8552aec2005-09-16 12:20:05 +00002518 file_modified++;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002519 close(fd);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002520 fi0:
Denis Vlasenko856be772007-08-17 08:29:48 +00002521#if ENABLE_FEATURE_VI_READONLY
2522 if (update_ro_status
2523 && ((access(fn, W_OK) < 0) ||
2524 /* root will always have access()
2525 * so we check fileperms too */
2526 !(statbuf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))
2527 )
2528 ) {
Denis Vlasenko2f6ae432007-07-19 22:50:47 +00002529 SET_READONLY_FILE(readonly_mode);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002530 }
Denis Vlasenko856be772007-08-17 08:29:48 +00002531#endif
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002532 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002533}
2534
Denis Vlasenko33875382008-06-21 20:31:50 +00002535static int file_write(char *fn, char *first, char *last)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002536{
2537 int fd, cnt, charcnt;
2538
2539 if (fn == 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002540 status_line_bold("No current filename");
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002541 return -2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002542 }
Denis Vlasenko8abae882008-05-03 11:35:59 +00002543 /* By popular request we do not open file with O_TRUNC,
2544 * but instead ftruncate() it _after_ successful write.
2545 * Might reduce amount of data lost on power fail etc.
2546 */
2547 fd = open(fn, (O_WRONLY | O_CREAT), 0666);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002548 if (fd < 0)
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002549 return -1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002550 cnt = last - first + 1;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002551 charcnt = full_write(fd, first, cnt);
Denis Vlasenko8abae882008-05-03 11:35:59 +00002552 ftruncate(fd, charcnt);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002553 if (charcnt == cnt) {
2554 // good write
Denis Vlasenkob1759462008-06-20 20:20:54 +00002555 //file_modified = FALSE;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002556 } else {
2557 charcnt = 0;
2558 }
2559 close(fd);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002560 return charcnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002561}
2562
2563//----- Terminal Drawing ---------------------------------------
2564// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002565// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002566// screen coordinates
2567// 0,0 ... 0,79
2568// 1,0 ... 1,79
2569// . ... .
2570// . ... .
2571// 22,0 ... 22,79
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002572// 23,0 ... 23,79 <- status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002573
2574//----- Move the cursor to row x col (count from 0, not 1) -------
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002575static void place_cursor(int row, int col, int optimize)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002576{
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002577 char cm1[sizeof(CMrc) + sizeof(int)*3 * 2];
Denis Vlasenko7b54dc72008-07-17 21:32:32 +00002578#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
2579 enum {
2580 SZ_UP = sizeof(CMup),
2581 SZ_DN = sizeof(CMdown),
2582 SEQ_SIZE = SZ_UP > SZ_DN ? SZ_UP : SZ_DN,
2583 };
2584 char cm2[SEQ_SIZE * 5 + 32]; // bigger than worst case size
2585#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002586 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002587
2588 if (row < 0) row = 0;
2589 if (row >= rows) row = rows - 1;
2590 if (col < 0) col = 0;
2591 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002592
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002593 //----- 1. Try the standard terminal ESC sequence
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002594 sprintf(cm1, CMrc, row + 1, col + 1);
2595 cm = cm1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002596
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002597#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002598 if (optimize && col < 16) {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002599 char *screenp;
2600 int Rrow = last_row;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002601 int diff = Rrow - row;
2602
2603 if (diff < -5 || diff > 5)
2604 goto skip;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002605
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002606 //----- find the minimum # of chars to move cursor -------------
2607 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2608 cm2[0] = '\0';
2609
2610 // move to the correct row
2611 while (row < Rrow) {
2612 // the cursor has to move up
2613 strcat(cm2, CMup);
2614 Rrow--;
2615 }
2616 while (row > Rrow) {
2617 // the cursor has to move down
2618 strcat(cm2, CMdown);
2619 Rrow++;
2620 }
2621
2622 // now move to the correct column
2623 strcat(cm2, "\r"); // start at col 0
2624 // just send out orignal source char to get to correct place
2625 screenp = &screen[row * columns]; // start of screen line
2626 strncat(cm2, screenp, col);
2627
2628 // pick the shortest cursor motion to send out
2629 if (strlen(cm2) < strlen(cm)) {
2630 cm = cm2;
2631 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002632 skip: ;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002633 }
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00002634 last_row = row;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002635#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002636 write1(cm);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002637}
2638
2639//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002640static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002641{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002642 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002643}
2644
Denis Vlasenko267e16c2008-10-14 10:34:41 +00002645static void go_bottom_and_clear_to_eol(void)
2646{
2647 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2648 clear_to_eol(); // erase to end of line
2649}
2650
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002651//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002652static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002653{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002654 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002655}
2656
2657//----- Start standout mode ------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002658static void standout_start(void) // send "start reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002659{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002660 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002661}
2662
2663//----- End standout mode --------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002664static void standout_end(void) // send "end reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002665{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002666 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002667}
2668
2669//----- Flash the screen --------------------------------------
2670static void flash(int h)
2671{
2672 standout_start(); // send "start reverse video" sequence
2673 redraw(TRUE);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002674 mysleep(h);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002675 standout_end(); // send "end reverse video" sequence
2676 redraw(TRUE);
2677}
2678
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002679static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002680{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002681#if ENABLE_FEATURE_VI_CRASHME
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002682 if (crashme > 0)
2683 return; // generate a random command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002684#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002685 if (!err_method) {
2686 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002687 } else {
2688 flash(10);
2689 }
2690}
2691
2692//----- Screen[] Routines --------------------------------------
2693//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002694static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002695{
2696 memset(screen, ' ', screensize); // clear new screen
2697}
2698
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002699static int bufsum(char *buf, int count)
Paul Fox8552aec2005-09-16 12:20:05 +00002700{
2701 int sum = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002702 char *e = buf + count;
2703
Paul Fox8552aec2005-09-16 12:20:05 +00002704 while (buf < e)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002705 sum += (unsigned char) *buf++;
Paul Fox8552aec2005-09-16 12:20:05 +00002706 return sum;
2707}
2708
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002709//----- Draw the status line at bottom of the screen -------------
2710static void show_status_line(void)
2711{
Paul Foxc3504852005-09-16 12:48:18 +00002712 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002713
Paul Fox8552aec2005-09-16 12:20:05 +00002714 // either we already have an error or status message, or we
2715 // create one.
2716 if (!have_status_msg) {
2717 cnt = format_edit_status();
2718 cksum = bufsum(status_buffer, cnt);
2719 }
2720 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002721 last_status_cksum = cksum; // remember if we have seen this line
Denis Vlasenko267e16c2008-10-14 10:34:41 +00002722 go_bottom_and_clear_to_eol();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002723 write1(status_buffer);
Paul Fox8552aec2005-09-16 12:20:05 +00002724 if (have_status_msg) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002725 if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
Paul Fox8552aec2005-09-16 12:20:05 +00002726 (columns - 1) ) {
2727 have_status_msg = 0;
2728 Hit_Return();
2729 }
2730 have_status_msg = 0;
2731 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002732 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2733 }
Denys Vlasenko8131eea2009-11-02 14:19:51 +01002734 fflush_all();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002735}
2736
2737//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00002738// format status buffer, with STANDOUT mode
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002739static void status_line_bold(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002740{
2741 va_list args;
2742
2743 va_start(args, format);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002744 strcpy(status_buffer, SOs); // Terminal standout mode on
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002745 vsprintf(status_buffer + sizeof(SOs)-1, format, args);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002746 strcat(status_buffer, SOn); // Terminal standout mode off
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002747 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002748
2749 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002750}
2751
Paul Fox8552aec2005-09-16 12:20:05 +00002752// format status buffer
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002753static void status_line(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002754{
2755 va_list args;
2756
2757 va_start(args, format);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002758 vsprintf(status_buffer, format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002759 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002760
2761 have_status_msg = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002762}
2763
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002764// copy s to buf, convert unprintable
2765static void print_literal(char *buf, const char *s)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002766{
Denys Vlasenkoc2704542009-11-20 19:14:19 +01002767 char *d;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002768 unsigned char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002769
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002770 buf[0] = '\0';
2771 if (!s[0])
2772 s = "(NULL)";
Denys Vlasenkoc2704542009-11-20 19:14:19 +01002773
2774 d = buf;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002775 for (; *s; s++) {
2776 int c_is_no_print;
2777
2778 c = *s;
2779 c_is_no_print = (c & 0x80) && !Isprint(c);
2780 if (c_is_no_print) {
Denys Vlasenkoc2704542009-11-20 19:14:19 +01002781 strcpy(d, SOn);
2782 d += sizeof(SOn)-1;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002783 c = '.';
2784 }
Denys Vlasenkoc2704542009-11-20 19:14:19 +01002785 if (c < ' ' || c == 0x7f) {
2786 *d++ = '^';
2787 c |= '@'; /* 0x40 */
2788 if (c == 0x7f)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002789 c = '?';
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002790 }
Denys Vlasenkoc2704542009-11-20 19:14:19 +01002791 *d++ = c;
2792 *d = '\0';
2793 if (c_is_no_print) {
2794 strcpy(d, SOs);
2795 d += sizeof(SOs)-1;
2796 }
2797 if (*s == '\n') {
2798 *d++ = '$';
2799 *d = '\0';
2800 }
2801 if (d - buf > MAX_INPUT_LEN - 10) // paranoia
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002802 break;
2803 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002804}
2805
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002806static void not_implemented(const char *s)
2807{
2808 char buf[MAX_INPUT_LEN];
2809
2810 print_literal(buf, s);
2811 status_line_bold("\'%s\' is not implemented", buf);
2812}
2813
2814// show file status on status line
2815static int format_edit_status(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002816{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002817 static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
Denis Vlasenkob1759462008-06-20 20:20:54 +00002818
2819#define tot format_edit_status__tot
2820
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002821 int cur, percent, ret, trunc_at;
2822
Paul Fox8552aec2005-09-16 12:20:05 +00002823 // file_modified is now a counter rather than a flag. this
2824 // helps reduce the amount of line counting we need to do.
2825 // (this will cause a mis-reporting of modified status
2826 // once every MAXINT editing operations.)
2827
2828 // it would be nice to do a similar optimization here -- if
2829 // we haven't done a motion that could have changed which line
2830 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002831 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00002832
2833 // reduce counting -- the total lines can't have
2834 // changed if we haven't done any edits.
2835 if (file_modified != last_file_modified) {
2836 tot = cur + count_lines(dot, end - 1) - 1;
2837 last_file_modified = file_modified;
2838 }
2839
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002840 // current line percent
2841 // ------------- ~~ ----------
2842 // total lines 100
2843 if (tot > 0) {
2844 percent = (100 * cur) / tot;
2845 } else {
2846 cur = tot = 0;
2847 percent = 100;
2848 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00002849
Paul Fox8552aec2005-09-16 12:20:05 +00002850 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2851 columns : STATUS_BUFFER_LEN-1;
2852
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002853 ret = snprintf(status_buffer, trunc_at+1,
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002854#if ENABLE_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002855 "%c %s%s%s %d/%d %d%%",
2856#else
2857 "%c %s%s %d/%d %d%%",
2858#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002859 cmd_mode_indicator[cmd_mode & 3],
2860 (current_filename != NULL ? current_filename : "No file"),
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002861#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002862 (readonly_mode ? " [Readonly]" : ""),
Paul Fox8552aec2005-09-16 12:20:05 +00002863#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002864 (file_modified ? " [Modified]" : ""),
Paul Fox8552aec2005-09-16 12:20:05 +00002865 cur, tot, percent);
2866
2867 if (ret >= 0 && ret < trunc_at)
2868 return ret; /* it all fit */
2869
2870 return trunc_at; /* had to truncate */
Denis Vlasenkob1759462008-06-20 20:20:54 +00002871#undef tot
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002872}
2873
2874//----- Force refresh of all Lines -----------------------------
2875static void redraw(int full_screen)
2876{
2877 place_cursor(0, 0, FALSE); // put cursor in correct place
Denis Vlasenkob1759462008-06-20 20:20:54 +00002878 clear_to_eos(); // tell terminal to erase display
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002879 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00002880 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002881 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00002882 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002883}
2884
2885//----- Format a text[] line into a buffer ---------------------
Denis Vlasenko68404f12008-03-17 09:00:54 +00002886static char* format_line(char *src /*, int li*/)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002887{
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002888 unsigned char c;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002889 int co;
2890 int ofs = offset;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002891 char *dest = scr_out_buf; // [MAX_SCR_COLS + MAX_TABSTOP * 2]
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002892
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002893 c = '~'; // char in col 0 in non-existent lines is '~'
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00002894 co = 0;
2895 while (co < columns + tabstop) {
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002896 // have we gone past the end?
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002897 if (src < end) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002898 c = *src++;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002899 if (c == '\n')
2900 break;
2901 if ((c & 0x80) && !Isprint(c)) {
2902 c = '.';
2903 }
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002904 if (c < ' ' || c == 0x7f) {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002905 if (c == '\t') {
2906 c = ' ';
2907 // co % 8 != 7
2908 while ((co % tabstop) != (tabstop - 1)) {
2909 dest[co++] = c;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002910 }
2911 } else {
2912 dest[co++] = '^';
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002913 if (c == 0x7f)
2914 c = '?';
2915 else
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002916 c += '@'; // Ctrl-X -> 'X'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002917 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002918 }
2919 }
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00002920 dest[co++] = c;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002921 // discard scrolled-off-to-the-left portion,
2922 // in tabstop-sized pieces
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002923 if (ofs >= tabstop && co >= tabstop) {
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00002924 memmove(dest, dest + tabstop, co);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002925 co -= tabstop;
2926 ofs -= tabstop;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002927 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002928 if (src >= end)
2929 break;
2930 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002931 // check "short line, gigantic offset" case
2932 if (co < ofs)
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00002933 ofs = co;
2934 // discard last scrolled off part
2935 co -= ofs;
2936 dest += ofs;
2937 // fill the rest with spaces
2938 if (co < columns)
2939 memset(&dest[co], ' ', columns - co);
2940 return dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002941}
2942
2943//----- Refresh the changed screen lines -----------------------
2944// Copy the source line from text[] into the buffer and note
2945// if the current screenline is different from the new buffer.
2946// If they differ then that line needs redrawing on the terminal.
2947//
2948static void refresh(int full_screen)
2949{
Denis Vlasenkob1759462008-06-20 20:20:54 +00002950#define old_offset refresh__old_offset
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002951
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002952 int li, changed;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002953 char *tp, *sp; // pointer into text[] and screen[]
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002954
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +02002955 if (ENABLE_FEATURE_VI_WIN_RESIZE IF_FEATURE_VI_ASK_TERMINAL(&& !G.get_rowcol_error) ) {
Denis Vlasenko55995022008-05-18 22:28:26 +00002956 unsigned c = columns, r = rows;
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002957 query_screen_dimensions();
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002958 full_screen |= (c - columns) | (r - rows);
2959 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002960 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2961 tp = screenbegin; // index into text[] of top line
2962
2963 // compare text[] to screen[] and mark screen[] lines that need updating
2964 for (li = 0; li < rows - 1; li++) {
2965 int cs, ce; // column start & end
Denis Vlasenkof882f082007-12-23 02:36:51 +00002966 char *out_buf;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002967 // format current text line
Denis Vlasenko68404f12008-03-17 09:00:54 +00002968 out_buf = format_line(tp /*, li*/);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002969
2970 // skip to the end of the current text[] line
Denis Vlasenkof882f082007-12-23 02:36:51 +00002971 if (tp < end) {
2972 char *t = memchr(tp, '\n', end - tp);
2973 if (!t) t = end - 1;
2974 tp = t + 1;
2975 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002976
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002977 // see if there are any changes between vitual screen and out_buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002978 changed = FALSE; // assume no change
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002979 cs = 0;
2980 ce = columns - 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002981 sp = &screen[li * columns]; // start of screen line
2982 if (full_screen) {
2983 // force re-draw of every single column from 0 - columns-1
2984 goto re0;
2985 }
2986 // compare newly formatted buffer with virtual screen
2987 // look forward for first difference between buf and screen
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002988 for (; cs <= ce; cs++) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002989 if (out_buf[cs] != sp[cs]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002990 changed = TRUE; // mark for redraw
2991 break;
2992 }
2993 }
2994
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002995 // look backward for last difference between out_buf and screen
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002996 for (; ce >= cs; ce--) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002997 if (out_buf[ce] != sp[ce]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002998 changed = TRUE; // mark for redraw
2999 break;
3000 }
3001 }
3002 // now, cs is index of first diff, and ce is index of last diff
3003
3004 // if horz offset has changed, force a redraw
3005 if (offset != old_offset) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003006 re0:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003007 changed = TRUE;
3008 }
3009
3010 // make a sanity check of columns indexes
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003011 if (cs < 0) cs = 0;
3012 if (ce > columns - 1) ce = columns - 1;
3013 if (cs > ce) { cs = 0; ce = columns - 1; }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003014 // is there a change between vitual screen and out_buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003015 if (changed) {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003016 // copy changed part of buffer to virtual screen
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003017 memcpy(sp+cs, out_buf+cs, ce-cs+1);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003018
3019 // move cursor to column of first change
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003020 //if (offset != old_offset) {
3021 // // place_cursor is still too stupid
3022 // // to handle offsets correctly
3023 // place_cursor(li, cs, FALSE);
3024 //} else {
3025 place_cursor(li, cs, TRUE);
3026 //}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003027
3028 // write line out to terminal
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003029 fwrite(&sp[cs], ce - cs + 1, 1, stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003030 }
3031 }
3032
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003033 place_cursor(crow, ccol, TRUE);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003034
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003035 old_offset = offset;
Denis Vlasenkob1759462008-06-20 20:20:54 +00003036#undef old_offset
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003037}
3038
Eric Andersen3f980402001-04-04 17:31:15 +00003039//---------------------------------------------------------------------
3040//----- the Ascii Chart -----------------------------------------------
3041//
3042// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
3043// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
3044// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
3045// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
3046// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
3047// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
3048// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
3049// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
3050// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
3051// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
3052// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
3053// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
3054// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
3055// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
3056// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
3057// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
3058//---------------------------------------------------------------------
3059
3060//----- Execute a Vi Command -----------------------------------
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003061static void do_cmd(int c)
Eric Andersen3f980402001-04-04 17:31:15 +00003062{
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00003063 const char *msg = msg; // for compiler
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003064 char *p, *q, *save_dot;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003065 char buf[12];
Denis Vlasenkoc3a9dc82008-10-29 00:58:04 +00003066 int dir;
Paul Foxc51fc7b2008-03-06 01:34:23 +00003067 int cnt, i, j;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003068 int c1;
Eric Andersen3f980402001-04-04 17:31:15 +00003069
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00003070// c1 = c; // quiet the compiler
3071// cnt = yf = 0; // quiet the compiler
3072// msg = p = q = save_dot = buf; // quiet the compiler
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003073 memset(buf, '\0', 12);
Eric Andersenbff7a602001-11-17 07:15:43 +00003074
Paul Fox8552aec2005-09-16 12:20:05 +00003075 show_status_line();
3076
Eric Andersenbff7a602001-11-17 07:15:43 +00003077 /* if this is a cursor key, skip these checks */
3078 switch (c) {
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003079 case KEYCODE_UP:
3080 case KEYCODE_DOWN:
3081 case KEYCODE_LEFT:
3082 case KEYCODE_RIGHT:
3083 case KEYCODE_HOME:
3084 case KEYCODE_END:
3085 case KEYCODE_PAGEUP:
3086 case KEYCODE_PAGEDOWN:
3087 case KEYCODE_DELETE:
Eric Andersenbff7a602001-11-17 07:15:43 +00003088 goto key_cmd_mode;
3089 }
3090
Eric Andersen3f980402001-04-04 17:31:15 +00003091 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003092 // flip-flop Insert/Replace mode
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003093 if (c == KEYCODE_INSERT)
Denis Vlasenko2a51af22007-03-21 22:31:24 +00003094 goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00003095 // we are 'R'eplacing the current *dot with new char
3096 if (*dot == '\n') {
3097 // don't Replace past E-o-l
3098 cmd_mode = 1; // convert to insert
3099 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003100 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003101 if (c != 27)
3102 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3103 dot = char_insert(dot, c); // insert new char
3104 }
3105 goto dc1;
3106 }
3107 }
3108 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003109 // hitting "Insert" twice means "R" replace mode
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003110 if (c == KEYCODE_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00003111 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003112 if (1 <= c || Isprint(c)) {
3113 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00003114 }
3115 goto dc1;
3116 }
3117
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003118 key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00003119 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00003120 //case 0x01: // soh
3121 //case 0x09: // ht
3122 //case 0x0b: // vt
3123 //case 0x0e: // so
3124 //case 0x0f: // si
3125 //case 0x10: // dle
3126 //case 0x11: // dc1
3127 //case 0x13: // dc3
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003128#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00003129 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00003130 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003131 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003132#endif
Eric Andersen822c3832001-05-07 17:37:43 +00003133 //case 0x16: // syn
3134 //case 0x17: // etb
3135 //case 0x18: // can
3136 //case 0x1c: // fs
3137 //case 0x1d: // gs
3138 //case 0x1e: // rs
3139 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003140 //case '!': // !-
3141 //case '#': // #-
3142 //case '&': // &-
3143 //case '(': // (-
3144 //case ')': // )-
3145 //case '*': // *-
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003146 //case '=': // =-
3147 //case '@': // @-
3148 //case 'F': // F-
3149 //case 'K': // K-
3150 //case 'Q': // Q-
3151 //case 'S': // S-
3152 //case 'T': // T-
3153 //case 'V': // V-
3154 //case '[': // [-
3155 //case '\\': // \-
3156 //case ']': // ]-
3157 //case '_': // _-
3158 //case '`': // `-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003159 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003160 //case 'v': // v-
Denys Vlasenkob22bbff2009-07-04 16:50:43 +02003161 default: // unrecognized command
Eric Andersen3f980402001-04-04 17:31:15 +00003162 buf[0] = c;
3163 buf[1] = '\0';
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003164 not_implemented(buf);
Eric Andersen3f980402001-04-04 17:31:15 +00003165 end_cmd_q(); // stop adding to q
3166 case 0x00: // nul- ignore
3167 break;
3168 case 2: // ctrl-B scroll up full screen
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003169 case KEYCODE_PAGEUP: // Cursor Key Page Up
Eric Andersen3f980402001-04-04 17:31:15 +00003170 dot_scroll(rows - 2, -1);
3171 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003172 case 4: // ctrl-D scroll down half screen
3173 dot_scroll((rows - 2) / 2, 1);
3174 break;
3175 case 5: // ctrl-E scroll down one line
3176 dot_scroll(1, 1);
3177 break;
3178 case 6: // ctrl-F scroll down full screen
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003179 case KEYCODE_PAGEDOWN: // Cursor Key Page Down
Eric Andersen3f980402001-04-04 17:31:15 +00003180 dot_scroll(rows - 2, 1);
3181 break;
3182 case 7: // ctrl-G show current status
Paul Fox8552aec2005-09-16 12:20:05 +00003183 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003184 break;
3185 case 'h': // h- move left
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003186 case KEYCODE_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003187 case 8: // ctrl-H- move left (This may be ERASE char)
Denis Vlasenko2a51af22007-03-21 22:31:24 +00003188 case 0x7f: // DEL- move left (This may be ERASE char)
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003189 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003190 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003191 }
Eric Andersen3f980402001-04-04 17:31:15 +00003192 dot_left();
3193 break;
3194 case 10: // Newline ^J
3195 case 'j': // j- goto next line, same col
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003196 case KEYCODE_DOWN: // cursor key Down
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003197 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003198 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003199 }
Eric Andersen3f980402001-04-04 17:31:15 +00003200 dot_next(); // go to next B-o-l
3201 dot = move_to_col(dot, ccol + offset); // try stay in same col
3202 break;
3203 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003204 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003205 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003206 clear_to_eos(); // tel terminal to erase display
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003207 mysleep(10);
Eric Andersen3f980402001-04-04 17:31:15 +00003208 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003209 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003210 refresh(TRUE); // this will redraw the entire display
3211 break;
3212 case 13: // Carriage Return ^M
3213 case '+': // +- goto next line
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003214 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003215 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003216 }
Eric Andersen3f980402001-04-04 17:31:15 +00003217 dot_next();
3218 dot_skip_over_ws();
3219 break;
3220 case 21: // ctrl-U scroll up half screen
3221 dot_scroll((rows - 2) / 2, -1);
3222 break;
3223 case 25: // ctrl-Y scroll up one line
3224 dot_scroll(1, -1);
3225 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003226 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003227 if (cmd_mode == 0)
3228 indicate_error(c);
3229 cmd_mode = 0; // stop insrting
3230 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003231 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003232 break;
3233 case ' ': // move right
3234 case 'l': // move right
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003235 case KEYCODE_RIGHT: // Cursor Key Right
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003236 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003237 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003238 }
Eric Andersen3f980402001-04-04 17:31:15 +00003239 dot_right();
3240 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003241#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003242 case '"': // "- name a register to use for Delete/Yank
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003243 c1 = (get_one_char() | 0x20) - 'a'; // | 0x20 is tolower()
3244 if ((unsigned)c1 <= 25) { // a-z?
3245 YDreg = c1;
Eric Andersen3f980402001-04-04 17:31:15 +00003246 } else {
3247 indicate_error(c);
3248 }
3249 break;
3250 case '\'': // '- goto a specific mark
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003251 c1 = (get_one_char() | 0x20) - 'a';
3252 if ((unsigned)c1 <= 25) { // a-z?
Eric Andersen3f980402001-04-04 17:31:15 +00003253 // get the b-o-l
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003254 q = mark[c1];
Eric Andersen3f980402001-04-04 17:31:15 +00003255 if (text <= q && q < end) {
3256 dot = q;
3257 dot_begin(); // go to B-o-l
3258 dot_skip_over_ws();
3259 }
3260 } else if (c1 == '\'') { // goto previous context
3261 dot = swap_context(dot); // swap current and previous context
3262 dot_begin(); // go to B-o-l
3263 dot_skip_over_ws();
3264 } else {
3265 indicate_error(c);
3266 }
3267 break;
3268 case 'm': // m- Mark a line
3269 // this is really stupid. If there are any inserts or deletes
3270 // between text[0] and dot then this mark will not point to the
3271 // correct location! It could be off by many lines!
3272 // Well..., at least its quick and dirty.
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003273 c1 = (get_one_char() | 0x20) - 'a';
3274 if ((unsigned)c1 <= 25) { // a-z?
Eric Andersen3f980402001-04-04 17:31:15 +00003275 // remember the line
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003276 mark[c1] = dot;
Eric Andersen3f980402001-04-04 17:31:15 +00003277 } else {
3278 indicate_error(c);
3279 }
3280 break;
3281 case 'P': // P- Put register before
3282 case 'p': // p- put register after
3283 p = reg[YDreg];
Denis Vlasenko00d84172008-11-24 07:34:42 +00003284 if (p == NULL) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003285 status_line_bold("Nothing in register %c", what_reg());
Eric Andersen3f980402001-04-04 17:31:15 +00003286 break;
3287 }
3288 // are we putting whole lines or strings
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003289 if (strchr(p, '\n') != NULL) {
Eric Andersen3f980402001-04-04 17:31:15 +00003290 if (c == 'P') {
3291 dot_begin(); // putting lines- Put above
3292 }
3293 if (c == 'p') {
3294 // are we putting after very last line?
3295 if (end_line(dot) == (end - 1)) {
3296 dot = end; // force dot to end of text[]
3297 } else {
3298 dot_next(); // next line, then put before
3299 }
3300 }
3301 } else {
3302 if (c == 'p')
3303 dot_right(); // move to right, can move to NL
3304 }
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00003305 string_insert(dot, p); // insert the string
Eric Andersen3f980402001-04-04 17:31:15 +00003306 end_cmd_q(); // stop adding to q
3307 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003308 case 'U': // U- Undo; replace current line with original version
3309 if (reg[Ureg] != 0) {
3310 p = begin_line(dot);
3311 q = end_line(dot);
3312 p = text_hole_delete(p, q); // delete cur line
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00003313 p += string_insert(p, reg[Ureg]); // insert orig line
Eric Andersen3f980402001-04-04 17:31:15 +00003314 dot = p;
3315 dot_skip_over_ws();
3316 }
3317 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003318#endif /* FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003319 case '$': // $- goto end of line
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003320 case KEYCODE_END: // Cursor Key End
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003321 if (--cmdcnt > 0) {
3322 dot_next();
Eric Andersen3f980402001-04-04 17:31:15 +00003323 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003324 }
Glenn L McGrathee829062004-01-21 10:59:45 +00003325 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003326 break;
3327 case '%': // %- find matching char of pair () [] {}
3328 for (q = dot; q < end && *q != '\n'; q++) {
3329 if (strchr("()[]{}", *q) != NULL) {
3330 // we found half of a pair
3331 p = find_pair(q, *q);
3332 if (p == NULL) {
3333 indicate_error(c);
3334 } else {
3335 dot = p;
3336 }
3337 break;
3338 }
3339 }
3340 if (*q == '\n')
3341 indicate_error(c);
3342 break;
3343 case 'f': // f- forward to a user specified char
3344 last_forward_char = get_one_char(); // get the search char
3345 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003346 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003347 //
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +00003348 //**** fall through to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003349 case ';': // ;- look at rest of line for last forward char
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003350 if (--cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003351 do_cmd(';');
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003352 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003353 if (last_forward_char == 0)
3354 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003355 q = dot + 1;
3356 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3357 q++;
3358 }
3359 if (*q == last_forward_char)
3360 dot = q;
3361 break;
Paul Foxb5ee8db2008-02-14 01:17:01 +00003362 case ',': // repeat latest 'f' in opposite direction
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003363 if (--cmdcnt > 0) {
Paul Foxb5ee8db2008-02-14 01:17:01 +00003364 do_cmd(',');
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003365 }
Paul Foxb5ee8db2008-02-14 01:17:01 +00003366 if (last_forward_char == 0)
3367 break;
3368 q = dot - 1;
3369 while (q >= text && *q != '\n' && *q != last_forward_char) {
3370 q--;
3371 }
3372 if (q >= text && *q == last_forward_char)
3373 dot = q;
3374 break;
3375
Eric Andersen3f980402001-04-04 17:31:15 +00003376 case '-': // -- goto prev line
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003377 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003378 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003379 }
Eric Andersen3f980402001-04-04 17:31:15 +00003380 dot_prev();
3381 dot_skip_over_ws();
3382 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003383#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003384 case '.': // .- repeat the last modifying command
3385 // Stuff the last_modifying_cmd back into stdin
3386 // and let it be re-executed.
Denis Vlasenkob1759462008-06-20 20:20:54 +00003387 if (lmc_len > 0) {
Paul Foxc51fc7b2008-03-06 01:34:23 +00003388 last_modifying_cmd[lmc_len] = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003389 ioq = ioq_start = xstrdup(last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003390 }
3391 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003392#endif
3393#if ENABLE_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003394 case '?': // /- search for a pattern
3395 case '/': // /- search for a pattern
3396 buf[0] = c;
3397 buf[1] = '\0';
3398 q = get_input_line(buf); // get input line- use "status line"
Paul Fox4917c112008-03-05 16:44:02 +00003399 if (q[0] && !q[1]) {
3400 if (last_search_pattern[0])
Denis Vlasenkoc3a9dc82008-10-29 00:58:04 +00003401 last_search_pattern[0] = c;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003402 goto dc3; // if no pat re-use old pat
Paul Fox4917c112008-03-05 16:44:02 +00003403 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003404 if (q[0]) { // strlen(q) > 1: new pat- save it and find
Eric Andersen3f980402001-04-04 17:31:15 +00003405 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003406 free(last_search_pattern);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003407 last_search_pattern = xstrdup(q);
Eric Andersen3f980402001-04-04 17:31:15 +00003408 goto dc3; // now find the pattern
3409 }
3410 // user changed mind and erased the "/"- do nothing
3411 break;
3412 case 'N': // N- backward search for last pattern
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003413 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003414 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003415 }
Eric Andersen3f980402001-04-04 17:31:15 +00003416 dir = BACK; // assume BACKWARD search
3417 p = dot - 1;
3418 if (last_search_pattern[0] == '?') {
3419 dir = FORWARD;
3420 p = dot + 1;
3421 }
3422 goto dc4; // now search for pattern
3423 break;
3424 case 'n': // n- repeat search for last pattern
3425 // search rest of text[] starting at next char
3426 // if search fails return orignal "p" not the "p+1" address
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003427 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003428 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003429 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003430 dc3:
Denis Vlasenkoc3a9dc82008-10-29 00:58:04 +00003431 dir = FORWARD; // assume FORWARD search
3432 p = dot + 1;
Eric Andersen3f980402001-04-04 17:31:15 +00003433 if (last_search_pattern[0] == '?') {
3434 dir = BACK;
3435 p = dot - 1;
3436 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003437 dc4:
Eric Andersen3f980402001-04-04 17:31:15 +00003438 q = char_search(p, last_search_pattern + 1, dir, FULL);
3439 if (q != NULL) {
3440 dot = q; // good search, update "dot"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003441 msg = "";
Eric Andersen3f980402001-04-04 17:31:15 +00003442 goto dc2;
3443 }
3444 // no pattern found between "dot" and "end"- continue at top
3445 p = text;
3446 if (dir == BACK) {
3447 p = end - 1;
3448 }
3449 q = char_search(p, last_search_pattern + 1, dir, FULL);
3450 if (q != NULL) { // found something
3451 dot = q; // found new pattern- goto it
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003452 msg = "search hit BOTTOM, continuing at TOP";
Eric Andersen3f980402001-04-04 17:31:15 +00003453 if (dir == BACK) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003454 msg = "search hit TOP, continuing at BOTTOM";
Eric Andersen3f980402001-04-04 17:31:15 +00003455 }
3456 } else {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003457 msg = "Pattern not found";
Eric Andersen3f980402001-04-04 17:31:15 +00003458 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003459 dc2:
3460 if (*msg)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003461 status_line_bold("%s", msg);
Eric Andersen3f980402001-04-04 17:31:15 +00003462 break;
3463 case '{': // {- move backward paragraph
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003464 q = char_search(dot, "\n\n", BACK, FULL);
Eric Andersen3f980402001-04-04 17:31:15 +00003465 if (q != NULL) { // found blank line
3466 dot = next_line(q); // move to next blank line
3467 }
3468 break;
3469 case '}': // }- move forward paragraph
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003470 q = char_search(dot, "\n\n", FORWARD, FULL);
Eric Andersen3f980402001-04-04 17:31:15 +00003471 if (q != NULL) { // found blank line
3472 dot = next_line(q); // move to next blank line
3473 }
3474 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003475#endif /* FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003476 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003477 case '1': // 1-
3478 case '2': // 2-
3479 case '3': // 3-
3480 case '4': // 4-
3481 case '5': // 5-
3482 case '6': // 6-
3483 case '7': // 7-
3484 case '8': // 8-
3485 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003486 if (c == '0' && cmdcnt < 1) {
3487 dot_begin(); // this was a standalone zero
3488 } else {
3489 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3490 }
3491 break;
3492 case ':': // :- the colon mode commands
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003493 p = get_input_line(":"); // get input line- use "status line"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003494#if ENABLE_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003495 colon(p); // execute the command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003496#else
Eric Andersen822c3832001-05-07 17:37:43 +00003497 if (*p == ':')
3498 p++; // move past the ':'
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003499 cnt = strlen(p);
Eric Andersen822c3832001-05-07 17:37:43 +00003500 if (cnt <= 0)
3501 break;
Denys Vlasenko6548edd2009-06-15 12:44:11 +02003502 if (strncmp(p, "quit", cnt) == 0
3503 || strncmp(p, "q!", cnt) == 0 // delete lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003504 ) {
Matt Kraai1f0c4362001-12-20 23:13:26 +00003505 if (file_modified && p[1] != '!') {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003506 status_line_bold("No write since last change (:quit! overrides)");
Eric Andersen3f980402001-04-04 17:31:15 +00003507 } else {
3508 editing = 0;
3509 }
Denys Vlasenko6548edd2009-06-15 12:44:11 +02003510 } else if (strncmp(p, "write", cnt) == 0
3511 || strncmp(p, "wq", cnt) == 0
3512 || strncmp(p, "wn", cnt) == 0
3513 || (p[0] == 'x' && !p[1])
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003514 ) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003515 cnt = file_write(current_filename, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003516 if (cnt < 0) {
3517 if (cnt == -1)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003518 status_line_bold("Write error: %s", strerror(errno));
Paul Fox61e45db2005-10-09 14:43:22 +00003519 } else {
3520 file_modified = 0;
3521 last_file_modified = -1;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003522 status_line("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003523 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
3524 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
3525 ) {
Paul Fox61e45db2005-10-09 14:43:22 +00003526 editing = 0;
3527 }
Eric Andersen3f980402001-04-04 17:31:15 +00003528 }
Denys Vlasenko6548edd2009-06-15 12:44:11 +02003529 } else if (strncmp(p, "file", cnt) == 0) {
Paul Fox8552aec2005-09-16 12:20:05 +00003530 last_status_cksum = 0; // force status update
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003531 } else if (sscanf(p, "%d", &j) > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003532 dot = find_line(j); // go to line # j
3533 dot_skip_over_ws();
Denys Vlasenkob22bbff2009-07-04 16:50:43 +02003534 } else { // unrecognized cmd
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003535 not_implemented(p);
Eric Andersen3f980402001-04-04 17:31:15 +00003536 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003537#endif /* !FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003538 break;
3539 case '<': // <- Left shift something
3540 case '>': // >- Right shift something
3541 cnt = count_lines(text, dot); // remember what line we are on
3542 c1 = get_one_char(); // get the type of thing to delete
3543 find_range(&p, &q, c1);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003544 yank_delete(p, q, 1, YANKONLY); // save copy before change
Eric Andersen3f980402001-04-04 17:31:15 +00003545 p = begin_line(p);
3546 q = end_line(q);
3547 i = count_lines(p, q); // # of lines we are shifting
3548 for ( ; i > 0; i--, p = next_line(p)) {
3549 if (c == '<') {
3550 // shift left- remove tab or 8 spaces
3551 if (*p == '\t') {
3552 // shrink buffer 1 char
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003553 text_hole_delete(p, p);
Eric Andersen3f980402001-04-04 17:31:15 +00003554 } else if (*p == ' ') {
3555 // we should be calculating columns, not just SPACE
3556 for (j = 0; *p == ' ' && j < tabstop; j++) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003557 text_hole_delete(p, p);
Eric Andersen3f980402001-04-04 17:31:15 +00003558 }
3559 }
3560 } else if (c == '>') {
3561 // shift right -- add tab or 8 spaces
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003562 char_insert(p, '\t');
Eric Andersen3f980402001-04-04 17:31:15 +00003563 }
3564 }
3565 dot = find_line(cnt); // what line were we on
3566 dot_skip_over_ws();
3567 end_cmd_q(); // stop adding to q
3568 break;
3569 case 'A': // A- append at e-o-l
3570 dot_end(); // go to e-o-l
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +00003571 //**** fall through to ... 'a'
Eric Andersen3f980402001-04-04 17:31:15 +00003572 case 'a': // a- append after current char
3573 if (*dot != '\n')
3574 dot++;
3575 goto dc_i;
3576 break;
3577 case 'B': // B- back a blank-delimited Word
3578 case 'E': // E- end of a blank-delimited word
3579 case 'W': // W- forward a blank-delimited word
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003580 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003581 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003582 }
Eric Andersen3f980402001-04-04 17:31:15 +00003583 dir = FORWARD;
3584 if (c == 'B')
3585 dir = BACK;
3586 if (c == 'W' || isspace(dot[dir])) {
3587 dot = skip_thing(dot, 1, dir, S_TO_WS);
3588 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3589 }
3590 if (c != 'W')
3591 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3592 break;
3593 case 'C': // C- Change to e-o-l
3594 case 'D': // D- delete to e-o-l
3595 save_dot = dot;
3596 dot = dollar_line(dot); // move to before NL
3597 // copy text into a register and delete
3598 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3599 if (c == 'C')
3600 goto dc_i; // start inserting
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003601#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003602 if (c == 'D')
3603 end_cmd_q(); // stop adding to q
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003604#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003605 break;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003606 case 'g': // 'gg' goto a line number (vim) (default: very first line)
Paul Foxb5ee8db2008-02-14 01:17:01 +00003607 c1 = get_one_char();
3608 if (c1 != 'g') {
3609 buf[0] = 'g';
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003610 buf[1] = c1; // TODO: if Unicode?
Paul Foxb5ee8db2008-02-14 01:17:01 +00003611 buf[2] = '\0';
3612 not_implemented(buf);
3613 break;
3614 }
3615 if (cmdcnt == 0)
3616 cmdcnt = 1;
3617 /* fall through */
Eric Andersen822c3832001-05-07 17:37:43 +00003618 case 'G': // G- goto to a line number (default= E-O-F)
3619 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003620 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003621 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003622 }
3623 dot_skip_over_ws();
3624 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003625 case 'H': // H- goto top line on screen
3626 dot = screenbegin;
3627 if (cmdcnt > (rows - 1)) {
3628 cmdcnt = (rows - 1);
3629 }
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003630 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003631 do_cmd('+');
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003632 }
Eric Andersen3f980402001-04-04 17:31:15 +00003633 dot_skip_over_ws();
3634 break;
3635 case 'I': // I- insert before first non-blank
3636 dot_begin(); // 0
3637 dot_skip_over_ws();
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +00003638 //**** fall through to ... 'i'
Eric Andersen3f980402001-04-04 17:31:15 +00003639 case 'i': // i- insert before current char
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003640 case KEYCODE_INSERT: // Cursor Key Insert
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003641 dc_i:
Eric Andersen3f980402001-04-04 17:31:15 +00003642 cmd_mode = 1; // start insrting
Eric Andersen3f980402001-04-04 17:31:15 +00003643 break;
3644 case 'J': // J- join current and next lines together
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003645 if (--cmdcnt > 1) {
Eric Andersen3f980402001-04-04 17:31:15 +00003646 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003647 }
Eric Andersen3f980402001-04-04 17:31:15 +00003648 dot_end(); // move to NL
3649 if (dot < end - 1) { // make sure not last char in text[]
3650 *dot++ = ' '; // replace NL with space
Paul Fox8552aec2005-09-16 12:20:05 +00003651 file_modified++;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003652 while (isblank(*dot)) { // delete leading WS
Eric Andersen3f980402001-04-04 17:31:15 +00003653 dot_delete();
3654 }
3655 }
3656 end_cmd_q(); // stop adding to q
3657 break;
3658 case 'L': // L- goto bottom line on screen
3659 dot = end_screen();
3660 if (cmdcnt > (rows - 1)) {
3661 cmdcnt = (rows - 1);
3662 }
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003663 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003664 do_cmd('-');
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003665 }
Eric Andersen3f980402001-04-04 17:31:15 +00003666 dot_begin();
3667 dot_skip_over_ws();
3668 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003669 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003670 dot = screenbegin;
3671 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3672 dot = next_line(dot);
3673 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003674 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003675 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003676 p = begin_line(dot);
3677 if (p[-1] == '\n') {
3678 dot_prev();
3679 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3680 dot_end();
3681 dot = char_insert(dot, '\n');
3682 } else {
3683 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003684 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003685 dot_prev(); // -
3686 }
3687 goto dc_i;
3688 break;
3689 case 'R': // R- continuous Replace char
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003690 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003691 cmd_mode = 2;
Eric Andersen3f980402001-04-04 17:31:15 +00003692 break;
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003693 case KEYCODE_DELETE:
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003694 c = 'x';
3695 // fall through
Eric Andersen3f980402001-04-04 17:31:15 +00003696 case 'X': // X- delete char before dot
3697 case 'x': // x- delete the current char
3698 case 's': // s- substitute the current char
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003699 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003700 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003701 }
Eric Andersen3f980402001-04-04 17:31:15 +00003702 dir = 0;
3703 if (c == 'X')
3704 dir = -1;
3705 if (dot[dir] != '\n') {
3706 if (c == 'X')
3707 dot--; // delete prev char
3708 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3709 }
3710 if (c == 's')
3711 goto dc_i; // start insrting
3712 end_cmd_q(); // stop adding to q
3713 break;
3714 case 'Z': // Z- if modified, {write}; exit
3715 // ZZ means to save file (if necessary), then exit
3716 c1 = get_one_char();
3717 if (c1 != 'Z') {
3718 indicate_error(c);
3719 break;
3720 }
Paul Foxf0305b72006-03-28 14:18:21 +00003721 if (file_modified) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003722 if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003723 status_line_bold("\"%s\" File is read only", current_filename);
Denis Vlasenko92758142006-10-03 19:56:34 +00003724 break;
Paul Foxf0305b72006-03-28 14:18:21 +00003725 }
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003726 cnt = file_write(current_filename, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003727 if (cnt < 0) {
3728 if (cnt == -1)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003729 status_line_bold("Write error: %s", strerror(errno));
Paul Fox61e45db2005-10-09 14:43:22 +00003730 } else if (cnt == (end - 1 - text + 1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003731 editing = 0;
3732 }
3733 } else {
3734 editing = 0;
3735 }
3736 break;
3737 case '^': // ^- move to first non-blank on line
3738 dot_begin();
3739 dot_skip_over_ws();
3740 break;
3741 case 'b': // b- back a word
3742 case 'e': // e- end of word
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003743 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003744 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003745 }
Eric Andersen3f980402001-04-04 17:31:15 +00003746 dir = FORWARD;
3747 if (c == 'b')
3748 dir = BACK;
3749 if ((dot + dir) < text || (dot + dir) > end - 1)
3750 break;
3751 dot += dir;
3752 if (isspace(*dot)) {
3753 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3754 }
3755 if (isalnum(*dot) || *dot == '_') {
3756 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3757 } else if (ispunct(*dot)) {
3758 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3759 }
3760 break;
3761 case 'c': // c- change something
3762 case 'd': // d- delete something
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003763#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003764 case 'y': // y- yank something
3765 case 'Y': // Y- Yank a line
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003766#endif
Denis Vlasenkod44c1532008-10-14 12:59:42 +00003767 {
Paul Foxc51fc7b2008-03-06 01:34:23 +00003768 int yf, ml, whole = 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003769 yf = YANKDEL; // assume either "c" or "d"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003770#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003771 if (c == 'y' || c == 'Y')
3772 yf = YANKONLY;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003773#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003774 c1 = 'y';
3775 if (c != 'Y')
3776 c1 = get_one_char(); // get the type of thing to delete
Paul Foxc51fc7b2008-03-06 01:34:23 +00003777 // determine range, and whether it spans lines
3778 ml = find_range(&p, &q, c1);
Eric Andersen3f980402001-04-04 17:31:15 +00003779 if (c1 == 27) { // ESC- user changed mind and wants out
3780 c = c1 = 27; // Escape- do nothing
3781 } else if (strchr("wW", c1)) {
3782 if (c == 'c') {
3783 // don't include trailing WS as part of word
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003784 while (isblank(*q)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003785 if (q <= text || q[-1] == '\n')
3786 break;
3787 q--;
3788 }
3789 }
Paul Foxc51fc7b2008-03-06 01:34:23 +00003790 dot = yank_delete(p, q, ml, yf); // delete word
3791 } else if (strchr("^0bBeEft%$ lh\b\177", c1)) {
3792 // partial line copy text into a register and delete
3793 dot = yank_delete(p, q, ml, yf); // delete word
3794 } else if (strchr("cdykjHL+-{}\r\n", c1)) {
3795 // whole line copy text into a register and delete
3796 dot = yank_delete(p, q, ml, yf); // delete lines
3797 whole = 1;
3798 } else {
3799 // could not recognize object
3800 c = c1 = 27; // error-
3801 ml = 0;
3802 indicate_error(c);
3803 }
3804 if (ml && whole) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003805 if (c == 'c') {
3806 dot = char_insert(dot, '\n');
3807 // on the last line of file don't move to prev line
Paul Foxc51fc7b2008-03-06 01:34:23 +00003808 if (whole && dot != (end-1)) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003809 dot_prev();
3810 }
3811 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003812 dot_begin();
3813 dot_skip_over_ws();
3814 }
Eric Andersen3f980402001-04-04 17:31:15 +00003815 }
3816 if (c1 != 27) {
3817 // if CHANGING, not deleting, start inserting after the delete
3818 if (c == 'c') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003819 strcpy(buf, "Change");
Eric Andersen3f980402001-04-04 17:31:15 +00003820 goto dc_i; // start inserting
3821 }
3822 if (c == 'd') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003823 strcpy(buf, "Delete");
Eric Andersen3f980402001-04-04 17:31:15 +00003824 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003825#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003826 if (c == 'y' || c == 'Y') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003827 strcpy(buf, "Yank");
Eric Andersen3f980402001-04-04 17:31:15 +00003828 }
3829 p = reg[YDreg];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003830 q = p + strlen(p);
Eric Andersen3f980402001-04-04 17:31:15 +00003831 for (cnt = 0; p <= q; p++) {
3832 if (*p == '\n')
3833 cnt++;
3834 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003835 status_line("%s %d lines (%d chars) using [%c]",
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003836 buf, cnt, strlen(reg[YDreg]), what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003837#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003838 end_cmd_q(); // stop adding to q
3839 }
3840 break;
Denis Vlasenkod44c1532008-10-14 12:59:42 +00003841 }
Eric Andersen3f980402001-04-04 17:31:15 +00003842 case 'k': // k- goto prev line, same col
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003843 case KEYCODE_UP: // cursor key Up
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003844 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003845 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003846 }
Eric Andersen3f980402001-04-04 17:31:15 +00003847 dot_prev();
3848 dot = move_to_col(dot, ccol + offset); // try stay in same col
3849 break;
3850 case 'r': // r- replace the current char with user input
3851 c1 = get_one_char(); // get the replacement char
3852 if (*dot != '\n') {
3853 *dot = c1;
Denis Vlasenkob1759462008-06-20 20:20:54 +00003854 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003855 }
3856 end_cmd_q(); // stop adding to q
3857 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003858 case 't': // t- move to char prior to next x
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00003859 last_forward_char = get_one_char();
3860 do_cmd(';');
3861 if (*dot == last_forward_char)
3862 dot_left();
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003863 last_forward_char = 0;
Eric Andersen822c3832001-05-07 17:37:43 +00003864 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003865 case 'w': // w- forward a word
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003866 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003867 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003868 }
Eric Andersen3f980402001-04-04 17:31:15 +00003869 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3870 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3871 } else if (ispunct(*dot)) { // we are on PUNCT
3872 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3873 }
3874 if (dot < end - 1)
3875 dot++; // move over word
3876 if (isspace(*dot)) {
3877 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3878 }
3879 break;
3880 case 'z': // z-
3881 c1 = get_one_char(); // get the replacement char
3882 cnt = 0;
3883 if (c1 == '.')
3884 cnt = (rows - 2) / 2; // put dot at center
3885 if (c1 == '-')
3886 cnt = rows - 2; // put dot at bottom
3887 screenbegin = begin_line(dot); // start dot at top
3888 dot_scroll(cnt, -1);
3889 break;
3890 case '|': // |- move to column "cmdcnt"
3891 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3892 break;
3893 case '~': // ~- flip the case of letters a-z -> A-Z
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003894 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003895 do_cmd(c);
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003896 }
Eric Andersen3f980402001-04-04 17:31:15 +00003897 if (islower(*dot)) {
3898 *dot = toupper(*dot);
Denis Vlasenkob1759462008-06-20 20:20:54 +00003899 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003900 } else if (isupper(*dot)) {
3901 *dot = tolower(*dot);
Denis Vlasenkob1759462008-06-20 20:20:54 +00003902 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003903 }
3904 dot_right();
3905 end_cmd_q(); // stop adding to q
3906 break;
3907 //----- The Cursor and Function Keys -----------------------------
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003908 case KEYCODE_HOME: // Cursor Key Home
Eric Andersen3f980402001-04-04 17:31:15 +00003909 dot_begin();
3910 break;
3911 // The Fn keys could point to do_macro which could translate them
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003912#if 0
3913 case KEYCODE_FUN1: // Function Key F1
3914 case KEYCODE_FUN2: // Function Key F2
3915 case KEYCODE_FUN3: // Function Key F3
3916 case KEYCODE_FUN4: // Function Key F4
3917 case KEYCODE_FUN5: // Function Key F5
3918 case KEYCODE_FUN6: // Function Key F6
3919 case KEYCODE_FUN7: // Function Key F7
3920 case KEYCODE_FUN8: // Function Key F8
3921 case KEYCODE_FUN9: // Function Key F9
3922 case KEYCODE_FUN10: // Function Key F10
3923 case KEYCODE_FUN11: // Function Key F11
3924 case KEYCODE_FUN12: // Function Key F12
Eric Andersen3f980402001-04-04 17:31:15 +00003925 break;
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003926#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003927 }
3928
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003929 dc1:
Eric Andersen3f980402001-04-04 17:31:15 +00003930 // if text[] just became empty, add back an empty line
3931 if (end == text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003932 char_insert(text, '\n'); // start empty buf with dummy line
Eric Andersen3f980402001-04-04 17:31:15 +00003933 dot = text;
3934 }
3935 // it is OK for dot to exactly equal to end, otherwise check dot validity
3936 if (dot != end) {
3937 dot = bound_dot(dot); // make sure "dot" is valid
3938 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003939#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003940 check_context(c); // update the current context
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003941#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003942
3943 if (!isdigit(c))
3944 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3945 cnt = dot - begin_line(dot);
3946 // Try to stay off of the Newline
3947 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3948 dot--;
3949}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003950
Paul Fox35e9c5d2008-03-06 16:26:12 +00003951/* NB! the CRASHME code is unmaintained, and doesn't currently build */
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003952#if ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003953static int totalcmds = 0;
3954static int Mp = 85; // Movement command Probability
3955static int Np = 90; // Non-movement command Probability
3956static int Dp = 96; // Delete command Probability
3957static int Ip = 97; // Insert command Probability
3958static int Yp = 98; // Yank command Probability
3959static int Pp = 99; // Put command Probability
3960static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003961static const char chars[20] = "\t012345 abcdABCD-=.$";
3962static const char *const words[20] = {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003963 "this", "is", "a", "test",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003964 "broadcast", "the", "emergency", "of",
3965 "system", "quick", "brown", "fox",
3966 "jumped", "over", "lazy", "dogs",
3967 "back", "January", "Febuary", "March"
3968};
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003969static const char *const lines[20] = {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003970 "You should have received a copy of the GNU General Public License\n",
3971 "char c, cm, *cmd, *cmd1;\n",
3972 "generate a command by percentages\n",
3973 "Numbers may be typed as a prefix to some commands.\n",
3974 "Quit, discarding changes!\n",
3975 "Forced write, if permission originally not valid.\n",
3976 "In general, any ex or ed command (such as substitute or delete).\n",
3977 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3978 "Please get w/ me and I will go over it with you.\n",
3979 "The following is a list of scheduled, committed changes.\n",
3980 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3981 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3982 "Any question about transactions please contact Sterling Huxley.\n",
3983 "I will try to get back to you by Friday, December 31.\n",
3984 "This Change will be implemented on Friday.\n",
3985 "Let me know if you have problems accessing this;\n",
3986 "Sterling Huxley recently added you to the access list.\n",
3987 "Would you like to go to lunch?\n",
3988 "The last command will be automatically run.\n",
3989 "This is too much english for a computer geek.\n",
3990};
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003991static char *multilines[20] = {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003992 "You should have received a copy of the GNU General Public License\n",
3993 "char c, cm, *cmd, *cmd1;\n",
3994 "generate a command by percentages\n",
3995 "Numbers may be typed as a prefix to some commands.\n",
3996 "Quit, discarding changes!\n",
3997 "Forced write, if permission originally not valid.\n",
3998 "In general, any ex or ed command (such as substitute or delete).\n",
3999 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
4000 "Please get w/ me and I will go over it with you.\n",
4001 "The following is a list of scheduled, committed changes.\n",
4002 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
4003 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
4004 "Any question about transactions please contact Sterling Huxley.\n",
4005 "I will try to get back to you by Friday, December 31.\n",
4006 "This Change will be implemented on Friday.\n",
4007 "Let me know if you have problems accessing this;\n",
4008 "Sterling Huxley recently added you to the access list.\n",
4009 "Would you like to go to lunch?\n",
4010 "The last command will be automatically run.\n",
4011 "This is too much english for a computer geek.\n",
4012};
4013
4014// create a random command to execute
4015static void crash_dummy()
4016{
4017 static int sleeptime; // how long to pause between commands
4018 char c, cm, *cmd, *cmd1;
4019 int i, cnt, thing, rbi, startrbi, percent;
4020
4021 // "dot" movement commands
4022 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
4023
4024 // is there already a command running?
Denys Vlasenko020f4062009-05-17 16:44:54 +02004025 if (readbuffer[0] > 0)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004026 goto cd1;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004027 cd0:
Denys Vlasenko020f4062009-05-17 16:44:54 +02004028 readbuffer[0] = 'X';
4029 startrbi = rbi = 1;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004030 sleeptime = 0; // how long to pause between commands
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004031 memset(readbuffer, '\0', sizeof(readbuffer));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004032 // generate a command by percentages
4033 percent = (int) lrand48() % 100; // get a number from 0-99
4034 if (percent < Mp) { // Movement commands
4035 // available commands
4036 cmd = cmd1;
4037 M++;
4038 } else if (percent < Np) { // non-movement commands
4039 cmd = "mz<>\'\""; // available commands
4040 N++;
4041 } else if (percent < Dp) { // Delete commands
4042 cmd = "dx"; // available commands
4043 D++;
4044 } else if (percent < Ip) { // Inset commands
4045 cmd = "iIaAsrJ"; // available commands
4046 I++;
4047 } else if (percent < Yp) { // Yank commands
4048 cmd = "yY"; // available commands
4049 Y++;
4050 } else if (percent < Pp) { // Put commands
4051 cmd = "pP"; // available commands
4052 P++;
4053 } else {
4054 // We do not know how to handle this command, try again
4055 U++;
4056 goto cd0;
4057 }
4058 // randomly pick one of the available cmds from "cmd[]"
4059 i = (int) lrand48() % strlen(cmd);
4060 cm = cmd[i];
4061 if (strchr(":\024", cm))
4062 goto cd0; // dont allow colon or ctrl-T commands
4063 readbuffer[rbi++] = cm; // put cmd into input buffer
4064
4065 // now we have the command-
4066 // there are 1, 2, and multi char commands
4067 // find out which and generate the rest of command as necessary
4068 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
4069 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
4070 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
4071 cmd1 = "abcdefghijklmnopqrstuvwxyz";
4072 }
4073 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
4074 c = cmd1[thing];
4075 readbuffer[rbi++] = c; // add movement to input buffer
4076 }
4077 if (strchr("iIaAsc", cm)) { // multi-char commands
4078 if (cm == 'c') {
4079 // change some thing
4080 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
4081 c = cmd1[thing];
4082 readbuffer[rbi++] = c; // add movement to input buffer
4083 }
4084 thing = (int) lrand48() % 4; // what thing to insert
4085 cnt = (int) lrand48() % 10; // how many to insert
4086 for (i = 0; i < cnt; i++) {
4087 if (thing == 0) { // insert chars
4088 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
4089 } else if (thing == 1) { // insert words
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004090 strcat(readbuffer, words[(int) lrand48() % 20]);
4091 strcat(readbuffer, " ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004092 sleeptime = 0; // how fast to type
4093 } else if (thing == 2) { // insert lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004094 strcat(readbuffer, lines[(int) lrand48() % 20]);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004095 sleeptime = 0; // how fast to type
4096 } else { // insert multi-lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004097 strcat(readbuffer, multilines[(int) lrand48() % 20]);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004098 sleeptime = 0; // how fast to type
4099 }
4100 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004101 strcat(readbuffer, "\033");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004102 }
Denys Vlasenko020f4062009-05-17 16:44:54 +02004103 readbuffer[0] = strlen(readbuffer + 1);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004104 cd1:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004105 totalcmds++;
4106 if (sleeptime > 0)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004107 mysleep(sleeptime); // sleep 1/100 sec
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004108}
4109
4110// test to see if there are any errors
4111static void crash_test()
4112{
4113 static time_t oldtim;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004114
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004115 time_t tim;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004116 char d[2], msg[80];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004117
4118 msg[0] = '\0';
4119 if (end < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004120 strcat(msg, "end<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004121 }
4122 if (end > textend) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004123 strcat(msg, "end>textend ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004124 }
4125 if (dot < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004126 strcat(msg, "dot<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004127 }
4128 if (dot > end) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004129 strcat(msg, "dot>end ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004130 }
4131 if (screenbegin < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004132 strcat(msg, "screenbegin<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004133 }
4134 if (screenbegin > end - 1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004135 strcat(msg, "screenbegin>end-1 ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004136 }
4137
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004138 if (msg[0]) {
Glenn L McGrath7127b582002-12-03 21:48:15 +00004139 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004140 totalcmds, last_input_char, msg, SOs, SOn);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01004141 fflush_all();
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00004142 while (safe_read(STDIN_FILENO, d, 1) > 0) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004143 if (d[0] == '\n' || d[0] == '\r')
4144 break;
4145 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004146 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004147 tim = time(NULL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004148 if (tim >= (oldtim + 3)) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004149 sprintf(status_buffer,
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004150 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
4151 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
4152 oldtim = tim;
4153 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004154}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004155#endif