blob: 2715294045ba436fbcf9c2581a099f67f2c7ab93 [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
Denys Vlasenko836d0a72018-11-29 14:19:57 +0100258 LIMITED = 0, // char_search() only current line
259 FULL = 1, // char_search() to the end/beginning of entire text
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
Denys Vlasenkob7330462018-11-29 14:39:52 +0100564static char *char_search(char *, const char *, 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 Vlasenko39043ad2018-06-27 14:46:08 +0200753static ALWAYS_INLINE int query_screen_dimensions(void)
754{
755 return 0;
756}
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700757#endif
758
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000759static void edit_file(char *fn)
Eric Andersen3f980402001-04-04 17:31:15 +0000760{
Denis Vlasenkob1759462008-06-20 20:20:54 +0000761#if ENABLE_FEATURE_VI_YANKMARK
762#define cur_line edit_file__cur_line
763#endif
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000764 int c;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000765#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000766 int sig;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000767#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000768
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000769 editing = 1; // 0 = exit, 1 = one file, 2 = multiple files
Eric Andersen3f980402001-04-04 17:31:15 +0000770 rawmode();
771 rows = 24;
772 columns = 80;
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200773 IF_FEATURE_VI_ASK_TERMINAL(G.get_rowcol_error =) query_screen_dimensions();
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700774#if ENABLE_FEATURE_VI_ASK_TERMINAL
775 if (G.get_rowcol_error /* TODO? && no input on stdin */) {
776 uint64_t k;
Denys Vlasenko8187e012017-09-13 22:48:30 +0200777 write1(ESC"[999;999H" ESC"[6n");
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700778 fflush_all();
779 k = read_key(STDIN_FILENO, readbuffer, /*timeout_ms:*/ 100);
780 if ((int32_t)k == KEYCODE_CURSOR_POS) {
781 uint32_t rc = (k >> 32);
782 columns = (rc & 0x7fff);
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200783 if (columns > MAX_SCR_COLS)
784 columns = MAX_SCR_COLS;
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700785 rows = ((rc >> 16) & 0x7fff);
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +0200786 if (rows > MAX_SCR_ROWS)
787 rows = MAX_SCR_ROWS;
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700788 }
Denys Vlasenkoc175c462010-04-18 22:09:30 -0700789 }
790#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000791 new_screen(rows, columns); // get memory for virtual screen
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000792 init_text_buffer(fn);
Eric Andersen3f980402001-04-04 17:31:15 +0000793
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000794#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000795 YDreg = 26; // default Yank/Delete reg
796 Ureg = 27; // hold orig line for "U" cmd
Eric Andersen3f980402001-04-04 17:31:15 +0000797 mark[26] = mark[27] = text; // init "previous context"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000798#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000799
Eric Andersen3f980402001-04-04 17:31:15 +0000800 last_forward_char = last_input_char = '\0';
801 crow = 0;
802 ccol = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000803
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000804#if ENABLE_FEATURE_VI_USE_SIGNALS
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700805 signal(SIGINT, catch_sig);
Eric Andersen3f980402001-04-04 17:31:15 +0000806 signal(SIGWINCH, winch_sig);
807 signal(SIGTSTP, suspend_sig);
Paul Fox2724fa92008-03-17 15:28:07 +0000808 sig = sigsetjmp(restart, 1);
Eric Andersen3f980402001-04-04 17:31:15 +0000809 if (sig != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000810 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000811 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000812#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000813
Eric Andersen3f980402001-04-04 17:31:15 +0000814 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
815 cmdcnt = 0;
816 tabstop = 8;
817 offset = 0; // no horizontal offset
818 c = '\0';
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000819#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000820 free(ioq_start);
Paul Foxc51fc7b2008-03-06 01:34:23 +0000821 ioq = ioq_start = NULL;
822 lmc_len = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000823 adding2q = 0;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000824#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000825
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000826#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000827 {
828 char *p, *q;
829 int n = 0;
830
Denys Vlasenko2bb651a2010-04-16 20:55:52 -0700831 while ((p = initial_cmds[n]) != NULL) {
Denis Vlasenkof9234132007-03-21 00:03:42 +0000832 do {
833 q = p;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000834 p = strchr(q, '\n');
Denis Vlasenkof9234132007-03-21 00:03:42 +0000835 if (p)
Denis Vlasenko51742f42007-04-12 00:32:05 +0000836 while (*p == '\n')
Denis Vlasenkof9234132007-03-21 00:03:42 +0000837 *p++ = '\0';
838 if (*q)
839 colon(q);
840 } while (p);
841 free(initial_cmds[n]);
842 initial_cmds[n] = NULL;
843 n++;
844 }
845 }
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000846#endif
Paul Fox35e9c5d2008-03-06 16:26:12 +0000847 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000848 //------This is the main Vi cmd handling loop -----------------------
849 while (editing > 0) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000850#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000851 if (crashme > 0) {
852 if ((end - text) > 1) {
853 crash_dummy(); // generate a random command
854 } else {
855 crashme = 0;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +0200856 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 +0000857 dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000858 refresh(FALSE);
859 }
860 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000861#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000862 last_input_char = c = get_one_char(); // get a cmd from user
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000863#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000864 // save a copy of the current line- for the 'U" command
865 if (begin_line(dot) != cur_line) {
866 cur_line = begin_line(dot);
867 text_yank(begin_line(dot), end_line(dot), Ureg);
868 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000869#endif
870#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000871 // These are commands that change text[].
872 // Remember the input for the "." command
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000873 if (!adding2q && ioq_start == NULL
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +0000874 && cmd_mode == 0 // command mode
875 && c > '\0' // exclude NUL and non-ASCII chars
876 && c < 0x7f // (Unicode and such)
877 && strchr(modifying_cmds, c)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000878 ) {
Eric Andersen3f980402001-04-04 17:31:15 +0000879 start_new_cmd_q(c);
880 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000881#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000882 do_cmd(c); // execute the user command
Denis Vlasenko8ef801b2008-10-16 09:46:07 +0000883
Eric Andersen3f980402001-04-04 17:31:15 +0000884 // poll to see if there is input already waiting. if we are
885 // not able to display output fast enough to keep up, skip
886 // the display update until we catch up with input.
Denys Vlasenko020f4062009-05-17 16:44:54 +0200887 if (!readbuffer[0] && mysleep(0) == 0) {
Denis Vlasenko8ef801b2008-10-16 09:46:07 +0000888 // no input pending - so update output
Eric Andersen3f980402001-04-04 17:31:15 +0000889 refresh(FALSE);
890 show_status_line();
891 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000892#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000893 if (crashme > 0)
894 crash_test(); // test editor variables
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000895#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000896 }
897 //-------------------------------------------------------------------
898
Denis Vlasenko267e16c2008-10-14 10:34:41 +0000899 go_bottom_and_clear_to_eol();
Eric Andersen3f980402001-04-04 17:31:15 +0000900 cookmode();
Denis Vlasenkob1759462008-06-20 20:20:54 +0000901#undef cur_line
Eric Andersen3f980402001-04-04 17:31:15 +0000902}
903
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000904//----- The Colon commands -------------------------------------
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000905#if ENABLE_FEATURE_VI_COLON
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000906static char *get_one_address(char *p, int *addr) // get colon addr, if present
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000907{
908 int st;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000909 char *q;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000910 IF_FEATURE_VI_YANKMARK(char c;)
911 IF_FEATURE_VI_SEARCH(char *pat;)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000912
913 *addr = -1; // assume no addr
914 if (*p == '.') { // the current line
915 p++;
916 q = begin_line(dot);
917 *addr = count_lines(text, q);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000918 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000919#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000920 else if (*p == '\'') { // is this a mark addr
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000921 p++;
922 c = tolower(*p);
923 p++;
924 if (c >= 'a' && c <= 'z') {
925 // we have a mark
926 c = c - 'a';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000927 q = mark[(unsigned char) c];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000928 if (q != NULL) { // is mark valid
Denis Vlasenko00d84172008-11-24 07:34:42 +0000929 *addr = count_lines(text, q);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000930 }
931 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000932 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000933#endif
934#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000935 else if (*p == '/') { // a search pattern
936 q = strchrnul(++p, '/');
937 pat = xstrndup(p, q - p); // save copy of pattern
938 p = q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000939 if (*p == '/')
940 p++;
Denys Vlasenkob7330462018-11-29 14:39:52 +0100941 q = char_search(dot, pat, (FORWARD << 1) | FULL);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000942 if (q != NULL) {
943 *addr = count_lines(text, q);
944 }
945 free(pat);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000946 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000947#endif
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000948 else if (*p == '$') { // the last line in file
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000949 p++;
950 q = begin_line(end - 1);
951 *addr = count_lines(text, q);
952 } else if (isdigit(*p)) { // specific line number
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000953 sscanf(p, "%d%n", addr, &st);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000954 p += st;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +0000955 } else {
Denys Vlasenkob22bbff2009-07-04 16:50:43 +0200956 // unrecognized address - assume -1
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000957 *addr = -1;
958 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000959 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000960}
961
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000962static char *get_address(char *p, int *b, int *e) // get two colon addrs, if present
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000963{
964 //----- get the address' i.e., 1,3 'a,'b -----
965 // get FIRST addr, if present
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000966 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000967 p++; // skip over leading spaces
968 if (*p == '%') { // alias for 1,$
969 p++;
970 *b = 1;
971 *e = count_lines(text, end-1);
972 goto ga0;
973 }
974 p = get_one_address(p, b);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000975 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000976 p++;
Eric Andersenaff114c2004-04-14 17:51:38 +0000977 if (*p == ',') { // is there a address separator
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000978 p++;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000979 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000980 p++;
981 // get SECOND addr, if present
982 p = get_one_address(p, e);
983 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000984 ga0:
Denis Vlasenkoeaabf062007-07-17 23:14:07 +0000985 while (isblank(*p))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000986 p++; // skip over trailing spaces
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000987 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000988}
989
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000990#if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000991static void setops(const char *args, const char *opname, int flg_no,
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000992 const char *short_opname, int opt)
993{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000994 const char *a = args + flg_no;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000995 int l = strlen(opname) - 1; /* opname have + ' ' */
996
Denys Vlasenko6548edd2009-06-15 12:44:11 +0200997 // maybe strncmp? we had tons of erroneous strncasecmp's...
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000998 if (strncasecmp(a, opname, l) == 0
999 || strncasecmp(a, short_opname, 2) == 0
1000 ) {
1001 if (flg_no)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001002 vi_setops &= ~opt;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001003 else
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001004 vi_setops |= opt;
1005 }
1006}
1007#endif
1008
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001009#endif /* FEATURE_VI_COLON */
1010
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001011// buf must be no longer than MAX_INPUT_LEN!
1012static void colon(char *buf)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001013{
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001014#if !ENABLE_FEATURE_VI_COLON
1015 /* Simple ":cmd" handler with minimal set of commands */
1016 char *p = buf;
1017 int cnt;
1018
1019 if (*p == ':')
1020 p++;
1021 cnt = strlen(p);
1022 if (cnt == 0)
1023 return;
1024 if (strncmp(p, "quit", cnt) == 0
1025 || strncmp(p, "q!", cnt) == 0
1026 ) {
1027 if (modified_count && p[1] != '!') {
1028 status_line_bold("No write since last change (:%s! overrides)", p);
1029 } else {
1030 editing = 0;
1031 }
1032 return;
1033 }
1034 if (strncmp(p, "write", cnt) == 0
1035 || strncmp(p, "wq", cnt) == 0
1036 || strncmp(p, "wn", cnt) == 0
1037 || (p[0] == 'x' && !p[1])
1038 ) {
Denys Vlasenkoe88608e2017-03-13 20:50:42 +01001039 if (modified_count != 0 || p[0] != 'x') {
1040 cnt = file_write(current_filename, text, end - 1);
1041 }
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001042 if (cnt < 0) {
1043 if (cnt == -1)
Denys Vlasenko6f97b302017-09-29 18:17:25 +02001044 status_line_bold("Write error: "STRERROR_FMT STRERROR_ERRNO);
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001045 } else {
1046 modified_count = 0;
1047 last_modified_count = -1;
1048 status_line("'%s' %dL, %dC",
1049 current_filename,
1050 count_lines(text, end - 1), cnt
1051 );
Denys Vlasenkoe88608e2017-03-13 20:50:42 +01001052 if (p[0] == 'x'
1053 || p[1] == 'q' || p[1] == 'n'
1054 || p[1] == 'Q' || p[1] == 'N'
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001055 ) {
1056 editing = 0;
1057 }
1058 }
1059 return;
1060 }
1061 if (strncmp(p, "file", cnt) == 0) {
1062 last_status_cksum = 0; // force status update
1063 return;
1064 }
1065 if (sscanf(p, "%d", &cnt) > 0) {
1066 dot = find_line(cnt);
1067 dot_skip_over_ws();
1068 return;
1069 }
1070 not_implemented(p);
1071#else
1072
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001073 char c, *buf1, *q, *r;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001074 char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN];
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001075 int i, l, li, b, e;
1076 int useforce;
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001077# if ENABLE_FEATURE_VI_SEARCH || ENABLE_FEATURE_ALLOW_EXEC
1078 char *orig_buf;
1079# endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001080
1081 // :3154 // if (-e line 3154) goto it else stay put
1082 // :4,33w! foo // write a portion of buffer to file "foo"
1083 // :w // write all of buffer to current file
1084 // :q // quit
1085 // :q! // quit- dont care about modified file
1086 // :'a,'z!sort -u // filter block through sort
1087 // :'f // goto mark "f"
1088 // :'fl // list literal the mark "f" line
1089 // :.r bar // read file "bar" into buffer before dot
1090 // :/123/,/abc/d // delete lines from "123" line to "abc" line
1091 // :/xyz/ // goto the "xyz" line
1092 // :s/find/replace/ // substitute pattern "find" with "replace"
1093 // :!<cmd> // run <cmd> then return
1094 //
Eric Andersen165e8cb2004-07-20 06:44:46 +00001095
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001096 if (!buf[0])
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001097 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001098 if (*buf == ':')
1099 buf++; // move past the ':'
1100
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001101 li = i = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001102 b = e = -1;
1103 q = text; // assume 1,$ for the range
1104 r = end - 1;
1105 li = count_lines(text, end - 1);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001106 fn = current_filename;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001107
1108 // look for optional address(es) :. :1 :1,9 :'q,'a :%
1109 buf = get_address(buf, &b, &e);
1110
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001111# if ENABLE_FEATURE_VI_SEARCH || ENABLE_FEATURE_ALLOW_EXEC
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001112 // remember orig command line
1113 orig_buf = buf;
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001114# endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001115
1116 // get the COMMAND into cmd[]
1117 buf1 = cmd;
1118 while (*buf != '\0') {
1119 if (isspace(*buf))
1120 break;
1121 *buf1++ = *buf++;
1122 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001123 *buf1 = '\0';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001124 // get any ARGuments
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001125 while (isblank(*buf))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001126 buf++;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001127 strcpy(args, buf);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001128 useforce = FALSE;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001129 buf1 = last_char_is(cmd, '!');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001130 if (buf1) {
1131 useforce = TRUE;
1132 *buf1 = '\0'; // get rid of !
1133 }
1134 if (b >= 0) {
1135 // if there is only one addr, then the addr
1136 // is the line number of the single line the
1137 // user wants. So, reset the end
1138 // pointer to point at end of the "b" line
1139 q = find_line(b); // what line is #b
1140 r = end_line(q);
1141 li = 1;
1142 }
1143 if (e >= 0) {
1144 // we were given two addrs. change the
1145 // end pointer to the addr given by user.
1146 r = find_line(e); // what line is #e
1147 r = end_line(r);
1148 li = e - b + 1;
1149 }
1150 // ------------ now look for the command ------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001151 i = strlen(cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001152 if (i == 0) { // :123CR goto line #123
1153 if (b >= 0) {
1154 dot = find_line(b); // what line is #b
1155 dot_skip_over_ws();
1156 }
Denis Vlasenko249fabf2006-12-19 00:29:22 +00001157 }
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001158# if ENABLE_FEATURE_ALLOW_EXEC
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001159 else if (cmd[0] == '!') { // run a cmd
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001160 int retcode;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001161 // :!ls run the <cmd>
Denis Vlasenko267e16c2008-10-14 10:34:41 +00001162 go_bottom_and_clear_to_eol();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001163 cookmode();
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001164 retcode = system(orig_buf + 1); // run the cmd
1165 if (retcode)
1166 printf("\nshell returned %i\n\n", retcode);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001167 rawmode();
1168 Hit_Return(); // let user see results
Denis Vlasenko249fabf2006-12-19 00:29:22 +00001169 }
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001170# endif
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001171 else if (cmd[0] == '=' && !cmd[1]) { // where is the address
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001172 if (b < 0) { // no addr given- use defaults
1173 b = e = count_lines(text, dot);
1174 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001175 status_line("%d", b);
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001176 } else if (strncmp(cmd, "delete", i) == 0) { // delete lines
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001177 if (b < 0) { // no addr given- use defaults
1178 q = begin_line(dot); // assume .,. for the range
1179 r = end_line(dot);
1180 }
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001181 dot = yank_delete(q, r, 1, YANKDEL, ALLOW_UNDO); // save, then delete lines
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001182 dot_skip_over_ws();
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001183 } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001184 int size;
1185
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001186 // don't edit, if the current file has been modified
Denys Vlasenkoe7430862014-04-03 12:47:48 +02001187 if (modified_count && !useforce) {
Dennis Groenenc0657e02012-01-31 14:12:38 +01001188 status_line_bold("No write since last change (:%s! overrides)", cmd);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001189 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001190 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001191 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001192 // the user supplied a file name
Denis Vlasenkoe8a07882007-06-10 15:08:44 +00001193 fn = args;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001194 } else if (current_filename && current_filename[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001195 // no user supplied name- use the current filename
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001196 // fn = current_filename; was set by default
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001197 } else {
1198 // no user file name, no current name- punt
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001199 status_line_bold("No current filename");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001200 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001201 }
1202
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001203 size = init_text_buffer(fn);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001204
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001205# if ENABLE_FEATURE_VI_YANKMARK
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001206 if (Ureg >= 0 && Ureg < 28) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001207 free(reg[Ureg]); // free orig line reg- for 'U'
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001208 reg[Ureg] = NULL;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001209 }
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001210 if (YDreg >= 0 && YDreg < 28) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001211 free(reg[YDreg]); // free default yank/delete register
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001212 reg[YDreg] = NULL;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001213 }
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001214# endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001215 // how many lines in text[]?
1216 li = count_lines(text, end - 1);
Denys Vlasenko778794d2013-01-22 10:13:52 +01001217 status_line("'%s'%s"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001218 IF_FEATURE_VI_READONLY("%s")
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001219 " %dL, %dC",
1220 current_filename,
1221 (size < 0 ? " [New file]" : ""),
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001222 IF_FEATURE_VI_READONLY(
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001223 ((readonly_mode) ? " [Readonly]" : ""),
1224 )
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001225 li, (int)(end - text)
1226 );
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001227 } else if (strncmp(cmd, "file", i) == 0) { // what File is this
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001228 if (b != -1 || e != -1) {
Denys Vlasenkoc2704542009-11-20 19:14:19 +01001229 status_line_bold("No address allowed on this command");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001230 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001231 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001232 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001233 // user wants a new filename
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001234 free(current_filename);
1235 current_filename = xstrdup(args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001236 } else {
1237 // user wants file status info
Paul Fox8552aec2005-09-16 12:20:05 +00001238 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001239 }
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001240 } else if (strncmp(cmd, "features", i) == 0) { // what features are available
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001241 // print out values of all features
Denis Vlasenko267e16c2008-10-14 10:34:41 +00001242 go_bottom_and_clear_to_eol();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001243 cookmode();
1244 show_help();
1245 rawmode();
1246 Hit_Return();
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001247 } else if (strncmp(cmd, "list", i) == 0) { // literal print line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001248 if (b < 0) { // no addr given- use defaults
1249 q = begin_line(dot); // assume .,. for the range
1250 r = end_line(dot);
1251 }
Denis Vlasenko267e16c2008-10-14 10:34:41 +00001252 go_bottom_and_clear_to_eol();
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001253 puts("\r");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001254 for (; q <= r; q++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001255 int c_is_no_print;
1256
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001257 c = *q;
Denis Vlasenko2a51af22007-03-21 22:31:24 +00001258 c_is_no_print = (c & 0x80) && !Isprint(c);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001259 if (c_is_no_print) {
1260 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001261 standout_start();
Denis Vlasenkod3c042f2007-12-30 01:59:53 +00001262 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001263 if (c == '\n') {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001264 write1("$\r");
1265 } else if (c < ' ' || c == 127) {
Denis Vlasenko4daad902007-09-27 10:20:47 +00001266 bb_putchar('^');
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001267 if (c == 127)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001268 c = '?';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001269 else
1270 c += '@';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001271 }
Denis Vlasenko4daad902007-09-27 10:20:47 +00001272 bb_putchar(c);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001273 if (c_is_no_print)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001274 standout_end();
1275 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001276 Hit_Return();
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001277 } else if (strncmp(cmd, "quit", i) == 0 // quit
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001278 || strncmp(cmd, "next", i) == 0 // edit next file
Dennis Groenenc0657e02012-01-31 14:12:38 +01001279 || strncmp(cmd, "prev", i) == 0 // edit previous file
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001280 ) {
Denys Vlasenko04cecd52010-04-16 22:13:55 -07001281 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001282 if (useforce) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001283 if (*cmd == 'q') {
Dennis Groenenc0657e02012-01-31 14:12:38 +01001284 // force end of argv list
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001285 optind = save_argc;
1286 }
1287 editing = 0;
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001288 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001289 }
1290 // don't exit if the file been modified
Denys Vlasenkoe7430862014-04-03 12:47:48 +02001291 if (modified_count) {
Dennis Groenenc0657e02012-01-31 14:12:38 +01001292 status_line_bold("No write since last change (:%s! overrides)", cmd);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001293 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001294 }
1295 // are there other file to edit
Denys Vlasenko04cecd52010-04-16 22:13:55 -07001296 n = save_argc - optind - 1;
1297 if (*cmd == 'q' && n > 0) {
1298 status_line_bold("%d more file(s) to edit", n);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001299 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001300 }
Denys Vlasenko04cecd52010-04-16 22:13:55 -07001301 if (*cmd == 'n' && n <= 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001302 status_line_bold("No more files to edit");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001303 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001304 }
Dennis Groenenc0657e02012-01-31 14:12:38 +01001305 if (*cmd == 'p') {
1306 // are there previous files to edit
1307 if (optind < 1) {
1308 status_line_bold("No previous files to edit");
1309 goto ret;
1310 }
1311 optind -= 2;
1312 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001313 editing = 0;
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001314 } else if (strncmp(cmd, "read", i) == 0) { // read file into text[]
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001315 int size;
1316
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001317 fn = args;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001318 if (!fn[0]) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001319 status_line_bold("No filename given");
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001320 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001321 }
1322 if (b < 0) { // no addr given- use defaults
1323 q = begin_line(dot); // assume "dot"
1324 }
1325 // read after current line- unless user said ":0r foo"
Ron Yorston70f43202014-11-30 20:39:53 +00001326 if (b != 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001327 q = next_line(q);
Ron Yorston70f43202014-11-30 20:39:53 +00001328 // read after last line
1329 if (q == end-1)
1330 ++q;
1331 }
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001332 { // dance around potentially-reallocated text[]
1333 uintptr_t ofs = q - text;
Ron Yorstone5213ce2014-11-30 20:39:25 +00001334 size = file_insert(fn, q, 0);
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001335 q = text + ofs;
1336 }
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001337 if (size < 0)
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001338 goto ret; // nothing was inserted
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001339 // how many lines in text[]?
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001340 li = count_lines(q, q + size - 1);
Denys Vlasenko778794d2013-01-22 10:13:52 +01001341 status_line("'%s'"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001342 IF_FEATURE_VI_READONLY("%s")
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001343 " %dL, %dC",
1344 fn,
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00001345 IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001346 li, size
1347 );
1348 if (size > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001349 // if the insert is before "dot" then we need to update
1350 if (q <= dot)
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001351 dot += size;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001352 }
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001353 } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args
Denys Vlasenkoe7430862014-04-03 12:47:48 +02001354 if (modified_count && !useforce) {
Dennis Groenenc0657e02012-01-31 14:12:38 +01001355 status_line_bold("No write since last change (:%s! overrides)", cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001356 } else {
1357 // reset the filenames to edit
Dennis Groenenc0657e02012-01-31 14:12:38 +01001358 optind = -1; /* start from 0th file */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001359 editing = 0;
1360 }
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001361# if ENABLE_FEATURE_VI_SET
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001362 } else if (strncmp(cmd, "set", i) == 0) { // set or clear features
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001363# if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkof9234132007-03-21 00:03:42 +00001364 char *argp;
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001365# endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001366 i = 0; // offset into args
Denys Vlasenko605f2642012-06-11 01:53:33 +02001367 // only blank is regarded as args delimiter. What about tab '\t'?
Denis Vlasenkof9234132007-03-21 00:03:42 +00001368 if (!args[0] || strcasecmp(args, "all") == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001369 // print out values of all options
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001370# if ENABLE_FEATURE_VI_SETOPTS
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001371 status_line_bold(
1372 "%sautoindent "
1373 "%sflash "
1374 "%signorecase "
1375 "%sshowmatch "
1376 "tabstop=%u",
1377 autoindent ? "" : "no",
1378 err_method ? "" : "no",
1379 ignorecase ? "" : "no",
1380 showmatch ? "" : "no",
1381 tabstop
1382 );
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001383# endif
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001384 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001385 }
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001386# if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001387 argp = args;
Denis Vlasenkoba2fb712007-04-01 09:39:03 +00001388 while (*argp) {
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001389 if (strncmp(argp, "no", 2) == 0)
Denis Vlasenkof9234132007-03-21 00:03:42 +00001390 i = 2; // ":set noautoindent"
1391 setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001392 setops(argp, "flash " , i, "fl", VI_ERR_METHOD);
Denis Vlasenkof9234132007-03-21 00:03:42 +00001393 setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001394 setops(argp, "showmatch " , i, "sm", VI_SHOWMATCH );
1395 if (strncmp(argp + i, "tabstop=", 8) == 0) {
1396 int t = 0;
1397 sscanf(argp + i+8, "%u", &t);
1398 if (t > 0 && t <= MAX_TABSTOP)
1399 tabstop = t;
Denis Vlasenkof9234132007-03-21 00:03:42 +00001400 }
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001401 argp = skip_non_whitespace(argp);
1402 argp = skip_whitespace(argp);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001403 }
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001404# endif /* FEATURE_VI_SETOPTS */
1405# endif /* FEATURE_VI_SET */
1406
1407# if ENABLE_FEATURE_VI_SEARCH
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001408 } else if (cmd[0] == 's') { // substitute a pattern with a replacement pattern
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001409 char *F, *R, *flags;
1410 size_t len_F, len_R;
1411 int gflag; // global replace flag
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001412# if ENABLE_FEATURE_VI_UNDO
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001413 int dont_chain_first_item = ALLOW_UNDO;
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001414# endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001415
1416 // F points to the "find" pattern
1417 // R points to the "replace" pattern
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001418 // replace the cmd line delimiters "/" with NULs
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001419 c = orig_buf[1]; // what is the delimiter
1420 F = orig_buf + 2; // start of "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001421 R = strchr(F, c); // middle delimiter
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001422 if (!R)
1423 goto colon_s_fail;
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001424 len_F = R - F;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001425 *R++ = '\0'; // terminate "find"
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001426 flags = strchr(R, c);
1427 if (!flags)
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001428 goto colon_s_fail;
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001429 len_R = flags - R;
1430 *flags++ = '\0'; // terminate "replace"
1431 gflag = *flags;
1432
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001433 q = begin_line(q);
1434 if (b < 0) { // maybe :s/foo/bar/
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001435 q = begin_line(dot); // start with cur line
1436 b = count_lines(text, q); // cur line number
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001437 }
1438 if (e < 0)
1439 e = b; // maybe :.s/foo/bar/
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001440
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001441 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001442 char *ls = q; // orig line start
1443 char *found;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001444 vc4:
Denys Vlasenkob7330462018-11-29 14:39:52 +01001445 found = char_search(q, F, (FORWARD << 1) | LIMITED); // search cur line only for "find"
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001446 if (found) {
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001447 uintptr_t bias;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001448 // we found the "find" pattern - delete it
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001449 // For undo support, the first item should not be chained
1450 text_hole_delete(found, found + len_F - 1, dont_chain_first_item);
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001451# if ENABLE_FEATURE_VI_UNDO
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001452 dont_chain_first_item = ALLOW_UNDO_CHAIN;
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001453# endif
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001454 // insert the "replace" patern
1455 bias = string_insert(found, R, ALLOW_UNDO_CHAIN);
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001456 found += bias;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00001457 ls += bias;
1458 /*q += bias; - recalculated anyway */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001459 // check for "global" :s/foo/bar/g
Denys Vlasenko800a9a02012-01-31 14:10:26 +01001460 if (gflag == 'g') {
1461 if ((found + len_R) < end_line(ls)) {
1462 q = found + len_R;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001463 goto vc4; // don't let q move past cur line
1464 }
1465 }
1466 }
1467 q = next_line(ls);
1468 }
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001469# endif /* FEATURE_VI_SEARCH */
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001470 } else if (strncmp(cmd, "version", i) == 0) { // show software version
Denys Vlasenkoeba7fe62017-01-29 19:14:26 +01001471 status_line(BB_VER);
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001472 } else if (strncmp(cmd, "write", i) == 0 // write text to file
1473 || strncmp(cmd, "wq", i) == 0
1474 || strncmp(cmd, "wn", i) == 0
1475 || (cmd[0] == 'x' && !cmd[1])
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001476 ) {
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001477 int size;
1478 //int forced = FALSE;
1479
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001480 // is there a file name to write to?
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001481 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001482 fn = args;
1483 }
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001484# if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001485 if (readonly_mode && !useforce) {
Denys Vlasenko778794d2013-01-22 10:13:52 +01001486 status_line_bold("'%s' is read only", fn);
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001487 goto ret;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001488 }
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001489# endif
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001490 //if (useforce) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001491 // if "fn" is not write-able, chmod u+w
1492 // sprintf(syscmd, "chmod u+w %s", fn);
1493 // system(syscmd);
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001494 // forced = TRUE;
1495 //}
Denys Vlasenkoe88608e2017-03-13 20:50:42 +01001496 if (modified_count != 0 || cmd[0] != 'x') {
1497 size = r - q + 1;
1498 l = file_write(fn, q, r);
1499 } else {
1500 size = 0;
1501 l = 0;
1502 }
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001503 //if (useforce && forced) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001504 // chmod u-w
1505 // sprintf(syscmd, "chmod u-w %s", fn);
1506 // system(syscmd);
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001507 // forced = FALSE;
1508 //}
Paul Fox61e45db2005-10-09 14:43:22 +00001509 if (l < 0) {
1510 if (l == -1)
Denys Vlasenko9e7c0022013-03-15 02:17:29 +01001511 status_line_bold_errno(fn);
Paul Fox61e45db2005-10-09 14:43:22 +00001512 } else {
Denys Vlasenkoe88608e2017-03-13 20:50:42 +01001513 // how many lines written
1514 li = count_lines(q, q + l - 1);
Denys Vlasenko778794d2013-01-22 10:13:52 +01001515 status_line("'%s' %dL, %dC", fn, li, l);
Denys Vlasenkoe88608e2017-03-13 20:50:42 +01001516 if (l == size) {
1517 if (q == text && q + l == end) {
1518 modified_count = 0;
1519 last_modified_count = -1;
1520 }
1521 if (cmd[0] == 'x'
1522 || cmd[1] == 'q' || cmd[1] == 'n'
1523 || cmd[1] == 'Q' || cmd[1] == 'N'
1524 ) {
1525 editing = 0;
1526 }
Paul Fox61e45db2005-10-09 14:43:22 +00001527 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001528 }
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001529# if ENABLE_FEATURE_VI_YANKMARK
Denys Vlasenko6548edd2009-06-15 12:44:11 +02001530 } else if (strncmp(cmd, "yank", i) == 0) { // yank lines
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001531 if (b < 0) { // no addr given- use defaults
1532 q = begin_line(dot); // assume .,. for the range
1533 r = end_line(dot);
1534 }
1535 text_yank(q, r, YDreg);
1536 li = count_lines(q, r);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001537 status_line("Yank %d lines (%d chars) into [%c]",
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001538 li, strlen(reg[YDreg]), what_reg());
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001539# endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001540 } else {
1541 // cmd unknown
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001542 not_implemented(cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001543 }
Denys Vlasenko9f82d0b2010-05-19 01:54:37 +02001544 ret:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001545 dot = bound_dot(dot); // make sure "dot" is valid
1546 return;
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001547# if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001548 colon_s_fail:
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001549 status_line(":s expression missing delimiters");
Denys Vlasenko8825cb62018-06-27 15:11:36 +02001550# endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001551#endif /* FEATURE_VI_COLON */
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02001552}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001553
1554static void Hit_Return(void)
1555{
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00001556 int c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001557
Denis Vlasenkof882f082007-12-23 02:36:51 +00001558 standout_start();
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001559 write1("[Hit return to continue]");
Denis Vlasenkof882f082007-12-23 02:36:51 +00001560 standout_end();
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001561 while ((c = get_one_char()) != '\n' && c != '\r')
1562 continue;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001563 redraw(TRUE); // force redraw all
1564}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001565
Denis Vlasenko91afdf82007-07-17 23:22:49 +00001566static int next_tabstop(int col)
1567{
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001568 return col + ((tabstop - 1) - (col % tabstop));
1569}
1570
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001571//----- Synchronize the cursor to Dot --------------------------
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02001572static NOINLINE void sync_cursor(char *d, int *row, int *col)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001573{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001574 char *beg_cur; // begin and end of "d" line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001575 char *tp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001576 int cnt, ro, co;
1577
1578 beg_cur = begin_line(d); // first char of cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001579
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001580 if (beg_cur < screenbegin) {
Denis Vlasenkof882f082007-12-23 02:36:51 +00001581 // "d" is before top line on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001582 // how many lines do we have to move
1583 cnt = count_lines(beg_cur, screenbegin);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001584 sc1:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001585 screenbegin = beg_cur;
1586 if (cnt > (rows - 1) / 2) {
1587 // we moved too many lines. put "dot" in middle of screen
1588 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1589 screenbegin = prev_line(screenbegin);
1590 }
1591 }
Denis Vlasenkof882f082007-12-23 02:36:51 +00001592 } else {
1593 char *end_scr; // begin and end of screen
1594 end_scr = end_screen(); // last char of screen
1595 if (beg_cur > end_scr) {
1596 // "d" is after bottom line on screen
1597 // how many lines do we have to move
1598 cnt = count_lines(end_scr, beg_cur);
1599 if (cnt > (rows - 1) / 2)
1600 goto sc1; // too many lines
1601 for (ro = 0; ro < cnt - 1; ro++) {
1602 // move screen begin the same amount
1603 screenbegin = next_line(screenbegin);
1604 // now, move the end of screen
1605 end_scr = next_line(end_scr);
1606 end_scr = end_line(end_scr);
1607 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001608 }
1609 }
1610 // "d" is on screen- find out which row
1611 tp = screenbegin;
1612 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1613 if (tp == beg_cur)
1614 break;
1615 tp = next_line(tp);
1616 }
1617
1618 // find out what col "d" is on
1619 co = 0;
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001620 while (tp < d) { // drive "co" to correct column
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001621 if (*tp == '\n') //vda || *tp == '\0')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001622 break;
1623 if (*tp == '\t') {
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001624 // handle tabs like real vi
1625 if (d == tp && cmd_mode) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001626 break;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001627 }
Denis Vlasenkoe3eae0d2008-06-22 16:38:53 +00001628 co = next_tabstop(co);
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001629 } else if ((unsigned char)*tp < ' ' || *tp == 0x7f) {
1630 co++; // display as ^X, use 2 columns
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001631 }
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001632 co++;
1633 tp++;
1634 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001635
1636 // "co" is the column where "dot" is.
1637 // The screen has "columns" columns.
1638 // The currently displayed columns are 0+offset -- columns+ofset
1639 // |-------------------------------------------------------------|
1640 // ^ ^ ^
1641 // offset | |------- columns ----------------|
1642 //
1643 // If "co" is already in this range then we do not have to adjust offset
1644 // but, we do have to subtract the "offset" bias from "co".
1645 // If "co" is outside this range then we have to change "offset".
1646 // If the first char of a line is a tab the cursor will try to stay
1647 // in column 7, but we have to set offset to 0.
1648
1649 if (co < 0 + offset) {
1650 offset = co;
1651 }
1652 if (co >= columns + offset) {
1653 offset = co - columns + 1;
1654 }
1655 // if the first char of the line is a tab, and "dot" is sitting on it
1656 // force offset to 0.
1657 if (d == beg_cur && *d == '\t') {
1658 offset = 0;
1659 }
1660 co -= offset;
1661
1662 *row = ro;
1663 *col = co;
1664}
1665
1666//----- Text Movement Routines ---------------------------------
Denis Vlasenkof882f082007-12-23 02:36:51 +00001667static char *begin_line(char *p) // return pointer to first char cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001668{
Denis Vlasenkof882f082007-12-23 02:36:51 +00001669 if (p > text) {
1670 p = memrchr(text, '\n', p - text);
1671 if (!p)
1672 return text;
1673 return p + 1;
1674 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001675 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001676}
1677
Denis Vlasenkoe3eae0d2008-06-22 16:38:53 +00001678static char *end_line(char *p) // return pointer to NL of cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001679{
Denis Vlasenkof882f082007-12-23 02:36:51 +00001680 if (p < end - 1) {
1681 p = memchr(p, '\n', end - p - 1);
1682 if (!p)
1683 return end - 1;
1684 }
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 *dollar_line(char *p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001689{
Denis Vlasenkof882f082007-12-23 02:36:51 +00001690 p = end_line(p);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001691 // Try to stay off of the Newline
1692 if (*p == '\n' && (p - begin_line(p)) > 0)
1693 p--;
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 *prev_line(char *p) // return pointer first char prev line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001698{
Maninder Singh97c64912015-05-25 13:46:36 +02001699 p = begin_line(p); // goto beginning of cur line
Denis Vlasenkoe3eae0d2008-06-22 16:38:53 +00001700 if (p > text && p[-1] == '\n')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001701 p--; // step to prev line
Maninder Singh97c64912015-05-25 13:46:36 +02001702 p = begin_line(p); // goto beginning of prev line
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001703 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001704}
1705
Denis Vlasenkof882f082007-12-23 02:36:51 +00001706static char *next_line(char *p) // return pointer first char next line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001707{
1708 p = end_line(p);
Denis Vlasenkoe3eae0d2008-06-22 16:38:53 +00001709 if (p < end - 1 && *p == '\n')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001710 p++; // step to next line
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001711 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001712}
1713
1714//----- Text Information Routines ------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001715static char *end_screen(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001716{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001717 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001718 int cnt;
1719
1720 // find new bottom line
1721 q = screenbegin;
1722 for (cnt = 0; cnt < rows - 2; cnt++)
1723 q = next_line(q);
1724 q = end_line(q);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001725 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001726}
1727
Denis Vlasenkof882f082007-12-23 02:36:51 +00001728// count line from start to stop
1729static int count_lines(char *start, char *stop)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001730{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001731 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001732 int cnt;
1733
Denis Vlasenkof882f082007-12-23 02:36:51 +00001734 if (stop < start) { // start and stop are backwards- reverse them
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001735 q = start;
1736 start = stop;
1737 stop = q;
1738 }
1739 cnt = 0;
Denis Vlasenkof882f082007-12-23 02:36:51 +00001740 stop = end_line(stop);
1741 while (start <= stop && start <= end - 1) {
1742 start = end_line(start);
1743 if (*start == '\n')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001744 cnt++;
Denis Vlasenkof882f082007-12-23 02:36:51 +00001745 start++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001746 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001747 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001748}
1749
Maninder Singh97c64912015-05-25 13:46:36 +02001750static char *find_line(int li) // find beginning of line #li
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001751{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001752 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001753
1754 for (q = text; li > 1; li--) {
1755 q = next_line(q);
1756 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001757 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001758}
1759
1760//----- Dot Movement Routines ----------------------------------
1761static void dot_left(void)
1762{
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001763 undo_queue_commit();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001764 if (dot > text && dot[-1] != '\n')
1765 dot--;
1766}
1767
1768static void dot_right(void)
1769{
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001770 undo_queue_commit();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001771 if (dot < end - 1 && *dot != '\n')
1772 dot++;
1773}
1774
1775static void dot_begin(void)
1776{
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001777 undo_queue_commit();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001778 dot = begin_line(dot); // return pointer to first char cur line
1779}
1780
1781static void dot_end(void)
1782{
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001783 undo_queue_commit();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001784 dot = end_line(dot); // return pointer to last char cur line
1785}
1786
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001787static char *move_to_col(char *p, int l)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001788{
1789 int co;
1790
1791 p = begin_line(p);
1792 co = 0;
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001793 while (co < l && p < end) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00001794 if (*p == '\n') //vda || *p == '\0')
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001795 break;
1796 if (*p == '\t') {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00001797 co = next_tabstop(co);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001798 } else if (*p < ' ' || *p == 127) {
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001799 co++; // display as ^X, use 2 columns
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001800 }
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00001801 co++;
1802 p++;
1803 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001804 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001805}
1806
1807static void dot_next(void)
1808{
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001809 undo_queue_commit();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001810 dot = next_line(dot);
1811}
1812
1813static void dot_prev(void)
1814{
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001815 undo_queue_commit();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001816 dot = prev_line(dot);
1817}
1818
1819static void dot_scroll(int cnt, int dir)
1820{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001821 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001822
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02001823 undo_queue_commit();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001824 for (; cnt > 0; cnt--) {
1825 if (dir < 0) {
1826 // scroll Backwards
Denis Vlasenkof882f082007-12-23 02:36:51 +00001827 // ctrl-Y scroll up one line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001828 screenbegin = prev_line(screenbegin);
1829 } else {
1830 // scroll Forwards
Denis Vlasenkof882f082007-12-23 02:36:51 +00001831 // ctrl-E scroll down one line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001832 screenbegin = next_line(screenbegin);
1833 }
1834 }
1835 // make sure "dot" stays on the screen so we dont scroll off
1836 if (dot < screenbegin)
1837 dot = screenbegin;
1838 q = end_screen(); // find new bottom line
1839 if (dot > q)
1840 dot = begin_line(q); // is dot is below bottom line?
1841 dot_skip_over_ws();
1842}
1843
1844static void dot_skip_over_ws(void)
1845{
1846 // skip WS
1847 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1848 dot++;
1849}
1850
Denis Vlasenkof882f082007-12-23 02:36:51 +00001851static char *bound_dot(char *p) // make sure text[0] <= P < "end"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001852{
1853 if (p >= end && end > text) {
1854 p = end - 1;
Denys Vlasenko05399fc2014-09-15 17:06:10 +02001855 indicate_error();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001856 }
1857 if (p < text) {
1858 p = text;
Denys Vlasenko05399fc2014-09-15 17:06:10 +02001859 indicate_error();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001860 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001861 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001862}
1863
1864//----- Helper Utility Routines --------------------------------
1865
1866//----------------------------------------------------------------
1867//----- Char Routines --------------------------------------------
1868/* Chars that are part of a word-
1869 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1870 * Chars that are Not part of a word (stoppers)
1871 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1872 * Chars that are WhiteSpace
1873 * TAB NEWLINE VT FF RETURN SPACE
1874 * DO NOT COUNT NEWLINE AS WHITESPACE
1875 */
1876
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001877static char *new_screen(int ro, int co)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001878{
1879 int li;
1880
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001881 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001882 screensize = ro * co + 8;
Denis Vlasenkob95636c2006-12-19 23:36:04 +00001883 screen = xmalloc(screensize);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001884 // initialize the new screen. assume this will be a empty file.
1885 screen_erase();
Eric Andersenaff114c2004-04-14 17:51:38 +00001886 // non-existent text[] lines start with a tilde (~).
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001887 for (li = 1; li < ro - 1; li++) {
1888 screen[(li * co) + 0] = '~';
1889 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001890 return screen;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001891}
1892
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001893#if ENABLE_FEATURE_VI_SEARCH
Walter Harmsb9ba5802011-06-27 02:59:37 +02001894
1895# if ENABLE_FEATURE_VI_REGEX_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001896
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001897// search for pattern starting at p
Denys Vlasenkob7330462018-11-29 14:39:52 +01001898static char *char_search(char *p, const char *pat, int dir_and_range)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001899{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001900 struct re_pattern_buffer preg;
Denys Vlasenko264f3732013-04-21 15:51:41 +02001901 const char *err;
1902 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001903 int i;
Walter Harmsb9ba5802011-06-27 02:59:37 +02001904 int size;
Denys Vlasenkob7330462018-11-29 14:39:52 +01001905 int range;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001906
1907 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
Denys Vlasenko264f3732013-04-21 15:51:41 +02001908 if (ignorecase)
1909 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED | RE_ICASE;
1910
1911 memset(&preg, 0, sizeof(preg));
1912 err = re_compile_pattern(pat, strlen(pat), &preg);
1913 if (err != NULL) {
1914 status_line_bold("bad search pattern '%s': %s", pat, err);
1915 return p;
1916 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001917
Denys Vlasenkob7330462018-11-29 14:39:52 +01001918 range = (dir_and_range & 1);
Denys Vlasenko836d0a72018-11-29 14:19:57 +01001919 q = end - 1; // if FULL
1920 if (range == LIMITED)
1921 q = next_line(p);
Denys Vlasenkob7330462018-11-29 14:39:52 +01001922 if (dir_and_range < 0) { // BACK?
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001923 q = text;
Denys Vlasenko836d0a72018-11-29 14:19:57 +01001924 if (range == LIMITED)
1925 q = prev_line(p);
1926 }
1927
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001928 // RANGE could be negative if we are searching backwards
1929 range = q - p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001930 q = p;
Denys Vlasenko264f3732013-04-21 15:51:41 +02001931 size = range;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001932 if (range < 0) {
Denys Vlasenko264f3732013-04-21 15:51:41 +02001933 size = -size;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001934 q = p - size;
1935 if (q < text)
1936 q = text;
1937 }
1938 // search for the compiled pattern, preg, in p[]
Denys Vlasenko264f3732013-04-21 15:51:41 +02001939 // range < 0: search backward
1940 // range > 0: search forward
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001941 // 0 < start < size
Denys Vlasenko264f3732013-04-21 15:51:41 +02001942 // re_search() < 0: not found or error
1943 // re_search() >= 0: index of found pattern
1944 // struct pattern char int int int struct reg
1945 // re_search(*pattern_buffer, *string, size, start, range, *regs)
1946 i = re_search(&preg, q, size, /*start:*/ 0, range, /*struct re_registers*:*/ NULL);
1947 regfree(&preg);
1948 if (i < 0)
1949 return NULL;
Denys Vlasenkob7330462018-11-29 14:39:52 +01001950 if (dir_and_range > 0) // FORWARD?
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001951 p = p + i;
Denys Vlasenko264f3732013-04-21 15:51:41 +02001952 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001953 p = p - i;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001954 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001955}
Walter Harmsb9ba5802011-06-27 02:59:37 +02001956
1957# else
1958
1959# if ENABLE_FEATURE_VI_SETOPTS
1960static int mycmp(const char *s1, const char *s2, int len)
1961{
1962 if (ignorecase) {
1963 return strncasecmp(s1, s2, len);
1964 }
1965 return strncmp(s1, s2, len);
1966}
1967# else
1968# define mycmp strncmp
1969# endif
1970
Denys Vlasenkob7330462018-11-29 14:39:52 +01001971static char *char_search(char *p, const char *pat, int dir_and_range)
Walter Harmsb9ba5802011-06-27 02:59:37 +02001972{
1973 char *start, *stop;
1974 int len;
Denys Vlasenkob7330462018-11-29 14:39:52 +01001975 int range;
Walter Harmsb9ba5802011-06-27 02:59:37 +02001976
1977 len = strlen(pat);
Denys Vlasenkob7330462018-11-29 14:39:52 +01001978 range = (dir_and_range & 1);
1979 if (dir_and_range > 0) { //FORWARD?
Denys Vlasenko264f3732013-04-21 15:51:41 +02001980 stop = end - 1; // assume range is p..end-1
Walter Harmsb9ba5802011-06-27 02:59:37 +02001981 if (range == LIMITED)
1982 stop = next_line(p); // range is to next line
1983 for (start = p; start < stop; start++) {
1984 if (mycmp(start, pat, len) == 0) {
1985 return start;
1986 }
1987 }
Denys Vlasenkob7330462018-11-29 14:39:52 +01001988 } else { //BACK
Denys Vlasenko264f3732013-04-21 15:51:41 +02001989 stop = text; // assume range is text..p
Walter Harmsb9ba5802011-06-27 02:59:37 +02001990 if (range == LIMITED)
1991 stop = prev_line(p); // range is to prev line
1992 for (start = p - len; start >= stop; start--) {
1993 if (mycmp(start, pat, len) == 0) {
1994 return start;
1995 }
1996 }
1997 }
1998 // pattern not found
1999 return NULL;
2000}
2001
2002# endif
2003
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002004#endif /* FEATURE_VI_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002005
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002006static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002007{
2008 if (c == 22) { // Is this an ctrl-V?
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002009 p += stupid_insert(p, '^'); // use ^ to indicate literal next
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002010 refresh(FALSE); // show the ^
2011 c = get_one_char();
2012 *p = c;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002013#if ENABLE_FEATURE_VI_UNDO
2014 switch (undo) {
2015 case ALLOW_UNDO:
2016 undo_push(p, 1, UNDO_INS);
2017 break;
2018 case ALLOW_UNDO_CHAIN:
2019 undo_push(p, 1, UNDO_INS_CHAIN);
2020 break;
2021# if ENABLE_FEATURE_VI_UNDO_QUEUE
2022 case ALLOW_UNDO_QUEUED:
2023 undo_push(p, 1, UNDO_INS_QUEUED);
2024 break;
2025# endif
2026 }
2027#else
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002028 modified_count++;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002029#endif /* ENABLE_FEATURE_VI_UNDO */
2030 p++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002031 } else if (c == 27) { // Is this an ESC?
2032 cmd_mode = 0;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002033 undo_queue_commit();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002034 cmdcnt = 0;
2035 end_cmd_q(); // stop adding to q
Paul Fox8552aec2005-09-16 12:20:05 +00002036 last_status_cksum = 0; // force status update
Denis Vlasenkodefc1ea2008-06-27 02:52:20 +00002037 if ((p[-1] != '\n') && (dot > text)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002038 p--;
2039 }
Paul Foxd13b90b2005-07-18 22:17:25 +00002040 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
Denys Vlasenko49acc1a2015-03-12 21:15:34 +01002041 if (p > text) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002042 p--;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002043 p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED); // shrink buffer 1 char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002044 }
2045 } else {
2046 // insert a char into text[]
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002047 if (c == 13)
2048 c = '\n'; // translate \r to \n
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002049#if ENABLE_FEATURE_VI_UNDO
2050# if ENABLE_FEATURE_VI_UNDO_QUEUE
2051 if (c == '\n')
2052 undo_queue_commit();
2053# endif
2054 switch (undo) {
2055 case ALLOW_UNDO:
2056 undo_push(p, 1, UNDO_INS);
2057 break;
2058 case ALLOW_UNDO_CHAIN:
2059 undo_push(p, 1, UNDO_INS_CHAIN);
2060 break;
2061# if ENABLE_FEATURE_VI_UNDO_QUEUE
2062 case ALLOW_UNDO_QUEUED:
2063 undo_push(p, 1, UNDO_INS_QUEUED);
2064 break;
2065# endif
2066 }
2067#else
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002068 modified_count++;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002069#endif /* ENABLE_FEATURE_VI_UNDO */
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002070 p += 1 + stupid_insert(p, c); // insert the char
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002071#if ENABLE_FEATURE_VI_SETOPTS
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002072 if (showmatch && strchr(")]}", c) != NULL) {
2073 showmatching(p - 1);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002074 }
2075 if (autoindent && c == '\n') { // auto indent the new line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002076 char *q;
Denis Vlasenko00d84172008-11-24 07:34:42 +00002077 size_t len;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002078 q = prev_line(p); // use prev line as template
Denis Vlasenko00d84172008-11-24 07:34:42 +00002079 len = strspn(q, " \t"); // space or tab
2080 if (len) {
Denis Vlasenko3266aa92009-04-01 11:24:04 +00002081 uintptr_t bias;
Denis Vlasenko00d84172008-11-24 07:34:42 +00002082 bias = text_hole_make(p, len);
2083 p += bias;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002084 q += bias;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002085#if ENABLE_FEATURE_VI_UNDO
2086 undo_push(p, len, UNDO_INS);
2087#endif
Denis Vlasenko00d84172008-11-24 07:34:42 +00002088 memcpy(p, q, len);
Denis Vlasenko3266aa92009-04-01 11:24:04 +00002089 p += len;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002090 }
2091 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002092#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002093 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002094 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002095}
2096
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002097// might reallocate text[]! use p += stupid_insert(p, ...),
2098// and be careful to not use pointers into potentially freed text[]!
2099static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002100{
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002101 uintptr_t bias;
2102 bias = text_hole_make(p, 1);
2103 p += bias;
Denis Vlasenkob1759462008-06-20 20:20:54 +00002104 *p = c;
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002105 return bias;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002106}
2107
Denis Vlasenko33875382008-06-21 20:31:50 +00002108static int find_range(char **start, char **stop, char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002109{
Paul Foxc51fc7b2008-03-06 01:34:23 +00002110 char *save_dot, *p, *q, *t;
2111 int cnt, multiline = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002112
2113 save_dot = dot;
2114 p = q = dot;
2115
2116 if (strchr("cdy><", c)) {
2117 // these cmds operate on whole lines
2118 p = q = begin_line(p);
2119 for (cnt = 1; cnt < cmdcnt; cnt++) {
2120 q = next_line(q);
2121 }
2122 q = end_line(q);
Paul Foxc51fc7b2008-03-06 01:34:23 +00002123 } else if (strchr("^%$0bBeEfth\b\177", c)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002124 // These cmds operate on char positions
2125 do_cmd(c); // execute movement cmd
2126 q = dot;
2127 } else if (strchr("wW", c)) {
2128 do_cmd(c); // execute movement cmd
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002129 // if we are at the next word's first char
2130 // step back one char
2131 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00002132 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002133 || (ispunct(dot[-1]) && !ispunct(dot[0]))
2134 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
2135 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002136 if (dot > text && *dot == '\n')
2137 dot--; // stay off NL
2138 q = dot;
2139 } else if (strchr("H-k{", c)) {
2140 // these operate on multi-lines backwards
2141 q = end_line(dot); // find NL
2142 do_cmd(c); // execute movement cmd
2143 dot_begin();
2144 p = dot;
2145 } else if (strchr("L+j}\r\n", c)) {
2146 // these operate on multi-lines forwards
2147 p = begin_line(dot);
2148 do_cmd(c); // execute movement cmd
2149 dot_end(); // find NL
2150 q = dot;
2151 } else {
Denys Vlasenko6830ade2013-01-15 13:58:01 +01002152 // nothing -- this causes any other values of c to
2153 // represent the one-character range under the
2154 // cursor. this is correct for ' ' and 'l', but
2155 // perhaps no others.
2156 //
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002157 }
Paul Foxc51fc7b2008-03-06 01:34:23 +00002158 if (q < p) {
2159 t = q;
2160 q = p;
2161 p = t;
2162 }
2163
Denis Vlasenko42cc3042008-03-24 02:05:58 +00002164 // backward char movements don't include start position
Paul Foxc51fc7b2008-03-06 01:34:23 +00002165 if (q > p && strchr("^0bBh\b\177", c)) q--;
2166
2167 multiline = 0;
2168 for (t = p; t <= q; t++) {
2169 if (*t == '\n') {
2170 multiline = 1;
2171 break;
2172 }
2173 }
2174
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002175 *start = p;
2176 *stop = q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002177 dot = save_dot;
Paul Foxc51fc7b2008-03-06 01:34:23 +00002178 return multiline;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002179}
2180
Denis Vlasenko33875382008-06-21 20:31:50 +00002181static int st_test(char *p, int type, int dir, char *tested)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002182{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002183 char c, c0, ci;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002184 int test, inc;
2185
2186 inc = dir;
2187 c = c0 = p[0];
2188 ci = p[inc];
2189 test = 0;
2190
2191 if (type == S_BEFORE_WS) {
2192 c = ci;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02002193 test = (!isspace(c) || c == '\n');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002194 }
2195 if (type == S_TO_WS) {
2196 c = c0;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02002197 test = (!isspace(c) || c == '\n');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002198 }
2199 if (type == S_OVER_WS) {
2200 c = c0;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02002201 test = isspace(c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002202 }
2203 if (type == S_END_PUNCT) {
2204 c = ci;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02002205 test = ispunct(c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002206 }
2207 if (type == S_END_ALNUM) {
2208 c = ci;
Denys Vlasenkoc0dab372009-10-22 22:28:08 +02002209 test = (isalnum(c) || c == '_');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002210 }
2211 *tested = c;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002212 return test;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002213}
2214
Denis Vlasenko33875382008-06-21 20:31:50 +00002215static char *skip_thing(char *p, int linecnt, int dir, int type)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002216{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002217 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002218
2219 while (st_test(p, type, dir, &c)) {
2220 // make sure we limit search to correct number of lines
2221 if (c == '\n' && --linecnt < 1)
2222 break;
2223 if (dir >= 0 && p >= end - 1)
2224 break;
2225 if (dir < 0 && p <= text)
2226 break;
2227 p += dir; // move to next char
2228 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002229 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002230}
2231
2232// find matching char of pair () [] {}
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002233// will crash if c is not one of these
Denis Vlasenko33875382008-06-21 20:31:50 +00002234static char *find_pair(char *p, const char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002235{
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002236 const char *braces = "()[]{}";
2237 char match;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002238 int dir, level;
2239
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002240 dir = strchr(braces, c) - braces;
2241 dir ^= 1;
2242 match = braces[dir];
2243 dir = ((dir & 1) << 1) - 1; /* 1 for ([{, -1 for )\} */
2244
2245 // look for match, count levels of pairs (( ))
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002246 level = 1;
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002247 for (;;) {
2248 p += dir;
2249 if (p < text || p >= end)
2250 return NULL;
2251 if (*p == c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002252 level++; // increase pair levels
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002253 if (*p == match) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002254 level--; // reduce pair level
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002255 if (level == 0)
2256 return p; // found matching pair
2257 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002258 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002259}
2260
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002261#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002262// show the matching char of a pair, () [] {}
Denis Vlasenkof882f082007-12-23 02:36:51 +00002263static void showmatching(char *p)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002264{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002265 char *q, *save_dot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002266
2267 // we found half of a pair
2268 q = find_pair(p, *p); // get loc of matching char
2269 if (q == NULL) {
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002270 indicate_error(); // no matching char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002271 } else {
2272 // "q" now points to matching pair
2273 save_dot = dot; // remember where we are
2274 dot = q; // go to new loc
2275 refresh(FALSE); // let the user see it
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002276 mysleep(40); // give user some time
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002277 dot = save_dot; // go back to old loc
2278 refresh(FALSE);
2279 }
2280}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002281#endif /* FEATURE_VI_SETOPTS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002282
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002283#if ENABLE_FEATURE_VI_UNDO
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002284static void flush_undo_data(void)
2285{
2286 struct undo_object *undo_entry;
2287
2288 while (undo_stack_tail) {
2289 undo_entry = undo_stack_tail;
2290 undo_stack_tail = undo_entry->prev;
2291 free(undo_entry);
2292 }
2293}
2294
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002295// Undo functions and hooks added by Jody Bruchon (jody@jodybruchon.com)
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002296static void undo_push(char *src, unsigned int length, uint8_t u_type) // Add to the undo stack
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002297{
Denys Vlasenko2c512022014-04-03 01:45:05 +02002298 struct undo_object *undo_entry;
2299
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002300 // "u_type" values
2301 // UNDO_INS: insertion, undo will remove from buffer
2302 // UNDO_DEL: deleted text, undo will restore to buffer
2303 // UNDO_{INS,DEL}_CHAIN: Same as above but also calls undo_pop() when complete
2304 // The CHAIN operations are for handling multiple operations that the user
2305 // performs with a single action, i.e. REPLACE mode or find-and-replace commands
2306 // UNDO_{INS,DEL}_QUEUED: If queuing feature is enabled, allow use of the queue
2307 // for the INS/DEL operation. The raw values should be equal to the values of
2308 // UNDO_{INS,DEL} ORed with UNDO_QUEUED_FLAG
2309
2310#if ENABLE_FEATURE_VI_UNDO_QUEUE
2311 // This undo queuing functionality groups multiple character typing or backspaces
2312 // into a single large undo object. This greatly reduces calls to malloc() for
2313 // single-character operations while typing and has the side benefit of letting
2314 // an undo operation remove chunks of text rather than a single character.
2315 switch (u_type) {
2316 case UNDO_EMPTY: // Just in case this ever happens...
2317 return;
2318 case UNDO_DEL_QUEUED:
2319 if (length != 1)
2320 return; // Only queue single characters
2321 switch (undo_queue_state) {
2322 case UNDO_EMPTY:
2323 undo_queue_state = UNDO_DEL;
2324 case UNDO_DEL:
2325 undo_queue_spos = src;
2326 undo_q++;
Denys Vlasenko2c512022014-04-03 01:45:05 +02002327 undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q] = *src;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002328 // If queue is full, dump it into an object
2329 if (undo_q == CONFIG_FEATURE_VI_UNDO_QUEUE_MAX)
2330 undo_queue_commit();
2331 return;
2332 case UNDO_INS:
2333 // Switch from storing inserted text to deleted text
2334 undo_queue_commit();
2335 undo_push(src, length, UNDO_DEL_QUEUED);
2336 return;
2337 }
2338 break;
2339 case UNDO_INS_QUEUED:
2340 if (length != 1)
2341 return;
2342 switch (undo_queue_state) {
2343 case UNDO_EMPTY:
2344 undo_queue_state = UNDO_INS;
2345 undo_queue_spos = src;
2346 case UNDO_INS:
2347 undo_q++; // Don't need to save any data for insertions
2348 if (undo_q == CONFIG_FEATURE_VI_UNDO_QUEUE_MAX)
2349 undo_queue_commit();
2350 return;
2351 case UNDO_DEL:
2352 // Switch from storing deleted text to inserted text
2353 undo_queue_commit();
2354 undo_push(src, length, UNDO_INS_QUEUED);
2355 return;
2356 }
2357 break;
2358 }
2359#else
2360 // If undo queuing is disabled, ignore the queuing flag entirely
2361 u_type = u_type & ~UNDO_QUEUED_FLAG;
2362#endif
2363
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002364 // Allocate a new undo object
2365 if (u_type == UNDO_DEL || u_type == UNDO_DEL_CHAIN) {
2366 // For UNDO_DEL objects, save deleted text
Ron Yorstond08206d2018-12-03 10:07:58 +01002367 if ((text + length) == end)
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002368 length--;
2369 // If this deletion empties text[], strip the newline. When the buffer becomes
2370 // zero-length, a newline is added back, which requires this to compensate.
2371 undo_entry = xzalloc(offsetof(struct undo_object, undo_text) + length);
2372 memcpy(undo_entry->undo_text, src, length);
2373 } else {
2374 undo_entry = xzalloc(sizeof(*undo_entry));
2375 }
2376 undo_entry->length = length;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002377#if ENABLE_FEATURE_VI_UNDO_QUEUE
2378 if ((u_type & UNDO_USE_SPOS) != 0) {
Denys Vlasenko2c512022014-04-03 01:45:05 +02002379 undo_entry->start = undo_queue_spos - text; // use start position from queue
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002380 } else {
Denys Vlasenko2c512022014-04-03 01:45:05 +02002381 undo_entry->start = src - text; // use offset from start of text buffer
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002382 }
2383 u_type = (u_type & ~UNDO_USE_SPOS);
2384#else
Denys Vlasenko2c512022014-04-03 01:45:05 +02002385 undo_entry->start = src - text;
2386#endif
Denys Vlasenko2c512022014-04-03 01:45:05 +02002387 undo_entry->u_type = u_type;
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002388
2389 // Push it on undo stack
2390 undo_entry->prev = undo_stack_tail;
2391 undo_stack_tail = undo_entry;
2392 modified_count++;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002393}
2394
2395static void undo_pop(void) // Undo the last operation
2396{
Denys Vlasenko2c512022014-04-03 01:45:05 +02002397 int repeat;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002398 char *u_start, *u_end;
Denys Vlasenko2c512022014-04-03 01:45:05 +02002399 struct undo_object *undo_entry;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002400
2401 // Commit pending undo queue before popping (should be unnecessary)
2402 undo_queue_commit();
2403
Denys Vlasenko2c512022014-04-03 01:45:05 +02002404 undo_entry = undo_stack_tail;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002405 // Check for an empty undo stack
Denys Vlasenko2c512022014-04-03 01:45:05 +02002406 if (!undo_entry) {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002407 status_line("Already at oldest change");
2408 return;
2409 }
2410
Denys Vlasenko2c512022014-04-03 01:45:05 +02002411 switch (undo_entry->u_type) {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002412 case UNDO_DEL:
2413 case UNDO_DEL_CHAIN:
2414 // make hole and put in text that was deleted; deallocate text
Denys Vlasenko2c512022014-04-03 01:45:05 +02002415 u_start = text + undo_entry->start;
2416 text_hole_make(u_start, undo_entry->length);
2417 memcpy(u_start, undo_entry->undo_text, undo_entry->length);
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002418 status_line("Undo [%d] %s %d chars at position %d",
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002419 modified_count, "restored",
Denys Vlasenko2c512022014-04-03 01:45:05 +02002420 undo_entry->length, undo_entry->start
2421 );
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002422 break;
2423 case UNDO_INS:
2424 case UNDO_INS_CHAIN:
2425 // delete what was inserted
Denys Vlasenko2c512022014-04-03 01:45:05 +02002426 u_start = undo_entry->start + text;
2427 u_end = u_start - 1 + undo_entry->length;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002428 text_hole_delete(u_start, u_end, NO_UNDO);
2429 status_line("Undo [%d] %s %d chars at position %d",
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002430 modified_count, "deleted",
Denys Vlasenko2c512022014-04-03 01:45:05 +02002431 undo_entry->length, undo_entry->start
2432 );
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002433 break;
2434 }
Denys Vlasenko2c512022014-04-03 01:45:05 +02002435 repeat = 0;
2436 switch (undo_entry->u_type) {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002437 // If this is the end of a chain, lower modification count and refresh display
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002438 case UNDO_DEL:
2439 case UNDO_INS:
Denys Vlasenko2c512022014-04-03 01:45:05 +02002440 dot = (text + undo_entry->start);
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002441 refresh(FALSE);
2442 break;
2443 case UNDO_DEL_CHAIN:
2444 case UNDO_INS_CHAIN:
2445 repeat = 1;
2446 break;
2447 }
2448 // Deallocate the undo object we just processed
Denys Vlasenko2c512022014-04-03 01:45:05 +02002449 undo_stack_tail = undo_entry->prev;
2450 free(undo_entry);
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002451 modified_count--;
Denys Vlasenko2c512022014-04-03 01:45:05 +02002452 // For chained operations, continue popping all the way down the chain.
2453 if (repeat) {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002454 undo_pop(); // Follow the undo chain if one exists
2455 }
2456}
2457
2458#if ENABLE_FEATURE_VI_UNDO_QUEUE
2459static void undo_queue_commit(void) // Flush any queued objects to the undo stack
2460{
2461 // Pushes the queue object onto the undo stack
2462 if (undo_q > 0) {
2463 // Deleted character undo events grow from the end
Denys Vlasenko2c512022014-04-03 01:45:05 +02002464 undo_push(undo_queue + CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q,
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002465 undo_q,
2466 (undo_queue_state | UNDO_USE_SPOS)
2467 );
2468 undo_queue_state = UNDO_EMPTY;
2469 undo_q = 0;
2470 }
2471}
2472#endif
2473
2474#endif /* ENABLE_FEATURE_VI_UNDO */
2475
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002476// open a hole in text[]
2477// might reallocate text[]! use p += text_hole_make(p, ...),
2478// and be careful to not use pointers into potentially freed text[]!
2479static uintptr_t text_hole_make(char *p, int size) // at "p", make a 'size' byte hole
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002480{
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002481 uintptr_t bias = 0;
2482
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002483 if (size <= 0)
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002484 return bias;
Denis Vlasenkob1759462008-06-20 20:20:54 +00002485 end += size; // adjust the new END
2486 if (end >= (text + text_size)) {
2487 char *new_text;
2488 text_size += end - (text + text_size) + 10240;
2489 new_text = xrealloc(text, text_size);
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002490 bias = (new_text - text);
2491 screenbegin += bias;
2492 dot += bias;
2493 end += bias;
2494 p += bias;
Denys Vlasenko800a9a02012-01-31 14:10:26 +01002495#if ENABLE_FEATURE_VI_YANKMARK
2496 {
2497 int i;
2498 for (i = 0; i < ARRAY_SIZE(mark); i++)
2499 if (mark[i])
2500 mark[i] += bias;
2501 }
2502#endif
Denis Vlasenkob1759462008-06-20 20:20:54 +00002503 text = new_text;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002504 }
Denis Vlasenkod6995442008-06-27 04:06:13 +00002505 memmove(p + size, p, end - size - p);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002506 memset(p, ' ', size); // clear new hole
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002507 return bias;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002508}
2509
2510// close a hole in text[]
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002511// "undo" value indicates if this operation should be undo-able
2512static char *text_hole_delete(char *p, char *q, int undo) // delete "p" through "q", inclusive
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002513{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002514 char *src, *dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002515 int cnt, hole_size;
2516
2517 // move forwards, from beginning
2518 // assume p <= q
2519 src = q + 1;
2520 dest = p;
2521 if (q < p) { // they are backward- swap them
2522 src = p + 1;
2523 dest = q;
2524 }
2525 hole_size = q - p + 1;
2526 cnt = end - src;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002527#if ENABLE_FEATURE_VI_UNDO
2528 switch (undo) {
2529 case NO_UNDO:
2530 break;
2531 case ALLOW_UNDO:
2532 undo_push(p, hole_size, UNDO_DEL);
2533 break;
2534 case ALLOW_UNDO_CHAIN:
2535 undo_push(p, hole_size, UNDO_DEL_CHAIN);
2536 break;
2537# if ENABLE_FEATURE_VI_UNDO_QUEUE
2538 case ALLOW_UNDO_QUEUED:
2539 undo_push(p, hole_size, UNDO_DEL_QUEUED);
2540 break;
2541# endif
2542 }
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002543 modified_count--;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002544#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002545 if (src < text || src > end)
2546 goto thd0;
2547 if (dest < text || dest >= end)
2548 goto thd0;
Denys Vlasenkoe7430862014-04-03 12:47:48 +02002549 modified_count++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002550 if (src >= end)
2551 goto thd_atend; // just delete the end of the buffer
Denis Vlasenkob1759462008-06-20 20:20:54 +00002552 memmove(dest, src, cnt);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002553 thd_atend:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002554 end = end - hole_size; // adjust the new END
2555 if (dest >= end)
2556 dest = end - 1; // make sure dest in below end-1
2557 if (end <= text)
2558 dest = end = text; // keep pointers valid
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002559 thd0:
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002560 return dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002561}
2562
2563// copy text into register, then delete text.
2564// if dist <= 0, do not include, or go past, a NewLine
2565//
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002566static char *yank_delete(char *start, char *stop, int dist, int yf, int undo)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002567{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002568 char *p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002569
2570 // make sure start <= stop
2571 if (start > stop) {
2572 // they are backwards, reverse them
2573 p = start;
2574 start = stop;
2575 stop = p;
2576 }
2577 if (dist <= 0) {
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +00002578 // we cannot cross NL boundaries
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002579 p = start;
2580 if (*p == '\n')
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002581 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002582 // dont go past a NewLine
2583 for (; p + 1 <= stop; p++) {
2584 if (p[1] == '\n') {
2585 stop = p; // "stop" just before NewLine
2586 break;
2587 }
2588 }
2589 }
2590 p = start;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002591#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002592 text_yank(start, stop, YDreg);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002593#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002594 if (yf == YANKDEL) {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002595 p = text_hole_delete(start, stop, undo);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002596 } // delete lines
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002597 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002598}
2599
2600static void show_help(void)
2601{
2602 puts("These features are available:"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002603#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002604 "\n\tPattern searches with / and ?"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002605#endif
2606#if ENABLE_FEATURE_VI_DOT_CMD
Denys Vlasenko605f2642012-06-11 01:53:33 +02002607 "\n\tLast command repeat with ."
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002608#endif
2609#if ENABLE_FEATURE_VI_YANKMARK
Bernhard Reutner-Fischerd24d5c82007-10-01 18:04:42 +00002610 "\n\tLine marking with 'x"
2611 "\n\tNamed buffers with \"x"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002612#endif
2613#if ENABLE_FEATURE_VI_READONLY
Walter Harmsb9ba5802011-06-27 02:59:37 +02002614 //not implemented: "\n\tReadonly if vi is called as \"view\""
2615 //redundant: usage text says this too: "\n\tReadonly with -R command line arg"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002616#endif
2617#if ENABLE_FEATURE_VI_SET
Denys Vlasenko605f2642012-06-11 01:53:33 +02002618 "\n\tSome colon mode commands with :"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002619#endif
2620#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002621 "\n\tSettable options with \":set\""
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002622#endif
2623#if ENABLE_FEATURE_VI_USE_SIGNALS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002624 "\n\tSignal catching- ^C"
2625 "\n\tJob suspend and resume with ^Z"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002626#endif
2627#if ENABLE_FEATURE_VI_WIN_RESIZE
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002628 "\n\tAdapt to window re-sizes"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002629#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002630 );
2631}
2632
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002633#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002634static void start_new_cmd_q(char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002635{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002636 // get buffer for new cmd
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002637 // if there is a current cmd count put it in the buffer first
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002638 if (cmdcnt > 0) {
Paul Foxc51fc7b2008-03-06 01:34:23 +00002639 lmc_len = sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002640 } else { // just save char c onto queue
Paul Foxd957b952005-11-28 18:07:53 +00002641 last_modifying_cmd[0] = c;
Paul Foxc51fc7b2008-03-06 01:34:23 +00002642 lmc_len = 1;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002643 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002644 adding2q = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002645}
2646
2647static void end_cmd_q(void)
2648{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002649#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002650 YDreg = 26; // go back to default Yank/Delete reg
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002651#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002652 adding2q = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002653}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002654#endif /* FEATURE_VI_DOT_CMD */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002655
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002656#if ENABLE_FEATURE_VI_YANKMARK \
2657 || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
2658 || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002659// might reallocate text[]! use p += string_insert(p, ...),
2660// and be careful to not use pointers into potentially freed text[]!
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002661static uintptr_t string_insert(char *p, const char *s, int undo) // insert the string at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002662{
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002663 uintptr_t bias;
2664 int i;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002665
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002666 i = strlen(s);
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002667#if ENABLE_FEATURE_VI_UNDO
2668 switch (undo) {
2669 case ALLOW_UNDO:
2670 undo_push(p, i, UNDO_INS);
2671 break;
2672 case ALLOW_UNDO_CHAIN:
2673 undo_push(p, i, UNDO_INS_CHAIN);
2674 break;
2675 }
2676#endif
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002677 bias = text_hole_make(p, i);
2678 p += bias;
Denis Vlasenko00d84172008-11-24 07:34:42 +00002679 memcpy(p, s, i);
Denis Vlasenko33875382008-06-21 20:31:50 +00002680#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002681 {
2682 int cnt;
2683 for (cnt = 0; *s != '\0'; s++) {
2684 if (*s == '\n')
2685 cnt++;
2686 }
2687 status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2688 }
Denis Vlasenko33875382008-06-21 20:31:50 +00002689#endif
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002690 return bias;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002691}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002692#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002693
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002694#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenko33875382008-06-21 20:31:50 +00002695static char *text_yank(char *p, char *q, int dest) // copy text into a register
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002696{
Denis Vlasenko00d84172008-11-24 07:34:42 +00002697 int cnt = q - p;
2698 if (cnt < 0) { // they are backwards- reverse them
2699 p = q;
2700 cnt = -cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002701 }
Denis Vlasenko00d84172008-11-24 07:34:42 +00002702 free(reg[dest]); // if already a yank register, free it
2703 reg[dest] = xstrndup(p, cnt + 1);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002704 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002705}
2706
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002707static char what_reg(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002708{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002709 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002710
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002711 c = 'D'; // default to D-reg
2712 if (0 <= YDreg && YDreg <= 25)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002713 c = 'a' + (char) YDreg;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002714 if (YDreg == 26)
2715 c = 'D';
2716 if (YDreg == 27)
2717 c = 'U';
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002718 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002719}
2720
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002721static void check_context(char cmd)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002722{
2723 // A context is defined to be "modifying text"
2724 // Any modifying command establishes a new context.
2725
2726 if (dot < context_start || dot > context_end) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002727 if (strchr(modifying_cmds, cmd) != NULL) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002728 // we are trying to modify text[]- make this the current context
2729 mark[27] = mark[26]; // move cur to prev
2730 mark[26] = dot; // move local to cur
2731 context_start = prev_line(prev_line(dot));
2732 context_end = next_line(next_line(dot));
2733 //loiter= start_loiter= now;
2734 }
2735 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002736}
2737
Denis Vlasenkof882f082007-12-23 02:36:51 +00002738static char *swap_context(char *p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002739{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002740 char *tmp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002741
2742 // the current context is in mark[26]
2743 // the previous context is in mark[27]
2744 // only swap context if other context is valid
2745 if (text <= mark[27] && mark[27] <= end - 1) {
2746 tmp = mark[27];
Denys Vlasenko61fcc8c2016-09-28 16:23:05 +02002747 mark[27] = p;
2748 mark[26] = p = tmp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002749 context_start = prev_line(prev_line(prev_line(p)));
2750 context_end = next_line(next_line(next_line(p)));
2751 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002752 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002753}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002754#endif /* FEATURE_VI_YANKMARK */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002755
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002756//----- Set terminal attributes --------------------------------
2757static void rawmode(void)
2758{
Denys Vlasenko01ccdd12017-01-11 16:17:59 +01002759 // no TERMIOS_CLEAR_ISIG: leave ISIG on - allow signals
2760 set_termios_to_raw(STDIN_FILENO, &term_orig, TERMIOS_RAW_CRNL);
2761 erase_char = term_orig.c_cc[VERASE];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002762}
2763
2764static void cookmode(void)
2765{
Denys Vlasenko8131eea2009-11-02 14:19:51 +01002766 fflush_all();
Denis Vlasenko202ac502008-11-05 13:20:58 +00002767 tcsetattr_stdin_TCSANOW(&term_orig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002768}
2769
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002770#if ENABLE_FEATURE_VI_USE_SIGNALS
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002771//----- Come here when we get a window resize signal ---------
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002772static void winch_sig(int sig UNUSED_PARAM)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002773{
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002774 int save_errno = errno;
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00002775 // FIXME: do it in main loop!!!
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002776 signal(SIGWINCH, winch_sig);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002777 query_screen_dimensions();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002778 new_screen(rows, columns); // get memory for virtual screen
2779 redraw(TRUE); // re-draw the screen
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002780 errno = save_errno;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002781}
2782
2783//----- Come here when we get a continue signal -------------------
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002784static void cont_sig(int sig UNUSED_PARAM)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002785{
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002786 int save_errno = errno;
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002787 rawmode(); // terminal to "raw"
2788 last_status_cksum = 0; // force status update
2789 redraw(TRUE); // re-draw the screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002790
2791 signal(SIGTSTP, suspend_sig);
2792 signal(SIGCONT, SIG_DFL);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002793 //kill(my_pid, SIGCONT); // huh? why? we are already "continued"...
2794 errno = save_errno;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002795}
2796
2797//----- Come here when we get a Suspend signal -------------------
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002798static void suspend_sig(int sig UNUSED_PARAM)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002799{
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002800 int save_errno = errno;
Denis Vlasenko267e16c2008-10-14 10:34:41 +00002801 go_bottom_and_clear_to_eol();
Denis Vlasenko30cfdf92008-09-21 15:29:29 +00002802 cookmode(); // terminal to "cooked"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002803
2804 signal(SIGCONT, cont_sig);
2805 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002806 kill(my_pid, SIGTSTP);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002807 errno = save_errno;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002808}
2809
2810//----- Come here when we get a signal ---------------------------
2811static void catch_sig(int sig)
2812{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002813 signal(SIGINT, catch_sig);
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07002814 siglongjmp(restart, sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002815}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002816#endif /* FEATURE_VI_USE_SIGNALS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002817
Denys Vlasenko606291b2009-09-23 23:15:43 +02002818static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002819{
Denis Vlasenko87f3b262007-09-07 13:43:28 +00002820 struct pollfd pfd[1];
Denis Vlasenkocd5c7862007-05-17 16:37:22 +00002821
Denys Vlasenko05399fc2014-09-15 17:06:10 +02002822 if (hund != 0)
2823 fflush_all();
2824
Denys Vlasenko606291b2009-09-23 23:15:43 +02002825 pfd[0].fd = STDIN_FILENO;
Denis Vlasenko87f3b262007-09-07 13:43:28 +00002826 pfd[0].events = POLLIN;
Denis Vlasenko5d61e712007-09-27 10:09:59 +00002827 return safe_poll(pfd, 1, hund*10) > 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002828}
2829
2830//----- IO Routines --------------------------------------------
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002831static int readit(void) // read (maybe cursor) key from stdin
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002832{
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002833 int c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002834
Denys Vlasenko8131eea2009-11-02 14:19:51 +01002835 fflush_all();
Denys Vlasenko0cc9b182017-05-02 20:39:02 +02002836
2837 // Wait for input. TIMEOUT = -1 makes read_key wait even
2838 // on nonblocking stdin.
2839 // Note: read_key sets errno to 0 on success.
2840 again:
2841 c = read_key(STDIN_FILENO, readbuffer, /*timeout:*/ -1);
Denis Vlasenko0112ff52008-10-25 23:23:00 +00002842 if (c == -1) { // EOF/error
Denys Vlasenko0cc9b182017-05-02 20:39:02 +02002843 if (errno == EAGAIN) // paranoia
2844 goto again;
Denis Vlasenko0112ff52008-10-25 23:23:00 +00002845 go_bottom_and_clear_to_eol();
2846 cookmode(); // terminal to "cooked"
2847 bb_error_msg_and_die("can't read user input");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002848 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002849 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002850}
2851
2852//----- IO Routines --------------------------------------------
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002853static int get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002854{
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002855 int c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002856
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002857#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002858 if (!adding2q) {
2859 // we are not adding to the q.
2860 // but, we may be reading from a q
2861 if (ioq == 0) {
2862 // there is no current q, read from STDIN
2863 c = readit(); // get the users input
2864 } else {
2865 // there is a queue to get chars from first
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002866 // careful with correct sign expansion!
2867 c = (unsigned char)*ioq++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002868 if (c == '\0') {
2869 // the end of the q, read from STDIN
2870 free(ioq_start);
2871 ioq_start = ioq = 0;
2872 c = readit(); // get the users input
2873 }
2874 }
2875 } else {
2876 // adding STDIN chars to q
2877 c = readit(); // get the users input
Denis Vlasenkob1759462008-06-20 20:20:54 +00002878 if (lmc_len >= MAX_INPUT_LEN - 1) {
2879 status_line_bold("last_modifying_cmd overrun");
2880 } else {
2881 // add new char to q
2882 last_modifying_cmd[lmc_len++] = c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002883 }
2884 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002885#else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002886 c = readit(); // get the users input
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002887#endif /* FEATURE_VI_DOT_CMD */
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002888 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002889}
2890
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002891// Get input line (uses "status line" area)
2892static char *get_input_line(const char *prompt)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002893{
Denis Vlasenkob1759462008-06-20 20:20:54 +00002894 // char [MAX_INPUT_LEN]
2895#define buf get_input_line__buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002896
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002897 int c;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002898 int i;
2899
2900 strcpy(buf, prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002901 last_status_cksum = 0; // force status update
Denis Vlasenko267e16c2008-10-14 10:34:41 +00002902 go_bottom_and_clear_to_eol();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002903 write1(prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002904
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002905 i = strlen(buf);
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002906 while (i < MAX_INPUT_LEN) {
2907 c = get_one_char();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002908 if (c == '\n' || c == '\r' || c == 27)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002909 break; // this is end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002910 if (c == erase_char || c == 8 || c == 127) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002911 // user wants to erase prev char
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002912 buf[--i] = '\0';
2913 write1("\b \b"); // erase char on screen
2914 if (i <= 0) // user backs up before b-o-l, exit
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002915 break;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00002916 } else if (c > 0 && c < 256) { // exclude Unicode
2917 // (TODO: need to handle Unicode)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002918 buf[i] = c;
2919 buf[++i] = '\0';
2920 bb_putchar(c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002921 }
2922 }
2923 refresh(FALSE);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00002924 return buf;
Denis Vlasenkob1759462008-06-20 20:20:54 +00002925#undef buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002926}
2927
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002928// might reallocate text[]!
Ron Yorstone5213ce2014-11-30 20:39:25 +00002929static int file_insert(const char *fn, char *p, int initial)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002930{
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002931 int cnt = -1;
2932 int fd, size;
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002933 struct stat statbuf;
2934
Denys Vlasenko7f3a2a22015-10-08 11:24:44 +02002935 if (p < text)
2936 p = text;
2937 if (p > end)
2938 p = end;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002939
Denis Vlasenko59a1f302007-07-14 22:43:10 +00002940 fd = open(fn, O_RDONLY);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002941 if (fd < 0) {
Ron Yorstone5213ce2014-11-30 20:39:25 +00002942 if (!initial)
2943 status_line_bold_errno(fn);
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02002944 return cnt;
2945 }
2946
2947 /* Validate file */
2948 if (fstat(fd, &statbuf) < 0) {
2949 status_line_bold_errno(fn);
2950 goto fi;
2951 }
2952 if (!S_ISREG(statbuf.st_mode)) {
2953 status_line_bold("'%s' is not a regular file", fn);
2954 goto fi;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002955 }
Denys Vlasenko778794d2013-01-22 10:13:52 +01002956 size = (statbuf.st_size < INT_MAX ? (int)statbuf.st_size : INT_MAX);
Denis Vlasenko4ae1e132008-11-19 13:25:14 +00002957 p += text_hole_make(p, size);
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02002958 cnt = full_read(fd, p, size);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002959 if (cnt < 0) {
Denys Vlasenko9e7c0022013-03-15 02:17:29 +01002960 status_line_bold_errno(fn);
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02002961 p = text_hole_delete(p, p + size - 1, NO_UNDO); // un-do buffer insert
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002962 } else if (cnt < size) {
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02002963 // There was a partial read, shrink unused space
2964 p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO);
Denys Vlasenko778794d2013-01-22 10:13:52 +01002965 status_line_bold("can't read '%s'", fn);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002966 }
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02002967 fi:
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002968 close(fd);
Denys Vlasenko32afd3a2014-04-05 22:57:46 +02002969
Denis Vlasenko856be772007-08-17 08:29:48 +00002970#if ENABLE_FEATURE_VI_READONLY
Ron Yorstone5213ce2014-11-30 20:39:25 +00002971 if (initial
Denis Vlasenko856be772007-08-17 08:29:48 +00002972 && ((access(fn, W_OK) < 0) ||
2973 /* root will always have access()
2974 * so we check fileperms too */
2975 !(statbuf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))
2976 )
2977 ) {
Denis Vlasenko2f6ae432007-07-19 22:50:47 +00002978 SET_READONLY_FILE(readonly_mode);
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00002979 }
Denis Vlasenko856be772007-08-17 08:29:48 +00002980#endif
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002981 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002982}
2983
Denis Vlasenko33875382008-06-21 20:31:50 +00002984static int file_write(char *fn, char *first, char *last)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002985{
2986 int fd, cnt, charcnt;
2987
2988 if (fn == 0) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00002989 status_line_bold("No current filename");
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002990 return -2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002991 }
Denis Vlasenko8abae882008-05-03 11:35:59 +00002992 /* By popular request we do not open file with O_TRUNC,
2993 * but instead ftruncate() it _after_ successful write.
2994 * Might reduce amount of data lost on power fail etc.
2995 */
2996 fd = open(fn, (O_WRONLY | O_CREAT), 0666);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002997 if (fd < 0)
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002998 return -1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002999 cnt = last - first + 1;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003000 charcnt = full_write(fd, first, cnt);
Denis Vlasenko8abae882008-05-03 11:35:59 +00003001 ftruncate(fd, charcnt);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003002 if (charcnt == cnt) {
3003 // good write
Denys Vlasenkoe7430862014-04-03 12:47:48 +02003004 //modified_count = FALSE;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003005 } else {
3006 charcnt = 0;
3007 }
3008 close(fd);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003009 return charcnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003010}
3011
3012//----- Terminal Drawing ---------------------------------------
3013// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00003014// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003015// screen coordinates
3016// 0,0 ... 0,79
3017// 1,0 ... 1,79
3018// . ... .
3019// . ... .
3020// 22,0 ... 22,79
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003021// 23,0 ... 23,79 <- status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003022
3023//----- Move the cursor to row x col (count from 0, not 1) -------
Denys Vlasenko04b52892012-06-11 13:51:38 +02003024static void place_cursor(int row, int col)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003025{
Denys Vlasenko04b52892012-06-11 13:51:38 +02003026 char cm1[sizeof(ESC_SET_CURSOR_POS) + sizeof(int)*3 * 2];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003027
3028 if (row < 0) row = 0;
3029 if (row >= rows) row = rows - 1;
3030 if (col < 0) col = 0;
3031 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003032
Denys Vlasenko04b52892012-06-11 13:51:38 +02003033 sprintf(cm1, ESC_SET_CURSOR_POS, row + 1, col + 1);
3034 write1(cm1);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003035}
3036
3037//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00003038static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003039{
Denys Vlasenko04b52892012-06-11 13:51:38 +02003040 write1(ESC_CLEAR2EOL);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003041}
3042
Denis Vlasenko267e16c2008-10-14 10:34:41 +00003043static void go_bottom_and_clear_to_eol(void)
3044{
Denys Vlasenko04b52892012-06-11 13:51:38 +02003045 place_cursor(rows - 1, 0);
3046 clear_to_eol();
Denis Vlasenko267e16c2008-10-14 10:34:41 +00003047}
3048
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003049//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00003050static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003051{
Denys Vlasenko04b52892012-06-11 13:51:38 +02003052 write1(ESC_CLEAR2EOS);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003053}
3054
3055//----- Start standout mode ------------------------------------
Denys Vlasenko04b52892012-06-11 13:51:38 +02003056static void standout_start(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003057{
Denys Vlasenko04b52892012-06-11 13:51:38 +02003058 write1(ESC_BOLD_TEXT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003059}
3060
3061//----- End standout mode --------------------------------------
Denys Vlasenko04b52892012-06-11 13:51:38 +02003062static void standout_end(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003063{
Denys Vlasenko04b52892012-06-11 13:51:38 +02003064 write1(ESC_NORM_TEXT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003065}
3066
3067//----- Flash the screen --------------------------------------
3068static void flash(int h)
3069{
Denys Vlasenko04b52892012-06-11 13:51:38 +02003070 standout_start();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003071 redraw(TRUE);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003072 mysleep(h);
Denys Vlasenko04b52892012-06-11 13:51:38 +02003073 standout_end();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003074 redraw(TRUE);
3075}
3076
Denys Vlasenko05399fc2014-09-15 17:06:10 +02003077static void indicate_error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003078{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003079#if ENABLE_FEATURE_VI_CRASHME
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003080 if (crashme > 0)
3081 return; // generate a random command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003082#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003083 if (!err_method) {
Denys Vlasenko04b52892012-06-11 13:51:38 +02003084 write1(ESC_BELL);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003085 } else {
3086 flash(10);
3087 }
3088}
3089
3090//----- Screen[] Routines --------------------------------------
3091//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00003092static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003093{
3094 memset(screen, ' ', screensize); // clear new screen
3095}
3096
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003097static int bufsum(char *buf, int count)
Paul Fox8552aec2005-09-16 12:20:05 +00003098{
3099 int sum = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003100 char *e = buf + count;
3101
Paul Fox8552aec2005-09-16 12:20:05 +00003102 while (buf < e)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003103 sum += (unsigned char) *buf++;
Paul Fox8552aec2005-09-16 12:20:05 +00003104 return sum;
3105}
3106
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003107//----- Draw the status line at bottom of the screen -------------
3108static void show_status_line(void)
3109{
Paul Foxc3504852005-09-16 12:48:18 +00003110 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003111
Paul Fox8552aec2005-09-16 12:20:05 +00003112 // either we already have an error or status message, or we
3113 // create one.
3114 if (!have_status_msg) {
3115 cnt = format_edit_status();
3116 cksum = bufsum(status_buffer, cnt);
3117 }
3118 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003119 last_status_cksum = cksum; // remember if we have seen this line
Denis Vlasenko267e16c2008-10-14 10:34:41 +00003120 go_bottom_and_clear_to_eol();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003121 write1(status_buffer);
Paul Fox8552aec2005-09-16 12:20:05 +00003122 if (have_status_msg) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003123 if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
Paul Fox8552aec2005-09-16 12:20:05 +00003124 (columns - 1) ) {
3125 have_status_msg = 0;
3126 Hit_Return();
3127 }
3128 have_status_msg = 0;
3129 }
Denys Vlasenko04b52892012-06-11 13:51:38 +02003130 place_cursor(crow, ccol); // put cursor back in correct place
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003131 }
Denys Vlasenko8131eea2009-11-02 14:19:51 +01003132 fflush_all();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003133}
3134
3135//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00003136// format status buffer, with STANDOUT mode
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003137static void status_line_bold(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003138{
3139 va_list args;
3140
3141 va_start(args, format);
Denys Vlasenko04b52892012-06-11 13:51:38 +02003142 strcpy(status_buffer, ESC_BOLD_TEXT);
3143 vsprintf(status_buffer + sizeof(ESC_BOLD_TEXT)-1, format, args);
3144 strcat(status_buffer, ESC_NORM_TEXT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003145 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00003146
Denys Vlasenko04b52892012-06-11 13:51:38 +02003147 have_status_msg = 1 + sizeof(ESC_BOLD_TEXT) + sizeof(ESC_NORM_TEXT) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003148}
3149
Denys Vlasenko9e7c0022013-03-15 02:17:29 +01003150static void status_line_bold_errno(const char *fn)
3151{
Denys Vlasenko6f97b302017-09-29 18:17:25 +02003152 status_line_bold("'%s' "STRERROR_FMT, fn STRERROR_ERRNO);
Denys Vlasenko9e7c0022013-03-15 02:17:29 +01003153}
3154
Paul Fox8552aec2005-09-16 12:20:05 +00003155// format status buffer
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003156static void status_line(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003157{
3158 va_list args;
3159
3160 va_start(args, format);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003161 vsprintf(status_buffer, format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003162 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00003163
3164 have_status_msg = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003165}
3166
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003167// copy s to buf, convert unprintable
3168static void print_literal(char *buf, const char *s)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003169{
Denys Vlasenkoc2704542009-11-20 19:14:19 +01003170 char *d;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003171 unsigned char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003172
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003173 buf[0] = '\0';
3174 if (!s[0])
3175 s = "(NULL)";
Denys Vlasenkoc2704542009-11-20 19:14:19 +01003176
3177 d = buf;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003178 for (; *s; s++) {
3179 int c_is_no_print;
3180
3181 c = *s;
3182 c_is_no_print = (c & 0x80) && !Isprint(c);
3183 if (c_is_no_print) {
Denys Vlasenko04b52892012-06-11 13:51:38 +02003184 strcpy(d, ESC_NORM_TEXT);
3185 d += sizeof(ESC_NORM_TEXT)-1;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003186 c = '.';
3187 }
Denys Vlasenkoc2704542009-11-20 19:14:19 +01003188 if (c < ' ' || c == 0x7f) {
3189 *d++ = '^';
3190 c |= '@'; /* 0x40 */
3191 if (c == 0x7f)
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003192 c = '?';
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003193 }
Denys Vlasenkoc2704542009-11-20 19:14:19 +01003194 *d++ = c;
3195 *d = '\0';
3196 if (c_is_no_print) {
Denys Vlasenko04b52892012-06-11 13:51:38 +02003197 strcpy(d, ESC_BOLD_TEXT);
3198 d += sizeof(ESC_BOLD_TEXT)-1;
Denys Vlasenkoc2704542009-11-20 19:14:19 +01003199 }
3200 if (*s == '\n') {
3201 *d++ = '$';
3202 *d = '\0';
3203 }
3204 if (d - buf > MAX_INPUT_LEN - 10) // paranoia
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003205 break;
3206 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003207}
3208
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003209static void not_implemented(const char *s)
3210{
3211 char buf[MAX_INPUT_LEN];
3212
3213 print_literal(buf, s);
3214 status_line_bold("\'%s\' is not implemented", buf);
3215}
3216
3217// show file status on status line
3218static int format_edit_status(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003219{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00003220 static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
Denis Vlasenkob1759462008-06-20 20:20:54 +00003221
3222#define tot format_edit_status__tot
3223
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003224 int cur, percent, ret, trunc_at;
3225
Denys Vlasenkoe7430862014-04-03 12:47:48 +02003226 // modified_count is now a counter rather than a flag. this
Paul Fox8552aec2005-09-16 12:20:05 +00003227 // helps reduce the amount of line counting we need to do.
3228 // (this will cause a mis-reporting of modified status
3229 // once every MAXINT editing operations.)
3230
3231 // it would be nice to do a similar optimization here -- if
3232 // we haven't done a motion that could have changed which line
3233 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003234 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003235
Denys Vlasenkoe7430862014-04-03 12:47:48 +02003236 // count_lines() is expensive.
3237 // Call it only if something was changed since last time
3238 // we were here:
3239 if (modified_count != last_modified_count) {
Paul Fox8552aec2005-09-16 12:20:05 +00003240 tot = cur + count_lines(dot, end - 1) - 1;
Denys Vlasenkoe7430862014-04-03 12:47:48 +02003241 last_modified_count = modified_count;
Paul Fox8552aec2005-09-16 12:20:05 +00003242 }
3243
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003244 // current line percent
3245 // ------------- ~~ ----------
3246 // total lines 100
3247 if (tot > 0) {
3248 percent = (100 * cur) / tot;
3249 } else {
3250 cur = tot = 0;
3251 percent = 100;
3252 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00003253
Paul Fox8552aec2005-09-16 12:20:05 +00003254 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
3255 columns : STATUS_BUFFER_LEN-1;
3256
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003257 ret = snprintf(status_buffer, trunc_at+1,
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003258#if ENABLE_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00003259 "%c %s%s%s %d/%d %d%%",
3260#else
3261 "%c %s%s %d/%d %d%%",
3262#endif
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003263 cmd_mode_indicator[cmd_mode & 3],
3264 (current_filename != NULL ? current_filename : "No file"),
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003265#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00003266 (readonly_mode ? " [Readonly]" : ""),
Paul Fox8552aec2005-09-16 12:20:05 +00003267#endif
Denys Vlasenkoe7430862014-04-03 12:47:48 +02003268 (modified_count ? " [Modified]" : ""),
Paul Fox8552aec2005-09-16 12:20:05 +00003269 cur, tot, percent);
3270
3271 if (ret >= 0 && ret < trunc_at)
3272 return ret; /* it all fit */
3273
3274 return trunc_at; /* had to truncate */
Denis Vlasenkob1759462008-06-20 20:20:54 +00003275#undef tot
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003276}
3277
3278//----- Force refresh of all Lines -----------------------------
3279static void redraw(int full_screen)
3280{
Denys Vlasenko04b52892012-06-11 13:51:38 +02003281 place_cursor(0, 0);
3282 clear_to_eos();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003283 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003284 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003285 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00003286 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003287}
3288
3289//----- Format a text[] line into a buffer ---------------------
Denis Vlasenko68404f12008-03-17 09:00:54 +00003290static char* format_line(char *src /*, int li*/)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003291{
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00003292 unsigned char c;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003293 int co;
3294 int ofs = offset;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003295 char *dest = scr_out_buf; // [MAX_SCR_COLS + MAX_TABSTOP * 2]
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003296
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003297 c = '~'; // char in col 0 in non-existent lines is '~'
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00003298 co = 0;
3299 while (co < columns + tabstop) {
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00003300 // have we gone past the end?
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003301 if (src < end) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003302 c = *src++;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003303 if (c == '\n')
3304 break;
3305 if ((c & 0x80) && !Isprint(c)) {
3306 c = '.';
3307 }
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00003308 if (c < ' ' || c == 0x7f) {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003309 if (c == '\t') {
3310 c = ' ';
3311 // co % 8 != 7
3312 while ((co % tabstop) != (tabstop - 1)) {
3313 dest[co++] = c;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003314 }
3315 } else {
3316 dest[co++] = '^';
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003317 if (c == 0x7f)
3318 c = '?';
3319 else
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003320 c += '@'; // Ctrl-X -> 'X'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003321 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003322 }
3323 }
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00003324 dest[co++] = c;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003325 // discard scrolled-off-to-the-left portion,
3326 // in tabstop-sized pieces
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003327 if (ofs >= tabstop && co >= tabstop) {
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00003328 memmove(dest, dest + tabstop, co);
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003329 co -= tabstop;
3330 ofs -= tabstop;
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003331 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003332 if (src >= end)
3333 break;
3334 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003335 // check "short line, gigantic offset" case
3336 if (co < ofs)
Denis Vlasenko4baed3a2007-12-22 17:31:29 +00003337 ofs = co;
3338 // discard last scrolled off part
3339 co -= ofs;
3340 dest += ofs;
3341 // fill the rest with spaces
3342 if (co < columns)
3343 memset(&dest[co], ' ', columns - co);
3344 return dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003345}
3346
3347//----- Refresh the changed screen lines -----------------------
3348// Copy the source line from text[] into the buffer and note
3349// if the current screenline is different from the new buffer.
3350// If they differ then that line needs redrawing on the terminal.
3351//
3352static void refresh(int full_screen)
3353{
Denis Vlasenkob1759462008-06-20 20:20:54 +00003354#define old_offset refresh__old_offset
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003355
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003356 int li, changed;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003357 char *tp, *sp; // pointer into text[] and screen[]
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003358
Denys Vlasenkoc5fb0ad2010-07-21 12:39:42 +02003359 if (ENABLE_FEATURE_VI_WIN_RESIZE IF_FEATURE_VI_ASK_TERMINAL(&& !G.get_rowcol_error) ) {
Denis Vlasenko55995022008-05-18 22:28:26 +00003360 unsigned c = columns, r = rows;
Denys Vlasenko2bb651a2010-04-16 20:55:52 -07003361 query_screen_dimensions();
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003362 full_screen |= (c - columns) | (r - rows);
3363 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003364 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
3365 tp = screenbegin; // index into text[] of top line
3366
3367 // compare text[] to screen[] and mark screen[] lines that need updating
3368 for (li = 0; li < rows - 1; li++) {
3369 int cs, ce; // column start & end
Denis Vlasenkof882f082007-12-23 02:36:51 +00003370 char *out_buf;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003371 // format current text line
Denis Vlasenko68404f12008-03-17 09:00:54 +00003372 out_buf = format_line(tp /*, li*/);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003373
3374 // skip to the end of the current text[] line
Denis Vlasenkof882f082007-12-23 02:36:51 +00003375 if (tp < end) {
3376 char *t = memchr(tp, '\n', end - tp);
3377 if (!t) t = end - 1;
3378 tp = t + 1;
3379 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003380
Maninder Singh97c64912015-05-25 13:46:36 +02003381 // see if there are any changes between virtual screen and out_buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003382 changed = FALSE; // assume no change
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003383 cs = 0;
3384 ce = columns - 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003385 sp = &screen[li * columns]; // start of screen line
3386 if (full_screen) {
3387 // force re-draw of every single column from 0 - columns-1
3388 goto re0;
3389 }
3390 // compare newly formatted buffer with virtual screen
3391 // look forward for first difference between buf and screen
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003392 for (; cs <= ce; cs++) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003393 if (out_buf[cs] != sp[cs]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003394 changed = TRUE; // mark for redraw
3395 break;
3396 }
3397 }
3398
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003399 // look backward for last difference between out_buf and screen
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003400 for (; ce >= cs; ce--) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003401 if (out_buf[ce] != sp[ce]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003402 changed = TRUE; // mark for redraw
3403 break;
3404 }
3405 }
3406 // now, cs is index of first diff, and ce is index of last diff
3407
3408 // if horz offset has changed, force a redraw
3409 if (offset != old_offset) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003410 re0:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003411 changed = TRUE;
3412 }
3413
3414 // make a sanity check of columns indexes
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003415 if (cs < 0) cs = 0;
3416 if (ce > columns - 1) ce = columns - 1;
3417 if (cs > ce) { cs = 0; ce = columns - 1; }
Maninder Singh97c64912015-05-25 13:46:36 +02003418 // is there a change between virtual screen and out_buf
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003419 if (changed) {
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003420 // copy changed part of buffer to virtual screen
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003421 memcpy(sp+cs, out_buf+cs, ce-cs+1);
Denys Vlasenko04b52892012-06-11 13:51:38 +02003422 place_cursor(li, cs);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003423 // write line out to terminal
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003424 fwrite(&sp[cs], ce - cs + 1, 1, stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003425 }
3426 }
3427
Denys Vlasenko04b52892012-06-11 13:51:38 +02003428 place_cursor(crow, ccol);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003429
Denis Vlasenko26b6fba2007-12-21 21:34:37 +00003430 old_offset = offset;
Denis Vlasenkob1759462008-06-20 20:20:54 +00003431#undef old_offset
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00003432}
3433
Eric Andersen3f980402001-04-04 17:31:15 +00003434//---------------------------------------------------------------------
3435//----- the Ascii Chart -----------------------------------------------
3436//
3437// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
3438// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
3439// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
3440// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
3441// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
3442// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
3443// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
3444// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
3445// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
3446// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
3447// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
3448// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
3449// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
3450// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
3451// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
3452// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
3453//---------------------------------------------------------------------
3454
3455//----- Execute a Vi Command -----------------------------------
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003456static void do_cmd(int c)
Eric Andersen3f980402001-04-04 17:31:15 +00003457{
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003458 char *p, *q, *save_dot;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003459 char buf[12];
Denis Vlasenkoc3a9dc82008-10-29 00:58:04 +00003460 int dir;
Paul Foxc51fc7b2008-03-06 01:34:23 +00003461 int cnt, i, j;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003462 int c1;
Eric Andersen3f980402001-04-04 17:31:15 +00003463
Denis Vlasenkoe3cbfb92007-12-22 17:00:11 +00003464// c1 = c; // quiet the compiler
3465// cnt = yf = 0; // quiet the compiler
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003466// p = q = save_dot = buf; // quiet the compiler
3467 memset(buf, '\0', sizeof(buf));
Eric Andersenbff7a602001-11-17 07:15:43 +00003468
Paul Fox8552aec2005-09-16 12:20:05 +00003469 show_status_line();
3470
Eric Andersenbff7a602001-11-17 07:15:43 +00003471 /* if this is a cursor key, skip these checks */
3472 switch (c) {
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003473 case KEYCODE_UP:
3474 case KEYCODE_DOWN:
3475 case KEYCODE_LEFT:
3476 case KEYCODE_RIGHT:
3477 case KEYCODE_HOME:
3478 case KEYCODE_END:
3479 case KEYCODE_PAGEUP:
3480 case KEYCODE_PAGEDOWN:
3481 case KEYCODE_DELETE:
Eric Andersenbff7a602001-11-17 07:15:43 +00003482 goto key_cmd_mode;
3483 }
3484
Eric Andersen3f980402001-04-04 17:31:15 +00003485 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003486 // flip-flop Insert/Replace mode
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003487 if (c == KEYCODE_INSERT)
Denis Vlasenko2a51af22007-03-21 22:31:24 +00003488 goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00003489 // we are 'R'eplacing the current *dot with new char
3490 if (*dot == '\n') {
3491 // don't Replace past E-o-l
3492 cmd_mode = 1; // convert to insert
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003493 undo_queue_commit();
Eric Andersen3f980402001-04-04 17:31:15 +00003494 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003495 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003496 if (c != 27)
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003497 dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char
3498 dot = char_insert(dot, c, ALLOW_UNDO_CHAIN); // insert new char
Eric Andersen3f980402001-04-04 17:31:15 +00003499 }
3500 goto dc1;
3501 }
3502 }
3503 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003504 // hitting "Insert" twice means "R" replace mode
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003505 if (c == KEYCODE_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00003506 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003507 if (1 <= c || Isprint(c)) {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003508 dot = char_insert(dot, c, ALLOW_UNDO_QUEUED);
Eric Andersen3f980402001-04-04 17:31:15 +00003509 }
3510 goto dc1;
3511 }
3512
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003513 key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00003514 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00003515 //case 0x01: // soh
3516 //case 0x09: // ht
3517 //case 0x0b: // vt
3518 //case 0x0e: // so
3519 //case 0x0f: // si
3520 //case 0x10: // dle
3521 //case 0x11: // dc1
3522 //case 0x13: // dc3
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003523#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00003524 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00003525 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003526 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003527#endif
Eric Andersen822c3832001-05-07 17:37:43 +00003528 //case 0x16: // syn
3529 //case 0x17: // etb
3530 //case 0x18: // can
3531 //case 0x1c: // fs
3532 //case 0x1d: // gs
3533 //case 0x1e: // rs
3534 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003535 //case '!': // !-
3536 //case '#': // #-
3537 //case '&': // &-
3538 //case '(': // (-
3539 //case ')': // )-
3540 //case '*': // *-
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003541 //case '=': // =-
3542 //case '@': // @-
3543 //case 'F': // F-
3544 //case 'K': // K-
3545 //case 'Q': // Q-
3546 //case 'S': // S-
3547 //case 'T': // T-
3548 //case 'V': // V-
3549 //case '[': // [-
3550 //case '\\': // \-
3551 //case ']': // ]-
3552 //case '_': // _-
3553 //case '`': // `-
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003554 //case 'v': // v-
Denys Vlasenkob22bbff2009-07-04 16:50:43 +02003555 default: // unrecognized command
Eric Andersen3f980402001-04-04 17:31:15 +00003556 buf[0] = c;
3557 buf[1] = '\0';
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003558 not_implemented(buf);
Eric Andersen3f980402001-04-04 17:31:15 +00003559 end_cmd_q(); // stop adding to q
3560 case 0x00: // nul- ignore
3561 break;
3562 case 2: // ctrl-B scroll up full screen
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003563 case KEYCODE_PAGEUP: // Cursor Key Page Up
Eric Andersen3f980402001-04-04 17:31:15 +00003564 dot_scroll(rows - 2, -1);
3565 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003566 case 4: // ctrl-D scroll down half screen
3567 dot_scroll((rows - 2) / 2, 1);
3568 break;
3569 case 5: // ctrl-E scroll down one line
3570 dot_scroll(1, 1);
3571 break;
3572 case 6: // ctrl-F scroll down full screen
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003573 case KEYCODE_PAGEDOWN: // Cursor Key Page Down
Eric Andersen3f980402001-04-04 17:31:15 +00003574 dot_scroll(rows - 2, 1);
3575 break;
3576 case 7: // ctrl-G show current status
Paul Fox8552aec2005-09-16 12:20:05 +00003577 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003578 break;
3579 case 'h': // h- move left
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003580 case KEYCODE_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003581 case 8: // ctrl-H- move left (This may be ERASE char)
Denis Vlasenko2a51af22007-03-21 22:31:24 +00003582 case 0x7f: // DEL- move left (This may be ERASE char)
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003583 do {
3584 dot_left();
3585 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003586 break;
3587 case 10: // Newline ^J
3588 case 'j': // j- goto next line, same col
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003589 case KEYCODE_DOWN: // cursor key Down
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003590 do {
3591 dot_next(); // go to next B-o-l
3592 // try stay in same col
3593 dot = move_to_col(dot, ccol + offset);
3594 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003595 break;
3596 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003597 case 18: // ctrl-R force redraw
Ron Yorston55279672016-04-26 15:23:38 +01003598 redraw(TRUE); // this will redraw the entire display
Eric Andersen3f980402001-04-04 17:31:15 +00003599 break;
3600 case 13: // Carriage Return ^M
3601 case '+': // +- goto next line
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003602 do {
3603 dot_next();
3604 dot_skip_over_ws();
3605 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003606 break;
3607 case 21: // ctrl-U scroll up half screen
3608 dot_scroll((rows - 2) / 2, -1);
3609 break;
3610 case 25: // ctrl-Y scroll up one line
3611 dot_scroll(1, -1);
3612 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003613 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003614 if (cmd_mode == 0)
Denys Vlasenko05399fc2014-09-15 17:06:10 +02003615 indicate_error();
Eric Andersen3f980402001-04-04 17:31:15 +00003616 cmd_mode = 0; // stop insrting
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003617 undo_queue_commit();
Eric Andersen3f980402001-04-04 17:31:15 +00003618 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003619 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003620 break;
3621 case ' ': // move right
3622 case 'l': // move right
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003623 case KEYCODE_RIGHT: // Cursor Key Right
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003624 do {
3625 dot_right();
3626 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003627 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003628#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003629 case '"': // "- name a register to use for Delete/Yank
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003630 c1 = (get_one_char() | 0x20) - 'a'; // | 0x20 is tolower()
3631 if ((unsigned)c1 <= 25) { // a-z?
3632 YDreg = c1;
Eric Andersen3f980402001-04-04 17:31:15 +00003633 } else {
Denys Vlasenko05399fc2014-09-15 17:06:10 +02003634 indicate_error();
Eric Andersen3f980402001-04-04 17:31:15 +00003635 }
3636 break;
3637 case '\'': // '- goto a specific mark
Denys Vlasenko61fcc8c2016-09-28 16:23:05 +02003638 c1 = (get_one_char() | 0x20);
3639 if ((unsigned)(c1 - 'a') <= 25) { // a-z?
3640 c1 = (c1 - 'a');
Eric Andersen3f980402001-04-04 17:31:15 +00003641 // get the b-o-l
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003642 q = mark[c1];
Eric Andersen3f980402001-04-04 17:31:15 +00003643 if (text <= q && q < end) {
3644 dot = q;
3645 dot_begin(); // go to B-o-l
3646 dot_skip_over_ws();
3647 }
3648 } else if (c1 == '\'') { // goto previous context
3649 dot = swap_context(dot); // swap current and previous context
3650 dot_begin(); // go to B-o-l
3651 dot_skip_over_ws();
3652 } else {
Denys Vlasenko05399fc2014-09-15 17:06:10 +02003653 indicate_error();
Eric Andersen3f980402001-04-04 17:31:15 +00003654 }
3655 break;
3656 case 'm': // m- Mark a line
3657 // this is really stupid. If there are any inserts or deletes
3658 // between text[0] and dot then this mark will not point to the
3659 // correct location! It could be off by many lines!
3660 // Well..., at least its quick and dirty.
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003661 c1 = (get_one_char() | 0x20) - 'a';
3662 if ((unsigned)c1 <= 25) { // a-z?
Eric Andersen3f980402001-04-04 17:31:15 +00003663 // remember the line
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003664 mark[c1] = dot;
Eric Andersen3f980402001-04-04 17:31:15 +00003665 } else {
Denys Vlasenko05399fc2014-09-15 17:06:10 +02003666 indicate_error();
Eric Andersen3f980402001-04-04 17:31:15 +00003667 }
3668 break;
3669 case 'P': // P- Put register before
3670 case 'p': // p- put register after
3671 p = reg[YDreg];
Denis Vlasenko00d84172008-11-24 07:34:42 +00003672 if (p == NULL) {
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00003673 status_line_bold("Nothing in register %c", what_reg());
Eric Andersen3f980402001-04-04 17:31:15 +00003674 break;
3675 }
3676 // are we putting whole lines or strings
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003677 if (strchr(p, '\n') != NULL) {
Eric Andersen3f980402001-04-04 17:31:15 +00003678 if (c == 'P') {
3679 dot_begin(); // putting lines- Put above
3680 }
3681 if (c == 'p') {
3682 // are we putting after very last line?
3683 if (end_line(dot) == (end - 1)) {
3684 dot = end; // force dot to end of text[]
3685 } else {
3686 dot_next(); // next line, then put before
3687 }
3688 }
3689 } else {
3690 if (c == 'p')
3691 dot_right(); // move to right, can move to NL
3692 }
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003693 string_insert(dot, p, ALLOW_UNDO); // insert the string
Eric Andersen3f980402001-04-04 17:31:15 +00003694 end_cmd_q(); // stop adding to q
3695 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003696 case 'U': // U- Undo; replace current line with original version
Denys Vlasenko800a9a02012-01-31 14:10:26 +01003697 if (reg[Ureg] != NULL) {
Eric Andersen3f980402001-04-04 17:31:15 +00003698 p = begin_line(dot);
3699 q = end_line(dot);
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003700 p = text_hole_delete(p, q, ALLOW_UNDO); // delete cur line
3701 p += string_insert(p, reg[Ureg], ALLOW_UNDO_CHAIN); // insert orig line
Eric Andersen3f980402001-04-04 17:31:15 +00003702 dot = p;
3703 dot_skip_over_ws();
3704 }
3705 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003706#endif /* FEATURE_VI_YANKMARK */
Andrew Fuller4d8ddb82015-05-03 18:18:25 +02003707#if ENABLE_FEATURE_VI_UNDO
3708 case 'u': // u- undo last operation
3709 undo_pop();
3710 break;
3711#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003712 case '$': // $- goto end of line
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003713 case KEYCODE_END: // Cursor Key End
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003714 for (;;) {
3715 dot = end_line(dot);
Denys Vlasenko1fd71292011-11-28 04:55:48 +01003716 if (--cmdcnt <= 0)
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003717 break;
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003718 dot_next();
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003719 }
Eric Andersen3f980402001-04-04 17:31:15 +00003720 break;
3721 case '%': // %- find matching char of pair () [] {}
3722 for (q = dot; q < end && *q != '\n'; q++) {
3723 if (strchr("()[]{}", *q) != NULL) {
3724 // we found half of a pair
3725 p = find_pair(q, *q);
3726 if (p == NULL) {
Denys Vlasenko05399fc2014-09-15 17:06:10 +02003727 indicate_error();
Eric Andersen3f980402001-04-04 17:31:15 +00003728 } else {
3729 dot = p;
3730 }
3731 break;
3732 }
3733 }
3734 if (*q == '\n')
Denys Vlasenko05399fc2014-09-15 17:06:10 +02003735 indicate_error();
Eric Andersen3f980402001-04-04 17:31:15 +00003736 break;
3737 case 'f': // f- forward to a user specified char
3738 last_forward_char = get_one_char(); // get the search char
3739 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003740 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003741 //
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +00003742 //**** fall through to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003743 case ';': // ;- look at rest of line for last forward char
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003744 do {
3745 if (last_forward_char == 0)
3746 break;
3747 q = dot + 1;
3748 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3749 q++;
3750 }
3751 if (*q == last_forward_char)
3752 dot = q;
3753 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003754 break;
Paul Foxb5ee8db2008-02-14 01:17:01 +00003755 case ',': // repeat latest 'f' in opposite direction
Paul Foxb5ee8db2008-02-14 01:17:01 +00003756 if (last_forward_char == 0)
3757 break;
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003758 do {
3759 q = dot - 1;
3760 while (q >= text && *q != '\n' && *q != last_forward_char) {
3761 q--;
3762 }
3763 if (q >= text && *q == last_forward_char)
3764 dot = q;
3765 } while (--cmdcnt > 0);
Paul Foxb5ee8db2008-02-14 01:17:01 +00003766 break;
3767
Eric Andersen3f980402001-04-04 17:31:15 +00003768 case '-': // -- goto prev line
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003769 do {
3770 dot_prev();
3771 dot_skip_over_ws();
3772 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003773 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003774#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003775 case '.': // .- repeat the last modifying command
3776 // Stuff the last_modifying_cmd back into stdin
3777 // and let it be re-executed.
Denis Vlasenkob1759462008-06-20 20:20:54 +00003778 if (lmc_len > 0) {
Paul Foxc51fc7b2008-03-06 01:34:23 +00003779 last_modifying_cmd[lmc_len] = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003780 ioq = ioq_start = xstrdup(last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003781 }
3782 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003783#endif
3784#if ENABLE_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003785 case '?': // /- search for a pattern
3786 case '/': // /- search for a pattern
3787 buf[0] = c;
3788 buf[1] = '\0';
3789 q = get_input_line(buf); // get input line- use "status line"
Paul Fox4917c112008-03-05 16:44:02 +00003790 if (q[0] && !q[1]) {
3791 if (last_search_pattern[0])
Denis Vlasenkoc3a9dc82008-10-29 00:58:04 +00003792 last_search_pattern[0] = c;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003793 goto dc3; // if no pat re-use old pat
Paul Fox4917c112008-03-05 16:44:02 +00003794 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003795 if (q[0]) { // strlen(q) > 1: new pat- save it and find
Eric Andersen3f980402001-04-04 17:31:15 +00003796 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003797 free(last_search_pattern);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003798 last_search_pattern = xstrdup(q);
Eric Andersen3f980402001-04-04 17:31:15 +00003799 goto dc3; // now find the pattern
3800 }
3801 // user changed mind and erased the "/"- do nothing
3802 break;
3803 case 'N': // N- backward search for last pattern
Eric Andersen3f980402001-04-04 17:31:15 +00003804 dir = BACK; // assume BACKWARD search
3805 p = dot - 1;
3806 if (last_search_pattern[0] == '?') {
3807 dir = FORWARD;
3808 p = dot + 1;
3809 }
3810 goto dc4; // now search for pattern
3811 break;
3812 case 'n': // n- repeat search for last pattern
3813 // search rest of text[] starting at next char
3814 // if search fails return orignal "p" not the "p+1" address
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003815 do {
3816 const char *msg;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003817 dc3:
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003818 dir = FORWARD; // assume FORWARD search
3819 p = dot + 1;
3820 if (last_search_pattern[0] == '?') {
3821 dir = BACK;
3822 p = dot - 1;
Eric Andersen3f980402001-04-04 17:31:15 +00003823 }
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003824 dc4:
Denys Vlasenkob7330462018-11-29 14:39:52 +01003825 q = char_search(p, last_search_pattern + 1, (dir << 1) | FULL);
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003826 if (q != NULL) {
3827 dot = q; // good search, update "dot"
3828 msg = NULL;
3829 goto dc2;
3830 }
3831 // no pattern found between "dot" and "end"- continue at top
3832 p = text;
3833 if (dir == BACK) {
3834 p = end - 1;
3835 }
Denys Vlasenkob7330462018-11-29 14:39:52 +01003836 q = char_search(p, last_search_pattern + 1, (dir << 1) | FULL);
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003837 if (q != NULL) { // found something
3838 dot = q; // found new pattern- goto it
3839 msg = "search hit BOTTOM, continuing at TOP";
3840 if (dir == BACK) {
3841 msg = "search hit TOP, continuing at BOTTOM";
3842 }
3843 } else {
3844 msg = "Pattern not found";
3845 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003846 dc2:
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003847 if (msg)
3848 status_line_bold("%s", msg);
3849 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003850 break;
3851 case '{': // {- move backward paragraph
Denys Vlasenkob7330462018-11-29 14:39:52 +01003852 q = char_search(dot, "\n\n", (BACK << 1) | FULL);
Eric Andersen3f980402001-04-04 17:31:15 +00003853 if (q != NULL) { // found blank line
3854 dot = next_line(q); // move to next blank line
3855 }
3856 break;
3857 case '}': // }- move forward paragraph
Denys Vlasenkob7330462018-11-29 14:39:52 +01003858 q = char_search(dot, "\n\n", (FORWARD << 1) | FULL);
Eric Andersen3f980402001-04-04 17:31:15 +00003859 if (q != NULL) { // found blank line
3860 dot = next_line(q); // move to next blank line
3861 }
3862 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003863#endif /* FEATURE_VI_SEARCH */
Maninder Singh97c64912015-05-25 13:46:36 +02003864 case '0': // 0- goto beginning of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003865 case '1': // 1-
3866 case '2': // 2-
3867 case '3': // 3-
3868 case '4': // 4-
3869 case '5': // 5-
3870 case '6': // 6-
3871 case '7': // 7-
3872 case '8': // 8-
3873 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003874 if (c == '0' && cmdcnt < 1) {
3875 dot_begin(); // this was a standalone zero
3876 } else {
3877 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3878 }
3879 break;
3880 case ':': // :- the colon mode commands
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003881 p = get_input_line(":"); // get input line- use "status line"
Eric Andersen3f980402001-04-04 17:31:15 +00003882 colon(p); // execute the command
Eric Andersen3f980402001-04-04 17:31:15 +00003883 break;
3884 case '<': // <- Left shift something
3885 case '>': // >- Right shift something
3886 cnt = count_lines(text, dot); // remember what line we are on
3887 c1 = get_one_char(); // get the type of thing to delete
3888 find_range(&p, &q, c1);
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003889 yank_delete(p, q, 1, YANKONLY, NO_UNDO); // save copy before change
Eric Andersen3f980402001-04-04 17:31:15 +00003890 p = begin_line(p);
3891 q = end_line(q);
3892 i = count_lines(p, q); // # of lines we are shifting
3893 for ( ; i > 0; i--, p = next_line(p)) {
3894 if (c == '<') {
3895 // shift left- remove tab or 8 spaces
3896 if (*p == '\t') {
3897 // shrink buffer 1 char
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003898 text_hole_delete(p, p, NO_UNDO);
Eric Andersen3f980402001-04-04 17:31:15 +00003899 } else if (*p == ' ') {
3900 // we should be calculating columns, not just SPACE
3901 for (j = 0; *p == ' ' && j < tabstop; j++) {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003902 text_hole_delete(p, p, NO_UNDO);
Eric Andersen3f980402001-04-04 17:31:15 +00003903 }
3904 }
3905 } else if (c == '>') {
3906 // shift right -- add tab or 8 spaces
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003907 char_insert(p, '\t', ALLOW_UNDO);
Eric Andersen3f980402001-04-04 17:31:15 +00003908 }
3909 }
3910 dot = find_line(cnt); // what line were we on
3911 dot_skip_over_ws();
3912 end_cmd_q(); // stop adding to q
3913 break;
3914 case 'A': // A- append at e-o-l
3915 dot_end(); // go to e-o-l
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +00003916 //**** fall through to ... 'a'
Eric Andersen3f980402001-04-04 17:31:15 +00003917 case 'a': // a- append after current char
3918 if (*dot != '\n')
3919 dot++;
3920 goto dc_i;
3921 break;
3922 case 'B': // B- back a blank-delimited Word
3923 case 'E': // E- end of a blank-delimited word
3924 case 'W': // W- forward a blank-delimited word
Eric Andersen3f980402001-04-04 17:31:15 +00003925 dir = FORWARD;
3926 if (c == 'B')
3927 dir = BACK;
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003928 do {
3929 if (c == 'W' || isspace(dot[dir])) {
3930 dot = skip_thing(dot, 1, dir, S_TO_WS);
3931 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3932 }
3933 if (c != 'W')
3934 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3935 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00003936 break;
3937 case 'C': // C- Change to e-o-l
3938 case 'D': // D- delete to e-o-l
3939 save_dot = dot;
3940 dot = dollar_line(dot); // move to before NL
3941 // copy text into a register and delete
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003942 dot = yank_delete(save_dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete to e-o-l
Eric Andersen3f980402001-04-04 17:31:15 +00003943 if (c == 'C')
3944 goto dc_i; // start inserting
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003945#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003946 if (c == 'D')
3947 end_cmd_q(); // stop adding to q
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003948#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003949 break;
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00003950 case 'g': // 'gg' goto a line number (vim) (default: very first line)
Paul Foxb5ee8db2008-02-14 01:17:01 +00003951 c1 = get_one_char();
3952 if (c1 != 'g') {
3953 buf[0] = 'g';
Denys Vlasenkode1996d2016-09-15 13:53:42 +02003954 // c1 < 0 if the key was special. Try "g<up-arrow>"
3955 // TODO: if Unicode?
3956 buf[1] = (c1 >= 0 ? c1 : '*');
Paul Foxb5ee8db2008-02-14 01:17:01 +00003957 buf[2] = '\0';
3958 not_implemented(buf);
3959 break;
3960 }
3961 if (cmdcnt == 0)
3962 cmdcnt = 1;
3963 /* fall through */
Eric Andersen822c3832001-05-07 17:37:43 +00003964 case 'G': // G- goto to a line number (default= E-O-F)
3965 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003966 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003967 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003968 }
3969 dot_skip_over_ws();
3970 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003971 case 'H': // H- goto top line on screen
3972 dot = screenbegin;
3973 if (cmdcnt > (rows - 1)) {
3974 cmdcnt = (rows - 1);
3975 }
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003976 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003977 do_cmd('+');
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01003978 }
Eric Andersen3f980402001-04-04 17:31:15 +00003979 dot_skip_over_ws();
3980 break;
3981 case 'I': // I- insert before first non-blank
3982 dot_begin(); // 0
3983 dot_skip_over_ws();
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +00003984 //**** fall through to ... 'i'
Eric Andersen3f980402001-04-04 17:31:15 +00003985 case 'i': // i- insert before current char
Denis Vlasenko0112ff52008-10-25 23:23:00 +00003986 case KEYCODE_INSERT: // Cursor Key Insert
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003987 dc_i:
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003988 cmd_mode = 1; // start inserting
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003989 undo_queue_commit(); // commit queue when cmd_mode changes
Eric Andersen3f980402001-04-04 17:31:15 +00003990 break;
3991 case 'J': // J- join current and next lines together
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003992 do {
3993 dot_end(); // move to NL
3994 if (dot < end - 1) { // make sure not last char in text[]
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003995#if ENABLE_FEATURE_VI_UNDO
3996 undo_push(dot, 1, UNDO_DEL);
Denys Vlasenko12e154f2011-09-09 12:35:49 +02003997 *dot++ = ' '; // replace NL with space
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02003998 undo_push((dot - 1), 1, UNDO_INS_CHAIN);
3999#else
4000 *dot++ = ' ';
Denys Vlasenkoe7430862014-04-03 12:47:48 +02004001 modified_count++;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004002#endif
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004003 while (isblank(*dot)) { // delete leading WS
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004004 text_hole_delete(dot, dot, ALLOW_UNDO_CHAIN);
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004005 }
Eric Andersen3f980402001-04-04 17:31:15 +00004006 }
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004007 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00004008 end_cmd_q(); // stop adding to q
4009 break;
4010 case 'L': // L- goto bottom line on screen
4011 dot = end_screen();
4012 if (cmdcnt > (rows - 1)) {
4013 cmdcnt = (rows - 1);
4014 }
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01004015 if (--cmdcnt > 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00004016 do_cmd('-');
Denys Vlasenko35fdb1b2010-03-26 16:10:14 +01004017 }
Eric Andersen3f980402001-04-04 17:31:15 +00004018 dot_begin();
4019 dot_skip_over_ws();
4020 break;
Eric Andersen822c3832001-05-07 17:37:43 +00004021 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00004022 dot = screenbegin;
4023 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
4024 dot = next_line(dot);
4025 break;
Eric Andersen3f980402001-04-04 17:31:15 +00004026 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00004027 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00004028 p = begin_line(dot);
4029 if (p[-1] == '\n') {
4030 dot_prev();
4031 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
4032 dot_end();
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004033 dot = char_insert(dot, '\n', ALLOW_UNDO);
Eric Andersen3f980402001-04-04 17:31:15 +00004034 } else {
4035 dot_begin(); // 0
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004036 dot = char_insert(dot, '\n', ALLOW_UNDO); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00004037 dot_prev(); // -
4038 }
4039 goto dc_i;
4040 break;
4041 case 'R': // R- continuous Replace char
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004042 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00004043 cmd_mode = 2;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004044 undo_queue_commit();
Eric Andersen3f980402001-04-04 17:31:15 +00004045 break;
Denis Vlasenko0112ff52008-10-25 23:23:00 +00004046 case KEYCODE_DELETE:
Denys Vlasenko49acc1a2015-03-12 21:15:34 +01004047 if (dot < end - 1)
4048 dot = yank_delete(dot, dot, 1, YANKDEL, ALLOW_UNDO);
4049 break;
Eric Andersen3f980402001-04-04 17:31:15 +00004050 case 'X': // X- delete char before dot
4051 case 'x': // x- delete the current char
4052 case 's': // s- substitute the current char
Eric Andersen3f980402001-04-04 17:31:15 +00004053 dir = 0;
4054 if (c == 'X')
4055 dir = -1;
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004056 do {
4057 if (dot[dir] != '\n') {
4058 if (c == 'X')
4059 dot--; // delete prev char
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004060 dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004061 }
4062 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00004063 end_cmd_q(); // stop adding to q
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004064 if (c == 's')
4065 goto dc_i; // start inserting
Eric Andersen3f980402001-04-04 17:31:15 +00004066 break;
4067 case 'Z': // Z- if modified, {write}; exit
4068 // ZZ means to save file (if necessary), then exit
4069 c1 = get_one_char();
4070 if (c1 != 'Z') {
Denys Vlasenko05399fc2014-09-15 17:06:10 +02004071 indicate_error();
Eric Andersen3f980402001-04-04 17:31:15 +00004072 break;
4073 }
Denys Vlasenkoe7430862014-04-03 12:47:48 +02004074 if (modified_count) {
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00004075 if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
Denys Vlasenko778794d2013-01-22 10:13:52 +01004076 status_line_bold("'%s' is read only", current_filename);
Denis Vlasenko92758142006-10-03 19:56:34 +00004077 break;
Paul Foxf0305b72006-03-28 14:18:21 +00004078 }
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00004079 cnt = file_write(current_filename, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00004080 if (cnt < 0) {
4081 if (cnt == -1)
Denys Vlasenko6f97b302017-09-29 18:17:25 +02004082 status_line_bold("Write error: "STRERROR_FMT STRERROR_ERRNO);
Paul Fox61e45db2005-10-09 14:43:22 +00004083 } else if (cnt == (end - 1 - text + 1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00004084 editing = 0;
4085 }
4086 } else {
4087 editing = 0;
4088 }
4089 break;
4090 case '^': // ^- move to first non-blank on line
4091 dot_begin();
4092 dot_skip_over_ws();
4093 break;
4094 case 'b': // b- back a word
4095 case 'e': // e- end of word
Eric Andersen3f980402001-04-04 17:31:15 +00004096 dir = FORWARD;
4097 if (c == 'b')
4098 dir = BACK;
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004099 do {
4100 if ((dot + dir) < text || (dot + dir) > end - 1)
4101 break;
4102 dot += dir;
4103 if (isspace(*dot)) {
4104 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
4105 }
4106 if (isalnum(*dot) || *dot == '_') {
4107 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
4108 } else if (ispunct(*dot)) {
4109 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
4110 }
4111 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00004112 break;
4113 case 'c': // c- change something
4114 case 'd': // d- delete something
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004115#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00004116 case 'y': // y- yank something
4117 case 'Y': // Y- Yank a line
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004118#endif
Denis Vlasenkod44c1532008-10-14 12:59:42 +00004119 {
Paul Foxc51fc7b2008-03-06 01:34:23 +00004120 int yf, ml, whole = 0;
Eric Andersen3f980402001-04-04 17:31:15 +00004121 yf = YANKDEL; // assume either "c" or "d"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004122#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00004123 if (c == 'y' || c == 'Y')
4124 yf = YANKONLY;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004125#endif
Eric Andersen3f980402001-04-04 17:31:15 +00004126 c1 = 'y';
4127 if (c != 'Y')
4128 c1 = get_one_char(); // get the type of thing to delete
Paul Foxc51fc7b2008-03-06 01:34:23 +00004129 // determine range, and whether it spans lines
4130 ml = find_range(&p, &q, c1);
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004131 place_cursor(0, 0);
Eric Andersen3f980402001-04-04 17:31:15 +00004132 if (c1 == 27) { // ESC- user changed mind and wants out
4133 c = c1 = 27; // Escape- do nothing
4134 } else if (strchr("wW", c1)) {
4135 if (c == 'c') {
4136 // don't include trailing WS as part of word
Denis Vlasenkoeaabf062007-07-17 23:14:07 +00004137 while (isblank(*q)) {
Eric Andersen3f980402001-04-04 17:31:15 +00004138 if (q <= text || q[-1] == '\n')
4139 break;
4140 q--;
4141 }
4142 }
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004143 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word
Paul Foxc51fc7b2008-03-06 01:34:23 +00004144 } else if (strchr("^0bBeEft%$ lh\b\177", c1)) {
4145 // partial line copy text into a register and delete
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004146 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word
Paul Foxc51fc7b2008-03-06 01:34:23 +00004147 } else if (strchr("cdykjHL+-{}\r\n", c1)) {
4148 // whole line copy text into a register and delete
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004149 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete lines
Paul Foxc51fc7b2008-03-06 01:34:23 +00004150 whole = 1;
4151 } else {
4152 // could not recognize object
4153 c = c1 = 27; // error-
4154 ml = 0;
Denys Vlasenko05399fc2014-09-15 17:06:10 +02004155 indicate_error();
Paul Foxc51fc7b2008-03-06 01:34:23 +00004156 }
4157 if (ml && whole) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00004158 if (c == 'c') {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004159 dot = char_insert(dot, '\n', ALLOW_UNDO_CHAIN);
Eric Andersen1c0d3112001-04-16 15:46:44 +00004160 // on the last line of file don't move to prev line
Paul Foxc51fc7b2008-03-06 01:34:23 +00004161 if (whole && dot != (end-1)) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00004162 dot_prev();
4163 }
4164 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00004165 dot_begin();
4166 dot_skip_over_ws();
4167 }
Eric Andersen3f980402001-04-04 17:31:15 +00004168 }
4169 if (c1 != 27) {
4170 // if CHANGING, not deleting, start inserting after the delete
4171 if (c == 'c') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004172 strcpy(buf, "Change");
Eric Andersen3f980402001-04-04 17:31:15 +00004173 goto dc_i; // start inserting
4174 }
4175 if (c == 'd') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004176 strcpy(buf, "Delete");
Eric Andersen3f980402001-04-04 17:31:15 +00004177 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004178#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00004179 if (c == 'y' || c == 'Y') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004180 strcpy(buf, "Yank");
Eric Andersen3f980402001-04-04 17:31:15 +00004181 }
4182 p = reg[YDreg];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004183 q = p + strlen(p);
Eric Andersen3f980402001-04-04 17:31:15 +00004184 for (cnt = 0; p <= q; p++) {
4185 if (*p == '\n')
4186 cnt++;
4187 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004188 status_line("%s %d lines (%d chars) using [%c]",
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004189 buf, cnt, strlen(reg[YDreg]), what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004190#endif
Eric Andersen3f980402001-04-04 17:31:15 +00004191 end_cmd_q(); // stop adding to q
4192 }
4193 break;
Denis Vlasenkod44c1532008-10-14 12:59:42 +00004194 }
Eric Andersen3f980402001-04-04 17:31:15 +00004195 case 'k': // k- goto prev line, same col
Denis Vlasenko0112ff52008-10-25 23:23:00 +00004196 case KEYCODE_UP: // cursor key Up
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004197 do {
4198 dot_prev();
4199 dot = move_to_col(dot, ccol + offset); // try stay in same col
4200 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00004201 break;
4202 case 'r': // r- replace the current char with user input
4203 c1 = get_one_char(); // get the replacement char
4204 if (*dot != '\n') {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004205#if ENABLE_FEATURE_VI_UNDO
4206 undo_push(dot, 1, UNDO_DEL);
4207 *dot = c1;
4208 undo_push(dot, 1, UNDO_INS_CHAIN);
4209#else
Eric Andersen3f980402001-04-04 17:31:15 +00004210 *dot = c1;
Denys Vlasenkoe7430862014-04-03 12:47:48 +02004211 modified_count++;
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004212#endif
Eric Andersen3f980402001-04-04 17:31:15 +00004213 }
4214 end_cmd_q(); // stop adding to q
4215 break;
Eric Andersen822c3832001-05-07 17:37:43 +00004216 case 't': // t- move to char prior to next x
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00004217 last_forward_char = get_one_char();
4218 do_cmd(';');
4219 if (*dot == last_forward_char)
4220 dot_left();
Denis Vlasenko4c9e9c42008-10-20 08:59:03 +00004221 last_forward_char = 0;
Eric Andersen822c3832001-05-07 17:37:43 +00004222 break;
Eric Andersen3f980402001-04-04 17:31:15 +00004223 case 'w': // w- forward a word
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004224 do {
4225 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
4226 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
4227 } else if (ispunct(*dot)) { // we are on PUNCT
4228 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
4229 }
4230 if (dot < end - 1)
4231 dot++; // move over word
4232 if (isspace(*dot)) {
4233 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
4234 }
4235 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00004236 break;
4237 case 'z': // z-
4238 c1 = get_one_char(); // get the replacement char
4239 cnt = 0;
4240 if (c1 == '.')
4241 cnt = (rows - 2) / 2; // put dot at center
4242 if (c1 == '-')
4243 cnt = rows - 2; // put dot at bottom
4244 screenbegin = begin_line(dot); // start dot at top
4245 dot_scroll(cnt, -1);
4246 break;
4247 case '|': // |- move to column "cmdcnt"
4248 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
4249 break;
4250 case '~': // ~- flip the case of letters a-z -> A-Z
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004251 do {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004252#if ENABLE_FEATURE_VI_UNDO
4253 if (islower(*dot)) {
4254 undo_push(dot, 1, UNDO_DEL);
4255 *dot = toupper(*dot);
4256 undo_push(dot, 1, UNDO_INS_CHAIN);
4257 } else if (isupper(*dot)) {
4258 undo_push(dot, 1, UNDO_DEL);
4259 *dot = tolower(*dot);
4260 undo_push(dot, 1, UNDO_INS_CHAIN);
4261 }
4262#else
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004263 if (islower(*dot)) {
4264 *dot = toupper(*dot);
Denys Vlasenkoe7430862014-04-03 12:47:48 +02004265 modified_count++;
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004266 } else if (isupper(*dot)) {
4267 *dot = tolower(*dot);
Denys Vlasenkoe7430862014-04-03 12:47:48 +02004268 modified_count++;
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004269 }
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004270#endif
Denys Vlasenko12e154f2011-09-09 12:35:49 +02004271 dot_right();
4272 } while (--cmdcnt > 0);
Eric Andersen3f980402001-04-04 17:31:15 +00004273 end_cmd_q(); // stop adding to q
4274 break;
4275 //----- The Cursor and Function Keys -----------------------------
Denis Vlasenko0112ff52008-10-25 23:23:00 +00004276 case KEYCODE_HOME: // Cursor Key Home
Eric Andersen3f980402001-04-04 17:31:15 +00004277 dot_begin();
4278 break;
4279 // The Fn keys could point to do_macro which could translate them
Denis Vlasenko0112ff52008-10-25 23:23:00 +00004280#if 0
4281 case KEYCODE_FUN1: // Function Key F1
4282 case KEYCODE_FUN2: // Function Key F2
4283 case KEYCODE_FUN3: // Function Key F3
4284 case KEYCODE_FUN4: // Function Key F4
4285 case KEYCODE_FUN5: // Function Key F5
4286 case KEYCODE_FUN6: // Function Key F6
4287 case KEYCODE_FUN7: // Function Key F7
4288 case KEYCODE_FUN8: // Function Key F8
4289 case KEYCODE_FUN9: // Function Key F9
4290 case KEYCODE_FUN10: // Function Key F10
4291 case KEYCODE_FUN11: // Function Key F11
4292 case KEYCODE_FUN12: // Function Key F12
Eric Andersen3f980402001-04-04 17:31:15 +00004293 break;
Denis Vlasenko0112ff52008-10-25 23:23:00 +00004294#endif
Eric Andersen3f980402001-04-04 17:31:15 +00004295 }
4296
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004297 dc1:
Eric Andersen3f980402001-04-04 17:31:15 +00004298 // if text[] just became empty, add back an empty line
4299 if (end == text) {
Jody Bruchona8d6f9b2014-04-02 13:49:26 +02004300 char_insert(text, '\n', NO_UNDO); // start empty buf with dummy line
Eric Andersen3f980402001-04-04 17:31:15 +00004301 dot = text;
4302 }
4303 // it is OK for dot to exactly equal to end, otherwise check dot validity
4304 if (dot != end) {
4305 dot = bound_dot(dot); // make sure "dot" is valid
4306 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004307#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00004308 check_context(c); // update the current context
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004309#endif
Eric Andersen3f980402001-04-04 17:31:15 +00004310
4311 if (!isdigit(c))
4312 cmdcnt = 0; // cmd was not a number, reset cmdcnt
4313 cnt = dot - begin_line(dot);
4314 // Try to stay off of the Newline
4315 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
4316 dot--;
4317}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004318
Paul Fox35e9c5d2008-03-06 16:26:12 +00004319/* NB! the CRASHME code is unmaintained, and doesn't currently build */
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004320#if ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004321static int totalcmds = 0;
4322static int Mp = 85; // Movement command Probability
4323static int Np = 90; // Non-movement command Probability
4324static int Dp = 96; // Delete command Probability
4325static int Ip = 97; // Insert command Probability
4326static int Yp = 98; // Yank command Probability
4327static int Pp = 99; // Put command Probability
4328static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004329static const char chars[20] = "\t012345 abcdABCD-=.$";
4330static const char *const words[20] = {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004331 "this", "is", "a", "test",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004332 "broadcast", "the", "emergency", "of",
4333 "system", "quick", "brown", "fox",
4334 "jumped", "over", "lazy", "dogs",
4335 "back", "January", "Febuary", "March"
4336};
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004337static const char *const lines[20] = {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004338 "You should have received a copy of the GNU General Public License\n",
4339 "char c, cm, *cmd, *cmd1;\n",
4340 "generate a command by percentages\n",
4341 "Numbers may be typed as a prefix to some commands.\n",
4342 "Quit, discarding changes!\n",
4343 "Forced write, if permission originally not valid.\n",
4344 "In general, any ex or ed command (such as substitute or delete).\n",
4345 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
4346 "Please get w/ me and I will go over it with you.\n",
4347 "The following is a list of scheduled, committed changes.\n",
4348 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
4349 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
4350 "Any question about transactions please contact Sterling Huxley.\n",
4351 "I will try to get back to you by Friday, December 31.\n",
4352 "This Change will be implemented on Friday.\n",
4353 "Let me know if you have problems accessing this;\n",
4354 "Sterling Huxley recently added you to the access list.\n",
4355 "Would you like to go to lunch?\n",
4356 "The last command will be automatically run.\n",
4357 "This is too much english for a computer geek.\n",
4358};
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004359static char *multilines[20] = {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004360 "You should have received a copy of the GNU General Public License\n",
4361 "char c, cm, *cmd, *cmd1;\n",
4362 "generate a command by percentages\n",
4363 "Numbers may be typed as a prefix to some commands.\n",
4364 "Quit, discarding changes!\n",
4365 "Forced write, if permission originally not valid.\n",
4366 "In general, any ex or ed command (such as substitute or delete).\n",
4367 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
4368 "Please get w/ me and I will go over it with you.\n",
4369 "The following is a list of scheduled, committed changes.\n",
4370 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
4371 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
4372 "Any question about transactions please contact Sterling Huxley.\n",
4373 "I will try to get back to you by Friday, December 31.\n",
4374 "This Change will be implemented on Friday.\n",
4375 "Let me know if you have problems accessing this;\n",
4376 "Sterling Huxley recently added you to the access list.\n",
4377 "Would you like to go to lunch?\n",
4378 "The last command will be automatically run.\n",
4379 "This is too much english for a computer geek.\n",
4380};
4381
4382// create a random command to execute
4383static void crash_dummy()
4384{
4385 static int sleeptime; // how long to pause between commands
4386 char c, cm, *cmd, *cmd1;
4387 int i, cnt, thing, rbi, startrbi, percent;
4388
4389 // "dot" movement commands
4390 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
4391
4392 // is there already a command running?
Denys Vlasenko020f4062009-05-17 16:44:54 +02004393 if (readbuffer[0] > 0)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004394 goto cd1;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004395 cd0:
Denys Vlasenko020f4062009-05-17 16:44:54 +02004396 readbuffer[0] = 'X';
4397 startrbi = rbi = 1;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004398 sleeptime = 0; // how long to pause between commands
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004399 memset(readbuffer, '\0', sizeof(readbuffer));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004400 // generate a command by percentages
4401 percent = (int) lrand48() % 100; // get a number from 0-99
4402 if (percent < Mp) { // Movement commands
4403 // available commands
4404 cmd = cmd1;
4405 M++;
4406 } else if (percent < Np) { // non-movement commands
4407 cmd = "mz<>\'\""; // available commands
4408 N++;
4409 } else if (percent < Dp) { // Delete commands
4410 cmd = "dx"; // available commands
4411 D++;
4412 } else if (percent < Ip) { // Inset commands
4413 cmd = "iIaAsrJ"; // available commands
4414 I++;
4415 } else if (percent < Yp) { // Yank commands
4416 cmd = "yY"; // available commands
4417 Y++;
4418 } else if (percent < Pp) { // Put commands
4419 cmd = "pP"; // available commands
4420 P++;
4421 } else {
4422 // We do not know how to handle this command, try again
4423 U++;
4424 goto cd0;
4425 }
4426 // randomly pick one of the available cmds from "cmd[]"
4427 i = (int) lrand48() % strlen(cmd);
4428 cm = cmd[i];
4429 if (strchr(":\024", cm))
4430 goto cd0; // dont allow colon or ctrl-T commands
4431 readbuffer[rbi++] = cm; // put cmd into input buffer
4432
4433 // now we have the command-
4434 // there are 1, 2, and multi char commands
4435 // find out which and generate the rest of command as necessary
4436 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
4437 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
4438 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
4439 cmd1 = "abcdefghijklmnopqrstuvwxyz";
4440 }
4441 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
4442 c = cmd1[thing];
4443 readbuffer[rbi++] = c; // add movement to input buffer
4444 }
4445 if (strchr("iIaAsc", cm)) { // multi-char commands
4446 if (cm == 'c') {
4447 // change some thing
4448 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
4449 c = cmd1[thing];
4450 readbuffer[rbi++] = c; // add movement to input buffer
4451 }
4452 thing = (int) lrand48() % 4; // what thing to insert
4453 cnt = (int) lrand48() % 10; // how many to insert
4454 for (i = 0; i < cnt; i++) {
4455 if (thing == 0) { // insert chars
4456 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
4457 } else if (thing == 1) { // insert words
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004458 strcat(readbuffer, words[(int) lrand48() % 20]);
4459 strcat(readbuffer, " ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004460 sleeptime = 0; // how fast to type
4461 } else if (thing == 2) { // insert lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004462 strcat(readbuffer, lines[(int) lrand48() % 20]);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004463 sleeptime = 0; // how fast to type
4464 } else { // insert multi-lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004465 strcat(readbuffer, multilines[(int) lrand48() % 20]);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004466 sleeptime = 0; // how fast to type
4467 }
4468 }
Denys Vlasenko8187e012017-09-13 22:48:30 +02004469 strcat(readbuffer, ESC);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004470 }
Denys Vlasenko020f4062009-05-17 16:44:54 +02004471 readbuffer[0] = strlen(readbuffer + 1);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004472 cd1:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004473 totalcmds++;
4474 if (sleeptime > 0)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004475 mysleep(sleeptime); // sleep 1/100 sec
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004476}
4477
4478// test to see if there are any errors
4479static void crash_test()
4480{
4481 static time_t oldtim;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004482
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004483 time_t tim;
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004484 char d[2], msg[80];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004485
4486 msg[0] = '\0';
4487 if (end < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004488 strcat(msg, "end<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004489 }
4490 if (end > textend) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004491 strcat(msg, "end>textend ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004492 }
4493 if (dot < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004494 strcat(msg, "dot<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004495 }
4496 if (dot > end) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004497 strcat(msg, "dot>end ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004498 }
4499 if (screenbegin < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004500 strcat(msg, "screenbegin<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004501 }
4502 if (screenbegin > end - 1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004503 strcat(msg, "screenbegin>end-1 ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004504 }
4505
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004506 if (msg[0]) {
Glenn L McGrath7127b582002-12-03 21:48:15 +00004507 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Denys Vlasenko04b52892012-06-11 13:51:38 +02004508 totalcmds, last_input_char, msg, ESC_BOLD_TEXT, ESC_NORM_TEXT);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01004509 fflush_all();
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00004510 while (safe_read(STDIN_FILENO, d, 1) > 0) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004511 if (d[0] == '\n' || d[0] == '\r')
4512 break;
4513 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004514 }
Denis Vlasenko88adfcd2007-12-22 15:40:13 +00004515 tim = time(NULL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004516 if (tim >= (oldtim + 3)) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00004517 sprintf(status_buffer,
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004518 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
4519 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
4520 oldtim = tim;
4521 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004522}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00004523#endif