blob: cdfb27cc5abeaea05daa7a8a6b7de89871cb69b6 [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 */
Eric Andersen3f980402001-04-04 17:31:15 +00008/*
Eric Andersen3f980402001-04-04 17:31:15 +00009 * Things To Do:
10 * EXINIT
Eric Andersen1c0d3112001-04-16 15:46:44 +000011 * $HOME/.exrc and ./.exrc
Eric Andersen3f980402001-04-04 17:31:15 +000012 * add magic to search /foo.*bar
13 * add :help command
14 * :map macros
Eric Andersen3f980402001-04-04 17:31:15 +000015 * if mark[] values were line numbers rather than pointers
Denys Vlasenko60cb48c2013-01-14 15:57:44 +010016 * it would be easier to change the mark when add/delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +000017 * More intelligence in refresh()
18 * ":r !cmd" and "!cmd" to filter text through an external command
Eric Andersen1c0d3112001-04-16 15:46:44 +000019 * An "ex" line oriented mode- maybe using "cmdedit"
Eric Andersen3f980402001-04-04 17:31:15 +000020 */
Walter Harmsb9ba5802011-06-27 02:59:37 +020021//config:config VI
Denys Vlasenko4eed2c62017-07-18 22:01:24 +020022//config: bool "vi (22 kb)"
Walter Harmsb9ba5802011-06-27 02:59:37 +020023//config: default y
24//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020025//config: 'vi' is a text editor. More specifically, it is the One True
26//config: text editor <grin>. It does, however, have a rather steep
27//config: learning curve. If you are not already comfortable with 'vi'
28//config: you may wish to use something else.
Walter Harmsb9ba5802011-06-27 02:59:37 +020029//config:
30//config:config FEATURE_VI_MAX_LEN
Denys Vlasenkof5604222017-01-10 14:58:54 +010031//config: int "Maximum screen width"
Walter Harmsb9ba5802011-06-27 02:59:37 +020032//config: range 256 16384
33//config: default 4096
34//config: depends on VI
35//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020036//config: Contrary to what you may think, this is not eating much.
37//config: Make it smaller than 4k only if you are very limited on memory.
Walter Harmsb9ba5802011-06-27 02:59:37 +020038//config:
39//config:config FEATURE_VI_8BIT
Denys Vlasenkof5604222017-01-10 14:58:54 +010040//config: bool "Allow to display 8-bit chars (otherwise shows dots)"
Walter Harmsb9ba5802011-06-27 02:59:37 +020041//config: default n
42//config: depends on VI
43//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020044//config: If your terminal can display characters with high bit set,
45//config: you may want to enable this. Note: vi is not Unicode-capable.
46//config: If your terminal combines several 8-bit bytes into one character
47//config: (as in Unicode mode), this will not work properly.
Walter Harmsb9ba5802011-06-27 02:59:37 +020048//config:
49//config:config FEATURE_VI_COLON
50//config: bool "Enable \":\" colon commands (no \"ex\" mode)"
51//config: default y
52//config: depends on VI
53//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020054//config: Enable a limited set of colon commands. This does not
55//config: provide an "ex" mode.
Walter Harmsb9ba5802011-06-27 02:59:37 +020056//config:
57//config:config FEATURE_VI_YANKMARK
58//config: bool "Enable yank/put commands and mark cmds"
59//config: default y
60//config: depends on VI
61//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020062//config: This enables you to use yank and put, as well as mark.
Walter Harmsb9ba5802011-06-27 02:59:37 +020063//config:
64//config:config FEATURE_VI_SEARCH
65//config: bool "Enable search and replace cmds"
66//config: default y
67//config: depends on VI
68//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020069//config: Select this if you wish to be able to do search and replace.
Walter Harmsb9ba5802011-06-27 02:59:37 +020070//config:
71//config:config FEATURE_VI_REGEX_SEARCH
72//config: bool "Enable regex in search and replace"
73//config: default n # Uses GNU regex, which may be unavailable. FIXME
74//config: depends on FEATURE_VI_SEARCH
75//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020076//config: Use extended regex search.
Walter Harmsb9ba5802011-06-27 02:59:37 +020077//config:
78//config:config FEATURE_VI_USE_SIGNALS
79//config: bool "Catch signals"
80//config: default y
81//config: depends on VI
82//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020083//config: Selecting this option will make vi signal aware. This will support
84//config: SIGWINCH to deal with Window Changes, catch ^Z and ^C and alarms.
Walter Harmsb9ba5802011-06-27 02:59:37 +020085//config:
86//config:config FEATURE_VI_DOT_CMD
87//config: bool "Remember previous cmd and \".\" cmd"
88//config: default y
89//config: depends on VI
90//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020091//config: Make vi remember the last command and be able to repeat it.
Walter Harmsb9ba5802011-06-27 02:59:37 +020092//config:
93//config:config FEATURE_VI_READONLY
94//config: bool "Enable -R option and \"view\" mode"
95//config: default y
96//config: depends on VI
97//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020098//config: Enable the read-only command line option, which allows the user to
99//config: open a file in read-only mode.
Walter Harmsb9ba5802011-06-27 02:59:37 +0200100//config:
101//config:config FEATURE_VI_SETOPTS
Denys Vlasenkof5604222017-01-10 14:58:54 +0100102//config: bool "Enable settable options, ai ic showmatch"
Walter Harmsb9ba5802011-06-27 02:59:37 +0200103//config: default y
104//config: depends on VI
105//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200106//config: Enable the editor to set some (ai, ic, showmatch) options.
Walter Harmsb9ba5802011-06-27 02:59:37 +0200107//config:
108//config:config FEATURE_VI_SET
Denys Vlasenkof5604222017-01-10 14:58:54 +0100109//config: bool "Support :set"
Walter Harmsb9ba5802011-06-27 02:59:37 +0200110//config: default y
111//config: depends on VI
Walter Harmsb9ba5802011-06-27 02:59:37 +0200112//config:
113//config:config FEATURE_VI_WIN_RESIZE
114//config: bool "Handle window resize"
115//config: default y
116//config: depends on VI
117//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200118//config: Behave nicely with terminals that get resized.
Walter Harmsb9ba5802011-06-27 02:59:37 +0200119//config:
120//config:config FEATURE_VI_ASK_TERMINAL
121//config: bool "Use 'tell me cursor position' ESC sequence to measure window"
122//config: default y
123//config: depends on VI
124//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200125//config: If terminal size can't be retrieved and $LINES/$COLUMNS are not set,
126//config: this option makes vi perform a last-ditch effort to find it:
127//config: position cursor to 999,999 and ask terminal to report real
128//config: cursor position using "ESC [ 6 n" escape sequence, then read stdin.
129//config: This is not clean but helps a lot on serial lines and such.
Denys Vlasenkof5604222017-01-10 14:58:54 +0100130//config:
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200131//config:config FEATURE_VI_UNDO
Denys Vlasenkof5604222017-01-10 14:58:54 +0100132//config: bool "Support undo command \"u\""
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200133//config: default y
134//config: depends on VI
135//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200136//config: Support the 'u' command to undo insertion, deletion, and replacement
137//config: of text.
Denys Vlasenkof5604222017-01-10 14:58:54 +0100138//config:
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200139//config:config FEATURE_VI_UNDO_QUEUE
140//config: bool "Enable undo operation queuing"
141//config: default y
142//config: depends on FEATURE_VI_UNDO
143//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200144//config: The vi undo functions can use an intermediate queue to greatly lower
145//config: malloc() calls and overhead. When the maximum size of this queue is
146//config: reached, the contents of the queue are committed to the undo stack.
147//config: This increases the size of the undo code and allows some undo
148//config: operations (especially un-typing/backspacing) to be far more useful.
Denys Vlasenkof5604222017-01-10 14:58:54 +0100149//config:
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200150//config:config FEATURE_VI_UNDO_QUEUE_MAX
151//config: int "Maximum undo character queue size"
152//config: default 256
153//config: range 32 65536
154//config: depends on FEATURE_VI_UNDO_QUEUE
155//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200156//config: This option sets the number of bytes used at runtime for the queue.
157//config: Smaller values will create more undo objects and reduce the amount
158//config: of typed or backspaced characters that are grouped into one undo
159//config: operation; larger values increase the potential size of each undo
160//config: and will generally malloc() larger objects and less frequently.
161//config: Unless you want more (or less) frequent "undo points" while typing,
162//config: you should probably leave this unchanged.
Walter Harmsb9ba5802011-06-27 02:59:37 +0200163
164//applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP))
165
166//kbuild:lib-$(CONFIG_VI) += vi.o
167
Pere Orga6a3e01d2011-04-01 22:56:30 +0200168//usage:#define vi_trivial_usage
169//usage: "[OPTIONS] [FILE]..."
170//usage:#define vi_full_usage "\n\n"
171//usage: "Edit FILE\n"
Pere Orga6a3e01d2011-04-01 22:56:30 +0200172//usage: IF_FEATURE_VI_COLON(
Denys Vlasenko605f2642012-06-11 01:53:33 +0200173//usage: "\n -c CMD Initial command to run ($EXINIT also available)"
Pere Orga6a3e01d2011-04-01 22:56:30 +0200174//usage: )
175//usage: IF_FEATURE_VI_READONLY(
176//usage: "\n -R Read-only"
177//usage: )
Denys Vlasenko605f2642012-06-11 01:53:33 +0200178//usage: "\n -H List available features"
Pere Orga6a3e01d2011-04-01 22:56:30 +0200179
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000180#include "libbb.h"
Denys Vlasenko066f3992011-07-03 03:19:43 +0200181/* Should be after libbb.h: on some systems regex.h needs sys/types.h: */
182#if ENABLE_FEATURE_VI_REGEX_SEARCH
183# include <regex.h>
184#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000185
Paul Fox35e9c5d2008-03-06 16:26:12 +0000186/* the CRASHME code is unmaintained, and doesn't currently build */
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000187#define ENABLE_FEATURE_VI_CRASHME 0
188
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +0000189
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000190#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +0000191
192#if ENABLE_FEATURE_VI_8BIT
Denys Vlasenkoc2704542009-11-20 19:14:19 +0100193//FIXME: this does not work properly for Unicode anyway
194# define Isprint(c) (isprint)(c)
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000195#else
Denys Vlasenkoc2704542009-11-20 19:14:19 +0100196# define Isprint(c) isprint_asciionly(c)
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000197#endif
198
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +0000199#else
200
201/* 0x9b is Meta-ESC */
202#if ENABLE_FEATURE_VI_8BIT
Denys Vlasenko066f3992011-07-03 03:19:43 +0200203# define Isprint(c) ((unsigned char)(c) >= ' ' && (c) != 0x7f && (unsigned char)(c) != 0x9b)
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +0000204#else
Denys Vlasenko066f3992011-07-03 03:19:43 +0200205# define Isprint(c) ((unsigned char)(c) >= ' ' && (unsigned char)(c) < 0x7f)
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +0000206#endif
207
208#endif
209
210
Denis Vlasenkoe8a07882007-06-10 15:08:44 +0000211enum {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +0000212 MAX_TABSTOP = 32, // sanity limit
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000213 // User input len. Need not be extra big.
214 // Lines in file being edited *can* be bigger than this.
215 MAX_INPUT_LEN = 128,
216 // Sanity limits. We have only one buffer of this size.
217 MAX_SCR_COLS = CONFIG_FEATURE_VI_MAX_LEN,
218 MAX_SCR_ROWS = CONFIG_FEATURE_VI_MAX_LEN,
Denis Vlasenkoe8a07882007-06-10 15:08:44 +0000219};
Eric Andersen3f980402001-04-04 17:31:15 +0000220
Denys Vlasenko04b52892012-06-11 13:51:38 +0200221/* VT102 ESC sequences.
222 * See "Xterm Control Sequences"
223 * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
224 */
Denys Vlasenko8187e012017-09-13 22:48:30 +0200225#define ESC "\033"
Denys Vlasenko04b52892012-06-11 13:51:38 +0200226/* Inverse/Normal text */
Denys Vlasenko8187e012017-09-13 22:48:30 +0200227#define ESC_BOLD_TEXT ESC"[7m"
228#define ESC_NORM_TEXT ESC"[m"
Denys Vlasenko04b52892012-06-11 13:51:38 +0200229/* Bell */
230#define ESC_BELL "\007"
231/* Clear-to-end-of-line */
Denys Vlasenko8187e012017-09-13 22:48:30 +0200232#define ESC_CLEAR2EOL ESC"[K"
Denys Vlasenko04b52892012-06-11 13:51:38 +0200233/* Clear-to-end-of-screen.
234 * (We use default param here.
235 * Full sequence is "ESC [ <num> J",
236 * <num> is 0/1/2 = "erase below/above/all".)
237 */
Denys Vlasenko8187e012017-09-13 22:48:30 +0200238#define ESC_CLEAR2EOS ESC"[J"
Denys Vlasenko04b52892012-06-11 13:51:38 +0200239/* Cursor to given coordinate (1,1: top left) */
Denys Vlasenko8187e012017-09-13 22:48:30 +0200240#define ESC_SET_CURSOR_POS ESC"[%u;%uH"
Denys Vlasenko04b52892012-06-11 13:51:38 +0200241//UNUSED
242///* Cursor up and down */
Denys Vlasenko8187e012017-09-13 22:48:30 +0200243//#define ESC_CURSOR_UP ESC"[A"
Denys Vlasenko04b52892012-06-11 13:51:38 +0200244//#define ESC_CURSOR_DOWN "\n"
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000245
Denis Vlasenkoded6ad32008-10-14 12:26:30 +0000246#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
247// cmds modifying text[]
248// vda: removed "aAiIs" as they switch us into insert mode
249// and remembering input for replay after them makes no sense
Denys Vlasenko3e134eb2016-04-22 18:09:21 +0200250static const char modifying_cmds[] ALIGN1 = "cCdDJoOpPrRxX<>~";
Denis Vlasenkoded6ad32008-10-14 12:26:30 +0000251#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000252
Rob Landleybc68cd12006-03-10 19:22:06 +0000253enum {
254 YANKONLY = FALSE,
255 YANKDEL = TRUE,
256 FORWARD = 1, // code depends on "1" for array index
257 BACK = -1, // code depends on "-1" for array index
258 LIMITED = 0, // how much of text[] in char_search
259 FULL = 1, // how much of text[] in char_search
Eric Andersen3f980402001-04-04 17:31:15 +0000260
Rob Landleybc68cd12006-03-10 19:22:06 +0000261 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
262 S_TO_WS = 2, // used in skip_thing() for moving "dot"
263 S_OVER_WS = 3, // used in skip_thing() for moving "dot"
264 S_END_PUNCT = 4, // used in skip_thing() for moving "dot"
Denis Vlasenko8e858e22007-03-07 09:35:43 +0000265 S_END_ALNUM = 5, // used in skip_thing() for moving "dot"
Rob Landleybc68cd12006-03-10 19:22:06 +0000266};
Eric Andersen3f980402001-04-04 17:31:15 +0000267
Denis Vlasenkob1759462008-06-20 20:20:54 +0000268
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000269/* vi.c expects chars to be unsigned. */
270/* busybox build system provides that, but it's better */
271/* to audit and fix the source */
Eric Andersen3f980402001-04-04 17:31:15 +0000272
Denis Vlasenkob1759462008-06-20 20:20:54 +0000273struct globals {
274 /* many references - keep near the top of globals */
275 char *text, *end; // pointers to the user data in memory
276 char *dot; // where all the action takes place
277 int text_size; // size of the allocated buffer
278
279 /* the rest */
280 smallint vi_setops;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000281#define VI_AUTOINDENT 1
282#define VI_SHOWMATCH 2
283#define VI_IGNORECASE 4
284#define VI_ERR_METHOD 8
285#define autoindent (vi_setops & VI_AUTOINDENT)
286#define showmatch (vi_setops & VI_SHOWMATCH )
287#define ignorecase (vi_setops & VI_IGNORECASE)
288/* indicate error with beep or flash */
289#define err_method (vi_setops & VI_ERR_METHOD)
290
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000291#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkob1759462008-06-20 20:20:54 +0000292 smallint readonly_mode;
Denis Vlasenko6a2f7f42007-08-16 10:35:17 +0000293#define SET_READONLY_FILE(flags) ((flags) |= 0x01)
294#define SET_READONLY_MODE(flags) ((flags) |= 0x02)
295#define UNSET_READONLY_FILE(flags) ((flags) &= 0xfe)
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000296#else
Denis Vlasenkob1759462008-06-20 20:20:54 +0000297#define SET_READONLY_FILE(flags) ((void)0)
298#define SET_READONLY_MODE(flags) ((void)0)
299#define UNSET_READONLY_FILE(flags) ((void)0)
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000300#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000301
Denis Vlasenkob1759462008-06-20 20:20:54 +0000302 smallint editing; // >0 while we are editing a file
Denis Vlasenko30cfdf92008-09-21 15:29:29 +0000303 // [code audit says "can be 0, 1 or 2 only"]
Denis Vlasenkob1759462008-06-20 20:20:54 +0000304 smallint cmd_mode; // 0=command 1=insert 2=replace
Denys Vlasenkoe7430862014-04-03 12:47:48 +0200305 int modified_count; // buffer contents changed if !0
306 int last_modified_count; // = -1;
Denis Vlasenkob1759462008-06-20 20:20:54 +0000307 int save_argc; // how many file names on cmd line
308 int cmdcnt; // repetition count
309 unsigned rows, columns; // the terminal screen is this size
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700310#if ENABLE_FEATURE_VI_ASK_TERMINAL
311 int get_rowcol_error;
312#endif
Denis Vlasenkob1759462008-06-20 20:20:54 +0000313 int crow, ccol; // cursor is on Crow x Ccol
314 int offset; // chars scrolled off the screen to the left
315 int have_status_msg; // is default edit status needed?
316 // [don't make smallint!]
317 int last_status_cksum; // hash of current status line
318 char *current_filename;
319 char *screenbegin; // index into text[], of top line on the screen
320 char *screen; // pointer to the virtual screen buffer
321 int screensize; // and its size
322 int tabstop;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000323 int last_forward_char; // last char searched for with 'f' (int because of Unicode)
Denis Vlasenkob1759462008-06-20 20:20:54 +0000324 char erase_char; // the users erase character
325 char last_input_char; // last char read from user
Denis Vlasenkob1759462008-06-20 20:20:54 +0000326
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000327#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkob1759462008-06-20 20:20:54 +0000328 smallint adding2q; // are we currently adding user input to q
329 int lmc_len; // length of last_modifying_cmd
330 char *ioq, *ioq_start; // pointer to string for get_one_char to "read"
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000331#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000332#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenkob1759462008-06-20 20:20:54 +0000333 int my_pid;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000334#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000335#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkob1759462008-06-20 20:20:54 +0000336 char *last_search_pattern; // last pattern from a '/' or '?' search
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000337#endif
Denis Vlasenko0112ff52008-10-25 23:23:00 +0000338
Denis Vlasenkob1759462008-06-20 20:20:54 +0000339 /* former statics */
340#if ENABLE_FEATURE_VI_YANKMARK
341 char *edit_file__cur_line;
342#endif
343 int refresh__old_offset;
344 int format_edit_status__tot;
Eric Andersen3f980402001-04-04 17:31:15 +0000345
Denis Vlasenkob1759462008-06-20 20:20:54 +0000346 /* a few references only */
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000347#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000348 int YDreg, Ureg; // default delete register and orig line for "U"
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000349 char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000350 char *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
351 char *context_start, *context_end;
352#endif
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000353#if ENABLE_FEATURE_VI_USE_SIGNALS
Denis Vlasenkob1759462008-06-20 20:20:54 +0000354 sigjmp_buf restart; // catch_sig()
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000355#endif
Denys Vlasenko01ccdd12017-01-11 16:17:59 +0100356 struct termios term_orig; // remember what the cooked mode was
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000357#if ENABLE_FEATURE_VI_COLON
358 char *initial_cmds[3]; // currently 2 entries, NULL terminated
359#endif
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000360 // Should be just enough to hold a key sequence,
Denis Vlasenko25497c12008-10-14 10:25:05 +0000361 // but CRASHME mode uses it as generated command buffer too
362#if ENABLE_FEATURE_VI_CRASHME
Denis Vlasenko1dfeeeb2008-10-18 19:04:37 +0000363 char readbuffer[128];
Denis Vlasenko25497c12008-10-14 10:25:05 +0000364#else
Denis Vlasenko5f6aaf32008-10-25 23:27:29 +0000365 char readbuffer[KEYCODE_BUFFER_SIZE];
Denis Vlasenko25497c12008-10-14 10:25:05 +0000366#endif
Denis Vlasenkob1759462008-06-20 20:20:54 +0000367#define STATUS_BUFFER_LEN 200
368 char status_buffer[STATUS_BUFFER_LEN]; // messages to the user
369#if ENABLE_FEATURE_VI_DOT_CMD
370 char last_modifying_cmd[MAX_INPUT_LEN]; // last modifying cmd for "."
371#endif
372 char get_input_line__buf[MAX_INPUT_LEN]; /* former static */
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000373
374 char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2];
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200375#if ENABLE_FEATURE_VI_UNDO
376// undo_push() operations
Denys Vlasenkoe7430862014-04-03 12:47:48 +0200377#define UNDO_INS 0
378#define UNDO_DEL 1
379#define UNDO_INS_CHAIN 2
380#define UNDO_DEL_CHAIN 3
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200381// UNDO_*_QUEUED must be equal to UNDO_xxx ORed with UNDO_QUEUED_FLAG
382#define UNDO_QUEUED_FLAG 4
Denys Vlasenkoe7430862014-04-03 12:47:48 +0200383#define UNDO_INS_QUEUED 4
384#define UNDO_DEL_QUEUED 5
385#define UNDO_USE_SPOS 32
386#define UNDO_EMPTY 64
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200387// Pass-through flags for functions that can be undone
Denys Vlasenkoe7430862014-04-03 12:47:48 +0200388#define NO_UNDO 0
389#define ALLOW_UNDO 1
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200390#define ALLOW_UNDO_CHAIN 2
Denys Vlasenkoe7430862014-04-03 12:47:48 +0200391# if ENABLE_FEATURE_VI_UNDO_QUEUE
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200392#define ALLOW_UNDO_QUEUED 3
393 char undo_queue_state;
394 int undo_q;
395 char *undo_queue_spos; // Start position of queued operation
396 char undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX];
Denys Vlasenkoe7430862014-04-03 12:47:48 +0200397# else
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200398// If undo queuing disabled, don't invoke the missing queue logic
399#define ALLOW_UNDO_QUEUED 1
Denys Vlasenkoe7430862014-04-03 12:47:48 +0200400# endif
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200401
402 struct undo_object {
403 struct undo_object *prev; // Linking back avoids list traversal (LIFO)
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200404 int start; // Offset where the data should be restored/deleted
405 int length; // total data size
Denys Vlasenkoe7430862014-04-03 12:47:48 +0200406 uint8_t u_type; // 0=deleted, 1=inserted, 2=swapped
407 char undo_text[1]; // text that was deleted (if deletion)
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200408 } *undo_stack_tail;
409#endif /* ENABLE_FEATURE_VI_UNDO */
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000410};
411#define G (*ptr_to_globals)
412#define text (G.text )
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000413#define text_size (G.text_size )
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000414#define end (G.end )
415#define dot (G.dot )
416#define reg (G.reg )
Denis Vlasenkob1759462008-06-20 20:20:54 +0000417
418#define vi_setops (G.vi_setops )
419#define editing (G.editing )
420#define cmd_mode (G.cmd_mode )
Denys Vlasenkoe7430862014-04-03 12:47:48 +0200421#define modified_count (G.modified_count )
422#define last_modified_count (G.last_modified_count)
Denis Vlasenkob1759462008-06-20 20:20:54 +0000423#define save_argc (G.save_argc )
424#define cmdcnt (G.cmdcnt )
425#define rows (G.rows )
426#define columns (G.columns )
427#define crow (G.crow )
428#define ccol (G.ccol )
429#define offset (G.offset )
430#define status_buffer (G.status_buffer )
431#define have_status_msg (G.have_status_msg )
432#define last_status_cksum (G.last_status_cksum )
433#define current_filename (G.current_filename )
434#define screen (G.screen )
435#define screensize (G.screensize )
436#define screenbegin (G.screenbegin )
437#define tabstop (G.tabstop )
Denis Vlasenko0112ff52008-10-25 23:23:00 +0000438#define last_forward_char (G.last_forward_char )
Denis Vlasenkob1759462008-06-20 20:20:54 +0000439#define erase_char (G.erase_char )
440#define last_input_char (G.last_input_char )
Denis Vlasenko2a210e52008-06-22 13:20:42 +0000441#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkob1759462008-06-20 20:20:54 +0000442#define readonly_mode (G.readonly_mode )
Denis Vlasenko2a210e52008-06-22 13:20:42 +0000443#else
444#define readonly_mode 0
445#endif
Denis Vlasenkob1759462008-06-20 20:20:54 +0000446#define adding2q (G.adding2q )
447#define lmc_len (G.lmc_len )
448#define ioq (G.ioq )
449#define ioq_start (G.ioq_start )
Denis Vlasenkob1759462008-06-20 20:20:54 +0000450#define my_pid (G.my_pid )
Denis Vlasenkob1759462008-06-20 20:20:54 +0000451#define last_search_pattern (G.last_search_pattern)
Denis Vlasenko2a210e52008-06-22 13:20:42 +0000452
Denis Vlasenkob1759462008-06-20 20:20:54 +0000453#define edit_file__cur_line (G.edit_file__cur_line)
454#define refresh__old_offset (G.refresh__old_offset)
455#define format_edit_status__tot (G.format_edit_status__tot)
456
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000457#define YDreg (G.YDreg )
458#define Ureg (G.Ureg )
459#define mark (G.mark )
460#define context_start (G.context_start )
461#define context_end (G.context_end )
462#define restart (G.restart )
463#define term_orig (G.term_orig )
Denis Vlasenko0b3b41b2007-05-30 02:01:40 +0000464#define initial_cmds (G.initial_cmds )
Denis Vlasenkoa96425f2007-12-09 04:13:43 +0000465#define readbuffer (G.readbuffer )
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000466#define scr_out_buf (G.scr_out_buf )
Denis Vlasenkob1759462008-06-20 20:20:54 +0000467#define last_modifying_cmd (G.last_modifying_cmd )
468#define get_input_line__buf (G.get_input_line__buf)
469
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200470#if ENABLE_FEATURE_VI_UNDO
471#define undo_stack_tail (G.undo_stack_tail )
472# if ENABLE_FEATURE_VI_UNDO_QUEUE
473#define undo_queue_state (G.undo_queue_state)
474#define undo_q (G.undo_q )
475#define undo_queue (G.undo_queue )
476#define undo_queue_spos (G.undo_queue_spos )
477# endif
478#endif
479
Denis Vlasenkoa96425f2007-12-09 04:13:43 +0000480#define INIT_G() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000481 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
Denys Vlasenkoe7430862014-04-03 12:47:48 +0200482 last_modified_count = -1; \
Denis Vlasenko31d58e52008-10-29 13:16:28 +0000483 /* "" but has space for 2 chars: */ \
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000484 IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \
Denis Vlasenkoa96425f2007-12-09 04:13:43 +0000485} while (0)
Eric Andersen3f980402001-04-04 17:31:15 +0000486
Denis Vlasenkob1759462008-06-20 20:20:54 +0000487
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000488static void edit_file(char *); // edit one file
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000489static void do_cmd(int); // execute a command
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000490static int next_tabstop(int);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000491static void sync_cursor(char *, int *, int *); // synchronize the screen cursor to dot
492static char *begin_line(char *); // return pointer to cur line B-o-l
493static char *end_line(char *); // return pointer to cur line E-o-l
494static char *prev_line(char *); // return pointer to prev line B-o-l
495static char *next_line(char *); // return pointer to next line B-o-l
496static char *end_screen(void); // get pointer to last char on screen
497static int count_lines(char *, char *); // count line from start to stop
Maninder Singh97c64912015-05-25 13:46:36 +0200498static char *find_line(int); // find beginning of line #li
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000499static char *move_to_col(char *, int); // move "p" to column l
Eric Andersen3f980402001-04-04 17:31:15 +0000500static void dot_left(void); // move dot left- dont leave line
501static void dot_right(void); // move dot right- dont leave line
502static void dot_begin(void); // move dot to B-o-l
503static void dot_end(void); // move dot to E-o-l
504static void dot_next(void); // move dot to next line B-o-l
505static void dot_prev(void); // move dot to prev line B-o-l
506static void dot_scroll(int, int); // move the screen up or down
507static void dot_skip_over_ws(void); // move dot pat WS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000508static char *bound_dot(char *); // make sure text[0] <= P < "end"
509static char *new_screen(int, int); // malloc virtual screen memory
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200510#if !ENABLE_FEATURE_VI_UNDO
511#define char_insert(a,b,c) char_insert(a,b)
512#endif
513static char *char_insert(char *, char, int); // insert the char c at 'p'
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000514// might reallocate text[]! use p += stupid_insert(p, ...),
515// and be careful to not use pointers into potentially freed text[]!
516static uintptr_t stupid_insert(char *, char); // stupidly insert the char c at 'p'
Paul Foxc51fc7b2008-03-06 01:34:23 +0000517static int find_range(char **, char **, char); // return pointers for an object
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000518static int st_test(char *, int, int, char *); // helper for skip_thing()
519static char *skip_thing(char *, int, int, int); // skip some object
520static char *find_pair(char *, char); // find matching pair () [] {}
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200521#if !ENABLE_FEATURE_VI_UNDO
522#define text_hole_delete(a,b,c) text_hole_delete(a,b)
523#endif
524static char *text_hole_delete(char *, char *, int); // at "p", delete a 'size' byte hole
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000525// might reallocate text[]! use p += text_hole_make(p, ...),
526// and be careful to not use pointers into potentially freed text[]!
527static uintptr_t text_hole_make(char *, int); // at "p", make a 'size' byte hole
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200528#if !ENABLE_FEATURE_VI_UNDO
529#define yank_delete(a,b,c,d,e) yank_delete(a,b,c,d)
530#endif
531static char *yank_delete(char *, char *, int, int, int); // yank text[] into register then delete
Eric Andersen3f980402001-04-04 17:31:15 +0000532static void show_help(void); // display some help info
Eric Andersen3f980402001-04-04 17:31:15 +0000533static void rawmode(void); // set "raw" mode on tty
534static void cookmode(void); // return to "cooked" mode on tty
Denis Vlasenko87f3b262007-09-07 13:43:28 +0000535// sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready)
536static int mysleep(int);
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000537static int readit(void); // read (maybe cursor) key from stdin
538static int get_one_char(void); // read 1 char from stdin
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000539// file_insert might reallocate text[]!
540static int file_insert(const char *, char *, int);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000541static int file_write(char *, char *, char *);
Denys Vlasenko04b52892012-06-11 13:51:38 +0200542static void place_cursor(int, int);
Eric Andersenbff7a602001-11-17 07:15:43 +0000543static void screen_erase(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000544static void clear_to_eol(void);
545static void clear_to_eos(void);
Denis Vlasenko267e16c2008-10-14 10:34:41 +0000546static void go_bottom_and_clear_to_eol(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000547static void standout_start(void); // send "start reverse video" sequence
548static void standout_end(void); // send "end reverse video" sequence
549static void flash(int); // flash the terminal screen
Eric Andersen3f980402001-04-04 17:31:15 +0000550static void show_status_line(void); // put a message on the bottom line
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000551static void status_line(const char *, ...); // print to status buf
552static void status_line_bold(const char *, ...);
Denys Vlasenko9e7c0022013-03-15 02:17:29 +0100553static void status_line_bold_errno(const char *fn);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000554static void not_implemented(const char *); // display "Not implemented" message
Paul Fox8552aec2005-09-16 12:20:05 +0000555static int format_edit_status(void); // format file status on status line
Eric Andersen3f980402001-04-04 17:31:15 +0000556static void redraw(int); // force a full screen refresh
Denis Vlasenko68404f12008-03-17 09:00:54 +0000557static char* format_line(char* /*, int*/);
Eric Andersen3f980402001-04-04 17:31:15 +0000558static void refresh(int); // update the terminal from screen[]
559
Denys Vlasenko05399fc2014-09-15 17:06:10 +0200560static void indicate_error(void); // use flash or beep to indicate error
Paul Fox90372ed2005-10-09 14:26:26 +0000561static void Hit_Return(void);
562
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000563#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000564static char *char_search(char *, const char *, int, int); // search for pattern starting at p
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000565#endif
566#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000567static char *get_one_address(char *, int *); // get colon addr, if present
568static char *get_address(char *, int *, int *); // get two colon addrs, if present
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000569#endif
Denys Vlasenko32afd3a2014-04-05 22:57:46 +0200570static void colon(char *); // execute the "colon" mode cmds
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000571#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000572static void winch_sig(int); // catch window size changes
573static void suspend_sig(int); // catch ctrl-Z
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000574static void catch_sig(int); // catch ctrl-C and alarm time-outs
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000575#endif
576#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000577static void start_new_cmd_q(char); // new queue for command
Eric Andersenbff7a602001-11-17 07:15:43 +0000578static void end_cmd_q(void); // stop saving input chars
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000579#else
580#define end_cmd_q() ((void)0)
581#endif
582#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000583static void showmatching(char *); // show the matching pair () [] {}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000584#endif
585#if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000586// might reallocate text[]! use p += string_insert(p, ...),
587// and be careful to not use pointers into potentially freed text[]!
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200588# if !ENABLE_FEATURE_VI_UNDO
589#define string_insert(a,b,c) string_insert(a,b)
590# endif
591static uintptr_t string_insert(char *, const char *, int); // insert the string at 'p'
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000592#endif
593#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000594static char *text_yank(char *, char *, int); // save copy of "p" into a register
595static char what_reg(void); // what is letter of current YDreg
596static void check_context(char); // remember context for '' command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000597#endif
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200598#if ENABLE_FEATURE_VI_UNDO
Denys Vlasenkoe7430862014-04-03 12:47:48 +0200599static void flush_undo_data(void);
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200600static void undo_push(char *, unsigned int, unsigned char); // Push an operation on the undo stack
601static void undo_pop(void); // Undo the last operation
602# if ENABLE_FEATURE_VI_UNDO_QUEUE
603static void undo_queue_commit(void); // Flush any queued objects to the undo stack
604# else
605# define undo_queue_commit() ((void)0)
606# endif
607#else
Denys Vlasenkoe7430862014-04-03 12:47:48 +0200608#define flush_undo_data() ((void)0)
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200609#define undo_queue_commit() ((void)0)
610#endif
611
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000612#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000613static void crash_dummy();
614static void crash_test();
615static int crashme = 0;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000616#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000617
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000618static void write1(const char *out)
619{
620 fputs(out, stdout);
621}
622
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000623int vi_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Rob Landleydfba7412006-03-06 20:47:33 +0000624int vi_main(int argc, char **argv)
Eric Andersen3f980402001-04-04 17:31:15 +0000625{
Eric Andersend402edf2001-04-04 19:29:48 +0000626 int c;
Denis Vlasenkob1759462008-06-20 20:20:54 +0000627
628 INIT_G();
Eric Andersen3f980402001-04-04 17:31:15 +0000629
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200630#if ENABLE_FEATURE_VI_UNDO
631 /* undo_stack_tail = NULL; - already is */
632#if ENABLE_FEATURE_VI_UNDO_QUEUE
633 undo_queue_state = UNDO_EMPTY;
634 /* undo_q = 0; - already is */
635#endif
636#endif
637
Denis Vlasenkocd5c7862007-05-17 16:37:22 +0000638#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000639 my_pid = getpid();
640#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000641#if ENABLE_FEATURE_VI_CRASHME
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000642 srand((long) my_pid);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000643#endif
Denis Vlasenko2414a962007-07-18 22:03:40 +0000644#ifdef NO_SUCH_APPLET_YET
645 /* If we aren't "vi", we are "view" */
646 if (ENABLE_FEATURE_VI_READONLY && applet_name[2]) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000647 SET_READONLY_MODE(readonly_mode);
Eric Andersen3f980402001-04-04 17:31:15 +0000648 }
Denis Vlasenko2414a962007-07-18 22:03:40 +0000649#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000650
Denys Vlasenko605f2642012-06-11 01:53:33 +0200651 // autoindent is not default in vim 7.3
652 vi_setops = /*VI_AUTOINDENT |*/ VI_SHOWMATCH | VI_IGNORECASE;
Denis Vlasenkof9234132007-03-21 00:03:42 +0000653 // 1- process $HOME/.exrc file (not inplemented yet)
Eric Andersen3f980402001-04-04 17:31:15 +0000654 // 2- process EXINIT variable from environment
655 // 3- process command line args
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000656#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000657 {
658 char *p = getenv("EXINIT");
659 if (p && *p)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000660 initial_cmds[0] = xstrndup(p, MAX_INPUT_LEN);
Denis Vlasenkof9234132007-03-21 00:03:42 +0000661 }
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000662#endif
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000663 while ((c = getopt(argc, argv, "hCRH" IF_FEATURE_VI_COLON("c:"))) != -1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000664 switch (c) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000665#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000666 case 'C':
667 crashme = 1;
668 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000669#endif
670#if ENABLE_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +0000671 case 'R': // Read-only flag
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000672 SET_READONLY_MODE(readonly_mode);
Eric Andersen3f980402001-04-04 17:31:15 +0000673 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000674#endif
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000675#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000676 case 'c': // cmd line vi command
677 if (*optarg)
Denys Vlasenko605f2642012-06-11 01:53:33 +0200678 initial_cmds[initial_cmds[0] != NULL] = xstrndup(optarg, MAX_INPUT_LEN);
Denis Vlasenkof9234132007-03-21 00:03:42 +0000679 break;
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000680#endif
Paul Fox35e9c5d2008-03-06 16:26:12 +0000681 case 'H':
Eric Andersen3f980402001-04-04 17:31:15 +0000682 show_help();
Paul Fox35e9c5d2008-03-06 16:26:12 +0000683 /* fall through */
Paul Fox35e9c5d2008-03-06 16:26:12 +0000684 default:
Denis Vlasenkoc6938402008-03-24 02:18:03 +0000685 bb_show_usage();
Eric Andersendd8500b2001-07-02 18:06:14 +0000686 return 1;
Eric Andersen3f980402001-04-04 17:31:15 +0000687 }
688 }
689
690 // The argv array can be used by the ":next" and ":rewind" commands
Dennis Groenenc0657e02012-01-31 14:12:38 +0100691 argv += optind;
692 argc -= optind;
Eric Andersen3f980402001-04-04 17:31:15 +0000693
694 //----- This is the main file handling loop --------------
Denys Vlasenko04b52892012-06-11 13:51:38 +0200695 save_argc = argc;
696 optind = 0;
Denys Vlasenkod3dff872012-06-11 13:53:26 +0200697 // "Save cursor, use alternate screen buffer, clear screen"
Denys Vlasenko8187e012017-09-13 22:48:30 +0200698 write1(ESC"[?1049h");
Denys Vlasenko04cecd52010-04-16 22:13:55 -0700699 while (1) {
700 edit_file(argv[optind]); /* param might be NULL */
701 if (++optind >= argc)
702 break;
Eric Andersen3f980402001-04-04 17:31:15 +0000703 }
Denys Vlasenkod3dff872012-06-11 13:53:26 +0200704 // "Use normal screen buffer, restore cursor"
Denys Vlasenko8187e012017-09-13 22:48:30 +0200705 write1(ESC"[?1049l");
Eric Andersen3f980402001-04-04 17:31:15 +0000706 //-----------------------------------------------------------
707
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000708 return 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000709}
710
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000711/* read text from file or create an empty buf */
712/* will also update current_filename */
713static int init_text_buffer(char *fn)
714{
715 int rc;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000716
717 /* allocate/reallocate text buffer */
718 free(text);
Denys Vlasenko32afd3a2014-04-05 22:57:46 +0200719 text_size = 10240;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000720 screenbegin = dot = end = text = xzalloc(text_size);
Denis Vlasenko2f6ae432007-07-19 22:50:47 +0000721
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000722 if (fn != current_filename) {
723 free(current_filename);
724 current_filename = xstrdup(fn);
725 }
Denys Vlasenko32afd3a2014-04-05 22:57:46 +0200726 rc = file_insert(fn, text, 1);
727 if (rc < 0) {
Denys Vlasenkoe7430862014-04-03 12:47:48 +0200728 // file doesnt exist. Start empty buf with dummy line
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200729 char_insert(text, '\n', NO_UNDO);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000730 }
Yousong Zhou8f3bf4f2017-03-24 21:13:10 +0100731
732 flush_undo_data();
733 modified_count = 0;
734 last_modified_count = -1;
735#if ENABLE_FEATURE_VI_YANKMARK
736 /* init the marks */
737 memset(mark, 0, sizeof(mark));
738#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000739 return rc;
Denis Vlasenko2f6ae432007-07-19 22:50:47 +0000740}
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000741
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700742#if ENABLE_FEATURE_VI_WIN_RESIZE
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200743static int query_screen_dimensions(void)
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700744{
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200745 int err = get_terminal_width_height(STDIN_FILENO, &columns, &rows);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700746 if (rows > MAX_SCR_ROWS)
747 rows = MAX_SCR_ROWS;
748 if (columns > MAX_SCR_COLS)
749 columns = MAX_SCR_COLS;
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200750 return err;
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700751}
752#else
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200753# define query_screen_dimensions() (0)
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700754#endif
755
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000756static void edit_file(char *fn)
Eric Andersen3f980402001-04-04 17:31:15 +0000757{
Denis Vlasenkob1759462008-06-20 20:20:54 +0000758#if ENABLE_FEATURE_VI_YANKMARK
759#define cur_line edit_file__cur_line
760#endif
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000761 int c;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000762#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000763 int sig;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000764#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000765
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000766 editing = 1; // 0 = exit, 1 = one file, 2 = multiple files
Eric Andersen3f980402001-04-04 17:31:15 +0000767 rawmode();
768 rows = 24;
769 columns = 80;
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200770 IF_FEATURE_VI_ASK_TERMINAL(G.get_rowcol_error =) query_screen_dimensions();
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700771#if ENABLE_FEATURE_VI_ASK_TERMINAL
772 if (G.get_rowcol_error /* TODO? && no input on stdin */) {
773 uint64_t k;
Denys Vlasenko8187e012017-09-13 22:48:30 +0200774 write1(ESC"[999;999H" ESC"[6n");
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700775 fflush_all();
776 k = read_key(STDIN_FILENO, readbuffer, /*timeout_ms:*/ 100);
777 if ((int32_t)k == KEYCODE_CURSOR_POS) {
778 uint32_t rc = (k >> 32);
779 columns = (rc & 0x7fff);
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200780 if (columns > MAX_SCR_COLS)
781 columns = MAX_SCR_COLS;
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700782 rows = ((rc >> 16) & 0x7fff);
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200783 if (rows > MAX_SCR_ROWS)
784 rows = MAX_SCR_ROWS;
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700785 }
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700786 }
787#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000788 new_screen(rows, columns); // get memory for virtual screen
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000789 init_text_buffer(fn);
Eric Andersen3f980402001-04-04 17:31:15 +0000790
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000791#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000792 YDreg = 26; // default Yank/Delete reg
793 Ureg = 27; // hold orig line for "U" cmd
Eric Andersen3f980402001-04-04 17:31:15 +0000794 mark[26] = mark[27] = text; // init "previous context"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000795#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000796
Eric Andersen3f980402001-04-04 17:31:15 +0000797 last_forward_char = last_input_char = '\0';
798 crow = 0;
799 ccol = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000800
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000801#if ENABLE_FEATURE_VI_USE_SIGNALS
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700802 signal(SIGINT, catch_sig);
Eric Andersen3f980402001-04-04 17:31:15 +0000803 signal(SIGWINCH, winch_sig);
804 signal(SIGTSTP, suspend_sig);
Paul Fox2724fa92008-03-17 15:28:07 +0000805 sig = sigsetjmp(restart, 1);
Eric Andersen3f980402001-04-04 17:31:15 +0000806 if (sig != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000807 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000808 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000809#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000810
Eric Andersen3f980402001-04-04 17:31:15 +0000811 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
812 cmdcnt = 0;
813 tabstop = 8;
814 offset = 0; // no horizontal offset
815 c = '\0';
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000816#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000817 free(ioq_start);
Paul Foxc51fc7b2008-03-06 01:34:23 +0000818 ioq = ioq_start = NULL;
819 lmc_len = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000820 adding2q = 0;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000821#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000822
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000823#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000824 {
825 char *p, *q;
826 int n = 0;
827
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700828 while ((p = initial_cmds[n]) != NULL) {
Denis Vlasenkof9234132007-03-21 00:03:42 +0000829 do {
830 q = p;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000831 p = strchr(q, '\n');
Denis Vlasenkof9234132007-03-21 00:03:42 +0000832 if (p)
Denis Vlasenko51742f42007-04-12 00:32:05 +0000833 while (*p == '\n')
Denis Vlasenkof9234132007-03-21 00:03:42 +0000834 *p++ = '\0';
835 if (*q)
836 colon(q);
837 } while (p);
838 free(initial_cmds[n]);
839 initial_cmds[n] = NULL;
840 n++;
841 }
842 }
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000843#endif
Paul Fox35e9c5d2008-03-06 16:26:12 +0000844 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000845 //------This is the main Vi cmd handling loop -----------------------
846 while (editing > 0) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000847#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000848 if (crashme > 0) {
849 if ((end - text) > 1) {
850 crash_dummy(); // generate a random command
851 } else {
852 crashme = 0;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200853 string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n", NO_UNDO); // insert the string
Denis Vlasenko4ae1e132008-11-19 13:25:14 +0000854 dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000855 refresh(FALSE);
856 }
857 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000858#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000859 last_input_char = c = get_one_char(); // get a cmd from user
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000860#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000861 // save a copy of the current line- for the 'U" command
862 if (begin_line(dot) != cur_line) {
863 cur_line = begin_line(dot);
864 text_yank(begin_line(dot), end_line(dot), Ureg);
865 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000866#endif
867#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000868 // These are commands that change text[].
869 // Remember the input for the "." command
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000870 if (!adding2q && ioq_start == NULL
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000871 && cmd_mode == 0 // command mode
872 && c > '\0' // exclude NUL and non-ASCII chars
873 && c < 0x7f // (Unicode and such)
874 && strchr(modifying_cmds, c)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000875 ) {
Eric Andersen3f980402001-04-04 17:31:15 +0000876 start_new_cmd_q(c);
877 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000878#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000879 do_cmd(c); // execute the user command
Denis Vlasenko8ef801b2008-10-16 09:46:07 +0000880
Eric Andersen3f980402001-04-04 17:31:15 +0000881 // poll to see if there is input already waiting. if we are
882 // not able to display output fast enough to keep up, skip
883 // the display update until we catch up with input.
Denys Vlasenko020f4062009-05-17 16:44:54 +0200884 if (!readbuffer[0] && mysleep(0) == 0) {
Denis Vlasenko8ef801b2008-10-16 09:46:07 +0000885 // no input pending - so update output
Eric Andersen3f980402001-04-04 17:31:15 +0000886 refresh(FALSE);
887 show_status_line();
888 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000889#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000890 if (crashme > 0)
891 crash_test(); // test editor variables
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000892#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000893 }
894 //-------------------------------------------------------------------
895
Denis Vlasenko267e16c2008-10-14 10:34:41 +0000896 go_bottom_and_clear_to_eol();
Eric Andersen3f980402001-04-04 17:31:15 +0000897 cookmode();
Denis Vlasenkob1759462008-06-20 20:20:54 +0000898#undef cur_line
Eric Andersen3f980402001-04-04 17:31:15 +0000899}
900
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000901//----- The Colon commands -------------------------------------
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000902#if ENABLE_FEATURE_VI_COLON
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000903static char *get_one_address(char *p, int *addr) // get colon addr, if present
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000904{
905 int st;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000906 char *q;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000907 IF_FEATURE_VI_YANKMARK(char c;)
908 IF_FEATURE_VI_SEARCH(char *pat;)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000909
910 *addr = -1; // assume no addr
911 if (*p == '.') { // the current line
912 p++;
913 q = begin_line(dot);
914 *addr = count_lines(text, q);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000915 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000916#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000917 else if (*p == '\'') { // is this a mark addr
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000918 p++;
919 c = tolower(*p);
920 p++;
921 if (c >= 'a' && c <= 'z') {
922 // we have a mark
923 c = c - 'a';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000924 q = mark[(unsigned char) c];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000925 if (q != NULL) { // is mark valid
Denis Vlasenko00d84172008-11-24 07:34:42 +0000926 *addr = count_lines(text, q);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000927 }
928 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000929 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000930#endif
931#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000932 else if (*p == '/') { // a search pattern
933 q = strchrnul(++p, '/');
934 pat = xstrndup(p, q - p); // save copy of pattern
935 p = q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000936 if (*p == '/')
937 p++;
938 q = char_search(dot, pat, FORWARD, FULL);
939 if (q != NULL) {
940 *addr = count_lines(text, q);
941 }
942 free(pat);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000943 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000944#endif
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000945 else if (*p == '$') { // the last line in file
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000946 p++;
947 q = begin_line(end - 1);
948 *addr = count_lines(text, q);
949 } else if (isdigit(*p)) { // specific line number
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000950 sscanf(p, "%d%n", addr, &st);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000951 p += st;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000952 } else {
Denys Vlasenkob22bbff2009-07-04 16:50:43 +0200953 // unrecognized address - assume -1
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000954 *addr = -1;
955 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000956 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000957}
958
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000959static char *get_address(char *p, int *b, int *e) // get two colon addrs, if present
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000960{
961 //----- get the address' i.e., 1,3 'a,'b -----
962 // get FIRST addr, if present
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000963 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000964 p++; // skip over leading spaces
965 if (*p == '%') { // alias for 1,$
966 p++;
967 *b = 1;
968 *e = count_lines(text, end-1);
969 goto ga0;
970 }
971 p = get_one_address(p, b);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000972 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000973 p++;
Eric Andersenaff114c2004-04-14 17:51:38 +0000974 if (*p == ',') { // is there a address separator
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000975 p++;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000976 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000977 p++;
978 // get SECOND addr, if present
979 p = get_one_address(p, e);
980 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000981 ga0:
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000982 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000983 p++; // skip over trailing spaces
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000984 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000985}
986
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000987#if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000988static void setops(const char *args, const char *opname, int flg_no,
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000989 const char *short_opname, int opt)
990{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000991 const char *a = args + flg_no;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000992 int l = strlen(opname) - 1; /* opname have + ' ' */
993
Denys Vlasenko6548edd2009-06-15 12:44:11 +0200994 // maybe strncmp? we had tons of erroneous strncasecmp's...
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000995 if (strncasecmp(a, opname, l) == 0
996 || strncasecmp(a, short_opname, 2) == 0
997 ) {
998 if (flg_no)
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000999 vi_setops &= ~opt;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001000 else
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001001 vi_setops |= opt;
1002 }
1003}
1004#endif
1005
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001006#endif /* FEATURE_VI_COLON */
1007
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001008// buf must be no longer than MAX_INPUT_LEN!
1009static void colon(char *buf)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001010{
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001011#if !ENABLE_FEATURE_VI_COLON
1012 /* Simple ":cmd" handler with minimal set of commands */
1013 char *p = buf;
1014 int cnt;
1015
1016 if (*p == ':')
1017 p++;
1018 cnt = strlen(p);
1019 if (cnt == 0)
1020 return;
1021 if (strncmp(p, "quit", cnt) == 0
1022 || strncmp(p, "q!", cnt) == 0
1023 ) {
1024 if (modified_count && p[1] != '!') {
1025 status_line_bold("No write since last change (:%s! overrides)", p);
1026 } else {
1027 editing = 0;
1028 }
1029 return;
1030 }
1031 if (strncmp(p, "write", cnt) == 0
1032 || strncmp(p, "wq", cnt) == 0
1033 || strncmp(p, "wn", cnt) == 0
1034 || (p[0] == 'x' && !p[1])
1035 ) {
Denys Vlasenkoe88608e2017-03-13 20:50:42 +01001036 if (modified_count != 0 || p[0] != 'x') {
1037 cnt = file_write(current_filename, text, end - 1);
1038 }
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001039 if (cnt < 0) {
1040 if (cnt == -1)
Denys Vlasenko6f97b302017-09-29 18:17:25 +02001041 status_line_bold("Write error: "STRERROR_FMT STRERROR_ERRNO);
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001042 } else {
1043 modified_count = 0;
1044 last_modified_count = -1;
1045 status_line("'%s' %dL, %dC",
1046 current_filename,
1047 count_lines(text, end - 1), cnt
1048 );
Denys Vlasenkoe88608e2017-03-13 20:50:42 +01001049 if (p[0] == 'x'
1050 || p[1] == 'q' || p[1] == 'n'
1051 || p[1] == 'Q' || p[1] == 'N'
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001052 ) {
1053 editing = 0;
1054 }
1055 }
1056 return;
1057 }
1058 if (strncmp(p, "file", cnt) == 0) {
1059 last_status_cksum = 0; // force status update
1060 return;
1061 }
1062 if (sscanf(p, "%d", &cnt) > 0) {
1063 dot = find_line(cnt);
1064 dot_skip_over_ws();
1065 return;
1066 }
1067 not_implemented(p);
1068#else
1069
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001070 char c, *orig_buf, *buf1, *q, *r;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001071 char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN];
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001072 int i, l, li, b, e;
1073 int useforce;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001074
1075 // :3154 // if (-e line 3154) goto it else stay put
1076 // :4,33w! foo // write a portion of buffer to file "foo"
1077 // :w // write all of buffer to current file
1078 // :q // quit
1079 // :q! // quit- dont care about modified file
1080 // :'a,'z!sort -u // filter block through sort
1081 // :'f // goto mark "f"
1082 // :'fl // list literal the mark "f" line
1083 // :.r bar // read file "bar" into buffer before dot
1084 // :/123/,/abc/d // delete lines from "123" line to "abc" line
1085 // :/xyz/ // goto the "xyz" line
1086 // :s/find/replace/ // substitute pattern "find" with "replace"
1087 // :!<cmd> // run <cmd> then return
1088 //
Eric Andersen165e8cb2004-07-20 06:44:46 +00001089
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001090 if (!buf[0])
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001091 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001092 if (*buf == ':')
1093 buf++; // move past the ':'
1094
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001095 li = i = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001096 b = e = -1;
1097 q = text; // assume 1,$ for the range
1098 r = end - 1;
1099 li = count_lines(text, end - 1);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001100 fn = current_filename;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001101
1102 // look for optional address(es) :. :1 :1,9 :'q,'a :%
1103 buf = get_address(buf, &b, &e);
1104
1105 // remember orig command line
1106 orig_buf = buf;
1107
1108 // get the COMMAND into cmd[]
1109 buf1 = cmd;
1110 while (*buf != '\0') {
1111 if (isspace(*buf))
1112 break;
1113 *buf1++ = *buf++;
1114 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001115 *buf1 = '\0';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001116 // get any ARGuments
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001117 while (isblank(*buf))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001118 buf++;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001119 strcpy(args, buf);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001120 useforce = FALSE;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001121 buf1 = last_char_is(cmd, '!');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001122 if (buf1) {
1123 useforce = TRUE;
1124 *buf1 = '\0'; // get rid of !
1125 }
1126 if (b >= 0) {
1127 // if there is only one addr, then the addr
1128 // is the line number of the single line the
1129 // user wants. So, reset the end
1130 // pointer to point at end of the "b" line
1131 q = find_line(b); // what line is #b
1132 r = end_line(q);
1133 li = 1;
1134 }
1135 if (e >= 0) {
1136 // we were given two addrs. change the
1137 // end pointer to the addr given by user.
1138 r = find_line(e); // what line is #e
1139 r = end_line(r);
1140 li = e - b + 1;
1141 }
1142 // ------------ now look for the command ------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001143 i = strlen(cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001144 if (i == 0) { // :123CR goto line #123
1145 if (b >= 0) {
1146 dot = find_line(b); // what line is #b
1147 dot_skip_over_ws();
1148 }
Denis Vlasenko249fabf2006-12-19 00:29:22 +00001149 }
1150#if ENABLE_FEATURE_ALLOW_EXEC
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001151 else if (cmd[0] == '!') { // run a cmd
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001152 int retcode;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001153 // :!ls run the <cmd>
Denis Vlasenko267e16c2008-10-14 10:34:41 +00001154 go_bottom_and_clear_to_eol();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001155 cookmode();
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001156 retcode = system(orig_buf + 1); // run the cmd
1157 if (retcode)
1158 printf("\nshell returned %i\n\n", retcode);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001159 rawmode();
1160 Hit_Return(); // let user see results
Denis Vlasenko249fabf2006-12-19 00:29:22 +00001161 }
1162#endif
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001163 else if (cmd[0] == '=' && !cmd[1]) { // where is the address
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001164 if (b < 0) { // no addr given- use defaults
1165 b = e = count_lines(text, dot);
1166 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001167 status_line("%d", b);
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001168 } else if (strncmp(cmd, "delete", i) == 0) { // delete lines
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001169 if (b < 0) { // no addr given- use defaults
1170 q = begin_line(dot); // assume .,. for the range
1171 r = end_line(dot);
1172 }
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001173 dot = yank_delete(q, r, 1, YANKDEL, ALLOW_UNDO); // save, then delete lines
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001174 dot_skip_over_ws();
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001175 } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001176 int size;
1177
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001178 // don't edit, if the current file has been modified
Denys Vlasenkoe7430862014-04-03 12:47:48 +02001179 if (modified_count && !useforce) {
Dennis Groenenc0657e02012-01-31 14:12:38 +01001180 status_line_bold("No write since last change (:%s! overrides)", cmd);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001181 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001182 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001183 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001184 // the user supplied a file name
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00001185 fn = args;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001186 } else if (current_filename && current_filename[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001187 // no user supplied name- use the current filename
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001188 // fn = current_filename; was set by default
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001189 } else {
1190 // no user file name, no current name- punt
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001191 status_line_bold("No current filename");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001192 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001193 }
1194
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001195 size = init_text_buffer(fn);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001196
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001197#if ENABLE_FEATURE_VI_YANKMARK
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001198 if (Ureg >= 0 && Ureg < 28) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001199 free(reg[Ureg]); // free orig line reg- for 'U'
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001200 reg[Ureg] = NULL;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001201 }
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001202 if (YDreg >= 0 && YDreg < 28) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001203 free(reg[YDreg]); // free default yank/delete register
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001204 reg[YDreg] = NULL;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001205 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001206#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001207 // how many lines in text[]?
1208 li = count_lines(text, end - 1);
Denys Vlasenko778794d2013-01-22 10:13:52 +01001209 status_line("'%s'%s"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001210 IF_FEATURE_VI_READONLY("%s")
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001211 " %dL, %dC",
1212 current_filename,
1213 (size < 0 ? " [New file]" : ""),
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001214 IF_FEATURE_VI_READONLY(
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001215 ((readonly_mode) ? " [Readonly]" : ""),
1216 )
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001217 li, (int)(end - text)
1218 );
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001219 } else if (strncmp(cmd, "file", i) == 0) { // what File is this
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001220 if (b != -1 || e != -1) {
Denys Vlasenkoc2704542009-11-20 19:14:19 +01001221 status_line_bold("No address allowed on this command");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001222 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001223 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001224 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001225 // user wants a new filename
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001226 free(current_filename);
1227 current_filename = xstrdup(args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001228 } else {
1229 // user wants file status info
Paul Fox8552aec2005-09-16 12:20:05 +00001230 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001231 }
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001232 } else if (strncmp(cmd, "features", i) == 0) { // what features are available
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001233 // print out values of all features
Denis Vlasenko267e16c2008-10-14 10:34:41 +00001234 go_bottom_and_clear_to_eol();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001235 cookmode();
1236 show_help();
1237 rawmode();
1238 Hit_Return();
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001239 } else if (strncmp(cmd, "list", i) == 0) { // literal print line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001240 if (b < 0) { // no addr given- use defaults
1241 q = begin_line(dot); // assume .,. for the range
1242 r = end_line(dot);
1243 }
Denis Vlasenko267e16c2008-10-14 10:34:41 +00001244 go_bottom_and_clear_to_eol();
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001245 puts("\r");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001246 for (; q <= r; q++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001247 int c_is_no_print;
1248
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001249 c = *q;
Denis Vlasenko2a51af22007-03-21 22:31:24 +00001250 c_is_no_print = (c & 0x80) && !Isprint(c);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001251 if (c_is_no_print) {
1252 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001253 standout_start();
Denis Vlasenkod3c042f2007-12-30 01:59:53 +00001254 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001255 if (c == '\n') {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001256 write1("$\r");
1257 } else if (c < ' ' || c == 127) {
Denis Vlasenko4daad902007-09-27 10:20:47 +00001258 bb_putchar('^');
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001259 if (c == 127)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001260 c = '?';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001261 else
1262 c += '@';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001263 }
Denis Vlasenko4daad902007-09-27 10:20:47 +00001264 bb_putchar(c);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001265 if (c_is_no_print)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001266 standout_end();
1267 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001268 Hit_Return();
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001269 } else if (strncmp(cmd, "quit", i) == 0 // quit
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001270 || strncmp(cmd, "next", i) == 0 // edit next file
Dennis Groenenc0657e02012-01-31 14:12:38 +01001271 || strncmp(cmd, "prev", i) == 0 // edit previous file
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001272 ) {
Denys Vlasenko04cecd52010-04-16 22:13:55 -07001273 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001274 if (useforce) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001275 if (*cmd == 'q') {
Dennis Groenenc0657e02012-01-31 14:12:38 +01001276 // force end of argv list
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001277 optind = save_argc;
1278 }
1279 editing = 0;
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001280 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001281 }
1282 // don't exit if the file been modified
Denys Vlasenkoe7430862014-04-03 12:47:48 +02001283 if (modified_count) {
Dennis Groenenc0657e02012-01-31 14:12:38 +01001284 status_line_bold("No write since last change (:%s! overrides)", cmd);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001285 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001286 }
1287 // are there other file to edit
Denys Vlasenko04cecd52010-04-16 22:13:55 -07001288 n = save_argc - optind - 1;
1289 if (*cmd == 'q' && n > 0) {
1290 status_line_bold("%d more file(s) to edit", n);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001291 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001292 }
Denys Vlasenko04cecd52010-04-16 22:13:55 -07001293 if (*cmd == 'n' && n <= 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001294 status_line_bold("No more files to edit");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001295 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001296 }
Dennis Groenenc0657e02012-01-31 14:12:38 +01001297 if (*cmd == 'p') {
1298 // are there previous files to edit
1299 if (optind < 1) {
1300 status_line_bold("No previous files to edit");
1301 goto ret;
1302 }
1303 optind -= 2;
1304 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001305 editing = 0;
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001306 } else if (strncmp(cmd, "read", i) == 0) { // read file into text[]
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001307 int size;
1308
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001309 fn = args;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001310 if (!fn[0]) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001311 status_line_bold("No filename given");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001312 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001313 }
1314 if (b < 0) { // no addr given- use defaults
1315 q = begin_line(dot); // assume "dot"
1316 }
1317 // read after current line- unless user said ":0r foo"
Ron Yorston70f43202014-11-30 20:39:53 +00001318 if (b != 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001319 q = next_line(q);
Ron Yorston70f43202014-11-30 20:39:53 +00001320 // read after last line
1321 if (q == end-1)
1322 ++q;
1323 }
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001324 { // dance around potentially-reallocated text[]
1325 uintptr_t ofs = q - text;
Ron Yorstone5213ce2014-11-30 20:39:25 +00001326 size = file_insert(fn, q, 0);
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001327 q = text + ofs;
1328 }
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001329 if (size < 0)
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001330 goto ret; // nothing was inserted
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001331 // how many lines in text[]?
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001332 li = count_lines(q, q + size - 1);
Denys Vlasenko778794d2013-01-22 10:13:52 +01001333 status_line("'%s'"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001334 IF_FEATURE_VI_READONLY("%s")
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001335 " %dL, %dC",
1336 fn,
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001337 IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001338 li, size
1339 );
1340 if (size > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001341 // if the insert is before "dot" then we need to update
1342 if (q <= dot)
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001343 dot += size;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001344 }
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001345 } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args
Denys Vlasenkoe7430862014-04-03 12:47:48 +02001346 if (modified_count && !useforce) {
Dennis Groenenc0657e02012-01-31 14:12:38 +01001347 status_line_bold("No write since last change (:%s! overrides)", cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001348 } else {
1349 // reset the filenames to edit
Dennis Groenenc0657e02012-01-31 14:12:38 +01001350 optind = -1; /* start from 0th file */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001351 editing = 0;
1352 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001353#if ENABLE_FEATURE_VI_SET
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001354 } else if (strncmp(cmd, "set", i) == 0) { // set or clear features
Denis Vlasenko58875ae2007-03-22 22:22:10 +00001355#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkof9234132007-03-21 00:03:42 +00001356 char *argp;
Denis Vlasenko58875ae2007-03-22 22:22:10 +00001357#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001358 i = 0; // offset into args
Denys Vlasenko605f2642012-06-11 01:53:33 +02001359 // only blank is regarded as args delimiter. What about tab '\t'?
Denis Vlasenkof9234132007-03-21 00:03:42 +00001360 if (!args[0] || strcasecmp(args, "all") == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001361 // print out values of all options
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001362#if ENABLE_FEATURE_VI_SETOPTS
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001363 status_line_bold(
1364 "%sautoindent "
1365 "%sflash "
1366 "%signorecase "
1367 "%sshowmatch "
1368 "tabstop=%u",
1369 autoindent ? "" : "no",
1370 err_method ? "" : "no",
1371 ignorecase ? "" : "no",
1372 showmatch ? "" : "no",
1373 tabstop
1374 );
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001375#endif
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001376 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001377 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001378#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001379 argp = args;
Denis Vlasenkoba2fb712007-04-01 09:39:03 +00001380 while (*argp) {
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001381 if (strncmp(argp, "no", 2) == 0)
Denis Vlasenkof9234132007-03-21 00:03:42 +00001382 i = 2; // ":set noautoindent"
1383 setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001384 setops(argp, "flash " , i, "fl", VI_ERR_METHOD);
Denis Vlasenkof9234132007-03-21 00:03:42 +00001385 setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001386 setops(argp, "showmatch " , i, "sm", VI_SHOWMATCH );
1387 if (strncmp(argp + i, "tabstop=", 8) == 0) {
1388 int t = 0;
1389 sscanf(argp + i+8, "%u", &t);
1390 if (t > 0 && t <= MAX_TABSTOP)
1391 tabstop = t;
Denis Vlasenkof9234132007-03-21 00:03:42 +00001392 }
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001393 argp = skip_non_whitespace(argp);
1394 argp = skip_whitespace(argp);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001395 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001396#endif /* FEATURE_VI_SETOPTS */
1397#endif /* FEATURE_VI_SET */
1398#if ENABLE_FEATURE_VI_SEARCH
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001399 } else if (cmd[0] == 's') { // substitute a pattern with a replacement pattern
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001400 char *F, *R, *flags;
1401 size_t len_F, len_R;
1402 int gflag; // global replace flag
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001403#if ENABLE_FEATURE_VI_UNDO
1404 int dont_chain_first_item = ALLOW_UNDO;
1405#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001406
1407 // F points to the "find" pattern
1408 // R points to the "replace" pattern
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001409 // replace the cmd line delimiters "/" with NULs
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001410 c = orig_buf[1]; // what is the delimiter
1411 F = orig_buf + 2; // start of "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001412 R = strchr(F, c); // middle delimiter
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001413 if (!R)
1414 goto colon_s_fail;
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001415 len_F = R - F;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001416 *R++ = '\0'; // terminate "find"
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001417 flags = strchr(R, c);
1418 if (!flags)
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001419 goto colon_s_fail;
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001420 len_R = flags - R;
1421 *flags++ = '\0'; // terminate "replace"
1422 gflag = *flags;
1423
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001424 q = begin_line(q);
1425 if (b < 0) { // maybe :s/foo/bar/
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001426 q = begin_line(dot); // start with cur line
1427 b = count_lines(text, q); // cur line number
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001428 }
1429 if (e < 0)
1430 e = b; // maybe :.s/foo/bar/
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001431
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001432 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001433 char *ls = q; // orig line start
1434 char *found;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001435 vc4:
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001436 found = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
1437 if (found) {
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001438 uintptr_t bias;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001439 // we found the "find" pattern - delete it
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001440 // For undo support, the first item should not be chained
1441 text_hole_delete(found, found + len_F - 1, dont_chain_first_item);
1442#if ENABLE_FEATURE_VI_UNDO
1443 dont_chain_first_item = ALLOW_UNDO_CHAIN;
1444#endif
1445 // insert the "replace" patern
1446 bias = string_insert(found, R, ALLOW_UNDO_CHAIN);
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001447 found += bias;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001448 ls += bias;
1449 /*q += bias; - recalculated anyway */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001450 // check for "global" :s/foo/bar/g
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001451 if (gflag == 'g') {
1452 if ((found + len_R) < end_line(ls)) {
1453 q = found + len_R;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001454 goto vc4; // don't let q move past cur line
1455 }
1456 }
1457 }
1458 q = next_line(ls);
1459 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001460#endif /* FEATURE_VI_SEARCH */
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001461 } else if (strncmp(cmd, "version", i) == 0) { // show software version
Denys Vlasenkoeba7fe62017-01-29 19:14:26 +01001462 status_line(BB_VER);
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001463 } else if (strncmp(cmd, "write", i) == 0 // write text to file
1464 || strncmp(cmd, "wq", i) == 0
1465 || strncmp(cmd, "wn", i) == 0
1466 || (cmd[0] == 'x' && !cmd[1])
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001467 ) {
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001468 int size;
1469 //int forced = FALSE;
1470
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001471 // is there a file name to write to?
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001472 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001473 fn = args;
1474 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001475#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001476 if (readonly_mode && !useforce) {
Denys Vlasenko778794d2013-01-22 10:13:52 +01001477 status_line_bold("'%s' is read only", fn);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001478 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001479 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001480#endif
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001481 //if (useforce) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001482 // if "fn" is not write-able, chmod u+w
1483 // sprintf(syscmd, "chmod u+w %s", fn);
1484 // system(syscmd);
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001485 // forced = TRUE;
1486 //}
Denys Vlasenkoe88608e2017-03-13 20:50:42 +01001487 if (modified_count != 0 || cmd[0] != 'x') {
1488 size = r - q + 1;
1489 l = file_write(fn, q, r);
1490 } else {
1491 size = 0;
1492 l = 0;
1493 }
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001494 //if (useforce && forced) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001495 // chmod u-w
1496 // sprintf(syscmd, "chmod u-w %s", fn);
1497 // system(syscmd);
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001498 // forced = FALSE;
1499 //}
Paul Fox61e45db2005-10-09 14:43:22 +00001500 if (l < 0) {
1501 if (l == -1)
Denys Vlasenko9e7c0022013-03-15 02:17:29 +01001502 status_line_bold_errno(fn);
Paul Fox61e45db2005-10-09 14:43:22 +00001503 } else {
Denys Vlasenkoe88608e2017-03-13 20:50:42 +01001504 // how many lines written
1505 li = count_lines(q, q + l - 1);
Denys Vlasenko778794d2013-01-22 10:13:52 +01001506 status_line("'%s' %dL, %dC", fn, li, l);
Denys Vlasenkoe88608e2017-03-13 20:50:42 +01001507 if (l == size) {
1508 if (q == text && q + l == end) {
1509 modified_count = 0;
1510 last_modified_count = -1;
1511 }
1512 if (cmd[0] == 'x'
1513 || cmd[1] == 'q' || cmd[1] == 'n'
1514 || cmd[1] == 'Q' || cmd[1] == 'N'
1515 ) {
1516 editing = 0;
1517 }
Paul Fox61e45db2005-10-09 14:43:22 +00001518 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001519 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001520#if ENABLE_FEATURE_VI_YANKMARK
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001521 } else if (strncmp(cmd, "yank", i) == 0) { // yank lines
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001522 if (b < 0) { // no addr given- use defaults
1523 q = begin_line(dot); // assume .,. for the range
1524 r = end_line(dot);
1525 }
1526 text_yank(q, r, YDreg);
1527 li = count_lines(q, r);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001528 status_line("Yank %d lines (%d chars) into [%c]",
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001529 li, strlen(reg[YDreg]), what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001530#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001531 } else {
1532 // cmd unknown
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001533 not_implemented(cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001534 }
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001535 ret:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001536 dot = bound_dot(dot); // make sure "dot" is valid
1537 return;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001538#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001539 colon_s_fail:
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001540 status_line(":s expression missing delimiters");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001541#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001542#endif /* FEATURE_VI_COLON */
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001543}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001544
1545static void Hit_Return(void)
1546{
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00001547 int c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001548
Denis Vlasenkof882f082007-12-23 02:36:51 +00001549 standout_start();
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001550 write1("[Hit return to continue]");
Denis Vlasenkof882f082007-12-23 02:36:51 +00001551 standout_end();
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001552 while ((c = get_one_char()) != '\n' && c != '\r')
1553 continue;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001554 redraw(TRUE); // force redraw all
1555}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001556
Denis Vlasenko91afdf82007-07-17 23:22:49 +00001557static int next_tabstop(int col)
1558{
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001559 return col + ((tabstop - 1) - (col % tabstop));
1560}
1561
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001562//----- Synchronize the cursor to Dot --------------------------
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02001563static NOINLINE void sync_cursor(char *d, int *row, int *col)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001564{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001565 char *beg_cur; // begin and end of "d" line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001566 char *tp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001567 int cnt, ro, co;
1568
1569 beg_cur = begin_line(d); // first char of cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001570
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001571 if (beg_cur < screenbegin) {
Denis Vlasenkof882f082007-12-23 02:36:51 +00001572 // "d" is before top line on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001573 // how many lines do we have to move
1574 cnt = count_lines(beg_cur, screenbegin);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001575 sc1:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001576 screenbegin = beg_cur;
1577 if (cnt > (rows - 1) / 2) {
1578 // we moved too many lines. put "dot" in middle of screen
1579 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1580 screenbegin = prev_line(screenbegin);
1581 }
1582 }
Denis Vlasenkof882f082007-12-23 02:36:51 +00001583 } else {
1584 char *end_scr; // begin and end of screen
1585 end_scr = end_screen(); // last char of screen
1586 if (beg_cur > end_scr) {
1587 // "d" is after bottom line on screen
1588 // how many lines do we have to move
1589 cnt = count_lines(end_scr, beg_cur);
1590 if (cnt > (rows - 1) / 2)
1591 goto sc1; // too many lines
1592 for (ro = 0; ro < cnt - 1; ro++) {
1593 // move screen begin the same amount
1594 screenbegin = next_line(screenbegin);
1595 // now, move the end of screen
1596 end_scr = next_line(end_scr);
1597 end_scr = end_line(end_scr);
1598 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001599 }
1600 }
1601 // "d" is on screen- find out which row
1602 tp = screenbegin;
1603 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1604 if (tp == beg_cur)
1605 break;
1606 tp = next_line(tp);
1607 }
1608
1609 // find out what col "d" is on
1610 co = 0;
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001611 while (tp < d) { // drive "co" to correct column
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001612 if (*tp == '\n') //vda || *tp == '\0')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001613 break;
1614 if (*tp == '\t') {
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001615 // handle tabs like real vi
1616 if (d == tp && cmd_mode) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001617 break;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001618 }
Denis Vlasenkoe3eae0d2008-06-22 16:38:53 +00001619 co = next_tabstop(co);
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001620 } else if ((unsigned char)*tp < ' ' || *tp == 0x7f) {
1621 co++; // display as ^X, use 2 columns
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001622 }
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001623 co++;
1624 tp++;
1625 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001626
1627 // "co" is the column where "dot" is.
1628 // The screen has "columns" columns.
1629 // The currently displayed columns are 0+offset -- columns+ofset
1630 // |-------------------------------------------------------------|
1631 // ^ ^ ^
1632 // offset | |------- columns ----------------|
1633 //
1634 // If "co" is already in this range then we do not have to adjust offset
1635 // but, we do have to subtract the "offset" bias from "co".
1636 // If "co" is outside this range then we have to change "offset".
1637 // If the first char of a line is a tab the cursor will try to stay
1638 // in column 7, but we have to set offset to 0.
1639
1640 if (co < 0 + offset) {
1641 offset = co;
1642 }
1643 if (co >= columns + offset) {
1644 offset = co - columns + 1;
1645 }
1646 // if the first char of the line is a tab, and "dot" is sitting on it
1647 // force offset to 0.
1648 if (d == beg_cur && *d == '\t') {
1649 offset = 0;
1650 }
1651 co -= offset;
1652
1653 *row = ro;
1654 *col = co;
1655}
1656
1657//----- Text Movement Routines ---------------------------------
Denis Vlasenkof882f082007-12-23 02:36:51 +00001658static char *begin_line(char *p) // return pointer to first char cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001659{
Denis Vlasenkof882f082007-12-23 02:36:51 +00001660 if (p > text) {
1661 p = memrchr(text, '\n', p - text);
1662 if (!p)
1663 return text;
1664 return p + 1;
1665 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001666 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001667}
1668
Denis Vlasenkoe3eae0d2008-06-22 16:38:53 +00001669static char *end_line(char *p) // return pointer to NL of cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001670{
Denis Vlasenkof882f082007-12-23 02:36:51 +00001671 if (p < end - 1) {
1672 p = memchr(p, '\n', end - p - 1);
1673 if (!p)
1674 return end - 1;
1675 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001676 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001677}
1678
Denis Vlasenkof882f082007-12-23 02:36:51 +00001679static char *dollar_line(char *p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001680{
Denis Vlasenkof882f082007-12-23 02:36:51 +00001681 p = end_line(p);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001682 // Try to stay off of the Newline
1683 if (*p == '\n' && (p - begin_line(p)) > 0)
1684 p--;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001685 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001686}
1687
Denis Vlasenkof882f082007-12-23 02:36:51 +00001688static char *prev_line(char *p) // return pointer first char prev line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001689{
Maninder Singh97c64912015-05-25 13:46:36 +02001690 p = begin_line(p); // goto beginning of cur line
Denis Vlasenkoe3eae0d2008-06-22 16:38:53 +00001691 if (p > text && p[-1] == '\n')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001692 p--; // step to prev line
Maninder Singh97c64912015-05-25 13:46:36 +02001693 p = begin_line(p); // goto beginning of prev line
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001694 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001695}
1696
Denis Vlasenkof882f082007-12-23 02:36:51 +00001697static char *next_line(char *p) // return pointer first char next line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001698{
1699 p = end_line(p);
Denis Vlasenkoe3eae0d2008-06-22 16:38:53 +00001700 if (p < end - 1 && *p == '\n')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001701 p++; // step to next line
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001702 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001703}
1704
1705//----- Text Information Routines ------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001706static char *end_screen(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001707{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001708 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001709 int cnt;
1710
1711 // find new bottom line
1712 q = screenbegin;
1713 for (cnt = 0; cnt < rows - 2; cnt++)
1714 q = next_line(q);
1715 q = end_line(q);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001716 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001717}
1718
Denis Vlasenkof882f082007-12-23 02:36:51 +00001719// count line from start to stop
1720static int count_lines(char *start, char *stop)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001721{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001722 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001723 int cnt;
1724
Denis Vlasenkof882f082007-12-23 02:36:51 +00001725 if (stop < start) { // start and stop are backwards- reverse them
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001726 q = start;
1727 start = stop;
1728 stop = q;
1729 }
1730 cnt = 0;
Denis Vlasenkof882f082007-12-23 02:36:51 +00001731 stop = end_line(stop);
1732 while (start <= stop && start <= end - 1) {
1733 start = end_line(start);
1734 if (*start == '\n')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001735 cnt++;
Denis Vlasenkof882f082007-12-23 02:36:51 +00001736 start++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001737 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001738 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001739}
1740
Maninder Singh97c64912015-05-25 13:46:36 +02001741static char *find_line(int li) // find beginning of line #li
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001742{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001743 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001744
1745 for (q = text; li > 1; li--) {
1746 q = next_line(q);
1747 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001748 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001749}
1750
1751//----- Dot Movement Routines ----------------------------------
1752static void dot_left(void)
1753{
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001754 undo_queue_commit();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001755 if (dot > text && dot[-1] != '\n')
1756 dot--;
1757}
1758
1759static void dot_right(void)
1760{
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001761 undo_queue_commit();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001762 if (dot < end - 1 && *dot != '\n')
1763 dot++;
1764}
1765
1766static void dot_begin(void)
1767{
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001768 undo_queue_commit();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001769 dot = begin_line(dot); // return pointer to first char cur line
1770}
1771
1772static void dot_end(void)
1773{
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001774 undo_queue_commit();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001775 dot = end_line(dot); // return pointer to last char cur line
1776}
1777
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001778static char *move_to_col(char *p, int l)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001779{
1780 int co;
1781
1782 p = begin_line(p);
1783 co = 0;
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001784 while (co < l && p < end) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001785 if (*p == '\n') //vda || *p == '\0')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001786 break;
1787 if (*p == '\t') {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001788 co = next_tabstop(co);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001789 } else if (*p < ' ' || *p == 127) {
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001790 co++; // display as ^X, use 2 columns
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001791 }
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001792 co++;
1793 p++;
1794 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001795 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001796}
1797
1798static void dot_next(void)
1799{
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001800 undo_queue_commit();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001801 dot = next_line(dot);
1802}
1803
1804static void dot_prev(void)
1805{
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001806 undo_queue_commit();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001807 dot = prev_line(dot);
1808}
1809
1810static void dot_scroll(int cnt, int dir)
1811{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001812 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001813
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001814 undo_queue_commit();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001815 for (; cnt > 0; cnt--) {
1816 if (dir < 0) {
1817 // scroll Backwards
Denis Vlasenkof882f082007-12-23 02:36:51 +00001818 // ctrl-Y scroll up one line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001819 screenbegin = prev_line(screenbegin);
1820 } else {
1821 // scroll Forwards
Denis Vlasenkof882f082007-12-23 02:36:51 +00001822 // ctrl-E scroll down one line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001823 screenbegin = next_line(screenbegin);
1824 }
1825 }
1826 // make sure "dot" stays on the screen so we dont scroll off
1827 if (dot < screenbegin)
1828 dot = screenbegin;
1829 q = end_screen(); // find new bottom line
1830 if (dot > q)
1831 dot = begin_line(q); // is dot is below bottom line?
1832 dot_skip_over_ws();
1833}
1834
1835static void dot_skip_over_ws(void)
1836{
1837 // skip WS
1838 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1839 dot++;
1840}
1841
Denis Vlasenkof882f082007-12-23 02:36:51 +00001842static char *bound_dot(char *p) // make sure text[0] <= P < "end"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001843{
1844 if (p >= end && end > text) {
1845 p = end - 1;
Denys Vlasenko05399fc2014-09-15 17:06:10 +02001846 indicate_error();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001847 }
1848 if (p < text) {
1849 p = text;
Denys Vlasenko05399fc2014-09-15 17:06:10 +02001850 indicate_error();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001851 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001852 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001853}
1854
1855//----- Helper Utility Routines --------------------------------
1856
1857//----------------------------------------------------------------
1858//----- Char Routines --------------------------------------------
1859/* Chars that are part of a word-
1860 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1861 * Chars that are Not part of a word (stoppers)
1862 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1863 * Chars that are WhiteSpace
1864 * TAB NEWLINE VT FF RETURN SPACE
1865 * DO NOT COUNT NEWLINE AS WHITESPACE
1866 */
1867
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001868static char *new_screen(int ro, int co)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001869{
1870 int li;
1871
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001872 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001873 screensize = ro * co + 8;
Denis Vlasenkob95636c2006-12-19 23:36:04 +00001874 screen = xmalloc(screensize);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001875 // initialize the new screen. assume this will be a empty file.
1876 screen_erase();
Eric Andersenaff114c2004-04-14 17:51:38 +00001877 // non-existent text[] lines start with a tilde (~).
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001878 for (li = 1; li < ro - 1; li++) {
1879 screen[(li * co) + 0] = '~';
1880 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001881 return screen;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001882}
1883
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001884#if ENABLE_FEATURE_VI_SEARCH
Walter Harmsb9ba5802011-06-27 02:59:37 +02001885
1886# if ENABLE_FEATURE_VI_REGEX_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001887
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001888// search for pattern starting at p
Denis Vlasenko33875382008-06-21 20:31:50 +00001889static char *char_search(char *p, const char *pat, int dir, int range)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001890{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001891 struct re_pattern_buffer preg;
Denys Vlasenko264f3732013-04-21 15:51:41 +02001892 const char *err;
1893 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001894 int i;
Walter Harmsb9ba5802011-06-27 02:59:37 +02001895 int size;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001896
1897 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
Denys Vlasenko264f3732013-04-21 15:51:41 +02001898 if (ignorecase)
1899 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED | RE_ICASE;
1900
1901 memset(&preg, 0, sizeof(preg));
1902 err = re_compile_pattern(pat, strlen(pat), &preg);
1903 if (err != NULL) {
1904 status_line_bold("bad search pattern '%s': %s", pat, err);
1905 return p;
1906 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001907
1908 // assume a LIMITED forward search
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001909 q = end - 1;
Denys Vlasenko264f3732013-04-21 15:51:41 +02001910 if (dir == BACK)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001911 q = text;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001912 // RANGE could be negative if we are searching backwards
1913 range = q - p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001914 q = p;
Denys Vlasenko264f3732013-04-21 15:51:41 +02001915 size = range;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001916 if (range < 0) {
Denys Vlasenko264f3732013-04-21 15:51:41 +02001917 size = -size;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001918 q = p - size;
1919 if (q < text)
1920 q = text;
1921 }
1922 // search for the compiled pattern, preg, in p[]
Denys Vlasenko264f3732013-04-21 15:51:41 +02001923 // range < 0: search backward
1924 // range > 0: search forward
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001925 // 0 < start < size
Denys Vlasenko264f3732013-04-21 15:51:41 +02001926 // re_search() < 0: not found or error
1927 // re_search() >= 0: index of found pattern
1928 // struct pattern char int int int struct reg
1929 // re_search(*pattern_buffer, *string, size, start, range, *regs)
1930 i = re_search(&preg, q, size, /*start:*/ 0, range, /*struct re_registers*:*/ NULL);
1931 regfree(&preg);
1932 if (i < 0)
1933 return NULL;
1934 if (dir == FORWARD)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001935 p = p + i;
Denys Vlasenko264f3732013-04-21 15:51:41 +02001936 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001937 p = p - i;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001938 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001939}
Walter Harmsb9ba5802011-06-27 02:59:37 +02001940
1941# else
1942
1943# if ENABLE_FEATURE_VI_SETOPTS
1944static int mycmp(const char *s1, const char *s2, int len)
1945{
1946 if (ignorecase) {
1947 return strncasecmp(s1, s2, len);
1948 }
1949 return strncmp(s1, s2, len);
1950}
1951# else
1952# define mycmp strncmp
1953# endif
1954
1955static char *char_search(char *p, const char *pat, int dir, int range)
1956{
1957 char *start, *stop;
1958 int len;
1959
1960 len = strlen(pat);
1961 if (dir == FORWARD) {
Denys Vlasenko264f3732013-04-21 15:51:41 +02001962 stop = end - 1; // assume range is p..end-1
Walter Harmsb9ba5802011-06-27 02:59:37 +02001963 if (range == LIMITED)
1964 stop = next_line(p); // range is to next line
1965 for (start = p; start < stop; start++) {
1966 if (mycmp(start, pat, len) == 0) {
1967 return start;
1968 }
1969 }
1970 } else if (dir == BACK) {
Denys Vlasenko264f3732013-04-21 15:51:41 +02001971 stop = text; // assume range is text..p
Walter Harmsb9ba5802011-06-27 02:59:37 +02001972 if (range == LIMITED)
1973 stop = prev_line(p); // range is to prev line
1974 for (start = p - len; start >= stop; start--) {
1975 if (mycmp(start, pat, len) == 0) {
1976 return start;
1977 }
1978 }
1979 }
1980 // pattern not found
1981 return NULL;
1982}
1983
1984# endif
1985
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001986#endif /* FEATURE_VI_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001987
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001988static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001989{
1990 if (c == 22) { // Is this an ctrl-V?
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001991 p += stupid_insert(p, '^'); // use ^ to indicate literal next
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001992 refresh(FALSE); // show the ^
1993 c = get_one_char();
1994 *p = c;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001995#if ENABLE_FEATURE_VI_UNDO
1996 switch (undo) {
1997 case ALLOW_UNDO:
1998 undo_push(p, 1, UNDO_INS);
1999 break;
2000 case ALLOW_UNDO_CHAIN:
2001 undo_push(p, 1, UNDO_INS_CHAIN);
2002 break;
2003# if ENABLE_FEATURE_VI_UNDO_QUEUE
2004 case ALLOW_UNDO_QUEUED:
2005 undo_push(p, 1, UNDO_INS_QUEUED);
2006 break;
2007# endif
2008 }
2009#else
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002010 modified_count++;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002011#endif /* ENABLE_FEATURE_VI_UNDO */
2012 p++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002013 } else if (c == 27) { // Is this an ESC?
2014 cmd_mode = 0;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002015 undo_queue_commit();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002016 cmdcnt = 0;
2017 end_cmd_q(); // stop adding to q
Paul Fox8552aec2005-09-16 12:20:05 +00002018 last_status_cksum = 0; // force status update
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +00002019 if ((p[-1] != '\n') && (dot > text)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002020 p--;
2021 }
Paul Foxd13b90b2005-07-18 22:17:25 +00002022 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
Denys Vlasenko49acc1a2015-03-12 21:15:34 +01002023 if (p > text) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002024 p--;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002025 p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED); // shrink buffer 1 char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002026 }
2027 } else {
2028 // insert a char into text[]
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002029 if (c == 13)
2030 c = '\n'; // translate \r to \n
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002031#if ENABLE_FEATURE_VI_UNDO
2032# if ENABLE_FEATURE_VI_UNDO_QUEUE
2033 if (c == '\n')
2034 undo_queue_commit();
2035# endif
2036 switch (undo) {
2037 case ALLOW_UNDO:
2038 undo_push(p, 1, UNDO_INS);
2039 break;
2040 case ALLOW_UNDO_CHAIN:
2041 undo_push(p, 1, UNDO_INS_CHAIN);
2042 break;
2043# if ENABLE_FEATURE_VI_UNDO_QUEUE
2044 case ALLOW_UNDO_QUEUED:
2045 undo_push(p, 1, UNDO_INS_QUEUED);
2046 break;
2047# endif
2048 }
2049#else
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002050 modified_count++;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002051#endif /* ENABLE_FEATURE_VI_UNDO */
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002052 p += 1 + stupid_insert(p, c); // insert the char
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002053#if ENABLE_FEATURE_VI_SETOPTS
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002054 if (showmatch && strchr(")]}", c) != NULL) {
2055 showmatching(p - 1);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002056 }
2057 if (autoindent && c == '\n') { // auto indent the new line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002058 char *q;
Denis Vlasenko00d84172008-11-24 07:34:42 +00002059 size_t len;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002060 q = prev_line(p); // use prev line as template
Denis Vlasenko00d84172008-11-24 07:34:42 +00002061 len = strspn(q, " \t"); // space or tab
2062 if (len) {
Denis Vlasenko3266aa92009-04-01 11:24:04 +00002063 uintptr_t bias;
Denis Vlasenko00d84172008-11-24 07:34:42 +00002064 bias = text_hole_make(p, len);
2065 p += bias;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002066 q += bias;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002067#if ENABLE_FEATURE_VI_UNDO
2068 undo_push(p, len, UNDO_INS);
2069#endif
Denis Vlasenko00d84172008-11-24 07:34:42 +00002070 memcpy(p, q, len);
Denis Vlasenko3266aa92009-04-01 11:24:04 +00002071 p += len;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002072 }
2073 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002074#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002075 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002076 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002077}
2078
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002079// might reallocate text[]! use p += stupid_insert(p, ...),
2080// and be careful to not use pointers into potentially freed text[]!
2081static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002082{
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002083 uintptr_t bias;
2084 bias = text_hole_make(p, 1);
2085 p += bias;
Denis Vlasenkob1759462008-06-20 20:20:54 +00002086 *p = c;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002087 return bias;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002088}
2089
Denis Vlasenko33875382008-06-21 20:31:50 +00002090static int find_range(char **start, char **stop, char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002091{
Paul Foxc51fc7b2008-03-06 01:34:23 +00002092 char *save_dot, *p, *q, *t;
2093 int cnt, multiline = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002094
2095 save_dot = dot;
2096 p = q = dot;
2097
2098 if (strchr("cdy><", c)) {
2099 // these cmds operate on whole lines
2100 p = q = begin_line(p);
2101 for (cnt = 1; cnt < cmdcnt; cnt++) {
2102 q = next_line(q);
2103 }
2104 q = end_line(q);
Paul Foxc51fc7b2008-03-06 01:34:23 +00002105 } else if (strchr("^%$0bBeEfth\b\177", c)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002106 // These cmds operate on char positions
2107 do_cmd(c); // execute movement cmd
2108 q = dot;
2109 } else if (strchr("wW", c)) {
2110 do_cmd(c); // execute movement cmd
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002111 // if we are at the next word's first char
2112 // step back one char
2113 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00002114 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002115 || (ispunct(dot[-1]) && !ispunct(dot[0]))
2116 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
2117 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002118 if (dot > text && *dot == '\n')
2119 dot--; // stay off NL
2120 q = dot;
2121 } else if (strchr("H-k{", c)) {
2122 // these operate on multi-lines backwards
2123 q = end_line(dot); // find NL
2124 do_cmd(c); // execute movement cmd
2125 dot_begin();
2126 p = dot;
2127 } else if (strchr("L+j}\r\n", c)) {
2128 // these operate on multi-lines forwards
2129 p = begin_line(dot);
2130 do_cmd(c); // execute movement cmd
2131 dot_end(); // find NL
2132 q = dot;
2133 } else {
Denys Vlasenko6830ade2013-01-15 13:58:01 +01002134 // nothing -- this causes any other values of c to
2135 // represent the one-character range under the
2136 // cursor. this is correct for ' ' and 'l', but
2137 // perhaps no others.
2138 //
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002139 }
Paul Foxc51fc7b2008-03-06 01:34:23 +00002140 if (q < p) {
2141 t = q;
2142 q = p;
2143 p = t;
2144 }
2145
Denis Vlasenko42cc3042008-03-24 02:05:58 +00002146 // backward char movements don't include start position
Paul Foxc51fc7b2008-03-06 01:34:23 +00002147 if (q > p && strchr("^0bBh\b\177", c)) q--;
2148
2149 multiline = 0;
2150 for (t = p; t <= q; t++) {
2151 if (*t == '\n') {
2152 multiline = 1;
2153 break;
2154 }
2155 }
2156
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002157 *start = p;
2158 *stop = q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002159 dot = save_dot;
Paul Foxc51fc7b2008-03-06 01:34:23 +00002160 return multiline;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002161}
2162
Denis Vlasenko33875382008-06-21 20:31:50 +00002163static int st_test(char *p, int type, int dir, char *tested)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002164{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002165 char c, c0, ci;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002166 int test, inc;
2167
2168 inc = dir;
2169 c = c0 = p[0];
2170 ci = p[inc];
2171 test = 0;
2172
2173 if (type == S_BEFORE_WS) {
2174 c = ci;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02002175 test = (!isspace(c) || c == '\n');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002176 }
2177 if (type == S_TO_WS) {
2178 c = c0;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02002179 test = (!isspace(c) || c == '\n');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002180 }
2181 if (type == S_OVER_WS) {
2182 c = c0;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02002183 test = isspace(c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002184 }
2185 if (type == S_END_PUNCT) {
2186 c = ci;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02002187 test = ispunct(c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002188 }
2189 if (type == S_END_ALNUM) {
2190 c = ci;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02002191 test = (isalnum(c) || c == '_');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002192 }
2193 *tested = c;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002194 return test;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002195}
2196
Denis Vlasenko33875382008-06-21 20:31:50 +00002197static char *skip_thing(char *p, int linecnt, int dir, int type)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002198{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002199 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002200
2201 while (st_test(p, type, dir, &c)) {
2202 // make sure we limit search to correct number of lines
2203 if (c == '\n' && --linecnt < 1)
2204 break;
2205 if (dir >= 0 && p >= end - 1)
2206 break;
2207 if (dir < 0 && p <= text)
2208 break;
2209 p += dir; // move to next char
2210 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002211 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002212}
2213
2214// find matching char of pair () [] {}
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002215// will crash if c is not one of these
Denis Vlasenko33875382008-06-21 20:31:50 +00002216static char *find_pair(char *p, const char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002217{
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002218 const char *braces = "()[]{}";
2219 char match;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002220 int dir, level;
2221
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002222 dir = strchr(braces, c) - braces;
2223 dir ^= 1;
2224 match = braces[dir];
2225 dir = ((dir & 1) << 1) - 1; /* 1 for ([{, -1 for )\} */
2226
2227 // look for match, count levels of pairs (( ))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002228 level = 1;
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002229 for (;;) {
2230 p += dir;
2231 if (p < text || p >= end)
2232 return NULL;
2233 if (*p == c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002234 level++; // increase pair levels
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002235 if (*p == match) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002236 level--; // reduce pair level
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002237 if (level == 0)
2238 return p; // found matching pair
2239 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002240 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002241}
2242
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002243#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002244// show the matching char of a pair, () [] {}
Denis Vlasenkof882f082007-12-23 02:36:51 +00002245static void showmatching(char *p)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002246{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002247 char *q, *save_dot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002248
2249 // we found half of a pair
2250 q = find_pair(p, *p); // get loc of matching char
2251 if (q == NULL) {
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002252 indicate_error(); // no matching char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002253 } else {
2254 // "q" now points to matching pair
2255 save_dot = dot; // remember where we are
2256 dot = q; // go to new loc
2257 refresh(FALSE); // let the user see it
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002258 mysleep(40); // give user some time
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002259 dot = save_dot; // go back to old loc
2260 refresh(FALSE);
2261 }
2262}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002263#endif /* FEATURE_VI_SETOPTS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002264
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002265#if ENABLE_FEATURE_VI_UNDO
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002266static void flush_undo_data(void)
2267{
2268 struct undo_object *undo_entry;
2269
2270 while (undo_stack_tail) {
2271 undo_entry = undo_stack_tail;
2272 undo_stack_tail = undo_entry->prev;
2273 free(undo_entry);
2274 }
2275}
2276
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002277// Undo functions and hooks added by Jody Bruchon (jody@jodybruchon.com)
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002278static void undo_push(char *src, unsigned int length, uint8_t u_type) // Add to the undo stack
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002279{
Denys Vlasenko2c512022014-04-03 01:45:05 +02002280 struct undo_object *undo_entry;
2281
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002282 // "u_type" values
2283 // UNDO_INS: insertion, undo will remove from buffer
2284 // UNDO_DEL: deleted text, undo will restore to buffer
2285 // UNDO_{INS,DEL}_CHAIN: Same as above but also calls undo_pop() when complete
2286 // The CHAIN operations are for handling multiple operations that the user
2287 // performs with a single action, i.e. REPLACE mode or find-and-replace commands
2288 // UNDO_{INS,DEL}_QUEUED: If queuing feature is enabled, allow use of the queue
2289 // for the INS/DEL operation. The raw values should be equal to the values of
2290 // UNDO_{INS,DEL} ORed with UNDO_QUEUED_FLAG
2291
2292#if ENABLE_FEATURE_VI_UNDO_QUEUE
2293 // This undo queuing functionality groups multiple character typing or backspaces
2294 // into a single large undo object. This greatly reduces calls to malloc() for
2295 // single-character operations while typing and has the side benefit of letting
2296 // an undo operation remove chunks of text rather than a single character.
2297 switch (u_type) {
2298 case UNDO_EMPTY: // Just in case this ever happens...
2299 return;
2300 case UNDO_DEL_QUEUED:
2301 if (length != 1)
2302 return; // Only queue single characters
2303 switch (undo_queue_state) {
2304 case UNDO_EMPTY:
2305 undo_queue_state = UNDO_DEL;
2306 case UNDO_DEL:
2307 undo_queue_spos = src;
2308 undo_q++;
Denys Vlasenko2c512022014-04-03 01:45:05 +02002309 undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q] = *src;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002310 // If queue is full, dump it into an object
2311 if (undo_q == CONFIG_FEATURE_VI_UNDO_QUEUE_MAX)
2312 undo_queue_commit();
2313 return;
2314 case UNDO_INS:
2315 // Switch from storing inserted text to deleted text
2316 undo_queue_commit();
2317 undo_push(src, length, UNDO_DEL_QUEUED);
2318 return;
2319 }
2320 break;
2321 case UNDO_INS_QUEUED:
2322 if (length != 1)
2323 return;
2324 switch (undo_queue_state) {
2325 case UNDO_EMPTY:
2326 undo_queue_state = UNDO_INS;
2327 undo_queue_spos = src;
2328 case UNDO_INS:
2329 undo_q++; // Don't need to save any data for insertions
2330 if (undo_q == CONFIG_FEATURE_VI_UNDO_QUEUE_MAX)
2331 undo_queue_commit();
2332 return;
2333 case UNDO_DEL:
2334 // Switch from storing deleted text to inserted text
2335 undo_queue_commit();
2336 undo_push(src, length, UNDO_INS_QUEUED);
2337 return;
2338 }
2339 break;
2340 }
2341#else
2342 // If undo queuing is disabled, ignore the queuing flag entirely
2343 u_type = u_type & ~UNDO_QUEUED_FLAG;
2344#endif
2345
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002346 // Allocate a new undo object
2347 if (u_type == UNDO_DEL || u_type == UNDO_DEL_CHAIN) {
2348 // For UNDO_DEL objects, save deleted text
2349 if ((src + length) == end)
2350 length--;
2351 // If this deletion empties text[], strip the newline. When the buffer becomes
2352 // zero-length, a newline is added back, which requires this to compensate.
2353 undo_entry = xzalloc(offsetof(struct undo_object, undo_text) + length);
2354 memcpy(undo_entry->undo_text, src, length);
2355 } else {
2356 undo_entry = xzalloc(sizeof(*undo_entry));
2357 }
2358 undo_entry->length = length;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002359#if ENABLE_FEATURE_VI_UNDO_QUEUE
2360 if ((u_type & UNDO_USE_SPOS) != 0) {
Denys Vlasenko2c512022014-04-03 01:45:05 +02002361 undo_entry->start = undo_queue_spos - text; // use start position from queue
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002362 } else {
Denys Vlasenko2c512022014-04-03 01:45:05 +02002363 undo_entry->start = src - text; // use offset from start of text buffer
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002364 }
2365 u_type = (u_type & ~UNDO_USE_SPOS);
2366#else
Denys Vlasenko2c512022014-04-03 01:45:05 +02002367 undo_entry->start = src - text;
2368#endif
Denys Vlasenko2c512022014-04-03 01:45:05 +02002369 undo_entry->u_type = u_type;
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002370
2371 // Push it on undo stack
2372 undo_entry->prev = undo_stack_tail;
2373 undo_stack_tail = undo_entry;
2374 modified_count++;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002375}
2376
2377static void undo_pop(void) // Undo the last operation
2378{
Denys Vlasenko2c512022014-04-03 01:45:05 +02002379 int repeat;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002380 char *u_start, *u_end;
Denys Vlasenko2c512022014-04-03 01:45:05 +02002381 struct undo_object *undo_entry;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002382
2383 // Commit pending undo queue before popping (should be unnecessary)
2384 undo_queue_commit();
2385
Denys Vlasenko2c512022014-04-03 01:45:05 +02002386 undo_entry = undo_stack_tail;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002387 // Check for an empty undo stack
Denys Vlasenko2c512022014-04-03 01:45:05 +02002388 if (!undo_entry) {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002389 status_line("Already at oldest change");
2390 return;
2391 }
2392
Denys Vlasenko2c512022014-04-03 01:45:05 +02002393 switch (undo_entry->u_type) {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002394 case UNDO_DEL:
2395 case UNDO_DEL_CHAIN:
2396 // make hole and put in text that was deleted; deallocate text
Denys Vlasenko2c512022014-04-03 01:45:05 +02002397 u_start = text + undo_entry->start;
2398 text_hole_make(u_start, undo_entry->length);
2399 memcpy(u_start, undo_entry->undo_text, undo_entry->length);
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002400 status_line("Undo [%d] %s %d chars at position %d",
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002401 modified_count, "restored",
Denys Vlasenko2c512022014-04-03 01:45:05 +02002402 undo_entry->length, undo_entry->start
2403 );
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002404 break;
2405 case UNDO_INS:
2406 case UNDO_INS_CHAIN:
2407 // delete what was inserted
Denys Vlasenko2c512022014-04-03 01:45:05 +02002408 u_start = undo_entry->start + text;
2409 u_end = u_start - 1 + undo_entry->length;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002410 text_hole_delete(u_start, u_end, NO_UNDO);
2411 status_line("Undo [%d] %s %d chars at position %d",
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002412 modified_count, "deleted",
Denys Vlasenko2c512022014-04-03 01:45:05 +02002413 undo_entry->length, undo_entry->start
2414 );
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002415 break;
2416 }
Denys Vlasenko2c512022014-04-03 01:45:05 +02002417 repeat = 0;
2418 switch (undo_entry->u_type) {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002419 // If this is the end of a chain, lower modification count and refresh display
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002420 case UNDO_DEL:
2421 case UNDO_INS:
Denys Vlasenko2c512022014-04-03 01:45:05 +02002422 dot = (text + undo_entry->start);
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002423 refresh(FALSE);
2424 break;
2425 case UNDO_DEL_CHAIN:
2426 case UNDO_INS_CHAIN:
2427 repeat = 1;
2428 break;
2429 }
2430 // Deallocate the undo object we just processed
Denys Vlasenko2c512022014-04-03 01:45:05 +02002431 undo_stack_tail = undo_entry->prev;
2432 free(undo_entry);
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002433 modified_count--;
Denys Vlasenko2c512022014-04-03 01:45:05 +02002434 // For chained operations, continue popping all the way down the chain.
2435 if (repeat) {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002436 undo_pop(); // Follow the undo chain if one exists
2437 }
2438}
2439
2440#if ENABLE_FEATURE_VI_UNDO_QUEUE
2441static void undo_queue_commit(void) // Flush any queued objects to the undo stack
2442{
2443 // Pushes the queue object onto the undo stack
2444 if (undo_q > 0) {
2445 // Deleted character undo events grow from the end
Denys Vlasenko2c512022014-04-03 01:45:05 +02002446 undo_push(undo_queue + CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q,
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002447 undo_q,
2448 (undo_queue_state | UNDO_USE_SPOS)
2449 );
2450 undo_queue_state = UNDO_EMPTY;
2451 undo_q = 0;
2452 }
2453}
2454#endif
2455
2456#endif /* ENABLE_FEATURE_VI_UNDO */
2457
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002458// open a hole in text[]
2459// might reallocate text[]! use p += text_hole_make(p, ...),
2460// and be careful to not use pointers into potentially freed text[]!
2461static uintptr_t text_hole_make(char *p, int size) // at "p", make a 'size' byte hole
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002462{
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002463 uintptr_t bias = 0;
2464
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002465 if (size <= 0)
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002466 return bias;
Denis Vlasenkob1759462008-06-20 20:20:54 +00002467 end += size; // adjust the new END
2468 if (end >= (text + text_size)) {
2469 char *new_text;
2470 text_size += end - (text + text_size) + 10240;
2471 new_text = xrealloc(text, text_size);
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002472 bias = (new_text - text);
2473 screenbegin += bias;
2474 dot += bias;
2475 end += bias;
2476 p += bias;
Denys Vlasenko800a9a02012-01-31 14:10:26 +01002477#if ENABLE_FEATURE_VI_YANKMARK
2478 {
2479 int i;
2480 for (i = 0; i < ARRAY_SIZE(mark); i++)
2481 if (mark[i])
2482 mark[i] += bias;
2483 }
2484#endif
Denis Vlasenkob1759462008-06-20 20:20:54 +00002485 text = new_text;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002486 }
Denis Vlasenkod6995442008-06-27 04:06:13 +00002487 memmove(p + size, p, end - size - p);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002488 memset(p, ' ', size); // clear new hole
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002489 return bias;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002490}
2491
2492// close a hole in text[]
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002493// "undo" value indicates if this operation should be undo-able
2494static char *text_hole_delete(char *p, char *q, int undo) // delete "p" through "q", inclusive
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002495{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002496 char *src, *dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002497 int cnt, hole_size;
2498
2499 // move forwards, from beginning
2500 // assume p <= q
2501 src = q + 1;
2502 dest = p;
2503 if (q < p) { // they are backward- swap them
2504 src = p + 1;
2505 dest = q;
2506 }
2507 hole_size = q - p + 1;
2508 cnt = end - src;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002509#if ENABLE_FEATURE_VI_UNDO
2510 switch (undo) {
2511 case NO_UNDO:
2512 break;
2513 case ALLOW_UNDO:
2514 undo_push(p, hole_size, UNDO_DEL);
2515 break;
2516 case ALLOW_UNDO_CHAIN:
2517 undo_push(p, hole_size, UNDO_DEL_CHAIN);
2518 break;
2519# if ENABLE_FEATURE_VI_UNDO_QUEUE
2520 case ALLOW_UNDO_QUEUED:
2521 undo_push(p, hole_size, UNDO_DEL_QUEUED);
2522 break;
2523# endif
2524 }
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002525 modified_count--;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002526#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002527 if (src < text || src > end)
2528 goto thd0;
2529 if (dest < text || dest >= end)
2530 goto thd0;
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002531 modified_count++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002532 if (src >= end)
2533 goto thd_atend; // just delete the end of the buffer
Denis Vlasenkob1759462008-06-20 20:20:54 +00002534 memmove(dest, src, cnt);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002535 thd_atend:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002536 end = end - hole_size; // adjust the new END
2537 if (dest >= end)
2538 dest = end - 1; // make sure dest in below end-1
2539 if (end <= text)
2540 dest = end = text; // keep pointers valid
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002541 thd0:
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002542 return dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002543}
2544
2545// copy text into register, then delete text.
2546// if dist <= 0, do not include, or go past, a NewLine
2547//
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002548static char *yank_delete(char *start, char *stop, int dist, int yf, int undo)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002549{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002550 char *p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002551
2552 // make sure start <= stop
2553 if (start > stop) {
2554 // they are backwards, reverse them
2555 p = start;
2556 start = stop;
2557 stop = p;
2558 }
2559 if (dist <= 0) {
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +00002560 // we cannot cross NL boundaries
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002561 p = start;
2562 if (*p == '\n')
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002563 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002564 // dont go past a NewLine
2565 for (; p + 1 <= stop; p++) {
2566 if (p[1] == '\n') {
2567 stop = p; // "stop" just before NewLine
2568 break;
2569 }
2570 }
2571 }
2572 p = start;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002573#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002574 text_yank(start, stop, YDreg);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002575#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002576 if (yf == YANKDEL) {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002577 p = text_hole_delete(start, stop, undo);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002578 } // delete lines
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002579 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002580}
2581
2582static void show_help(void)
2583{
2584 puts("These features are available:"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002585#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002586 "\n\tPattern searches with / and ?"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002587#endif
2588#if ENABLE_FEATURE_VI_DOT_CMD
Denys Vlasenko605f2642012-06-11 01:53:33 +02002589 "\n\tLast command repeat with ."
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002590#endif
2591#if ENABLE_FEATURE_VI_YANKMARK
Bernhard Reutner-Fischerd24d5c82007-10-01 18:04:42 +00002592 "\n\tLine marking with 'x"
2593 "\n\tNamed buffers with \"x"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002594#endif
2595#if ENABLE_FEATURE_VI_READONLY
Walter Harmsb9ba5802011-06-27 02:59:37 +02002596 //not implemented: "\n\tReadonly if vi is called as \"view\""
2597 //redundant: usage text says this too: "\n\tReadonly with -R command line arg"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002598#endif
2599#if ENABLE_FEATURE_VI_SET
Denys Vlasenko605f2642012-06-11 01:53:33 +02002600 "\n\tSome colon mode commands with :"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002601#endif
2602#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002603 "\n\tSettable options with \":set\""
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002604#endif
2605#if ENABLE_FEATURE_VI_USE_SIGNALS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002606 "\n\tSignal catching- ^C"
2607 "\n\tJob suspend and resume with ^Z"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002608#endif
2609#if ENABLE_FEATURE_VI_WIN_RESIZE
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002610 "\n\tAdapt to window re-sizes"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002611#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002612 );
2613}
2614
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002615#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002616static void start_new_cmd_q(char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002617{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002618 // get buffer for new cmd
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002619 // if there is a current cmd count put it in the buffer first
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002620 if (cmdcnt > 0) {
Paul Foxc51fc7b2008-03-06 01:34:23 +00002621 lmc_len = sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002622 } else { // just save char c onto queue
Paul Foxd957b952005-11-28 18:07:53 +00002623 last_modifying_cmd[0] = c;
Paul Foxc51fc7b2008-03-06 01:34:23 +00002624 lmc_len = 1;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002625 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002626 adding2q = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002627}
2628
2629static void end_cmd_q(void)
2630{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002631#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002632 YDreg = 26; // go back to default Yank/Delete reg
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002633#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002634 adding2q = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002635}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002636#endif /* FEATURE_VI_DOT_CMD */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002637
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002638#if ENABLE_FEATURE_VI_YANKMARK \
2639 || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
2640 || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002641// might reallocate text[]! use p += string_insert(p, ...),
2642// and be careful to not use pointers into potentially freed text[]!
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002643static uintptr_t string_insert(char *p, const char *s, int undo) // insert the string at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002644{
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002645 uintptr_t bias;
2646 int i;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002647
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002648 i = strlen(s);
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002649#if ENABLE_FEATURE_VI_UNDO
2650 switch (undo) {
2651 case ALLOW_UNDO:
2652 undo_push(p, i, UNDO_INS);
2653 break;
2654 case ALLOW_UNDO_CHAIN:
2655 undo_push(p, i, UNDO_INS_CHAIN);
2656 break;
2657 }
2658#endif
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002659 bias = text_hole_make(p, i);
2660 p += bias;
Denis Vlasenko00d84172008-11-24 07:34:42 +00002661 memcpy(p, s, i);
Denis Vlasenko33875382008-06-21 20:31:50 +00002662#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002663 {
2664 int cnt;
2665 for (cnt = 0; *s != '\0'; s++) {
2666 if (*s == '\n')
2667 cnt++;
2668 }
2669 status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2670 }
Denis Vlasenko33875382008-06-21 20:31:50 +00002671#endif
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002672 return bias;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002673}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002674#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002675
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002676#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko33875382008-06-21 20:31:50 +00002677static char *text_yank(char *p, char *q, int dest) // copy text into a register
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002678{
Denis Vlasenko00d84172008-11-24 07:34:42 +00002679 int cnt = q - p;
2680 if (cnt < 0) { // they are backwards- reverse them
2681 p = q;
2682 cnt = -cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002683 }
Denis Vlasenko00d84172008-11-24 07:34:42 +00002684 free(reg[dest]); // if already a yank register, free it
2685 reg[dest] = xstrndup(p, cnt + 1);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002686 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002687}
2688
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002689static char what_reg(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002690{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002691 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002692
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002693 c = 'D'; // default to D-reg
2694 if (0 <= YDreg && YDreg <= 25)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002695 c = 'a' + (char) YDreg;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002696 if (YDreg == 26)
2697 c = 'D';
2698 if (YDreg == 27)
2699 c = 'U';
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002700 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002701}
2702
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002703static void check_context(char cmd)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002704{
2705 // A context is defined to be "modifying text"
2706 // Any modifying command establishes a new context.
2707
2708 if (dot < context_start || dot > context_end) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002709 if (strchr(modifying_cmds, cmd) != NULL) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002710 // we are trying to modify text[]- make this the current context
2711 mark[27] = mark[26]; // move cur to prev
2712 mark[26] = dot; // move local to cur
2713 context_start = prev_line(prev_line(dot));
2714 context_end = next_line(next_line(dot));
2715 //loiter= start_loiter= now;
2716 }
2717 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002718}
2719
Denis Vlasenkof882f082007-12-23 02:36:51 +00002720static char *swap_context(char *p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002721{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002722 char *tmp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002723
2724 // the current context is in mark[26]
2725 // the previous context is in mark[27]
2726 // only swap context if other context is valid
2727 if (text <= mark[27] && mark[27] <= end - 1) {
2728 tmp = mark[27];
Denys Vlasenko61fcc8c2016-09-28 16:23:05 +02002729 mark[27] = p;
2730 mark[26] = p = tmp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002731 context_start = prev_line(prev_line(prev_line(p)));
2732 context_end = next_line(next_line(next_line(p)));
2733 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002734 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002735}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002736#endif /* FEATURE_VI_YANKMARK */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002737
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002738//----- Set terminal attributes --------------------------------
2739static void rawmode(void)
2740{
Denys Vlasenko01ccdd12017-01-11 16:17:59 +01002741 // no TERMIOS_CLEAR_ISIG: leave ISIG on - allow signals
2742 set_termios_to_raw(STDIN_FILENO, &term_orig, TERMIOS_RAW_CRNL);
2743 erase_char = term_orig.c_cc[VERASE];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002744}
2745
2746static void cookmode(void)
2747{
Denys Vlasenko8131eea2009-11-02 14:19:51 +01002748 fflush_all();
Denis Vlasenko202ac502008-11-05 13:20:58 +00002749 tcsetattr_stdin_TCSANOW(&term_orig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002750}
2751
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002752#if ENABLE_FEATURE_VI_USE_SIGNALS
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002753//----- Come here when we get a window resize signal ---------
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002754static void winch_sig(int sig UNUSED_PARAM)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002755{
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002756 int save_errno = errno;
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002757 // FIXME: do it in main loop!!!
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002758 signal(SIGWINCH, winch_sig);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002759 query_screen_dimensions();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002760 new_screen(rows, columns); // get memory for virtual screen
2761 redraw(TRUE); // re-draw the screen
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002762 errno = save_errno;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002763}
2764
2765//----- Come here when we get a continue signal -------------------
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002766static void cont_sig(int sig UNUSED_PARAM)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002767{
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002768 int save_errno = errno;
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002769 rawmode(); // terminal to "raw"
2770 last_status_cksum = 0; // force status update
2771 redraw(TRUE); // re-draw the screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002772
2773 signal(SIGTSTP, suspend_sig);
2774 signal(SIGCONT, SIG_DFL);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002775 //kill(my_pid, SIGCONT); // huh? why? we are already "continued"...
2776 errno = save_errno;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002777}
2778
2779//----- Come here when we get a Suspend signal -------------------
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002780static void suspend_sig(int sig UNUSED_PARAM)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002781{
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002782 int save_errno = errno;
Denis Vlasenko267e16c2008-10-14 10:34:41 +00002783 go_bottom_and_clear_to_eol();
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002784 cookmode(); // terminal to "cooked"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002785
2786 signal(SIGCONT, cont_sig);
2787 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002788 kill(my_pid, SIGTSTP);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002789 errno = save_errno;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002790}
2791
2792//----- Come here when we get a signal ---------------------------
2793static void catch_sig(int sig)
2794{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002795 signal(SIGINT, catch_sig);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002796 siglongjmp(restart, sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002797}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002798#endif /* FEATURE_VI_USE_SIGNALS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002799
Denys Vlasenko606291b2009-09-23 23:15:43 +02002800static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002801{
Denis Vlasenko87f3b262007-09-07 13:43:28 +00002802 struct pollfd pfd[1];
Denis Vlasenkocd5c7862007-05-17 16:37:22 +00002803
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002804 if (hund != 0)
2805 fflush_all();
2806
Denys Vlasenko606291b2009-09-23 23:15:43 +02002807 pfd[0].fd = STDIN_FILENO;
Denis Vlasenko87f3b262007-09-07 13:43:28 +00002808 pfd[0].events = POLLIN;
Denis Vlasenko5d61e712007-09-27 10:09:59 +00002809 return safe_poll(pfd, 1, hund*10) > 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002810}
2811
2812//----- IO Routines --------------------------------------------
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002813static int readit(void) // read (maybe cursor) key from stdin
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002814{
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002815 int c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002816
Denys Vlasenko8131eea2009-11-02 14:19:51 +01002817 fflush_all();
Denys Vlasenko0cc9b182017-05-02 20:39:02 +02002818
2819 // Wait for input. TIMEOUT = -1 makes read_key wait even
2820 // on nonblocking stdin.
2821 // Note: read_key sets errno to 0 on success.
2822 again:
2823 c = read_key(STDIN_FILENO, readbuffer, /*timeout:*/ -1);
Denis Vlasenko0112ff52008-10-25 23:23:00 +00002824 if (c == -1) { // EOF/error
Denys Vlasenko0cc9b182017-05-02 20:39:02 +02002825 if (errno == EAGAIN) // paranoia
2826 goto again;
Denis Vlasenko0112ff52008-10-25 23:23:00 +00002827 go_bottom_and_clear_to_eol();
2828 cookmode(); // terminal to "cooked"
2829 bb_error_msg_and_die("can't read user input");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002830 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002831 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002832}
2833
2834//----- IO Routines --------------------------------------------
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002835static int get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002836{
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002837 int c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002838
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002839#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002840 if (!adding2q) {
2841 // we are not adding to the q.
2842 // but, we may be reading from a q
2843 if (ioq == 0) {
2844 // there is no current q, read from STDIN
2845 c = readit(); // get the users input
2846 } else {
2847 // there is a queue to get chars from first
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002848 // careful with correct sign expansion!
2849 c = (unsigned char)*ioq++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002850 if (c == '\0') {
2851 // the end of the q, read from STDIN
2852 free(ioq_start);
2853 ioq_start = ioq = 0;
2854 c = readit(); // get the users input
2855 }
2856 }
2857 } else {
2858 // adding STDIN chars to q
2859 c = readit(); // get the users input
Denis Vlasenkob1759462008-06-20 20:20:54 +00002860 if (lmc_len >= MAX_INPUT_LEN - 1) {
2861 status_line_bold("last_modifying_cmd overrun");
2862 } else {
2863 // add new char to q
2864 last_modifying_cmd[lmc_len++] = c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002865 }
2866 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002867#else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002868 c = readit(); // get the users input
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002869#endif /* FEATURE_VI_DOT_CMD */
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002870 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002871}
2872
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002873// Get input line (uses "status line" area)
2874static char *get_input_line(const char *prompt)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002875{
Denis Vlasenkob1759462008-06-20 20:20:54 +00002876 // char [MAX_INPUT_LEN]
2877#define buf get_input_line__buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002878
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002879 int c;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002880 int i;
2881
2882 strcpy(buf, prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002883 last_status_cksum = 0; // force status update
Denis Vlasenko267e16c2008-10-14 10:34:41 +00002884 go_bottom_and_clear_to_eol();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002885 write1(prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002886
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002887 i = strlen(buf);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002888 while (i < MAX_INPUT_LEN) {
2889 c = get_one_char();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002890 if (c == '\n' || c == '\r' || c == 27)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002891 break; // this is end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002892 if (c == erase_char || c == 8 || c == 127) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002893 // user wants to erase prev char
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002894 buf[--i] = '\0';
2895 write1("\b \b"); // erase char on screen
2896 if (i <= 0) // user backs up before b-o-l, exit
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002897 break;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002898 } else if (c > 0 && c < 256) { // exclude Unicode
2899 // (TODO: need to handle Unicode)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002900 buf[i] = c;
2901 buf[++i] = '\0';
2902 bb_putchar(c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002903 }
2904 }
2905 refresh(FALSE);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002906 return buf;
Denis Vlasenkob1759462008-06-20 20:20:54 +00002907#undef buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002908}
2909
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002910// might reallocate text[]!
Ron Yorstone5213ce2014-11-30 20:39:25 +00002911static int file_insert(const char *fn, char *p, int initial)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002912{
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002913 int cnt = -1;
2914 int fd, size;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002915 struct stat statbuf;
2916
Denys Vlasenko7f3a2a22015-10-08 11:24:44 +02002917 if (p < text)
2918 p = text;
2919 if (p > end)
2920 p = end;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002921
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002922 fd = open(fn, O_RDONLY);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002923 if (fd < 0) {
Ron Yorstone5213ce2014-11-30 20:39:25 +00002924 if (!initial)
2925 status_line_bold_errno(fn);
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02002926 return cnt;
2927 }
2928
2929 /* Validate file */
2930 if (fstat(fd, &statbuf) < 0) {
2931 status_line_bold_errno(fn);
2932 goto fi;
2933 }
2934 if (!S_ISREG(statbuf.st_mode)) {
2935 status_line_bold("'%s' is not a regular file", fn);
2936 goto fi;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002937 }
Denys Vlasenko778794d2013-01-22 10:13:52 +01002938 size = (statbuf.st_size < INT_MAX ? (int)statbuf.st_size : INT_MAX);
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002939 p += text_hole_make(p, size);
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02002940 cnt = full_read(fd, p, size);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002941 if (cnt < 0) {
Denys Vlasenko9e7c0022013-03-15 02:17:29 +01002942 status_line_bold_errno(fn);
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002943 p = text_hole_delete(p, p + size - 1, NO_UNDO); // un-do buffer insert
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002944 } else if (cnt < size) {
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02002945 // There was a partial read, shrink unused space
2946 p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO);
Denys Vlasenko778794d2013-01-22 10:13:52 +01002947 status_line_bold("can't read '%s'", fn);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002948 }
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02002949 fi:
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002950 close(fd);
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02002951
Denis Vlasenko856be772007-08-17 08:29:48 +00002952#if ENABLE_FEATURE_VI_READONLY
Ron Yorstone5213ce2014-11-30 20:39:25 +00002953 if (initial
Denis Vlasenko856be772007-08-17 08:29:48 +00002954 && ((access(fn, W_OK) < 0) ||
2955 /* root will always have access()
2956 * so we check fileperms too */
2957 !(statbuf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))
2958 )
2959 ) {
Denis Vlasenko2f6ae432007-07-19 22:50:47 +00002960 SET_READONLY_FILE(readonly_mode);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002961 }
Denis Vlasenko856be772007-08-17 08:29:48 +00002962#endif
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002963 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002964}
2965
Denis Vlasenko33875382008-06-21 20:31:50 +00002966static int file_write(char *fn, char *first, char *last)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002967{
2968 int fd, cnt, charcnt;
2969
2970 if (fn == 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002971 status_line_bold("No current filename");
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002972 return -2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002973 }
Denis Vlasenko8abae882008-05-03 11:35:59 +00002974 /* By popular request we do not open file with O_TRUNC,
2975 * but instead ftruncate() it _after_ successful write.
2976 * Might reduce amount of data lost on power fail etc.
2977 */
2978 fd = open(fn, (O_WRONLY | O_CREAT), 0666);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002979 if (fd < 0)
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002980 return -1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002981 cnt = last - first + 1;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002982 charcnt = full_write(fd, first, cnt);
Denis Vlasenko8abae882008-05-03 11:35:59 +00002983 ftruncate(fd, charcnt);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002984 if (charcnt == cnt) {
2985 // good write
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002986 //modified_count = FALSE;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002987 } else {
2988 charcnt = 0;
2989 }
2990 close(fd);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002991 return charcnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002992}
2993
2994//----- Terminal Drawing ---------------------------------------
2995// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002996// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002997// screen coordinates
2998// 0,0 ... 0,79
2999// 1,0 ... 1,79
3000// . ... .
3001// . ... .
3002// 22,0 ... 22,79
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003003// 23,0 ... 23,79 <- status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003004
3005//----- Move the cursor to row x col (count from 0, not 1) -------
Denys Vlasenko04b52892012-06-11 13:51:38 +02003006static void place_cursor(int row, int col)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003007{
Denys Vlasenko04b52892012-06-11 13:51:38 +02003008 char cm1[sizeof(ESC_SET_CURSOR_POS) + sizeof(int)*3 * 2];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003009
3010 if (row < 0) row = 0;
3011 if (row >= rows) row = rows - 1;
3012 if (col < 0) col = 0;
3013 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003014
Denys Vlasenko04b52892012-06-11 13:51:38 +02003015 sprintf(cm1, ESC_SET_CURSOR_POS, row + 1, col + 1);
3016 write1(cm1);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003017}
3018
3019//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00003020static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003021{
Denys Vlasenko04b52892012-06-11 13:51:38 +02003022 write1(ESC_CLEAR2EOL);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003023}
3024
Denis Vlasenko267e16c2008-10-14 10:34:41 +00003025static void go_bottom_and_clear_to_eol(void)
3026{
Denys Vlasenko04b52892012-06-11 13:51:38 +02003027 place_cursor(rows - 1, 0);
3028 clear_to_eol();
Denis Vlasenko267e16c2008-10-14 10:34:41 +00003029}
3030
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003031//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00003032static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003033{
Denys Vlasenko04b52892012-06-11 13:51:38 +02003034 write1(ESC_CLEAR2EOS);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003035}
3036
3037//----- Start standout mode ------------------------------------
Denys Vlasenko04b52892012-06-11 13:51:38 +02003038static void standout_start(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003039{
Denys Vlasenko04b52892012-06-11 13:51:38 +02003040 write1(ESC_BOLD_TEXT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003041}
3042
3043//----- End standout mode --------------------------------------
Denys Vlasenko04b52892012-06-11 13:51:38 +02003044static void standout_end(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003045{
Denys Vlasenko04b52892012-06-11 13:51:38 +02003046 write1(ESC_NORM_TEXT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003047}
3048
3049//----- Flash the screen --------------------------------------
3050static void flash(int h)
3051{
Denys Vlasenko04b52892012-06-11 13:51:38 +02003052 standout_start();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003053 redraw(TRUE);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003054 mysleep(h);
Denys Vlasenko04b52892012-06-11 13:51:38 +02003055 standout_end();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003056 redraw(TRUE);
3057}
3058
Denys Vlasenko05399fc2014-09-15 17:06:10 +02003059static void indicate_error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003060{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003061#if ENABLE_FEATURE_VI_CRASHME
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003062 if (crashme > 0)
3063 return; // generate a random command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003064#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003065 if (!err_method) {
Denys Vlasenko04b52892012-06-11 13:51:38 +02003066 write1(ESC_BELL);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003067 } else {
3068 flash(10);
3069 }
3070}
3071
3072//----- Screen[] Routines --------------------------------------
3073//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00003074static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003075{
3076 memset(screen, ' ', screensize); // clear new screen
3077}
3078
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003079static int bufsum(char *buf, int count)
Paul Fox8552aec2005-09-16 12:20:05 +00003080{
3081 int sum = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003082 char *e = buf + count;
3083
Paul Fox8552aec2005-09-16 12:20:05 +00003084 while (buf < e)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003085 sum += (unsigned char) *buf++;
Paul Fox8552aec2005-09-16 12:20:05 +00003086 return sum;
3087}
3088
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003089//----- Draw the status line at bottom of the screen -------------
3090static void show_status_line(void)
3091{
Paul Foxc3504852005-09-16 12:48:18 +00003092 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003093
Paul Fox8552aec2005-09-16 12:20:05 +00003094 // either we already have an error or status message, or we
3095 // create one.
3096 if (!have_status_msg) {
3097 cnt = format_edit_status();
3098 cksum = bufsum(status_buffer, cnt);
3099 }
3100 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003101 last_status_cksum = cksum; // remember if we have seen this line
Denis Vlasenko267e16c2008-10-14 10:34:41 +00003102 go_bottom_and_clear_to_eol();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003103 write1(status_buffer);
Paul Fox8552aec2005-09-16 12:20:05 +00003104 if (have_status_msg) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003105 if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
Paul Fox8552aec2005-09-16 12:20:05 +00003106 (columns - 1) ) {
3107 have_status_msg = 0;
3108 Hit_Return();
3109 }
3110 have_status_msg = 0;
3111 }
Denys Vlasenko04b52892012-06-11 13:51:38 +02003112 place_cursor(crow, ccol); // put cursor back in correct place
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003113 }
Denys Vlasenko8131eea2009-11-02 14:19:51 +01003114 fflush_all();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003115}
3116
3117//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00003118// format status buffer, with STANDOUT mode
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003119static void status_line_bold(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003120{
3121 va_list args;
3122
3123 va_start(args, format);
Denys Vlasenko04b52892012-06-11 13:51:38 +02003124 strcpy(status_buffer, ESC_BOLD_TEXT);
3125 vsprintf(status_buffer + sizeof(ESC_BOLD_TEXT)-1, format, args);
3126 strcat(status_buffer, ESC_NORM_TEXT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003127 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00003128
Denys Vlasenko04b52892012-06-11 13:51:38 +02003129 have_status_msg = 1 + sizeof(ESC_BOLD_TEXT) + sizeof(ESC_NORM_TEXT) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003130}
3131
Denys Vlasenko9e7c0022013-03-15 02:17:29 +01003132static void status_line_bold_errno(const char *fn)
3133{
Denys Vlasenko6f97b302017-09-29 18:17:25 +02003134 status_line_bold("'%s' "STRERROR_FMT, fn STRERROR_ERRNO);
Denys Vlasenko9e7c0022013-03-15 02:17:29 +01003135}
3136
Paul Fox8552aec2005-09-16 12:20:05 +00003137// format status buffer
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003138static void status_line(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003139{
3140 va_list args;
3141
3142 va_start(args, format);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003143 vsprintf(status_buffer, format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003144 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00003145
3146 have_status_msg = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003147}
3148
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003149// copy s to buf, convert unprintable
3150static void print_literal(char *buf, const char *s)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003151{
Denys Vlasenkoc2704542009-11-20 19:14:19 +01003152 char *d;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003153 unsigned char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003154
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003155 buf[0] = '\0';
3156 if (!s[0])
3157 s = "(NULL)";
Denys Vlasenkoc2704542009-11-20 19:14:19 +01003158
3159 d = buf;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003160 for (; *s; s++) {
3161 int c_is_no_print;
3162
3163 c = *s;
3164 c_is_no_print = (c & 0x80) && !Isprint(c);
3165 if (c_is_no_print) {
Denys Vlasenko04b52892012-06-11 13:51:38 +02003166 strcpy(d, ESC_NORM_TEXT);
3167 d += sizeof(ESC_NORM_TEXT)-1;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003168 c = '.';
3169 }
Denys Vlasenkoc2704542009-11-20 19:14:19 +01003170 if (c < ' ' || c == 0x7f) {
3171 *d++ = '^';
3172 c |= '@'; /* 0x40 */
3173 if (c == 0x7f)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003174 c = '?';
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003175 }
Denys Vlasenkoc2704542009-11-20 19:14:19 +01003176 *d++ = c;
3177 *d = '\0';
3178 if (c_is_no_print) {
Denys Vlasenko04b52892012-06-11 13:51:38 +02003179 strcpy(d, ESC_BOLD_TEXT);
3180 d += sizeof(ESC_BOLD_TEXT)-1;
Denys Vlasenkoc2704542009-11-20 19:14:19 +01003181 }
3182 if (*s == '\n') {
3183 *d++ = '$';
3184 *d = '\0';
3185 }
3186 if (d - buf > MAX_INPUT_LEN - 10) // paranoia
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003187 break;
3188 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003189}
3190
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003191static void not_implemented(const char *s)
3192{
3193 char buf[MAX_INPUT_LEN];
3194
3195 print_literal(buf, s);
3196 status_line_bold("\'%s\' is not implemented", buf);
3197}
3198
3199// show file status on status line
3200static int format_edit_status(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003201{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00003202 static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
Denis Vlasenkob1759462008-06-20 20:20:54 +00003203
3204#define tot format_edit_status__tot
3205
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003206 int cur, percent, ret, trunc_at;
3207
Denys Vlasenkoe7430862014-04-03 12:47:48 +02003208 // modified_count is now a counter rather than a flag. this
Paul Fox8552aec2005-09-16 12:20:05 +00003209 // helps reduce the amount of line counting we need to do.
3210 // (this will cause a mis-reporting of modified status
3211 // once every MAXINT editing operations.)
3212
3213 // it would be nice to do a similar optimization here -- if
3214 // we haven't done a motion that could have changed which line
3215 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003216 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003217
Denys Vlasenkoe7430862014-04-03 12:47:48 +02003218 // count_lines() is expensive.
3219 // Call it only if something was changed since last time
3220 // we were here:
3221 if (modified_count != last_modified_count) {
Paul Fox8552aec2005-09-16 12:20:05 +00003222 tot = cur + count_lines(dot, end - 1) - 1;
Denys Vlasenkoe7430862014-04-03 12:47:48 +02003223 last_modified_count = modified_count;
Paul Fox8552aec2005-09-16 12:20:05 +00003224 }
3225
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003226 // current line percent
3227 // ------------- ~~ ----------
3228 // total lines 100
3229 if (tot > 0) {
3230 percent = (100 * cur) / tot;
3231 } else {
3232 cur = tot = 0;
3233 percent = 100;
3234 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00003235
Paul Fox8552aec2005-09-16 12:20:05 +00003236 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
3237 columns : STATUS_BUFFER_LEN-1;
3238
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003239 ret = snprintf(status_buffer, trunc_at+1,
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003240#if ENABLE_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00003241 "%c %s%s%s %d/%d %d%%",
3242#else
3243 "%c %s%s %d/%d %d%%",
3244#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003245 cmd_mode_indicator[cmd_mode & 3],
3246 (current_filename != NULL ? current_filename : "No file"),
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003247#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003248 (readonly_mode ? " [Readonly]" : ""),
Paul Fox8552aec2005-09-16 12:20:05 +00003249#endif
Denys Vlasenkoe7430862014-04-03 12:47:48 +02003250 (modified_count ? " [Modified]" : ""),
Paul Fox8552aec2005-09-16 12:20:05 +00003251 cur, tot, percent);
3252
3253 if (ret >= 0 && ret < trunc_at)
3254 return ret; /* it all fit */
3255
3256 return trunc_at; /* had to truncate */
Denis Vlasenkob1759462008-06-20 20:20:54 +00003257#undef tot
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003258}
3259
3260//----- Force refresh of all Lines -----------------------------
3261static void redraw(int full_screen)
3262{
Denys Vlasenko04b52892012-06-11 13:51:38 +02003263 place_cursor(0, 0);
3264 clear_to_eos();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003265 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003266 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003267 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00003268 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003269}
3270
3271//----- Format a text[] line into a buffer ---------------------
Denis Vlasenko68404f12008-03-17 09:00:54 +00003272static char* format_line(char *src /*, int li*/)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003273{
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00003274 unsigned char c;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003275 int co;
3276 int ofs = offset;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003277 char *dest = scr_out_buf; // [MAX_SCR_COLS + MAX_TABSTOP * 2]
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003278
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003279 c = '~'; // char in col 0 in non-existent lines is '~'
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00003280 co = 0;
3281 while (co < columns + tabstop) {
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00003282 // have we gone past the end?
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003283 if (src < end) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003284 c = *src++;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003285 if (c == '\n')
3286 break;
3287 if ((c & 0x80) && !Isprint(c)) {
3288 c = '.';
3289 }
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00003290 if (c < ' ' || c == 0x7f) {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003291 if (c == '\t') {
3292 c = ' ';
3293 // co % 8 != 7
3294 while ((co % tabstop) != (tabstop - 1)) {
3295 dest[co++] = c;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003296 }
3297 } else {
3298 dest[co++] = '^';
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003299 if (c == 0x7f)
3300 c = '?';
3301 else
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003302 c += '@'; // Ctrl-X -> 'X'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003303 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003304 }
3305 }
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00003306 dest[co++] = c;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003307 // discard scrolled-off-to-the-left portion,
3308 // in tabstop-sized pieces
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003309 if (ofs >= tabstop && co >= tabstop) {
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00003310 memmove(dest, dest + tabstop, co);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003311 co -= tabstop;
3312 ofs -= tabstop;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003313 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003314 if (src >= end)
3315 break;
3316 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003317 // check "short line, gigantic offset" case
3318 if (co < ofs)
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00003319 ofs = co;
3320 // discard last scrolled off part
3321 co -= ofs;
3322 dest += ofs;
3323 // fill the rest with spaces
3324 if (co < columns)
3325 memset(&dest[co], ' ', columns - co);
3326 return dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003327}
3328
3329//----- Refresh the changed screen lines -----------------------
3330// Copy the source line from text[] into the buffer and note
3331// if the current screenline is different from the new buffer.
3332// If they differ then that line needs redrawing on the terminal.
3333//
3334static void refresh(int full_screen)
3335{
Denis Vlasenkob1759462008-06-20 20:20:54 +00003336#define old_offset refresh__old_offset
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003337
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003338 int li, changed;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003339 char *tp, *sp; // pointer into text[] and screen[]
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003340
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +02003341 if (ENABLE_FEATURE_VI_WIN_RESIZE IF_FEATURE_VI_ASK_TERMINAL(&& !G.get_rowcol_error) ) {
Denis Vlasenko55995022008-05-18 22:28:26 +00003342 unsigned c = columns, r = rows;
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07003343 query_screen_dimensions();
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003344 full_screen |= (c - columns) | (r - rows);
3345 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003346 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
3347 tp = screenbegin; // index into text[] of top line
3348
3349 // compare text[] to screen[] and mark screen[] lines that need updating
3350 for (li = 0; li < rows - 1; li++) {
3351 int cs, ce; // column start & end
Denis Vlasenkof882f082007-12-23 02:36:51 +00003352 char *out_buf;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003353 // format current text line
Denis Vlasenko68404f12008-03-17 09:00:54 +00003354 out_buf = format_line(tp /*, li*/);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003355
3356 // skip to the end of the current text[] line
Denis Vlasenkof882f082007-12-23 02:36:51 +00003357 if (tp < end) {
3358 char *t = memchr(tp, '\n', end - tp);
3359 if (!t) t = end - 1;
3360 tp = t + 1;
3361 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003362
Maninder Singh97c64912015-05-25 13:46:36 +02003363 // see if there are any changes between virtual screen and out_buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003364 changed = FALSE; // assume no change
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003365 cs = 0;
3366 ce = columns - 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003367 sp = &screen[li * columns]; // start of screen line
3368 if (full_screen) {
3369 // force re-draw of every single column from 0 - columns-1
3370 goto re0;
3371 }
3372 // compare newly formatted buffer with virtual screen
3373 // look forward for first difference between buf and screen
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003374 for (; cs <= ce; cs++) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003375 if (out_buf[cs] != sp[cs]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003376 changed = TRUE; // mark for redraw
3377 break;
3378 }
3379 }
3380
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003381 // look backward for last difference between out_buf and screen
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003382 for (; ce >= cs; ce--) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003383 if (out_buf[ce] != sp[ce]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003384 changed = TRUE; // mark for redraw
3385 break;
3386 }
3387 }
3388 // now, cs is index of first diff, and ce is index of last diff
3389
3390 // if horz offset has changed, force a redraw
3391 if (offset != old_offset) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003392 re0:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003393 changed = TRUE;
3394 }
3395
3396 // make a sanity check of columns indexes
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003397 if (cs < 0) cs = 0;
3398 if (ce > columns - 1) ce = columns - 1;
3399 if (cs > ce) { cs = 0; ce = columns - 1; }
Maninder Singh97c64912015-05-25 13:46:36 +02003400 // is there a change between virtual screen and out_buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003401 if (changed) {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003402 // copy changed part of buffer to virtual screen
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003403 memcpy(sp+cs, out_buf+cs, ce-cs+1);
Denys Vlasenko04b52892012-06-11 13:51:38 +02003404 place_cursor(li, cs);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003405 // write line out to terminal
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003406 fwrite(&sp[cs], ce - cs + 1, 1, stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003407 }
3408 }
3409
Denys Vlasenko04b52892012-06-11 13:51:38 +02003410 place_cursor(crow, ccol);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003411
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003412 old_offset = offset;
Denis Vlasenkob1759462008-06-20 20:20:54 +00003413#undef old_offset
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003414}
3415
Eric Andersen3f980402001-04-04 17:31:15 +00003416//---------------------------------------------------------------------
3417//----- the Ascii Chart -----------------------------------------------
3418//
3419// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
3420// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
3421// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
3422// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
3423// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
3424// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
3425// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
3426// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
3427// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
3428// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
3429// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
3430// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
3431// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
3432// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
3433// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
3434// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
3435//---------------------------------------------------------------------
3436
3437//----- Execute a Vi Command -----------------------------------
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003438static void do_cmd(int c)
Eric Andersen3f980402001-04-04 17:31:15 +00003439{
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003440 char *p, *q, *save_dot;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003441 char buf[12];
Denis Vlasenkoc3a9dc82008-10-29 00:58:04 +00003442 int dir;
Paul Foxc51fc7b2008-03-06 01:34:23 +00003443 int cnt, i, j;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003444 int c1;
Eric Andersen3f980402001-04-04 17:31:15 +00003445
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00003446// c1 = c; // quiet the compiler
3447// cnt = yf = 0; // quiet the compiler
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003448// p = q = save_dot = buf; // quiet the compiler
3449 memset(buf, '\0', sizeof(buf));
Eric Andersenbff7a602001-11-17 07:15:43 +00003450
Paul Fox8552aec2005-09-16 12:20:05 +00003451 show_status_line();
3452
Eric Andersenbff7a602001-11-17 07:15:43 +00003453 /* if this is a cursor key, skip these checks */
3454 switch (c) {
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003455 case KEYCODE_UP:
3456 case KEYCODE_DOWN:
3457 case KEYCODE_LEFT:
3458 case KEYCODE_RIGHT:
3459 case KEYCODE_HOME:
3460 case KEYCODE_END:
3461 case KEYCODE_PAGEUP:
3462 case KEYCODE_PAGEDOWN:
3463 case KEYCODE_DELETE:
Eric Andersenbff7a602001-11-17 07:15:43 +00003464 goto key_cmd_mode;
3465 }
3466
Eric Andersen3f980402001-04-04 17:31:15 +00003467 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003468 // flip-flop Insert/Replace mode
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003469 if (c == KEYCODE_INSERT)
Denis Vlasenko2a51af22007-03-21 22:31:24 +00003470 goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00003471 // we are 'R'eplacing the current *dot with new char
3472 if (*dot == '\n') {
3473 // don't Replace past E-o-l
3474 cmd_mode = 1; // convert to insert
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003475 undo_queue_commit();
Eric Andersen3f980402001-04-04 17:31:15 +00003476 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003477 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003478 if (c != 27)
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003479 dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char
3480 dot = char_insert(dot, c, ALLOW_UNDO_CHAIN); // insert new char
Eric Andersen3f980402001-04-04 17:31:15 +00003481 }
3482 goto dc1;
3483 }
3484 }
3485 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003486 // hitting "Insert" twice means "R" replace mode
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003487 if (c == KEYCODE_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00003488 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003489 if (1 <= c || Isprint(c)) {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003490 dot = char_insert(dot, c, ALLOW_UNDO_QUEUED);
Eric Andersen3f980402001-04-04 17:31:15 +00003491 }
3492 goto dc1;
3493 }
3494
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003495 key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00003496 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00003497 //case 0x01: // soh
3498 //case 0x09: // ht
3499 //case 0x0b: // vt
3500 //case 0x0e: // so
3501 //case 0x0f: // si
3502 //case 0x10: // dle
3503 //case 0x11: // dc1
3504 //case 0x13: // dc3
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003505#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00003506 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00003507 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003508 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003509#endif
Eric Andersen822c3832001-05-07 17:37:43 +00003510 //case 0x16: // syn
3511 //case 0x17: // etb
3512 //case 0x18: // can
3513 //case 0x1c: // fs
3514 //case 0x1d: // gs
3515 //case 0x1e: // rs
3516 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003517 //case '!': // !-
3518 //case '#': // #-
3519 //case '&': // &-
3520 //case '(': // (-
3521 //case ')': // )-
3522 //case '*': // *-
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003523 //case '=': // =-
3524 //case '@': // @-
3525 //case 'F': // F-
3526 //case 'K': // K-
3527 //case 'Q': // Q-
3528 //case 'S': // S-
3529 //case 'T': // T-
3530 //case 'V': // V-
3531 //case '[': // [-
3532 //case '\\': // \-
3533 //case ']': // ]-
3534 //case '_': // _-
3535 //case '`': // `-
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003536 //case 'v': // v-
Denys Vlasenkob22bbff2009-07-04 16:50:43 +02003537 default: // unrecognized command
Eric Andersen3f980402001-04-04 17:31:15 +00003538 buf[0] = c;
3539 buf[1] = '\0';
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003540 not_implemented(buf);
Eric Andersen3f980402001-04-04 17:31:15 +00003541 end_cmd_q(); // stop adding to q
3542 case 0x00: // nul- ignore
3543 break;
3544 case 2: // ctrl-B scroll up full screen
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003545 case KEYCODE_PAGEUP: // Cursor Key Page Up
Eric Andersen3f980402001-04-04 17:31:15 +00003546 dot_scroll(rows - 2, -1);
3547 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003548 case 4: // ctrl-D scroll down half screen
3549 dot_scroll((rows - 2) / 2, 1);
3550 break;
3551 case 5: // ctrl-E scroll down one line
3552 dot_scroll(1, 1);
3553 break;
3554 case 6: // ctrl-F scroll down full screen
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003555 case KEYCODE_PAGEDOWN: // Cursor Key Page Down
Eric Andersen3f980402001-04-04 17:31:15 +00003556 dot_scroll(rows - 2, 1);
3557 break;
3558 case 7: // ctrl-G show current status
Paul Fox8552aec2005-09-16 12:20:05 +00003559 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003560 break;
3561 case 'h': // h- move left
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003562 case KEYCODE_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003563 case 8: // ctrl-H- move left (This may be ERASE char)
Denis Vlasenko2a51af22007-03-21 22:31:24 +00003564 case 0x7f: // DEL- move left (This may be ERASE char)
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003565 do {
3566 dot_left();
3567 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003568 break;
3569 case 10: // Newline ^J
3570 case 'j': // j- goto next line, same col
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003571 case KEYCODE_DOWN: // cursor key Down
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003572 do {
3573 dot_next(); // go to next B-o-l
3574 // try stay in same col
3575 dot = move_to_col(dot, ccol + offset);
3576 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003577 break;
3578 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003579 case 18: // ctrl-R force redraw
Denys Vlasenko04b52892012-06-11 13:51:38 +02003580 place_cursor(0, 0);
3581 clear_to_eos();
3582 //mysleep(10); // why???
Eric Andersen3f980402001-04-04 17:31:15 +00003583 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003584 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003585 refresh(TRUE); // this will redraw the entire display
3586 break;
3587 case 13: // Carriage Return ^M
3588 case '+': // +- goto next line
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003589 do {
3590 dot_next();
3591 dot_skip_over_ws();
3592 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003593 break;
3594 case 21: // ctrl-U scroll up half screen
3595 dot_scroll((rows - 2) / 2, -1);
3596 break;
3597 case 25: // ctrl-Y scroll up one line
3598 dot_scroll(1, -1);
3599 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003600 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003601 if (cmd_mode == 0)
Denys Vlasenko05399fc2014-09-15 17:06:10 +02003602 indicate_error();
Eric Andersen3f980402001-04-04 17:31:15 +00003603 cmd_mode = 0; // stop insrting
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003604 undo_queue_commit();
Eric Andersen3f980402001-04-04 17:31:15 +00003605 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003606 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003607 break;
3608 case ' ': // move right
3609 case 'l': // move right
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003610 case KEYCODE_RIGHT: // Cursor Key Right
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003611 do {
3612 dot_right();
3613 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003614 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003615#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003616 case '"': // "- name a register to use for Delete/Yank
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003617 c1 = (get_one_char() | 0x20) - 'a'; // | 0x20 is tolower()
3618 if ((unsigned)c1 <= 25) { // a-z?
3619 YDreg = c1;
Eric Andersen3f980402001-04-04 17:31:15 +00003620 } else {
Denys Vlasenko05399fc2014-09-15 17:06:10 +02003621 indicate_error();
Eric Andersen3f980402001-04-04 17:31:15 +00003622 }
3623 break;
3624 case '\'': // '- goto a specific mark
Denys Vlasenko61fcc8c2016-09-28 16:23:05 +02003625 c1 = (get_one_char() | 0x20);
3626 if ((unsigned)(c1 - 'a') <= 25) { // a-z?
3627 c1 = (c1 - 'a');
Eric Andersen3f980402001-04-04 17:31:15 +00003628 // get the b-o-l
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003629 q = mark[c1];
Eric Andersen3f980402001-04-04 17:31:15 +00003630 if (text <= q && q < end) {
3631 dot = q;
3632 dot_begin(); // go to B-o-l
3633 dot_skip_over_ws();
3634 }
3635 } else if (c1 == '\'') { // goto previous context
3636 dot = swap_context(dot); // swap current and previous context
3637 dot_begin(); // go to B-o-l
3638 dot_skip_over_ws();
3639 } else {
Denys Vlasenko05399fc2014-09-15 17:06:10 +02003640 indicate_error();
Eric Andersen3f980402001-04-04 17:31:15 +00003641 }
3642 break;
3643 case 'm': // m- Mark a line
3644 // this is really stupid. If there are any inserts or deletes
3645 // between text[0] and dot then this mark will not point to the
3646 // correct location! It could be off by many lines!
3647 // Well..., at least its quick and dirty.
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003648 c1 = (get_one_char() | 0x20) - 'a';
3649 if ((unsigned)c1 <= 25) { // a-z?
Eric Andersen3f980402001-04-04 17:31:15 +00003650 // remember the line
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003651 mark[c1] = dot;
Eric Andersen3f980402001-04-04 17:31:15 +00003652 } else {
Denys Vlasenko05399fc2014-09-15 17:06:10 +02003653 indicate_error();
Eric Andersen3f980402001-04-04 17:31:15 +00003654 }
3655 break;
3656 case 'P': // P- Put register before
3657 case 'p': // p- put register after
3658 p = reg[YDreg];
Denis Vlasenko00d84172008-11-24 07:34:42 +00003659 if (p == NULL) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003660 status_line_bold("Nothing in register %c", what_reg());
Eric Andersen3f980402001-04-04 17:31:15 +00003661 break;
3662 }
3663 // are we putting whole lines or strings
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003664 if (strchr(p, '\n') != NULL) {
Eric Andersen3f980402001-04-04 17:31:15 +00003665 if (c == 'P') {
3666 dot_begin(); // putting lines- Put above
3667 }
3668 if (c == 'p') {
3669 // are we putting after very last line?
3670 if (end_line(dot) == (end - 1)) {
3671 dot = end; // force dot to end of text[]
3672 } else {
3673 dot_next(); // next line, then put before
3674 }
3675 }
3676 } else {
3677 if (c == 'p')
3678 dot_right(); // move to right, can move to NL
3679 }
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003680 string_insert(dot, p, ALLOW_UNDO); // insert the string
Eric Andersen3f980402001-04-04 17:31:15 +00003681 end_cmd_q(); // stop adding to q
3682 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003683 case 'U': // U- Undo; replace current line with original version
Denys Vlasenko800a9a02012-01-31 14:10:26 +01003684 if (reg[Ureg] != NULL) {
Eric Andersen3f980402001-04-04 17:31:15 +00003685 p = begin_line(dot);
3686 q = end_line(dot);
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003687 p = text_hole_delete(p, q, ALLOW_UNDO); // delete cur line
3688 p += string_insert(p, reg[Ureg], ALLOW_UNDO_CHAIN); // insert orig line
Eric Andersen3f980402001-04-04 17:31:15 +00003689 dot = p;
3690 dot_skip_over_ws();
3691 }
3692 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003693#endif /* FEATURE_VI_YANKMARK */
Andrew Fuller4d8ddb82015-05-03 18:18:25 +02003694#if ENABLE_FEATURE_VI_UNDO
3695 case 'u': // u- undo last operation
3696 undo_pop();
3697 break;
3698#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003699 case '$': // $- goto end of line
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003700 case KEYCODE_END: // Cursor Key End
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003701 for (;;) {
3702 dot = end_line(dot);
Denys Vlasenko1fd71292011-11-28 04:55:48 +01003703 if (--cmdcnt <= 0)
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003704 break;
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003705 dot_next();
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003706 }
Eric Andersen3f980402001-04-04 17:31:15 +00003707 break;
3708 case '%': // %- find matching char of pair () [] {}
3709 for (q = dot; q < end && *q != '\n'; q++) {
3710 if (strchr("()[]{}", *q) != NULL) {
3711 // we found half of a pair
3712 p = find_pair(q, *q);
3713 if (p == NULL) {
Denys Vlasenko05399fc2014-09-15 17:06:10 +02003714 indicate_error();
Eric Andersen3f980402001-04-04 17:31:15 +00003715 } else {
3716 dot = p;
3717 }
3718 break;
3719 }
3720 }
3721 if (*q == '\n')
Denys Vlasenko05399fc2014-09-15 17:06:10 +02003722 indicate_error();
Eric Andersen3f980402001-04-04 17:31:15 +00003723 break;
3724 case 'f': // f- forward to a user specified char
3725 last_forward_char = get_one_char(); // get the search char
3726 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003727 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003728 //
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +00003729 //**** fall through to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003730 case ';': // ;- look at rest of line for last forward char
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003731 do {
3732 if (last_forward_char == 0)
3733 break;
3734 q = dot + 1;
3735 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3736 q++;
3737 }
3738 if (*q == last_forward_char)
3739 dot = q;
3740 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003741 break;
Paul Foxb5ee8db2008-02-14 01:17:01 +00003742 case ',': // repeat latest 'f' in opposite direction
Paul Foxb5ee8db2008-02-14 01:17:01 +00003743 if (last_forward_char == 0)
3744 break;
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003745 do {
3746 q = dot - 1;
3747 while (q >= text && *q != '\n' && *q != last_forward_char) {
3748 q--;
3749 }
3750 if (q >= text && *q == last_forward_char)
3751 dot = q;
3752 } while (--cmdcnt > 0);
Paul Foxb5ee8db2008-02-14 01:17:01 +00003753 break;
3754
Eric Andersen3f980402001-04-04 17:31:15 +00003755 case '-': // -- goto prev line
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003756 do {
3757 dot_prev();
3758 dot_skip_over_ws();
3759 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003760 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003761#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003762 case '.': // .- repeat the last modifying command
3763 // Stuff the last_modifying_cmd back into stdin
3764 // and let it be re-executed.
Denis Vlasenkob1759462008-06-20 20:20:54 +00003765 if (lmc_len > 0) {
Paul Foxc51fc7b2008-03-06 01:34:23 +00003766 last_modifying_cmd[lmc_len] = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003767 ioq = ioq_start = xstrdup(last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003768 }
3769 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003770#endif
3771#if ENABLE_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003772 case '?': // /- search for a pattern
3773 case '/': // /- search for a pattern
3774 buf[0] = c;
3775 buf[1] = '\0';
3776 q = get_input_line(buf); // get input line- use "status line"
Paul Fox4917c112008-03-05 16:44:02 +00003777 if (q[0] && !q[1]) {
3778 if (last_search_pattern[0])
Denis Vlasenkoc3a9dc82008-10-29 00:58:04 +00003779 last_search_pattern[0] = c;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003780 goto dc3; // if no pat re-use old pat
Paul Fox4917c112008-03-05 16:44:02 +00003781 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003782 if (q[0]) { // strlen(q) > 1: new pat- save it and find
Eric Andersen3f980402001-04-04 17:31:15 +00003783 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003784 free(last_search_pattern);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003785 last_search_pattern = xstrdup(q);
Eric Andersen3f980402001-04-04 17:31:15 +00003786 goto dc3; // now find the pattern
3787 }
3788 // user changed mind and erased the "/"- do nothing
3789 break;
3790 case 'N': // N- backward search for last pattern
Eric Andersen3f980402001-04-04 17:31:15 +00003791 dir = BACK; // assume BACKWARD search
3792 p = dot - 1;
3793 if (last_search_pattern[0] == '?') {
3794 dir = FORWARD;
3795 p = dot + 1;
3796 }
3797 goto dc4; // now search for pattern
3798 break;
3799 case 'n': // n- repeat search for last pattern
3800 // search rest of text[] starting at next char
3801 // if search fails return orignal "p" not the "p+1" address
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003802 do {
3803 const char *msg;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003804 dc3:
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003805 dir = FORWARD; // assume FORWARD search
3806 p = dot + 1;
3807 if (last_search_pattern[0] == '?') {
3808 dir = BACK;
3809 p = dot - 1;
Eric Andersen3f980402001-04-04 17:31:15 +00003810 }
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003811 dc4:
3812 q = char_search(p, last_search_pattern + 1, dir, FULL);
3813 if (q != NULL) {
3814 dot = q; // good search, update "dot"
3815 msg = NULL;
3816 goto dc2;
3817 }
3818 // no pattern found between "dot" and "end"- continue at top
3819 p = text;
3820 if (dir == BACK) {
3821 p = end - 1;
3822 }
3823 q = char_search(p, last_search_pattern + 1, dir, FULL);
3824 if (q != NULL) { // found something
3825 dot = q; // found new pattern- goto it
3826 msg = "search hit BOTTOM, continuing at TOP";
3827 if (dir == BACK) {
3828 msg = "search hit TOP, continuing at BOTTOM";
3829 }
3830 } else {
3831 msg = "Pattern not found";
3832 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003833 dc2:
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003834 if (msg)
3835 status_line_bold("%s", msg);
3836 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003837 break;
3838 case '{': // {- move backward paragraph
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003839 q = char_search(dot, "\n\n", BACK, FULL);
Eric Andersen3f980402001-04-04 17:31:15 +00003840 if (q != NULL) { // found blank line
3841 dot = next_line(q); // move to next blank line
3842 }
3843 break;
3844 case '}': // }- move forward paragraph
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003845 q = char_search(dot, "\n\n", FORWARD, FULL);
Eric Andersen3f980402001-04-04 17:31:15 +00003846 if (q != NULL) { // found blank line
3847 dot = next_line(q); // move to next blank line
3848 }
3849 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003850#endif /* FEATURE_VI_SEARCH */
Maninder Singh97c64912015-05-25 13:46:36 +02003851 case '0': // 0- goto beginning of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003852 case '1': // 1-
3853 case '2': // 2-
3854 case '3': // 3-
3855 case '4': // 4-
3856 case '5': // 5-
3857 case '6': // 6-
3858 case '7': // 7-
3859 case '8': // 8-
3860 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003861 if (c == '0' && cmdcnt < 1) {
3862 dot_begin(); // this was a standalone zero
3863 } else {
3864 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3865 }
3866 break;
3867 case ':': // :- the colon mode commands
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003868 p = get_input_line(":"); // get input line- use "status line"
Eric Andersen3f980402001-04-04 17:31:15 +00003869 colon(p); // execute the command
Eric Andersen3f980402001-04-04 17:31:15 +00003870 break;
3871 case '<': // <- Left shift something
3872 case '>': // >- Right shift something
3873 cnt = count_lines(text, dot); // remember what line we are on
3874 c1 = get_one_char(); // get the type of thing to delete
3875 find_range(&p, &q, c1);
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003876 yank_delete(p, q, 1, YANKONLY, NO_UNDO); // save copy before change
Eric Andersen3f980402001-04-04 17:31:15 +00003877 p = begin_line(p);
3878 q = end_line(q);
3879 i = count_lines(p, q); // # of lines we are shifting
3880 for ( ; i > 0; i--, p = next_line(p)) {
3881 if (c == '<') {
3882 // shift left- remove tab or 8 spaces
3883 if (*p == '\t') {
3884 // shrink buffer 1 char
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003885 text_hole_delete(p, p, NO_UNDO);
Eric Andersen3f980402001-04-04 17:31:15 +00003886 } else if (*p == ' ') {
3887 // we should be calculating columns, not just SPACE
3888 for (j = 0; *p == ' ' && j < tabstop; j++) {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003889 text_hole_delete(p, p, NO_UNDO);
Eric Andersen3f980402001-04-04 17:31:15 +00003890 }
3891 }
3892 } else if (c == '>') {
3893 // shift right -- add tab or 8 spaces
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003894 char_insert(p, '\t', ALLOW_UNDO);
Eric Andersen3f980402001-04-04 17:31:15 +00003895 }
3896 }
3897 dot = find_line(cnt); // what line were we on
3898 dot_skip_over_ws();
3899 end_cmd_q(); // stop adding to q
3900 break;
3901 case 'A': // A- append at e-o-l
3902 dot_end(); // go to e-o-l
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +00003903 //**** fall through to ... 'a'
Eric Andersen3f980402001-04-04 17:31:15 +00003904 case 'a': // a- append after current char
3905 if (*dot != '\n')
3906 dot++;
3907 goto dc_i;
3908 break;
3909 case 'B': // B- back a blank-delimited Word
3910 case 'E': // E- end of a blank-delimited word
3911 case 'W': // W- forward a blank-delimited word
Eric Andersen3f980402001-04-04 17:31:15 +00003912 dir = FORWARD;
3913 if (c == 'B')
3914 dir = BACK;
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003915 do {
3916 if (c == 'W' || isspace(dot[dir])) {
3917 dot = skip_thing(dot, 1, dir, S_TO_WS);
3918 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3919 }
3920 if (c != 'W')
3921 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3922 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003923 break;
3924 case 'C': // C- Change to e-o-l
3925 case 'D': // D- delete to e-o-l
3926 save_dot = dot;
3927 dot = dollar_line(dot); // move to before NL
3928 // copy text into a register and delete
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003929 dot = yank_delete(save_dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete to e-o-l
Eric Andersen3f980402001-04-04 17:31:15 +00003930 if (c == 'C')
3931 goto dc_i; // start inserting
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003932#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003933 if (c == 'D')
3934 end_cmd_q(); // stop adding to q
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003935#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003936 break;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003937 case 'g': // 'gg' goto a line number (vim) (default: very first line)
Paul Foxb5ee8db2008-02-14 01:17:01 +00003938 c1 = get_one_char();
3939 if (c1 != 'g') {
3940 buf[0] = 'g';
Denys Vlasenkode1996d2016-09-15 13:53:42 +02003941 // c1 < 0 if the key was special. Try "g<up-arrow>"
3942 // TODO: if Unicode?
3943 buf[1] = (c1 >= 0 ? c1 : '*');
Paul Foxb5ee8db2008-02-14 01:17:01 +00003944 buf[2] = '\0';
3945 not_implemented(buf);
3946 break;
3947 }
3948 if (cmdcnt == 0)
3949 cmdcnt = 1;
3950 /* fall through */
Eric Andersen822c3832001-05-07 17:37:43 +00003951 case 'G': // G- goto to a line number (default= E-O-F)
3952 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003953 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003954 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003955 }
3956 dot_skip_over_ws();
3957 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003958 case 'H': // H- goto top line on screen
3959 dot = screenbegin;
3960 if (cmdcnt > (rows - 1)) {
3961 cmdcnt = (rows - 1);
3962 }
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003963 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003964 do_cmd('+');
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003965 }
Eric Andersen3f980402001-04-04 17:31:15 +00003966 dot_skip_over_ws();
3967 break;
3968 case 'I': // I- insert before first non-blank
3969 dot_begin(); // 0
3970 dot_skip_over_ws();
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +00003971 //**** fall through to ... 'i'
Eric Andersen3f980402001-04-04 17:31:15 +00003972 case 'i': // i- insert before current char
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003973 case KEYCODE_INSERT: // Cursor Key Insert
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003974 dc_i:
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003975 cmd_mode = 1; // start inserting
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003976 undo_queue_commit(); // commit queue when cmd_mode changes
Eric Andersen3f980402001-04-04 17:31:15 +00003977 break;
3978 case 'J': // J- join current and next lines together
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003979 do {
3980 dot_end(); // move to NL
3981 if (dot < end - 1) { // make sure not last char in text[]
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003982#if ENABLE_FEATURE_VI_UNDO
3983 undo_push(dot, 1, UNDO_DEL);
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003984 *dot++ = ' '; // replace NL with space
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003985 undo_push((dot - 1), 1, UNDO_INS_CHAIN);
3986#else
3987 *dot++ = ' ';
Denys Vlasenkoe7430862014-04-03 12:47:48 +02003988 modified_count++;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003989#endif
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003990 while (isblank(*dot)) { // delete leading WS
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003991 text_hole_delete(dot, dot, ALLOW_UNDO_CHAIN);
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003992 }
Eric Andersen3f980402001-04-04 17:31:15 +00003993 }
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003994 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003995 end_cmd_q(); // stop adding to q
3996 break;
3997 case 'L': // L- goto bottom line on screen
3998 dot = end_screen();
3999 if (cmdcnt > (rows - 1)) {
4000 cmdcnt = (rows - 1);
4001 }
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01004002 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00004003 do_cmd('-');
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01004004 }
Eric Andersen3f980402001-04-04 17:31:15 +00004005 dot_begin();
4006 dot_skip_over_ws();
4007 break;
Eric Andersen822c3832001-05-07 17:37:43 +00004008 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00004009 dot = screenbegin;
4010 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
4011 dot = next_line(dot);
4012 break;
Eric Andersen3f980402001-04-04 17:31:15 +00004013 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00004014 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00004015 p = begin_line(dot);
4016 if (p[-1] == '\n') {
4017 dot_prev();
4018 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
4019 dot_end();
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004020 dot = char_insert(dot, '\n', ALLOW_UNDO);
Eric Andersen3f980402001-04-04 17:31:15 +00004021 } else {
4022 dot_begin(); // 0
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004023 dot = char_insert(dot, '\n', ALLOW_UNDO); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00004024 dot_prev(); // -
4025 }
4026 goto dc_i;
4027 break;
4028 case 'R': // R- continuous Replace char
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004029 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00004030 cmd_mode = 2;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004031 undo_queue_commit();
Eric Andersen3f980402001-04-04 17:31:15 +00004032 break;
Denis Vlasenko0112ff52008-10-25 23:23:00 +00004033 case KEYCODE_DELETE:
Denys Vlasenko49acc1a2015-03-12 21:15:34 +01004034 if (dot < end - 1)
4035 dot = yank_delete(dot, dot, 1, YANKDEL, ALLOW_UNDO);
4036 break;
Eric Andersen3f980402001-04-04 17:31:15 +00004037 case 'X': // X- delete char before dot
4038 case 'x': // x- delete the current char
4039 case 's': // s- substitute the current char
Eric Andersen3f980402001-04-04 17:31:15 +00004040 dir = 0;
4041 if (c == 'X')
4042 dir = -1;
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004043 do {
4044 if (dot[dir] != '\n') {
4045 if (c == 'X')
4046 dot--; // delete prev char
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004047 dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004048 }
4049 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00004050 end_cmd_q(); // stop adding to q
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004051 if (c == 's')
4052 goto dc_i; // start inserting
Eric Andersen3f980402001-04-04 17:31:15 +00004053 break;
4054 case 'Z': // Z- if modified, {write}; exit
4055 // ZZ means to save file (if necessary), then exit
4056 c1 = get_one_char();
4057 if (c1 != 'Z') {
Denys Vlasenko05399fc2014-09-15 17:06:10 +02004058 indicate_error();
Eric Andersen3f980402001-04-04 17:31:15 +00004059 break;
4060 }
Denys Vlasenkoe7430862014-04-03 12:47:48 +02004061 if (modified_count) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00004062 if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
Denys Vlasenko778794d2013-01-22 10:13:52 +01004063 status_line_bold("'%s' is read only", current_filename);
Denis Vlasenko92758142006-10-03 19:56:34 +00004064 break;
Paul Foxf0305b72006-03-28 14:18:21 +00004065 }
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00004066 cnt = file_write(current_filename, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00004067 if (cnt < 0) {
4068 if (cnt == -1)
Denys Vlasenko6f97b302017-09-29 18:17:25 +02004069 status_line_bold("Write error: "STRERROR_FMT STRERROR_ERRNO);
Paul Fox61e45db2005-10-09 14:43:22 +00004070 } else if (cnt == (end - 1 - text + 1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00004071 editing = 0;
4072 }
4073 } else {
4074 editing = 0;
4075 }
4076 break;
4077 case '^': // ^- move to first non-blank on line
4078 dot_begin();
4079 dot_skip_over_ws();
4080 break;
4081 case 'b': // b- back a word
4082 case 'e': // e- end of word
Eric Andersen3f980402001-04-04 17:31:15 +00004083 dir = FORWARD;
4084 if (c == 'b')
4085 dir = BACK;
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004086 do {
4087 if ((dot + dir) < text || (dot + dir) > end - 1)
4088 break;
4089 dot += dir;
4090 if (isspace(*dot)) {
4091 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
4092 }
4093 if (isalnum(*dot) || *dot == '_') {
4094 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
4095 } else if (ispunct(*dot)) {
4096 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
4097 }
4098 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00004099 break;
4100 case 'c': // c- change something
4101 case 'd': // d- delete something
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004102#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00004103 case 'y': // y- yank something
4104 case 'Y': // Y- Yank a line
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004105#endif
Denis Vlasenkod44c1532008-10-14 12:59:42 +00004106 {
Paul Foxc51fc7b2008-03-06 01:34:23 +00004107 int yf, ml, whole = 0;
Eric Andersen3f980402001-04-04 17:31:15 +00004108 yf = YANKDEL; // assume either "c" or "d"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004109#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00004110 if (c == 'y' || c == 'Y')
4111 yf = YANKONLY;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004112#endif
Eric Andersen3f980402001-04-04 17:31:15 +00004113 c1 = 'y';
4114 if (c != 'Y')
4115 c1 = get_one_char(); // get the type of thing to delete
Paul Foxc51fc7b2008-03-06 01:34:23 +00004116 // determine range, and whether it spans lines
4117 ml = find_range(&p, &q, c1);
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004118 place_cursor(0, 0);
Eric Andersen3f980402001-04-04 17:31:15 +00004119 if (c1 == 27) { // ESC- user changed mind and wants out
4120 c = c1 = 27; // Escape- do nothing
4121 } else if (strchr("wW", c1)) {
4122 if (c == 'c') {
4123 // don't include trailing WS as part of word
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00004124 while (isblank(*q)) {
Eric Andersen3f980402001-04-04 17:31:15 +00004125 if (q <= text || q[-1] == '\n')
4126 break;
4127 q--;
4128 }
4129 }
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004130 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word
Paul Foxc51fc7b2008-03-06 01:34:23 +00004131 } else if (strchr("^0bBeEft%$ lh\b\177", c1)) {
4132 // partial line copy text into a register and delete
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004133 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word
Paul Foxc51fc7b2008-03-06 01:34:23 +00004134 } else if (strchr("cdykjHL+-{}\r\n", c1)) {
4135 // whole line copy text into a register and delete
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004136 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete lines
Paul Foxc51fc7b2008-03-06 01:34:23 +00004137 whole = 1;
4138 } else {
4139 // could not recognize object
4140 c = c1 = 27; // error-
4141 ml = 0;
Denys Vlasenko05399fc2014-09-15 17:06:10 +02004142 indicate_error();
Paul Foxc51fc7b2008-03-06 01:34:23 +00004143 }
4144 if (ml && whole) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00004145 if (c == 'c') {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004146 dot = char_insert(dot, '\n', ALLOW_UNDO_CHAIN);
Eric Andersen1c0d3112001-04-16 15:46:44 +00004147 // on the last line of file don't move to prev line
Paul Foxc51fc7b2008-03-06 01:34:23 +00004148 if (whole && dot != (end-1)) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00004149 dot_prev();
4150 }
4151 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00004152 dot_begin();
4153 dot_skip_over_ws();
4154 }
Eric Andersen3f980402001-04-04 17:31:15 +00004155 }
4156 if (c1 != 27) {
4157 // if CHANGING, not deleting, start inserting after the delete
4158 if (c == 'c') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004159 strcpy(buf, "Change");
Eric Andersen3f980402001-04-04 17:31:15 +00004160 goto dc_i; // start inserting
4161 }
4162 if (c == 'd') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004163 strcpy(buf, "Delete");
Eric Andersen3f980402001-04-04 17:31:15 +00004164 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004165#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00004166 if (c == 'y' || c == 'Y') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004167 strcpy(buf, "Yank");
Eric Andersen3f980402001-04-04 17:31:15 +00004168 }
4169 p = reg[YDreg];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004170 q = p + strlen(p);
Eric Andersen3f980402001-04-04 17:31:15 +00004171 for (cnt = 0; p <= q; p++) {
4172 if (*p == '\n')
4173 cnt++;
4174 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004175 status_line("%s %d lines (%d chars) using [%c]",
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004176 buf, cnt, strlen(reg[YDreg]), what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004177#endif
Eric Andersen3f980402001-04-04 17:31:15 +00004178 end_cmd_q(); // stop adding to q
4179 }
4180 break;
Denis Vlasenkod44c1532008-10-14 12:59:42 +00004181 }
Eric Andersen3f980402001-04-04 17:31:15 +00004182 case 'k': // k- goto prev line, same col
Denis Vlasenko0112ff52008-10-25 23:23:00 +00004183 case KEYCODE_UP: // cursor key Up
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004184 do {
4185 dot_prev();
4186 dot = move_to_col(dot, ccol + offset); // try stay in same col
4187 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00004188 break;
4189 case 'r': // r- replace the current char with user input
4190 c1 = get_one_char(); // get the replacement char
4191 if (*dot != '\n') {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004192#if ENABLE_FEATURE_VI_UNDO
4193 undo_push(dot, 1, UNDO_DEL);
4194 *dot = c1;
4195 undo_push(dot, 1, UNDO_INS_CHAIN);
4196#else
Eric Andersen3f980402001-04-04 17:31:15 +00004197 *dot = c1;
Denys Vlasenkoe7430862014-04-03 12:47:48 +02004198 modified_count++;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004199#endif
Eric Andersen3f980402001-04-04 17:31:15 +00004200 }
4201 end_cmd_q(); // stop adding to q
4202 break;
Eric Andersen822c3832001-05-07 17:37:43 +00004203 case 't': // t- move to char prior to next x
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00004204 last_forward_char = get_one_char();
4205 do_cmd(';');
4206 if (*dot == last_forward_char)
4207 dot_left();
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00004208 last_forward_char = 0;
Eric Andersen822c3832001-05-07 17:37:43 +00004209 break;
Eric Andersen3f980402001-04-04 17:31:15 +00004210 case 'w': // w- forward a word
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004211 do {
4212 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
4213 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
4214 } else if (ispunct(*dot)) { // we are on PUNCT
4215 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
4216 }
4217 if (dot < end - 1)
4218 dot++; // move over word
4219 if (isspace(*dot)) {
4220 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
4221 }
4222 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00004223 break;
4224 case 'z': // z-
4225 c1 = get_one_char(); // get the replacement char
4226 cnt = 0;
4227 if (c1 == '.')
4228 cnt = (rows - 2) / 2; // put dot at center
4229 if (c1 == '-')
4230 cnt = rows - 2; // put dot at bottom
4231 screenbegin = begin_line(dot); // start dot at top
4232 dot_scroll(cnt, -1);
4233 break;
4234 case '|': // |- move to column "cmdcnt"
4235 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
4236 break;
4237 case '~': // ~- flip the case of letters a-z -> A-Z
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004238 do {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004239#if ENABLE_FEATURE_VI_UNDO
4240 if (islower(*dot)) {
4241 undo_push(dot, 1, UNDO_DEL);
4242 *dot = toupper(*dot);
4243 undo_push(dot, 1, UNDO_INS_CHAIN);
4244 } else if (isupper(*dot)) {
4245 undo_push(dot, 1, UNDO_DEL);
4246 *dot = tolower(*dot);
4247 undo_push(dot, 1, UNDO_INS_CHAIN);
4248 }
4249#else
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004250 if (islower(*dot)) {
4251 *dot = toupper(*dot);
Denys Vlasenkoe7430862014-04-03 12:47:48 +02004252 modified_count++;
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004253 } else if (isupper(*dot)) {
4254 *dot = tolower(*dot);
Denys Vlasenkoe7430862014-04-03 12:47:48 +02004255 modified_count++;
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004256 }
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004257#endif
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004258 dot_right();
4259 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00004260 end_cmd_q(); // stop adding to q
4261 break;
4262 //----- The Cursor and Function Keys -----------------------------
Denis Vlasenko0112ff52008-10-25 23:23:00 +00004263 case KEYCODE_HOME: // Cursor Key Home
Eric Andersen3f980402001-04-04 17:31:15 +00004264 dot_begin();
4265 break;
4266 // The Fn keys could point to do_macro which could translate them
Denis Vlasenko0112ff52008-10-25 23:23:00 +00004267#if 0
4268 case KEYCODE_FUN1: // Function Key F1
4269 case KEYCODE_FUN2: // Function Key F2
4270 case KEYCODE_FUN3: // Function Key F3
4271 case KEYCODE_FUN4: // Function Key F4
4272 case KEYCODE_FUN5: // Function Key F5
4273 case KEYCODE_FUN6: // Function Key F6
4274 case KEYCODE_FUN7: // Function Key F7
4275 case KEYCODE_FUN8: // Function Key F8
4276 case KEYCODE_FUN9: // Function Key F9
4277 case KEYCODE_FUN10: // Function Key F10
4278 case KEYCODE_FUN11: // Function Key F11
4279 case KEYCODE_FUN12: // Function Key F12
Eric Andersen3f980402001-04-04 17:31:15 +00004280 break;
Denis Vlasenko0112ff52008-10-25 23:23:00 +00004281#endif
Eric Andersen3f980402001-04-04 17:31:15 +00004282 }
4283
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004284 dc1:
Eric Andersen3f980402001-04-04 17:31:15 +00004285 // if text[] just became empty, add back an empty line
4286 if (end == text) {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004287 char_insert(text, '\n', NO_UNDO); // start empty buf with dummy line
Eric Andersen3f980402001-04-04 17:31:15 +00004288 dot = text;
4289 }
4290 // it is OK for dot to exactly equal to end, otherwise check dot validity
4291 if (dot != end) {
4292 dot = bound_dot(dot); // make sure "dot" is valid
4293 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004294#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00004295 check_context(c); // update the current context
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004296#endif
Eric Andersen3f980402001-04-04 17:31:15 +00004297
4298 if (!isdigit(c))
4299 cmdcnt = 0; // cmd was not a number, reset cmdcnt
4300 cnt = dot - begin_line(dot);
4301 // Try to stay off of the Newline
4302 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
4303 dot--;
4304}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004305
Paul Fox35e9c5d2008-03-06 16:26:12 +00004306/* NB! the CRASHME code is unmaintained, and doesn't currently build */
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004307#if ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004308static int totalcmds = 0;
4309static int Mp = 85; // Movement command Probability
4310static int Np = 90; // Non-movement command Probability
4311static int Dp = 96; // Delete command Probability
4312static int Ip = 97; // Insert command Probability
4313static int Yp = 98; // Yank command Probability
4314static int Pp = 99; // Put command Probability
4315static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004316static const char chars[20] = "\t012345 abcdABCD-=.$";
4317static const char *const words[20] = {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004318 "this", "is", "a", "test",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004319 "broadcast", "the", "emergency", "of",
4320 "system", "quick", "brown", "fox",
4321 "jumped", "over", "lazy", "dogs",
4322 "back", "January", "Febuary", "March"
4323};
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004324static const char *const lines[20] = {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004325 "You should have received a copy of the GNU General Public License\n",
4326 "char c, cm, *cmd, *cmd1;\n",
4327 "generate a command by percentages\n",
4328 "Numbers may be typed as a prefix to some commands.\n",
4329 "Quit, discarding changes!\n",
4330 "Forced write, if permission originally not valid.\n",
4331 "In general, any ex or ed command (such as substitute or delete).\n",
4332 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
4333 "Please get w/ me and I will go over it with you.\n",
4334 "The following is a list of scheduled, committed changes.\n",
4335 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
4336 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
4337 "Any question about transactions please contact Sterling Huxley.\n",
4338 "I will try to get back to you by Friday, December 31.\n",
4339 "This Change will be implemented on Friday.\n",
4340 "Let me know if you have problems accessing this;\n",
4341 "Sterling Huxley recently added you to the access list.\n",
4342 "Would you like to go to lunch?\n",
4343 "The last command will be automatically run.\n",
4344 "This is too much english for a computer geek.\n",
4345};
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004346static char *multilines[20] = {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004347 "You should have received a copy of the GNU General Public License\n",
4348 "char c, cm, *cmd, *cmd1;\n",
4349 "generate a command by percentages\n",
4350 "Numbers may be typed as a prefix to some commands.\n",
4351 "Quit, discarding changes!\n",
4352 "Forced write, if permission originally not valid.\n",
4353 "In general, any ex or ed command (such as substitute or delete).\n",
4354 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
4355 "Please get w/ me and I will go over it with you.\n",
4356 "The following is a list of scheduled, committed changes.\n",
4357 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
4358 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
4359 "Any question about transactions please contact Sterling Huxley.\n",
4360 "I will try to get back to you by Friday, December 31.\n",
4361 "This Change will be implemented on Friday.\n",
4362 "Let me know if you have problems accessing this;\n",
4363 "Sterling Huxley recently added you to the access list.\n",
4364 "Would you like to go to lunch?\n",
4365 "The last command will be automatically run.\n",
4366 "This is too much english for a computer geek.\n",
4367};
4368
4369// create a random command to execute
4370static void crash_dummy()
4371{
4372 static int sleeptime; // how long to pause between commands
4373 char c, cm, *cmd, *cmd1;
4374 int i, cnt, thing, rbi, startrbi, percent;
4375
4376 // "dot" movement commands
4377 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
4378
4379 // is there already a command running?
Denys Vlasenko020f4062009-05-17 16:44:54 +02004380 if (readbuffer[0] > 0)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004381 goto cd1;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004382 cd0:
Denys Vlasenko020f4062009-05-17 16:44:54 +02004383 readbuffer[0] = 'X';
4384 startrbi = rbi = 1;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004385 sleeptime = 0; // how long to pause between commands
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004386 memset(readbuffer, '\0', sizeof(readbuffer));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004387 // generate a command by percentages
4388 percent = (int) lrand48() % 100; // get a number from 0-99
4389 if (percent < Mp) { // Movement commands
4390 // available commands
4391 cmd = cmd1;
4392 M++;
4393 } else if (percent < Np) { // non-movement commands
4394 cmd = "mz<>\'\""; // available commands
4395 N++;
4396 } else if (percent < Dp) { // Delete commands
4397 cmd = "dx"; // available commands
4398 D++;
4399 } else if (percent < Ip) { // Inset commands
4400 cmd = "iIaAsrJ"; // available commands
4401 I++;
4402 } else if (percent < Yp) { // Yank commands
4403 cmd = "yY"; // available commands
4404 Y++;
4405 } else if (percent < Pp) { // Put commands
4406 cmd = "pP"; // available commands
4407 P++;
4408 } else {
4409 // We do not know how to handle this command, try again
4410 U++;
4411 goto cd0;
4412 }
4413 // randomly pick one of the available cmds from "cmd[]"
4414 i = (int) lrand48() % strlen(cmd);
4415 cm = cmd[i];
4416 if (strchr(":\024", cm))
4417 goto cd0; // dont allow colon or ctrl-T commands
4418 readbuffer[rbi++] = cm; // put cmd into input buffer
4419
4420 // now we have the command-
4421 // there are 1, 2, and multi char commands
4422 // find out which and generate the rest of command as necessary
4423 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
4424 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
4425 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
4426 cmd1 = "abcdefghijklmnopqrstuvwxyz";
4427 }
4428 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
4429 c = cmd1[thing];
4430 readbuffer[rbi++] = c; // add movement to input buffer
4431 }
4432 if (strchr("iIaAsc", cm)) { // multi-char commands
4433 if (cm == 'c') {
4434 // change some thing
4435 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
4436 c = cmd1[thing];
4437 readbuffer[rbi++] = c; // add movement to input buffer
4438 }
4439 thing = (int) lrand48() % 4; // what thing to insert
4440 cnt = (int) lrand48() % 10; // how many to insert
4441 for (i = 0; i < cnt; i++) {
4442 if (thing == 0) { // insert chars
4443 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
4444 } else if (thing == 1) { // insert words
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004445 strcat(readbuffer, words[(int) lrand48() % 20]);
4446 strcat(readbuffer, " ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004447 sleeptime = 0; // how fast to type
4448 } else if (thing == 2) { // insert lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004449 strcat(readbuffer, lines[(int) lrand48() % 20]);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004450 sleeptime = 0; // how fast to type
4451 } else { // insert multi-lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004452 strcat(readbuffer, multilines[(int) lrand48() % 20]);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004453 sleeptime = 0; // how fast to type
4454 }
4455 }
Denys Vlasenko8187e012017-09-13 22:48:30 +02004456 strcat(readbuffer, ESC);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004457 }
Denys Vlasenko020f4062009-05-17 16:44:54 +02004458 readbuffer[0] = strlen(readbuffer + 1);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004459 cd1:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004460 totalcmds++;
4461 if (sleeptime > 0)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004462 mysleep(sleeptime); // sleep 1/100 sec
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004463}
4464
4465// test to see if there are any errors
4466static void crash_test()
4467{
4468 static time_t oldtim;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004469
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004470 time_t tim;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004471 char d[2], msg[80];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004472
4473 msg[0] = '\0';
4474 if (end < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004475 strcat(msg, "end<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004476 }
4477 if (end > textend) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004478 strcat(msg, "end>textend ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004479 }
4480 if (dot < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004481 strcat(msg, "dot<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004482 }
4483 if (dot > end) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004484 strcat(msg, "dot>end ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004485 }
4486 if (screenbegin < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004487 strcat(msg, "screenbegin<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004488 }
4489 if (screenbegin > end - 1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004490 strcat(msg, "screenbegin>end-1 ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004491 }
4492
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004493 if (msg[0]) {
Glenn L McGrath7127b582002-12-03 21:48:15 +00004494 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Denys Vlasenko04b52892012-06-11 13:51:38 +02004495 totalcmds, last_input_char, msg, ESC_BOLD_TEXT, ESC_NORM_TEXT);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01004496 fflush_all();
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00004497 while (safe_read(STDIN_FILENO, d, 1) > 0) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004498 if (d[0] == '\n' || d[0] == '\r')
4499 break;
4500 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004501 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004502 tim = time(NULL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004503 if (tim >= (oldtim + 3)) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004504 sprintf(status_buffer,
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004505 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
4506 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
4507 oldtim = tim;
4508 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004509}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004510#endif