aboutsummaryrefslogtreecommitdiff
path: root/editors
diff options
context:
space:
mode:
Diffstat (limited to 'editors')
-rw-r--r--editors/vi.c1503
1 files changed, 927 insertions, 576 deletions
diff --git a/editors/vi.c b/editors/vi.c
index 8181a5384..3dcde9b24 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -7,7 +7,6 @@
7 */ 7 */
8// 8//
9//Things To Do: 9//Things To Do:
10// EXINIT
11// $HOME/.exrc and ./.exrc 10// $HOME/.exrc and ./.exrc
12// add magic to search /foo.*bar 11// add magic to search /foo.*bar
13// add :help command 12// add :help command
@@ -54,6 +53,14 @@
54//config: Enable a limited set of colon commands. This does not 53//config: Enable a limited set of colon commands. This does not
55//config: provide an "ex" mode. 54//config: provide an "ex" mode.
56//config: 55//config:
56//config:config FEATURE_VI_COLON_EXPAND
57//config: bool "Expand \"%\" and \"#\" in colon commands"
58//config: default y
59//config: depends on FEATURE_VI_COLON
60//config: help
61//config: Expand the special characters \"%\" (current filename)
62//config: and \"#\" (alternate filename) in colon commands.
63//config:
57//config:config FEATURE_VI_YANKMARK 64//config:config FEATURE_VI_YANKMARK
58//config: bool "Enable yank/put commands and mark cmds" 65//config: bool "Enable yank/put commands and mark cmds"
59//config: default y 66//config: default y
@@ -160,6 +167,14 @@
160//config: and will generally malloc() larger objects and less frequently. 167//config: and will generally malloc() larger objects and less frequently.
161//config: Unless you want more (or less) frequent "undo points" while typing, 168//config: Unless you want more (or less) frequent "undo points" while typing,
162//config: you should probably leave this unchanged. 169//config: you should probably leave this unchanged.
170//config:
171//config:config FEATURE_VI_VERBOSE_STATUS
172//config: bool "Enable verbose status reporting"
173//config: default y
174//config: depends on VI
175//config: help
176//config: Enable more verbose reporting of the results of yank, change,
177//config: delete, undo and substitution commands.
163 178
164//applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP)) 179//applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP))
165 180
@@ -242,7 +257,7 @@ enum {
242//#define ESC_CURSOR_UP ESC"[A" 257//#define ESC_CURSOR_UP ESC"[A"
243//#define ESC_CURSOR_DOWN "\n" 258//#define ESC_CURSOR_DOWN "\n"
244 259
245#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK 260#if ENABLE_FEATURE_VI_DOT_CMD
246// cmds modifying text[] 261// cmds modifying text[]
247static const char modifying_cmds[] ALIGN1 = "aAcCdDiIJoOpPrRs""xX<>~"; 262static const char modifying_cmds[] ALIGN1 = "aAcCdDiIJoOpPrRs""xX<>~";
248#endif 263#endif
@@ -254,12 +269,17 @@ enum {
254 BACK = -1, // code depends on "-1" for array index 269 BACK = -1, // code depends on "-1" for array index
255 LIMITED = 0, // char_search() only current line 270 LIMITED = 0, // char_search() only current line
256 FULL = 1, // char_search() to the end/beginning of entire text 271 FULL = 1, // char_search() to the end/beginning of entire text
272 PARTIAL = 0, // buffer contains partial line
273 WHOLE = 1, // buffer contains whole lines
274 MULTI = 2, // buffer may include newlines
257 275
258 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot" 276 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
259 S_TO_WS = 2, // used in skip_thing() for moving "dot" 277 S_TO_WS = 2, // used in skip_thing() for moving "dot"
260 S_OVER_WS = 3, // used in skip_thing() for moving "dot" 278 S_OVER_WS = 3, // used in skip_thing() for moving "dot"
261 S_END_PUNCT = 4, // used in skip_thing() for moving "dot" 279 S_END_PUNCT = 4, // used in skip_thing() for moving "dot"
262 S_END_ALNUM = 5, // used in skip_thing() for moving "dot" 280 S_END_ALNUM = 5, // used in skip_thing() for moving "dot"
281
282 C_END = -1, // cursor is at end of line due to '$' command
263}; 283};
264 284
265 285
@@ -274,16 +294,38 @@ struct globals {
274 int text_size; // size of the allocated buffer 294 int text_size; // size of the allocated buffer
275 295
276 // the rest 296 // the rest
277 smallint vi_setops; 297#if ENABLE_FEATURE_VI_SETOPTS
278#define VI_AUTOINDENT 1 298 smallint vi_setops; // set by setops()
279#define VI_SHOWMATCH 2 299#define VI_AUTOINDENT (1 << 0)
280#define VI_IGNORECASE 4 300#define VI_EXPANDTAB (1 << 1)
281#define VI_ERR_METHOD 8 301#define VI_ERR_METHOD (1 << 2)
302#define VI_IGNORECASE (1 << 3)
303#define VI_SHOWMATCH (1 << 4)
304#define VI_TABSTOP (1 << 5)
282#define autoindent (vi_setops & VI_AUTOINDENT) 305#define autoindent (vi_setops & VI_AUTOINDENT)
283#define showmatch (vi_setops & VI_SHOWMATCH ) 306#define expandtab (vi_setops & VI_EXPANDTAB )
307#define err_method (vi_setops & VI_ERR_METHOD) // indicate error with beep or flash
284#define ignorecase (vi_setops & VI_IGNORECASE) 308#define ignorecase (vi_setops & VI_IGNORECASE)
285// indicate error with beep or flash 309#define showmatch (vi_setops & VI_SHOWMATCH )
286#define err_method (vi_setops & VI_ERR_METHOD) 310#define openabove (vi_setops & VI_TABSTOP )
311// order of constants and strings must match
312#define OPTS_STR \
313 "ai\0""autoindent\0" \
314 "et\0""expandtab\0" \
315 "fl\0""flash\0" \
316 "ic\0""ignorecase\0" \
317 "sm\0""showmatch\0" \
318 "ts\0""tabstop\0"
319#define set_openabove() (vi_setops |= VI_TABSTOP)
320#define clear_openabove() (vi_setops &= ~VI_TABSTOP)
321#else
322#define autoindent (0)
323#define expandtab (0)
324#define err_method (0)
325#define openabove (0)
326#define set_openabove() ((void)0)
327#define clear_openabove() ((void)0)
328#endif
287 329
288#if ENABLE_FEATURE_VI_READONLY 330#if ENABLE_FEATURE_VI_READONLY
289 smallint readonly_mode; 331 smallint readonly_mode;
@@ -313,19 +355,27 @@ struct globals {
313 // [don't make smallint!] 355 // [don't make smallint!]
314 int last_status_cksum; // hash of current status line 356 int last_status_cksum; // hash of current status line
315 char *current_filename; 357 char *current_filename;
358#if ENABLE_FEATURE_VI_COLON_EXPAND
359 char *alt_filename;
360#endif
316 char *screenbegin; // index into text[], of top line on the screen 361 char *screenbegin; // index into text[], of top line on the screen
317 char *screen; // pointer to the virtual screen buffer 362 char *screen; // pointer to the virtual screen buffer
318 int screensize; // and its size 363 int screensize; // and its size
319 int tabstop; 364 int tabstop;
320 int last_forward_char; // last char searched for with 'f' (int because of Unicode) 365 int last_search_char; // last char searched for (int because of Unicode)
366 smallint last_search_cmd; // command used to invoke last char search
321#if ENABLE_FEATURE_VI_CRASHME 367#if ENABLE_FEATURE_VI_CRASHME
322 char last_input_char; // last char read from user 368 char last_input_char; // last char read from user
323#endif 369#endif
370#if ENABLE_FEATURE_VI_UNDO_QUEUE
371 char undo_queue_state; // One of UNDO_INS, UNDO_DEL, UNDO_EMPTY
372#endif
324 373
325#if ENABLE_FEATURE_VI_DOT_CMD 374#if ENABLE_FEATURE_VI_DOT_CMD
326 smallint adding2q; // are we currently adding user input to q 375 smallint adding2q; // are we currently adding user input to q
327 int lmc_len; // length of last_modifying_cmd 376 int lmc_len; // length of last_modifying_cmd
328 char *ioq, *ioq_start; // pointer to string for get_one_char to "read" 377 char *ioq, *ioq_start; // pointer to string for get_one_char to "read"
378 int dotcnt; // number of times to repeat '.' command
329#endif 379#endif
330#if ENABLE_FEATURE_VI_SEARCH 380#if ENABLE_FEATURE_VI_SEARCH
331 char *last_search_pattern; // last pattern from a '/' or '?' search 381 char *last_search_pattern; // last pattern from a '/' or '?' search
@@ -343,8 +393,8 @@ struct globals {
343 smalluint YDreg;//,Ureg;// default delete register and orig line for "U" 393 smalluint YDreg;//,Ureg;// default delete register and orig line for "U"
344#define Ureg 27 394#define Ureg 27
345 char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27 395 char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
396 char regtype[28]; // buffer type: WHOLE, MULTI or PARTIAL
346 char *mark[28]; // user marks points somewhere in text[]- a-z and previous context '' 397 char *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
347 char *context_start, *context_end;
348#endif 398#endif
349#if ENABLE_FEATURE_VI_USE_SIGNALS 399#if ENABLE_FEATURE_VI_USE_SIGNALS
350 sigjmp_buf restart; // int_handler() jumps to location remembered here 400 sigjmp_buf restart; // int_handler() jumps to location remembered here
@@ -352,6 +402,8 @@ struct globals {
352#if !ENABLE_PLATFORM_MINGW32 402#if !ENABLE_PLATFORM_MINGW32
353 struct termios term_orig; // remember what the cooked mode was 403 struct termios term_orig; // remember what the cooked mode was
354#endif 404#endif
405 int cindex; // saved character index for up/down motion
406 smallint keep_index; // retain saved character index
355#if ENABLE_FEATURE_VI_COLON 407#if ENABLE_FEATURE_VI_COLON
356 char *initial_cmds[3]; // currently 2 entries, NULL terminated 408 char *initial_cmds[3]; // currently 2 entries, NULL terminated
357#endif 409#endif
@@ -377,26 +429,22 @@ struct globals {
377#define UNDO_DEL 1 429#define UNDO_DEL 1
378#define UNDO_INS_CHAIN 2 430#define UNDO_INS_CHAIN 2
379#define UNDO_DEL_CHAIN 3 431#define UNDO_DEL_CHAIN 3
380// UNDO_*_QUEUED must be equal to UNDO_xxx ORed with UNDO_QUEUED_FLAG 432# if ENABLE_FEATURE_VI_UNDO_QUEUE
381#define UNDO_QUEUED_FLAG 4
382#define UNDO_INS_QUEUED 4 433#define UNDO_INS_QUEUED 4
383#define UNDO_DEL_QUEUED 5 434#define UNDO_DEL_QUEUED 5
384#define UNDO_USE_SPOS 32 435# endif
385#define UNDO_EMPTY 64 436
386// Pass-through flags for functions that can be undone 437// Pass-through flags for functions that can be undone
387#define NO_UNDO 0 438#define NO_UNDO 0
388#define ALLOW_UNDO 1 439#define ALLOW_UNDO 1
389#define ALLOW_UNDO_CHAIN 2 440#define ALLOW_UNDO_CHAIN 2
390# if ENABLE_FEATURE_VI_UNDO_QUEUE 441# if ENABLE_FEATURE_VI_UNDO_QUEUE
391#define ALLOW_UNDO_QUEUED 3 442#define ALLOW_UNDO_QUEUED 3
392 char undo_queue_state;
393 int undo_q;
394 char *undo_queue_spos; // Start position of queued operation
395 char undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX];
396# else 443# else
397// If undo queuing disabled, don't invoke the missing queue logic 444// If undo queuing disabled, don't invoke the missing queue logic
398#define ALLOW_UNDO_QUEUED 1 445#define ALLOW_UNDO_QUEUED ALLOW_UNDO
399# endif 446# endif
447
400 struct undo_object { 448 struct undo_object {
401 struct undo_object *prev; // Linking back avoids list traversal (LIFO) 449 struct undo_object *prev; // Linking back avoids list traversal (LIFO)
402 int start; // Offset where the data should be restored/deleted 450 int start; // Offset where the data should be restored/deleted
@@ -404,6 +452,13 @@ struct globals {
404 uint8_t u_type; // 0=deleted, 1=inserted, 2=swapped 452 uint8_t u_type; // 0=deleted, 1=inserted, 2=swapped
405 char undo_text[1]; // text that was deleted (if deletion) 453 char undo_text[1]; // text that was deleted (if deletion)
406 } *undo_stack_tail; 454 } *undo_stack_tail;
455# if ENABLE_FEATURE_VI_UNDO_QUEUE
456#define UNDO_USE_SPOS 32
457#define UNDO_EMPTY 64
458 char *undo_queue_spos; // Start position of queued operation
459 int undo_q;
460 char undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX];
461# endif
407#endif /* ENABLE_FEATURE_VI_UNDO */ 462#endif /* ENABLE_FEATURE_VI_UNDO */
408}; 463};
409#define G (*ptr_to_globals) 464#define G (*ptr_to_globals)
@@ -429,11 +484,13 @@ struct globals {
429#define have_status_msg (G.have_status_msg ) 484#define have_status_msg (G.have_status_msg )
430#define last_status_cksum (G.last_status_cksum ) 485#define last_status_cksum (G.last_status_cksum )
431#define current_filename (G.current_filename ) 486#define current_filename (G.current_filename )
487#define alt_filename (G.alt_filename )
432#define screen (G.screen ) 488#define screen (G.screen )
433#define screensize (G.screensize ) 489#define screensize (G.screensize )
434#define screenbegin (G.screenbegin ) 490#define screenbegin (G.screenbegin )
435#define tabstop (G.tabstop ) 491#define tabstop (G.tabstop )
436#define last_forward_char (G.last_forward_char ) 492#define last_search_char (G.last_search_char )
493#define last_search_cmd (G.last_search_cmd )
437#if ENABLE_FEATURE_VI_CRASHME 494#if ENABLE_FEATURE_VI_CRASHME
438#define last_input_char (G.last_input_char ) 495#define last_input_char (G.last_input_char )
439#endif 496#endif
@@ -446,6 +503,7 @@ struct globals {
446#define lmc_len (G.lmc_len ) 503#define lmc_len (G.lmc_len )
447#define ioq (G.ioq ) 504#define ioq (G.ioq )
448#define ioq_start (G.ioq_start ) 505#define ioq_start (G.ioq_start )
506#define dotcnt (G.dotcnt )
449#define last_search_pattern (G.last_search_pattern) 507#define last_search_pattern (G.last_search_pattern)
450 508
451#define edit_file__cur_line (G.edit_file__cur_line) 509#define edit_file__cur_line (G.edit_file__cur_line)
@@ -454,11 +512,12 @@ struct globals {
454 512
455#define YDreg (G.YDreg ) 513#define YDreg (G.YDreg )
456//#define Ureg (G.Ureg ) 514//#define Ureg (G.Ureg )
515#define regtype (G.regtype )
457#define mark (G.mark ) 516#define mark (G.mark )
458#define context_start (G.context_start )
459#define context_end (G.context_end )
460#define restart (G.restart ) 517#define restart (G.restart )
461#define term_orig (G.term_orig ) 518#define term_orig (G.term_orig )
519#define cindex (G.cindex )
520#define keep_index (G.keep_index )
462#define initial_cmds (G.initial_cmds ) 521#define initial_cmds (G.initial_cmds )
463#define readbuffer (G.readbuffer ) 522#define readbuffer (G.readbuffer )
464#define scr_out_buf (G.scr_out_buf ) 523#define scr_out_buf (G.scr_out_buf )
@@ -741,6 +800,30 @@ static int next_tabstop(int col)
741 return col + ((tabstop - 1) - (col % tabstop)); 800 return col + ((tabstop - 1) - (col % tabstop));
742} 801}
743 802
803static int prev_tabstop(int col)
804{
805 return col - ((col % tabstop) ?: tabstop);
806}
807
808static int next_column(char c, int co)
809{
810 if (c == '\t')
811 co = next_tabstop(co);
812 else if ((unsigned char)c < ' ' || c == 0x7f)
813 co++; // display as ^X, use 2 columns
814 return co + 1;
815}
816
817static int get_column(char *p)
818{
819 const char *r;
820 int co = 0;
821
822 for (r = begin_line(p); r < p; r++)
823 co = next_column(*r, co);
824 return co;
825}
826
744//----- Erase the Screen[] memory ------------------------------ 827//----- Erase the Screen[] memory ------------------------------
745static void screen_erase(void) 828static void screen_erase(void)
746{ 829{
@@ -769,7 +852,7 @@ static void new_screen(int ro, int co)
769} 852}
770 853
771//----- Synchronize the cursor to Dot -------------------------- 854//----- Synchronize the cursor to Dot --------------------------
772static NOINLINE void sync_cursor(char *d, int *row, int *col) 855static void sync_cursor(char *d, int *row, int *col)
773{ 856{
774 char *beg_cur; // begin and end of "d" line 857 char *beg_cur; // begin and end of "d" line
775 char *tp; 858 char *tp;
@@ -817,21 +900,16 @@ static NOINLINE void sync_cursor(char *d, int *row, int *col)
817 900
818 // find out what col "d" is on 901 // find out what col "d" is on
819 co = 0; 902 co = 0;
820 while (tp < d) { // drive "co" to correct column 903 do { // drive "co" to correct column
821 if (*tp == '\n') //vda || *tp == '\0') 904 if (*tp == '\n') //vda || *tp == '\0')
822 break; 905 break;
823 if (*tp == '\t') { 906 co = next_column(*tp, co) - 1;
824 // handle tabs like real vi 907 // inserting text before a tab, don't include its position
825 if (d == tp && cmd_mode) { 908 if (cmd_mode && tp == d - 1 && *d == '\t') {
826 break; 909 co++;
827 } 910 break;
828 co = next_tabstop(co);
829 } else if ((unsigned char)*tp < ' ' || *tp == 0x7f) {
830 co++; // display as ^X, use 2 columns
831 } 911 }
832 co++; 912 } while (tp++ < d && ++co);
833 tp++;
834 }
835 913
836 // "co" is the column where "dot" is. 914 // "co" is the column where "dot" is.
837 // The screen has "columns" columns. 915 // The screen has "columns" columns.
@@ -1012,6 +1090,9 @@ static void refresh(int full_screen)
1012 1090
1013 place_cursor(crow, ccol); 1091 place_cursor(crow, ccol);
1014 1092
1093 if (!keep_index)
1094 cindex = ccol + offset;
1095
1015 old_offset = offset; 1096 old_offset = offset;
1016#undef old_offset 1097#undef old_offset
1017} 1098}
@@ -1097,8 +1178,8 @@ static int get_one_char(void)
1097 } 1178 }
1098 // we are adding STDIN chars to q. 1179 // we are adding STDIN chars to q.
1099 c = readit(); 1180 c = readit();
1100 if (lmc_len >= ARRAY_SIZE(last_modifying_cmd) - 1) { 1181 if (lmc_len >= ARRAY_SIZE(last_modifying_cmd) - 2) {
1101 // last_modifying_cmd[] is too small, can't remeber the cmd 1182 // last_modifying_cmd[] is too small, can't remember the cmd
1102 // - drop it 1183 // - drop it
1103 adding2q = 0; 1184 adding2q = 0;
1104 lmc_len = 0; 1185 lmc_len = 0;
@@ -1111,6 +1192,27 @@ static int get_one_char(void)
1111# define get_one_char() readit() 1192# define get_one_char() readit()
1112#endif 1193#endif
1113 1194
1195// Get type of thing to operate on and adjust count
1196static int get_motion_char(void)
1197{
1198 int c, cnt;
1199
1200 c = get_one_char();
1201 if (isdigit(c)) {
1202 if (c != '0') {
1203 // get any non-zero motion count
1204 for (cnt = 0; isdigit(c); c = get_one_char())
1205 cnt = cnt * 10 + (c - '0');
1206 cmdcnt = (cmdcnt ?: 1) * cnt;
1207 } else {
1208 // ensure standalone '0' works
1209 cmdcnt = 0;
1210 }
1211 }
1212
1213 return c;
1214}
1215
1114// Get input line (uses "status line" area) 1216// Get input line (uses "status line" area)
1115static char *get_input_line(const char *prompt) 1217static char *get_input_line(const char *prompt)
1116{ 1218{
@@ -1123,10 +1225,10 @@ static char *get_input_line(const char *prompt)
1123 strcpy(buf, prompt); 1225 strcpy(buf, prompt);
1124 last_status_cksum = 0; // force status update 1226 last_status_cksum = 0; // force status update
1125 go_bottom_and_clear_to_eol(); 1227 go_bottom_and_clear_to_eol();
1126 write1(prompt); // write out the :, /, or ? prompt 1228 write1(buf); // write out the :, /, or ? prompt
1127 1229
1128 i = strlen(buf); 1230 i = strlen(buf);
1129 while (i < MAX_INPUT_LEN) { 1231 while (i < MAX_INPUT_LEN - 1) {
1130 c = get_one_char(); 1232 c = get_one_char();
1131 if (c == '\n' || c == '\r' || c == 27) 1233 if (c == '\n' || c == '\r' || c == 27)
1132 break; // this is end of input 1234 break; // this is end of input
@@ -1136,8 +1238,8 @@ static char *get_input_line(const char *prompt)
1136 if (c == 8 || c == 127) { 1238 if (c == 8 || c == 127) {
1137#endif 1239#endif
1138 // user wants to erase prev char 1240 // user wants to erase prev char
1139 buf[--i] = '\0';
1140 write1("\b \b"); // erase char on screen 1241 write1("\b \b"); // erase char on screen
1242 buf[--i] = '\0';
1141 if (i <= 0) // user backs up before b-o-l, exit 1243 if (i <= 0) // user backs up before b-o-l, exit
1142 break; 1244 break;
1143 } else if (c > 0 && c < 256) { // exclude Unicode 1245 } else if (c > 0 && c < 256) { // exclude Unicode
@@ -1343,15 +1445,20 @@ static void not_implemented(const char *s)
1343 1445
1344//----- Block insert/delete, undo ops -------------------------- 1446//----- Block insert/delete, undo ops --------------------------
1345#if ENABLE_FEATURE_VI_YANKMARK 1447#if ENABLE_FEATURE_VI_YANKMARK
1346static char *text_yank(char *p, char *q, int dest) // copy text into a register 1448// copy text into a register
1449static char *text_yank(char *p, char *q, int dest, int buftype)
1347{ 1450{
1451 char *oldreg = reg[dest];
1348 int cnt = q - p; 1452 int cnt = q - p;
1349 if (cnt < 0) { // they are backwards- reverse them 1453 if (cnt < 0) { // they are backwards- reverse them
1350 p = q; 1454 p = q;
1351 cnt = -cnt; 1455 cnt = -cnt;
1352 } 1456 }
1353 free(reg[dest]); // if already a yank register, free it 1457 // Don't free register yet. This prevents the memory allocator
1458 // from reusing the free block so we can detect if it's changed.
1354 reg[dest] = xstrndup(p, cnt + 1); 1459 reg[dest] = xstrndup(p, cnt + 1);
1460 regtype[dest] = buftype;
1461 free(oldreg);
1355 return p; 1462 return p;
1356} 1463}
1357 1464
@@ -1371,18 +1478,10 @@ static char what_reg(void)
1371 1478
1372static void check_context(char cmd) 1479static void check_context(char cmd)
1373{ 1480{
1374 // A context is defined to be "modifying text" 1481 // Certain movement commands update the context.
1375 // Any modifying command establishes a new context. 1482 if (strchr(":%{}'GHLMz/?Nn", cmd) != NULL) {
1376 1483 mark[27] = mark[26]; // move cur to prev
1377 if (dot < context_start || dot > context_end) { 1484 mark[26] = dot; // move local to cur
1378 if (strchr(modifying_cmds, cmd) != NULL) {
1379 // we are trying to modify text[]- make this the current context
1380 mark[27] = mark[26]; // move cur to prev
1381 mark[26] = dot; // move local to cur
1382 context_start = prev_line(prev_line(dot));
1383 context_end = next_line(next_line(dot));
1384 //loiter= start_loiter= now;
1385 }
1386 } 1485 }
1387} 1486}
1388 1487
@@ -1397,15 +1496,29 @@ static char *swap_context(char *p) // goto new context for '' command make this
1397 tmp = mark[27]; 1496 tmp = mark[27];
1398 mark[27] = p; 1497 mark[27] = p;
1399 mark[26] = p = tmp; 1498 mark[26] = p = tmp;
1400 context_start = prev_line(prev_line(prev_line(p)));
1401 context_end = next_line(next_line(next_line(p)));
1402 } 1499 }
1403 return p; 1500 return p;
1404} 1501}
1502
1503# if ENABLE_FEATURE_VI_VERBOSE_STATUS
1504static void yank_status(const char *op, const char *p, int cnt)
1505{
1506 int lines, chars;
1507
1508 lines = chars = 0;
1509 while (*p) {
1510 ++chars;
1511 if (*p++ == '\n')
1512 ++lines;
1513 }
1514 status_line("%s %d lines (%d chars) from [%c]",
1515 op, lines * cnt, chars * cnt, what_reg());
1516}
1517# endif
1405#endif /* FEATURE_VI_YANKMARK */ 1518#endif /* FEATURE_VI_YANKMARK */
1406 1519
1407#if ENABLE_FEATURE_VI_UNDO 1520#if ENABLE_FEATURE_VI_UNDO
1408static void undo_push(char *, unsigned, unsigned char); 1521static void undo_push(char *, unsigned, int);
1409#endif 1522#endif
1410 1523
1411// open a hole in text[] 1524// open a hole in text[]
@@ -1532,9 +1645,12 @@ static void flush_undo_data(void)
1532 1645
1533// Undo functions and hooks added by Jody Bruchon (jody@jodybruchon.com) 1646// Undo functions and hooks added by Jody Bruchon (jody@jodybruchon.com)
1534// Add to the undo stack 1647// Add to the undo stack
1535static void undo_push(char *src, unsigned length, uint8_t u_type) 1648static void undo_push(char *src, unsigned length, int u_type)
1536{ 1649{
1537 struct undo_object *undo_entry; 1650 struct undo_object *undo_entry;
1651# if ENABLE_FEATURE_VI_UNDO_QUEUE
1652 int use_spos = u_type & UNDO_USE_SPOS;
1653# endif
1538 1654
1539 // "u_type" values 1655 // "u_type" values
1540 // UNDO_INS: insertion, undo will remove from buffer 1656 // UNDO_INS: insertion, undo will remove from buffer
@@ -1543,8 +1659,8 @@ static void undo_push(char *src, unsigned length, uint8_t u_type)
1543 // The CHAIN operations are for handling multiple operations that the user 1659 // The CHAIN operations are for handling multiple operations that the user
1544 // performs with a single action, i.e. REPLACE mode or find-and-replace commands 1660 // performs with a single action, i.e. REPLACE mode or find-and-replace commands
1545 // UNDO_{INS,DEL}_QUEUED: If queuing feature is enabled, allow use of the queue 1661 // UNDO_{INS,DEL}_QUEUED: If queuing feature is enabled, allow use of the queue
1546 // for the INS/DEL operation. The raw values should be equal to the values of 1662 // for the INS/DEL operation.
1547 // UNDO_{INS,DEL} ORed with UNDO_QUEUED_FLAG 1663 // UNDO_{INS,DEL} ORed with UNDO_USE_SPOS: commit the undo queue
1548 1664
1549# if ENABLE_FEATURE_VI_UNDO_QUEUE 1665# if ENABLE_FEATURE_VI_UNDO_QUEUE
1550 // This undo queuing functionality groups multiple character typing or backspaces 1666 // This undo queuing functionality groups multiple character typing or backspaces
@@ -1597,9 +1713,7 @@ static void undo_push(char *src, unsigned length, uint8_t u_type)
1597 } 1713 }
1598 break; 1714 break;
1599 } 1715 }
1600# else 1716 u_type &= ~UNDO_USE_SPOS;
1601 // If undo queuing is disabled, ignore the queuing flag entirely
1602 u_type = u_type & ~UNDO_QUEUED_FLAG;
1603# endif 1717# endif
1604 1718
1605 // Allocate a new undo object 1719 // Allocate a new undo object
@@ -1616,12 +1730,11 @@ static void undo_push(char *src, unsigned length, uint8_t u_type)
1616 } 1730 }
1617 undo_entry->length = length; 1731 undo_entry->length = length;
1618# if ENABLE_FEATURE_VI_UNDO_QUEUE 1732# if ENABLE_FEATURE_VI_UNDO_QUEUE
1619 if ((u_type & UNDO_USE_SPOS) != 0) { 1733 if (use_spos) {
1620 undo_entry->start = undo_queue_spos - text; // use start position from queue 1734 undo_entry->start = undo_queue_spos - text; // use start position from queue
1621 } else { 1735 } else {
1622 undo_entry->start = src - text; // use offset from start of text buffer 1736 undo_entry->start = src - text; // use offset from start of text buffer
1623 } 1737 }
1624 u_type = (u_type & ~UNDO_USE_SPOS);
1625# else 1738# else
1626 undo_entry->start = src - text; 1739 undo_entry->start = src - text;
1627# endif 1740# endif
@@ -1674,10 +1787,12 @@ static void undo_pop(void)
1674 u_start = text + undo_entry->start; 1787 u_start = text + undo_entry->start;
1675 text_hole_make(u_start, undo_entry->length); 1788 text_hole_make(u_start, undo_entry->length);
1676 memcpy(u_start, undo_entry->undo_text, undo_entry->length); 1789 memcpy(u_start, undo_entry->undo_text, undo_entry->length);
1790# if ENABLE_FEATURE_VI_VERBOSE_STATUS
1677 status_line("Undo [%d] %s %d chars at position %d", 1791 status_line("Undo [%d] %s %d chars at position %d",
1678 modified_count, "restored", 1792 modified_count, "restored",
1679 undo_entry->length, undo_entry->start 1793 undo_entry->length, undo_entry->start
1680 ); 1794 );
1795# endif
1681 break; 1796 break;
1682 case UNDO_INS: 1797 case UNDO_INS:
1683 case UNDO_INS_CHAIN: 1798 case UNDO_INS_CHAIN:
@@ -1685,10 +1800,12 @@ static void undo_pop(void)
1685 u_start = undo_entry->start + text; 1800 u_start = undo_entry->start + text;
1686 u_end = u_start - 1 + undo_entry->length; 1801 u_end = u_start - 1 + undo_entry->length;
1687 text_hole_delete(u_start, u_end, NO_UNDO); 1802 text_hole_delete(u_start, u_end, NO_UNDO);
1803# if ENABLE_FEATURE_VI_VERBOSE_STATUS
1688 status_line("Undo [%d] %s %d chars at position %d", 1804 status_line("Undo [%d] %s %d chars at position %d",
1689 modified_count, "deleted", 1805 modified_count, "deleted",
1690 undo_entry->length, undo_entry->start 1806 undo_entry->length, undo_entry->start
1691 ); 1807 );
1808# endif
1692 break; 1809 break;
1693 } 1810 }
1694 repeat = 0; 1811 repeat = 0;
@@ -1752,17 +1869,11 @@ static char *move_to_col(char *p, int l)
1752 1869
1753 p = begin_line(p); 1870 p = begin_line(p);
1754 co = 0; 1871 co = 0;
1755 while (co < l && p < end) { 1872 do {
1756 if (*p == '\n') //vda || *p == '\0') 1873 if (*p == '\n') //vda || *p == '\0')
1757 break; 1874 break;
1758 if (*p == '\t') { 1875 co = next_column(*p, co);
1759 co = next_tabstop(co); 1876 } while (co <= l && p++ < end);
1760 } else if (*p < ' ' || *p == 127) {
1761 co++; // display as ^X, use 2 columns
1762 }
1763 co++;
1764 p++;
1765 }
1766 return p; 1877 return p;
1767} 1878}
1768 1879
@@ -1785,6 +1896,33 @@ static void dot_skip_over_ws(void)
1785 dot++; 1896 dot++;
1786} 1897}
1787 1898
1899static void dot_to_char(int cmd)
1900{
1901 char *q = dot;
1902 int dir = islower(cmd) ? FORWARD : BACK;
1903
1904 if (last_search_char == 0)
1905 return;
1906
1907 do {
1908 do {
1909 q += dir;
1910 if ((dir == FORWARD ? q > end - 1 : q < text) || *q == '\n') {
1911 indicate_error();
1912 return;
1913 }
1914 } while (*q != last_search_char);
1915 } while (--cmdcnt > 0);
1916
1917 dot = q;
1918
1919 // place cursor before/after char as required
1920 if (cmd == 't')
1921 dot_left();
1922 else if (cmd == 'T')
1923 dot_right();
1924}
1925
1788static void dot_scroll(int cnt, int dir) 1926static void dot_scroll(int cnt, int dir)
1789{ 1927{
1790 char *q; 1928 char *q;
@@ -1827,13 +1965,9 @@ static char *bound_dot(char *p) // make sure text[0] <= P < "end"
1827static void start_new_cmd_q(char c) 1965static void start_new_cmd_q(char c)
1828{ 1966{
1829 // get buffer for new cmd 1967 // get buffer for new cmd
1830 // if there is a current cmd count put it in the buffer first 1968 dotcnt = cmdcnt ?: 1;
1831 if (cmdcnt > 0) { 1969 last_modifying_cmd[0] = c;
1832 lmc_len = sprintf(last_modifying_cmd, "%u%c", cmdcnt, c); 1970 lmc_len = 1;
1833 } else { // just save char c onto queue
1834 last_modifying_cmd[0] = c;
1835 lmc_len = 1;
1836 }
1837 adding2q = 1; 1971 adding2q = 1;
1838} 1972}
1839static void end_cmd_q(void) 1973static void end_cmd_q(void)
@@ -1848,12 +1982,11 @@ static void end_cmd_q(void)
1848#endif /* FEATURE_VI_DOT_CMD */ 1982#endif /* FEATURE_VI_DOT_CMD */
1849 1983
1850// copy text into register, then delete text. 1984// copy text into register, then delete text.
1851// if dist <= 0, do not include, or go past, a NewLine
1852// 1985//
1853#if !ENABLE_FEATURE_VI_UNDO 1986#if !ENABLE_FEATURE_VI_UNDO
1854#define yank_delete(a,b,c,d,e) yank_delete(a,b,c,d) 1987#define yank_delete(a,b,c,d,e) yank_delete(a,b,c,d)
1855#endif 1988#endif
1856static char *yank_delete(char *start, char *stop, int dist, int yf, int undo) 1989static char *yank_delete(char *start, char *stop, int buftype, int yf, int undo)
1857{ 1990{
1858 char *p; 1991 char *p;
1859 1992
@@ -1864,22 +1997,11 @@ static char *yank_delete(char *start, char *stop, int dist, int yf, int undo)
1864 start = stop; 1997 start = stop;
1865 stop = p; 1998 stop = p;
1866 } 1999 }
1867 if (dist <= 0) { 2000 if (buftype == PARTIAL && *start == '\n')
1868 // we cannot cross NL boundaries 2001 return start;
1869 p = start;
1870 if (*p == '\n')
1871 return p;
1872 // dont go past a NewLine
1873 for (; p + 1 <= stop; p++) {
1874 if (p[1] == '\n') {
1875 stop = p; // "stop" just before NewLine
1876 break;
1877 }
1878 }
1879 }
1880 p = start; 2002 p = start;
1881#if ENABLE_FEATURE_VI_YANKMARK 2003#if ENABLE_FEATURE_VI_YANKMARK
1882 text_yank(start, stop, YDreg); 2004 text_yank(start, stop, YDreg, buftype);
1883#endif 2005#endif
1884 if (yf == YANKDEL) { 2006 if (yf == YANKDEL) {
1885 p = text_hole_delete(start, stop, undo); 2007 p = text_hole_delete(start, stop, undo);
@@ -1949,6 +2071,11 @@ static int file_insert(const char *fn, char *p, int initial)
1949#endif 2071#endif
1950 status_line_bold("can't read '%s'", fn); 2072 status_line_bold("can't read '%s'", fn);
1951 } 2073 }
2074# if ENABLE_FEATURE_VI_UNDO
2075 else {
2076 undo_push_insert(p, size, ALLOW_UNDO);
2077 }
2078# endif
1952 fi: 2079 fi:
1953 close(fd); 2080 close(fd);
1954 2081
@@ -2033,6 +2160,11 @@ static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at
2033#endif 2160#endif
2034static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' 2161static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2035{ 2162{
2163#if ENABLE_FEATURE_VI_SETOPTS
2164 char *q;
2165 size_t len;
2166#endif
2167
2036 if (c == 22) { // Is this an ctrl-V? 2168 if (c == 22) { // Is this an ctrl-V?
2037 p += stupid_insert(p, '^'); // use ^ to indicate literal next 2169 p += stupid_insert(p, '^'); // use ^ to indicate literal next
2038 refresh(FALSE); // show the ^ 2170 refresh(FALSE); // show the ^
@@ -2053,6 +2185,35 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2053 if ((p[-1] != '\n') && (dot > text)) { 2185 if ((p[-1] != '\n') && (dot > text)) {
2054 p--; 2186 p--;
2055 } 2187 }
2188 } else if (c == 4) { // ctrl-D reduces indentation
2189 int prev;
2190 char *r, *bol;
2191 bol = begin_line(p);
2192 for (r = bol; r < end_line(p); ++r) {
2193 if (!isblank(*r))
2194 break;
2195 }
2196
2197 prev = prev_tabstop(get_column(r));
2198 while (r > bol && get_column(r) > prev) {
2199 if (p > bol)
2200 p--;
2201 r--;
2202 r = text_hole_delete(r, r, ALLOW_UNDO_QUEUED);
2203 }
2204#if ENABLE_FEATURE_VI_SETOPTS
2205 } else if (c == '\t' && expandtab) { // expand tab
2206 int col = get_column(p);
2207 col = next_tabstop(col) - col + 1;
2208 while (col--) {
2209# if ENABLE_FEATURE_VI_UNDO
2210 undo_push_insert(p, 1, undo);
2211# else
2212 modified_count++;
2213# endif
2214 p += 1 + stupid_insert(p, ' ');
2215 }
2216#endif
2056#if !ENABLE_PLATFORM_MINGW32 2217#if !ENABLE_PLATFORM_MINGW32
2057 } else if (c == term_orig.c_cc[VERASE] || c == 8 || c == 127) { // Is this a BS 2218 } else if (c == term_orig.c_cc[VERASE] || c == 8 || c == 127) { // Is this a BS
2058#else 2219#else
@@ -2081,18 +2242,21 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2081 showmatching(p - 1); 2242 showmatching(p - 1);
2082 } 2243 }
2083 if (autoindent && c == '\n') { // auto indent the new line 2244 if (autoindent && c == '\n') { // auto indent the new line
2084 char *q; 2245 // use current/previous line as template
2085 size_t len; 2246 q = openabove ? p : prev_line(p);
2086 q = prev_line(p); // use prev line as template
2087 len = strspn(q, " \t"); // space or tab 2247 len = strspn(q, " \t"); // space or tab
2248 if (openabove) {
2249 p--; // this replaces dot_prev() in do_cmd()
2250 q += len; // template will be shifted by text_hole_make()
2251 }
2088 if (len) { 2252 if (len) {
2089 uintptr_t bias; 2253 uintptr_t bias;
2090 bias = text_hole_make(p, len); 2254 bias = text_hole_make(p, len);
2091 p += bias; 2255 p += bias;
2092 q += bias; 2256 q += bias;
2093#if ENABLE_FEATURE_VI_UNDO 2257# if ENABLE_FEATURE_VI_UNDO
2094 undo_push_insert(p, len, undo); 2258 undo_push_insert(p, len, undo);
2095#endif 2259# endif
2096 memcpy(p, q, len); 2260 memcpy(p, q, len);
2097 p += len; 2261 p += len;
2098 } 2262 }
@@ -2102,6 +2266,41 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2102 return p; 2266 return p;
2103} 2267}
2104 2268
2269#if ENABLE_FEATURE_VI_COLON_EXPAND
2270static void init_filename(char *fn)
2271{
2272 char *copy = xstrdup(fn);
2273
2274 if (current_filename == NULL) {
2275 current_filename = copy;
2276 } else {
2277 free(alt_filename);
2278 alt_filename = copy;
2279 }
2280}
2281#else
2282# define init_filename(f) ((void)(0))
2283#endif
2284
2285static void update_filename(char *fn)
2286{
2287#if ENABLE_FEATURE_VI_COLON_EXPAND
2288 if (fn == NULL)
2289 return;
2290
2291 if (current_filename == NULL || strcmp(fn, current_filename) != 0) {
2292 free(alt_filename);
2293 alt_filename = current_filename;
2294 current_filename = xstrdup(fn);
2295 }
2296#else
2297 if (fn != current_filename) {
2298 free(current_filename);
2299 current_filename = xstrdup(fn);
2300 }
2301#endif
2302}
2303
2105// read text from file or create an empty buf 2304// read text from file or create an empty buf
2106// will also update current_filename 2305// will also update current_filename
2107static int init_text_buffer(char *fn) 2306static int init_text_buffer(char *fn)
@@ -2113,10 +2312,7 @@ static int init_text_buffer(char *fn)
2113 text_size = 10240; 2312 text_size = 10240;
2114 screenbegin = dot = end = text = xzalloc(text_size); 2313 screenbegin = dot = end = text = xzalloc(text_size);
2115 2314
2116 if (fn != current_filename) { 2315 update_filename(fn);
2117 free(current_filename);
2118 current_filename = xstrdup(fn);
2119 }
2120 rc = file_insert(fn, text, 1); 2316 rc = file_insert(fn, text, 1);
2121 if (rc < 0) { 2317 if (rc < 0) {
2122 // file doesnt exist. Start empty buf with dummy line 2318 // file doesnt exist. Start empty buf with dummy line
@@ -2153,16 +2349,6 @@ static uintptr_t string_insert(char *p, const char *s, int undo) // insert the s
2153 bias = text_hole_make(p, i); 2349 bias = text_hole_make(p, i);
2154 p += bias; 2350 p += bias;
2155 memcpy(p, s, i); 2351 memcpy(p, s, i);
2156#if ENABLE_FEATURE_VI_YANKMARK
2157 {
2158 int cnt;
2159 for (cnt = 0; *s != '\0'; s++) {
2160 if (*s == '\n')
2161 cnt++;
2162 }
2163 status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2164 }
2165#endif
2166 return bias; 2352 return bias;
2167} 2353}
2168#endif 2354#endif
@@ -2312,112 +2498,211 @@ static char *char_search(char *p, const char *pat, int dir_and_range)
2312 2498
2313//----- The Colon commands ------------------------------------- 2499//----- The Colon commands -------------------------------------
2314#if ENABLE_FEATURE_VI_COLON 2500#if ENABLE_FEATURE_VI_COLON
2315static char *get_one_address(char *p, int *addr) // get colon addr, if present 2501static char *get_one_address(char *p, int *result) // get colon addr, if present
2316{ 2502{
2317 int st; 2503 int st, num, sign, addr, new_addr;
2318 char *q; 2504# if ENABLE_FEATURE_VI_YANKMARK || ENABLE_FEATURE_VI_SEARCH
2319 IF_FEATURE_VI_YANKMARK(char c;) 2505 char *q, c;
2506# endif
2507 IF_FEATURE_VI_SEARCH(int dir;)
2320 2508
2321 *addr = -1; // assume no addr 2509 addr = -1; // assume no addr
2322 if (*p == '.') { // the current line 2510 sign = 0;
2323 p++; 2511 for (;;) {
2324 q = begin_line(dot); 2512 new_addr = -1;
2325 *addr = count_lines(text, q); 2513 if (isblank(*p)) {
2326 } 2514 p++;
2327#if ENABLE_FEATURE_VI_YANKMARK 2515 } else if (*p == '.') { // the current line
2328 else if (*p == '\'') { // is this a mark addr 2516 p++;
2329 p++; 2517 new_addr = count_lines(text, dot);
2330 c = tolower(*p); 2518 }
2331 p++; 2519# if ENABLE_FEATURE_VI_YANKMARK
2332 if (c >= 'a' && c <= 'z') { 2520 else if (*p == '\'') { // is this a mark addr
2333 // we have a mark 2521 p++;
2334 c = c - 'a'; 2522 c = tolower(*p);
2335 q = mark[(unsigned char) c]; 2523 p++;
2336 if (q != NULL) { // is mark valid 2524 q = NULL;
2337 *addr = count_lines(text, q); 2525 if (c >= 'a' && c <= 'z') {
2526 // we have a mark
2527 c = c - 'a';
2528 q = mark[(unsigned char) c];
2338 } 2529 }
2530 if (q == NULL) // is mark valid
2531 return NULL;
2532 new_addr = count_lines(text, q);
2339 } 2533 }
2340 } 2534# endif
2341#endif 2535# if ENABLE_FEATURE_VI_SEARCH
2342#if ENABLE_FEATURE_VI_SEARCH 2536 else if (*p == '/' || *p == '?') { // a search pattern
2343 else if (*p == '/') { // a search pattern 2537 c = *p;
2344 q = strchrnul(p + 1, '/'); 2538 q = strchrnul(p + 1, c);
2345 if (p + 1 != q) { 2539 if (p + 1 != q) {
2346 // save copy of new pattern 2540 // save copy of new pattern
2347 free(last_search_pattern); 2541 free(last_search_pattern);
2348 last_search_pattern = xstrndup(p, q - p); 2542 last_search_pattern = xstrndup(p, q - p);
2543 }
2544 p = q;
2545 if (*p == c)
2546 p++;
2547 if (c == '/') {
2548 q = next_line(dot);
2549 dir = (FORWARD << 1) | FULL;
2550 } else {
2551 q = begin_line(dot);
2552 dir = ((unsigned)BACK << 1) | FULL;
2553 }
2554 q = char_search(q, last_search_pattern + 1, dir);
2555 if (q == NULL)
2556 return NULL;
2557 new_addr = count_lines(text, q);
2349 } 2558 }
2350 p = q; 2559# endif
2351 if (*p == '/') 2560 else if (*p == '$') { // the last line in file
2352 p++; 2561 p++;
2353 q = char_search(next_line(dot), last_search_pattern + 1, 2562 new_addr = count_lines(text, end - 1);
2354 (FORWARD << 1) | FULL); 2563 } else if (isdigit(*p)) {
2355 if (q != NULL) { 2564 sscanf(p, "%d%n", &num, &st);
2356 *addr = count_lines(text, q); 2565 p += st;
2566 if (addr < 0) { // specific line number
2567 addr = num;
2568 } else { // offset from current addr
2569 addr += sign >= 0 ? num : -num;
2570 }
2571 sign = 0;
2572 } else if (*p == '-' || *p == '+') {
2573 sign = *p++ == '-' ? -1 : 1;
2574 if (addr < 0) { // default address is dot
2575 addr = count_lines(text, dot);
2576 }
2577 } else {
2578 addr += sign; // consume unused trailing sign
2579 break;
2580 }
2581 if (new_addr >= 0) {
2582 if (addr >= 0) // only one new address per expression
2583 return NULL;
2584 addr = new_addr;
2357 } 2585 }
2358 } 2586 }
2359#endif 2587 *result = addr;
2360 else if (*p == '$') { // the last line in file
2361 p++;
2362 q = begin_line(end - 1);
2363 *addr = count_lines(text, q);
2364 } else if (isdigit(*p)) { // specific line number
2365 sscanf(p, "%d%n", addr, &st);
2366 p += st;
2367 } else {
2368 // unrecognized address - assume -1
2369 *addr = -1;
2370 }
2371 return p; 2588 return p;
2372} 2589}
2373 2590
2374static char *get_address(char *p, int *b, int *e) // get two colon addrs, if present 2591# define GET_ADDRESS 0
2592# define GET_SEPARATOR 1
2593
2594// Read line addresses for a colon command. The user can enter as
2595// many as they like but only the last two will be used.
2596static char *get_address(char *p, int *b, int *e)
2375{ 2597{
2598 int state = GET_ADDRESS;
2599 char *save_dot = dot;
2600
2376 //----- get the address' i.e., 1,3 'a,'b ----- 2601 //----- get the address' i.e., 1,3 'a,'b -----
2377 // get FIRST addr, if present 2602 for (;;) {
2378 while (isblank(*p)) 2603 if (isblank(*p)) {
2379 p++; // skip over leading spaces 2604 p++;
2380 if (*p == '%') { // alias for 1,$ 2605 } else if (*p == '%' && state == GET_ADDRESS) { // alias for 1,$
2381 p++;
2382 *b = 1;
2383 *e = count_lines(text, end-1);
2384 goto ga0;
2385 }
2386 p = get_one_address(p, b);
2387 while (isblank(*p))
2388 p++;
2389 if (*p == ',') { // is there a address separator
2390 p++;
2391 while (isblank(*p))
2392 p++; 2606 p++;
2393 // get SECOND addr, if present 2607 *b = 1;
2394 p = get_one_address(p, e); 2608 *e = count_lines(text, end-1);
2609 state = GET_SEPARATOR;
2610 } else if (state == GET_SEPARATOR && (*p == ',' || *p == ';')) {
2611 if (*p == ';')
2612 dot = find_line(*e);
2613 p++;
2614 *b = *e;
2615 state = GET_ADDRESS;
2616 } else if (state == GET_ADDRESS) {
2617 p = get_one_address(p, e);
2618 if (p == NULL)
2619 break;
2620 state = GET_SEPARATOR;
2621 } else {
2622 if (state == GET_SEPARATOR && *b >= 0 && *e < 0)
2623 *e = count_lines(text, dot);
2624 break;
2625 }
2395 } 2626 }
2396 ga0: 2627 dot = save_dot;
2397 while (isblank(*p))
2398 p++; // skip over trailing spaces
2399 return p; 2628 return p;
2400} 2629}
2401 2630
2402#if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS 2631# if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
2403static void setops(const char *args, const char *opname, int flg_no, 2632static void setops(char *args, int flg_no)
2404 const char *short_opname, int opt)
2405{ 2633{
2406 const char *a = args + flg_no; 2634 char *eq;
2407 int l = strlen(opname) - 1; // opname have + ' ' 2635 int index;
2636
2637 eq = strchr(args, '=');
2638 if (eq) *eq = '\0';
2639 index = index_in_strings(OPTS_STR, args + flg_no);
2640 if (eq) *eq = '=';
2641 if (index < 0) {
2642 bad:
2643 status_line_bold("bad option: %s", args);
2644 return;
2645 }
2408 2646
2409 // maybe strncmp? we had tons of erroneous strncasecmp's... 2647 index = 1 << (index >> 1); // convert to VI_bit
2410 if (strncasecmp(a, opname, l) == 0 2648
2411 || strncasecmp(a, short_opname, 2) == 0 2649 if (index & VI_TABSTOP) {
2412 ) { 2650 // don't set this bit in vi_setops, it's reused as 'openabove'
2413 if (flg_no) 2651 int t;
2414 vi_setops &= ~opt; 2652 if (!eq || flg_no) // no "=NNN" or it is "notabstop"?
2415 else 2653 goto bad;
2416 vi_setops |= opt; 2654 t = bb_strtou(eq + 1, NULL, 10);
2655 if (t <= 0 || t > MAX_TABSTOP)
2656 goto bad;
2657 tabstop = t;
2658 return;
2659 }
2660 if (eq) goto bad; // boolean option has "="?
2661 if (flg_no) {
2662 vi_setops &= ~index;
2663 } else {
2664 vi_setops |= index;
2417 } 2665 }
2418} 2666}
2419#endif 2667# endif
2420 2668
2669# if ENABLE_FEATURE_VI_COLON_EXPAND
2670static char *expand_args(char *args)
2671{
2672 char *s, *t;
2673 const char *replace;
2674
2675 args = xstrdup(args);
2676 for (s = args; *s; s++) {
2677 if (*s == '%') {
2678 replace = current_filename;
2679 } else if (*s == '#') {
2680 replace = alt_filename;
2681 } else {
2682 if (*s == '\\' && s[1] != '\0') {
2683 for (t = s++; *t; t++)
2684 *t = t[1];
2685 }
2686 continue;
2687 }
2688
2689 if (replace == NULL) {
2690 free(args);
2691 status_line_bold("No previous filename");
2692 return NULL;
2693 }
2694
2695 *s = '\0';
2696 t = xasprintf("%s%s%s", args, replace, s+1);
2697 s = t + (s - args) + strlen(replace);
2698 free(args);
2699 args = t;
2700 }
2701 return args;
2702}
2703# else
2704# define expand_args(a) (a)
2705# endif
2421#endif /* FEATURE_VI_COLON */ 2706#endif /* FEATURE_VI_COLON */
2422 2707
2423// buf must be no longer than MAX_INPUT_LEN! 2708// buf must be no longer than MAX_INPUT_LEN!
@@ -2434,7 +2719,7 @@ static void colon(char *buf)
2434 if (cnt == 0) 2719 if (cnt == 0)
2435 return; 2720 return;
2436 if (strncmp(p, "quit", cnt) == 0 2721 if (strncmp(p, "quit", cnt) == 0
2437 || strncmp(p, "q!", cnt) == 0 2722 || strcmp(p, "q!") == 0
2438 ) { 2723 ) {
2439 if (modified_count && p[1] != '!') { 2724 if (modified_count && p[1] != '!') {
2440 status_line_bold("No write since last change (:%s! overrides)", p); 2725 status_line_bold("No write since last change (:%s! overrides)", p);
@@ -2444,8 +2729,8 @@ static void colon(char *buf)
2444 return; 2729 return;
2445 } 2730 }
2446 if (strncmp(p, "write", cnt) == 0 2731 if (strncmp(p, "write", cnt) == 0
2447 || strncmp(p, "wq", cnt) == 0 2732 || strcmp(p, "wq") == 0
2448 || strncmp(p, "wn", cnt) == 0 2733 || strcmp(p, "wn") == 0
2449 || (p[0] == 'x' && !p[1]) 2734 || (p[0] == 'x' && !p[1])
2450 ) { 2735 ) {
2451 if (modified_count != 0 || p[0] != 'x') { 2736 if (modified_count != 0 || p[0] != 'x') {
@@ -2463,7 +2748,6 @@ static void colon(char *buf)
2463 ); 2748 );
2464 if (p[0] == 'x' 2749 if (p[0] == 'x'
2465 || p[1] == 'q' || p[1] == 'n' 2750 || p[1] == 'q' || p[1] == 'n'
2466 || p[1] == 'Q' || p[1] == 'N'
2467 ) { 2751 ) {
2468 editing = 0; 2752 editing = 0;
2469 } 2753 }
@@ -2483,12 +2767,9 @@ static void colon(char *buf)
2483#else 2767#else
2484 2768
2485 char c, *buf1, *q, *r; 2769 char c, *buf1, *q, *r;
2486 char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN]; 2770 char *fn, cmd[MAX_INPUT_LEN], *cmdend, *args, *exp = NULL;
2487 int i, l, li, b, e; 2771 int i, l, li, b, e;
2488 int useforce; 2772 int useforce;
2489# if ENABLE_FEATURE_VI_SEARCH || ENABLE_FEATURE_ALLOW_EXEC
2490 char *orig_buf;
2491# endif
2492 2773
2493 // :3154 // if (-e line 3154) goto it else stay put 2774 // :3154 // if (-e line 3154) goto it else stay put
2494 // :4,33w! foo // write a portion of buffer to file "foo" 2775 // :4,33w! foo // write a portion of buffer to file "foo"
@@ -2512,58 +2793,61 @@ static void colon(char *buf)
2512 2793
2513 li = i = 0; 2794 li = i = 0;
2514 b = e = -1; 2795 b = e = -1;
2515 q = text; // assume 1,$ for the range
2516 r = end - 1;
2517 li = count_lines(text, end - 1); 2796 li = count_lines(text, end - 1);
2518 fn = current_filename; 2797 fn = current_filename;
2519 2798
2520 // look for optional address(es) :. :1 :1,9 :'q,'a :% 2799 // look for optional address(es) :. :1 :1,9 :'q,'a :%
2800 buf1 = buf;
2521 buf = get_address(buf, &b, &e); 2801 buf = get_address(buf, &b, &e);
2522 2802 if (buf == NULL) {
2523# if ENABLE_FEATURE_VI_SEARCH || ENABLE_FEATURE_ALLOW_EXEC 2803 status_line_bold("Bad address: %s", buf1);
2524 // remember orig command line 2804 goto ret;
2525 orig_buf = buf; 2805 }
2526# endif
2527 2806
2528 // get the COMMAND into cmd[] 2807 // get the COMMAND into cmd[]
2808 strcpy(cmd, buf);
2529 buf1 = cmd; 2809 buf1 = cmd;
2530 while (*buf != '\0') { 2810 while (!isspace(*buf1) && *buf1 != '\0') {
2531 if (isspace(*buf)) 2811 buf1++;
2532 break;
2533 *buf1++ = *buf++;
2534 } 2812 }
2535 *buf1 = '\0'; 2813 cmdend = buf1;
2536 // get any ARGuments 2814 // get any ARGuments
2537 while (isblank(*buf)) 2815 while (isblank(*buf1))
2538 buf++; 2816 buf1++;
2539 strcpy(args, buf); 2817 args = buf1;
2818 *cmdend = '\0';
2540 useforce = FALSE; 2819 useforce = FALSE;
2541 buf1 = last_char_is(cmd, '!'); 2820 if (cmdend > cmd && cmdend[-1] == '!') {
2542 if (buf1) {
2543 useforce = TRUE; 2821 useforce = TRUE;
2544 *buf1 = '\0'; // get rid of ! 2822 cmdend[-1] = '\0'; // get rid of !
2545 }
2546 if (b >= 0) {
2547 // if there is only one addr, then the addr
2548 // is the line number of the single line the
2549 // user wants. So, reset the end
2550 // pointer to point at end of the "b" line
2551 q = find_line(b); // what line is #b
2552 r = end_line(q);
2553 li = 1;
2554 } 2823 }
2555 if (e >= 0) { 2824 // assume the command will want a range, certain commands
2556 // we were given two addrs. change the 2825 // (read, substitute) need to adjust these assumptions
2557 // end pointer to the addr given by user. 2826 if (e < 0) {
2558 r = find_line(e); // what line is #e 2827 q = text; // no addr, use 1,$ for the range
2559 r = end_line(r); 2828 r = end - 1;
2560 li = e - b + 1; 2829 } else {
2830 // at least one addr was given, get its details
2831 q = r = find_line(e);
2832 if (b < 0) {
2833 // if there is only one addr, then it's the line
2834 // number of the single line the user wants.
2835 // Reset the end pointer to the end of that line.
2836 r = end_line(q);
2837 li = 1;
2838 } else {
2839 // we were given two addrs. change the
2840 // start pointer to the addr given by user.
2841 q = find_line(b); // what line is #b
2842 r = end_line(r);
2843 li = e - b + 1;
2844 }
2561 } 2845 }
2562 // ------------ now look for the command ------------ 2846 // ------------ now look for the command ------------
2563 i = strlen(cmd); 2847 i = strlen(cmd);
2564 if (i == 0) { // :123CR goto line #123 2848 if (i == 0) { // :123CR goto line #123
2565 if (b >= 0) { 2849 if (e >= 0) {
2566 dot = find_line(b); // what line is #b 2850 dot = find_line(e); // what line is #e
2567 dot_skip_over_ws(); 2851 dot_skip_over_ws();
2568 } 2852 }
2569 } 2853 }
@@ -2571,9 +2855,12 @@ static void colon(char *buf)
2571 else if (cmd[0] == '!') { // run a cmd 2855 else if (cmd[0] == '!') { // run a cmd
2572 int retcode; 2856 int retcode;
2573 // :!ls run the <cmd> 2857 // :!ls run the <cmd>
2858 exp = expand_args(buf + 1);
2859 if (exp == NULL)
2860 goto ret;
2574 go_bottom_and_clear_to_eol(); 2861 go_bottom_and_clear_to_eol();
2575 cookmode(); 2862 cookmode();
2576 retcode = system(orig_buf + 1); // run the cmd 2863 retcode = system(exp); // run the cmd
2577 if (retcode) 2864 if (retcode)
2578 printf("\nshell returned %i\n\n", retcode); 2865 printf("\nshell returned %i\n\n", retcode);
2579 rawmode(); 2866 rawmode();
@@ -2581,16 +2868,16 @@ static void colon(char *buf)
2581 } 2868 }
2582# endif 2869# endif
2583 else if (cmd[0] == '=' && !cmd[1]) { // where is the address 2870 else if (cmd[0] == '=' && !cmd[1]) { // where is the address
2584 if (b < 0) { // no addr given- use defaults 2871 if (e < 0) { // no addr given- use defaults
2585 b = e = count_lines(text, dot); 2872 e = count_lines(text, dot);
2586 } 2873 }
2587 status_line("%d", b); 2874 status_line("%d", e);
2588 } else if (strncmp(cmd, "delete", i) == 0) { // delete lines 2875 } else if (strncmp(cmd, "delete", i) == 0) { // delete lines
2589 if (b < 0) { // no addr given- use defaults 2876 if (e < 0) { // no addr given- use defaults
2590 q = begin_line(dot); // assume .,. for the range 2877 q = begin_line(dot); // assume .,. for the range
2591 r = end_line(dot); 2878 r = end_line(dot);
2592 } 2879 }
2593 dot = yank_delete(q, r, 1, YANKDEL, ALLOW_UNDO); // save, then delete lines 2880 dot = yank_delete(q, r, WHOLE, YANKDEL, ALLOW_UNDO); // save, then delete lines
2594 dot_skip_over_ws(); 2881 dot_skip_over_ws();
2595 } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file 2882 } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file
2596 int size; 2883 int size;
@@ -2602,11 +2889,10 @@ static void colon(char *buf)
2602 } 2889 }
2603 if (args[0]) { 2890 if (args[0]) {
2604 // the user supplied a file name 2891 // the user supplied a file name
2605 fn = args; 2892 fn = exp = expand_args(args);
2606 } else if (current_filename && current_filename[0]) { 2893 if (exp == NULL)
2607 // no user supplied name- use the current filename 2894 goto ret;
2608 // fn = current_filename; was set by default 2895 } else if (current_filename == NULL) {
2609 } else {
2610 // no user file name, no current name- punt 2896 // no user file name, no current name- punt
2611 status_line_bold("No current filename"); 2897 status_line_bold("No current filename");
2612 goto ret; 2898 goto ret;
@@ -2629,7 +2915,7 @@ static void colon(char *buf)
2629 status_line("'%s'%s" 2915 status_line("'%s'%s"
2630 IF_FEATURE_VI_READONLY("%s") 2916 IF_FEATURE_VI_READONLY("%s")
2631 " %uL, %uC", 2917 " %uL, %uC",
2632 current_filename, 2918 fn,
2633 (size < 0 ? " [New file]" : ""), 2919 (size < 0 ? " [New file]" : ""),
2634 IF_FEATURE_VI_READONLY( 2920 IF_FEATURE_VI_READONLY(
2635 ((readonly_mode) ? " [Readonly]" : ""), 2921 ((readonly_mode) ? " [Readonly]" : ""),
@@ -2637,14 +2923,16 @@ static void colon(char *buf)
2637 li, (int)(end - text) 2923 li, (int)(end - text)
2638 ); 2924 );
2639 } else if (strncmp(cmd, "file", i) == 0) { // what File is this 2925 } else if (strncmp(cmd, "file", i) == 0) { // what File is this
2640 if (b != -1 || e != -1) { 2926 if (e >= 0) {
2641 status_line_bold("No address allowed on this command"); 2927 status_line_bold("No address allowed on this command");
2642 goto ret; 2928 goto ret;
2643 } 2929 }
2644 if (args[0]) { 2930 if (args[0]) {
2645 // user wants a new filename 2931 // user wants a new filename
2646 free(current_filename); 2932 exp = expand_args(args);
2647 current_filename = xstrdup(args); 2933 if (exp == NULL)
2934 goto ret;
2935 update_filename(exp);
2648 } else { 2936 } else {
2649 // user wants file status info 2937 // user wants file status info
2650 last_status_cksum = 0; // force status update 2938 last_status_cksum = 0; // force status update
@@ -2657,7 +2945,7 @@ static void colon(char *buf)
2657 rawmode(); 2945 rawmode();
2658 Hit_Return(); 2946 Hit_Return();
2659 } else if (strncmp(cmd, "list", i) == 0) { // literal print line 2947 } else if (strncmp(cmd, "list", i) == 0) { // literal print line
2660 if (b < 0) { // no addr given- use defaults 2948 if (e < 0) { // no addr given- use defaults
2661 q = begin_line(dot); // assume .,. for the range 2949 q = begin_line(dot); // assume .,. for the range
2662 r = end_line(dot); 2950 r = end_line(dot);
2663 } 2951 }
@@ -2724,23 +3012,32 @@ static void colon(char *buf)
2724 } 3012 }
2725 editing = 0; 3013 editing = 0;
2726 } else if (strncmp(cmd, "read", i) == 0) { // read file into text[] 3014 } else if (strncmp(cmd, "read", i) == 0) { // read file into text[]
2727 int size; 3015 int size, num;
2728 3016
2729 fn = args; 3017 if (args[0]) {
2730 if (!fn[0]) { 3018 // the user supplied a file name
2731 status_line_bold("No filename given"); 3019 fn = exp = expand_args(args);
3020 if (exp == NULL)
3021 goto ret;
3022 init_filename(fn);
3023 } else if (current_filename == NULL) {
3024 // no user file name, no current name- punt
3025 status_line_bold("No current filename");
2732 goto ret; 3026 goto ret;
2733 } 3027 }
2734 if (b < 0) { // no addr given- use defaults 3028 if (e < 0) { // no addr given- read after current line
2735 q = begin_line(dot); // assume "dot" 3029 q = begin_line(dot);
2736 } 3030 } else if (e == 0) { // user said ":0r foo"
2737 // read after current line- unless user said ":0r foo" 3031 q = text;
2738 if (b != 0) { 3032 } else { // addr given- read after that line
2739 q = next_line(q); 3033 q = next_line(find_line(e));
2740 // read after last line 3034 // read after last line
2741 if (q == end-1) 3035 if (q == end-1)
2742 ++q; 3036 ++q;
2743 } 3037 }
3038 num = count_lines(text, q);
3039 if (q == end)
3040 num++;
2744 { // dance around potentially-reallocated text[] 3041 { // dance around potentially-reallocated text[]
2745 uintptr_t ofs = q - text; 3042 uintptr_t ofs = q - text;
2746 size = file_insert(fn, q, 0); 3043 size = file_insert(fn, q, 0);
@@ -2757,11 +3054,7 @@ static void colon(char *buf)
2757 IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),) 3054 IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
2758 li, size 3055 li, size
2759 ); 3056 );
2760 if (size > 0) { 3057 dot = find_line(num);
2761 // if the insert is before "dot" then we need to update
2762 if (q <= dot)
2763 dot += size;
2764 }
2765 } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args 3058 } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args
2766 if (modified_count && !useforce) { 3059 if (modified_count && !useforce) {
2767 status_line_bold("No write since last change (:%s! overrides)", cmd); 3060 status_line_bold("No write since last change (:%s! overrides)", cmd);
@@ -2773,20 +3066,21 @@ static void colon(char *buf)
2773# if ENABLE_FEATURE_VI_SET 3066# if ENABLE_FEATURE_VI_SET
2774 } else if (strncmp(cmd, "set", i) == 0) { // set or clear features 3067 } else if (strncmp(cmd, "set", i) == 0) { // set or clear features
2775# if ENABLE_FEATURE_VI_SETOPTS 3068# if ENABLE_FEATURE_VI_SETOPTS
2776 char *argp; 3069 char *argp, *argn, oldch;
2777# endif 3070# endif
2778 i = 0; // offset into args
2779 // only blank is regarded as args delimiter. What about tab '\t'? 3071 // only blank is regarded as args delimiter. What about tab '\t'?
2780 if (!args[0] || strcasecmp(args, "all") == 0) { 3072 if (!args[0] || strcmp(args, "all") == 0) {
2781 // print out values of all options 3073 // print out values of all options
2782# if ENABLE_FEATURE_VI_SETOPTS 3074# if ENABLE_FEATURE_VI_SETOPTS
2783 status_line_bold( 3075 status_line_bold(
2784 "%sautoindent " 3076 "%sautoindent "
3077 "%sexpandtab "
2785 "%sflash " 3078 "%sflash "
2786 "%signorecase " 3079 "%signorecase "
2787 "%sshowmatch " 3080 "%sshowmatch "
2788 "tabstop=%u", 3081 "tabstop=%u",
2789 autoindent ? "" : "no", 3082 autoindent ? "" : "no",
3083 expandtab ? "" : "no",
2790 err_method ? "" : "no", 3084 err_method ? "" : "no",
2791 ignorecase ? "" : "no", 3085 ignorecase ? "" : "no",
2792 showmatch ? "" : "no", 3086 showmatch ? "" : "no",
@@ -2798,20 +3092,15 @@ static void colon(char *buf)
2798# if ENABLE_FEATURE_VI_SETOPTS 3092# if ENABLE_FEATURE_VI_SETOPTS
2799 argp = args; 3093 argp = args;
2800 while (*argp) { 3094 while (*argp) {
2801 if (strncmp(argp, "no", 2) == 0) 3095 i = 0;
2802 i = 2; // ":set noautoindent" 3096 if (argp[0] == 'n' && argp[1] == 'o') // "noXXX"
2803 setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT); 3097 i = 2;
2804 setops(argp, "flash " , i, "fl", VI_ERR_METHOD); 3098 argn = skip_non_whitespace(argp);
2805 setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE); 3099 oldch = *argn;
2806 setops(argp, "showmatch " , i, "sm", VI_SHOWMATCH ); 3100 *argn = '\0';
2807 if (strncmp(argp + i, "tabstop=", 8) == 0) { 3101 setops(argp, i);
2808 int t = 0; 3102 *argn = oldch;
2809 sscanf(argp + i+8, "%u", &t); 3103 argp = skip_whitespace(argn);
2810 if (t > 0 && t <= MAX_TABSTOP)
2811 tabstop = t;
2812 }
2813 argp = skip_non_whitespace(argp);
2814 argp = skip_whitespace(argp);
2815 } 3104 }
2816# endif /* FEATURE_VI_SETOPTS */ 3105# endif /* FEATURE_VI_SETOPTS */
2817# endif /* FEATURE_VI_SET */ 3106# endif /* FEATURE_VI_SET */
@@ -2820,35 +3109,36 @@ static void colon(char *buf)
2820 } else if (cmd[0] == 's') { // substitute a pattern with a replacement pattern 3109 } else if (cmd[0] == 's') { // substitute a pattern with a replacement pattern
2821 char *F, *R, *flags; 3110 char *F, *R, *flags;
2822 size_t len_F, len_R; 3111 size_t len_F, len_R;
2823 int gflag; // global replace flag 3112 int gflag = 0; // global replace flag
2824# if ENABLE_FEATURE_VI_UNDO 3113 int subs = 0; // number of substitutions
2825 int dont_chain_first_item = ALLOW_UNDO; 3114# if ENABLE_FEATURE_VI_VERBOSE_STATUS
3115 int last_line = 0, lines = 0;
2826# endif 3116# endif
2827 3117
2828 // F points to the "find" pattern 3118 // F points to the "find" pattern
2829 // R points to the "replace" pattern 3119 // R points to the "replace" pattern
2830 // replace the cmd line delimiters "/" with NULs 3120 // replace the cmd line delimiters "/" with NULs
2831 c = orig_buf[1]; // what is the delimiter 3121 c = buf[1]; // what is the delimiter
2832 F = orig_buf + 2; // start of "find" 3122 F = buf + 2; // start of "find"
2833 R = strchr(F, c); // middle delimiter 3123 R = strchr(F, c); // middle delimiter
2834 if (!R) 3124 if (!R)
2835 goto colon_s_fail; 3125 goto colon_s_fail;
2836 len_F = R - F; 3126 len_F = R - F;
2837 *R++ = '\0'; // terminate "find" 3127 *R++ = '\0'; // terminate "find"
2838 flags = strchr(R, c); 3128 flags = strchr(R, c);
2839 if (!flags) 3129 if (flags) {
2840 goto colon_s_fail; 3130 *flags++ = '\0'; // terminate "replace"
2841 len_R = flags - R; 3131 gflag = *flags;
2842 *flags++ = '\0'; // terminate "replace" 3132 }
2843 gflag = *flags; 3133 len_R = strlen(R);
2844 3134
2845 q = begin_line(q); 3135 if (e < 0) { // no addr given
2846 if (b < 0) { // maybe :s/foo/bar/
2847 q = begin_line(dot); // start with cur line 3136 q = begin_line(dot); // start with cur line
2848 b = count_lines(text, q); // cur line number 3137 r = end_line(dot);
3138 b = e = count_lines(text, q); // cur line number
3139 } else if (b < 0) { // one addr given
3140 b = e;
2849 } 3141 }
2850 if (e < 0)
2851 e = b; // maybe :.s/foo/bar/
2852 3142
2853 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0 3143 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
2854 char *ls = q; // orig line start 3144 char *ls = q; // orig line start
@@ -2859,14 +3149,21 @@ static void colon(char *buf)
2859 uintptr_t bias; 3149 uintptr_t bias;
2860 // we found the "find" pattern - delete it 3150 // we found the "find" pattern - delete it
2861 // For undo support, the first item should not be chained 3151 // For undo support, the first item should not be chained
2862 text_hole_delete(found, found + len_F - 1, dont_chain_first_item); 3152 text_hole_delete(found, found + len_F - 1,
2863# if ENABLE_FEATURE_VI_UNDO 3153 subs ? ALLOW_UNDO_CHAIN: ALLOW_UNDO);
2864 dont_chain_first_item = ALLOW_UNDO_CHAIN; 3154 // can't do this above, no undo => no third argument
3155 subs++;
3156# if ENABLE_FEATURE_VI_VERBOSE_STATUS
3157 if (last_line != i) {
3158 last_line = i;
3159 ++lines;
3160 }
2865# endif 3161# endif
2866 // insert the "replace" patern 3162 // insert the "replace" patern
2867 bias = string_insert(found, R, ALLOW_UNDO_CHAIN); 3163 bias = string_insert(found, R, ALLOW_UNDO_CHAIN);
2868 found += bias; 3164 found += bias;
2869 ls += bias; 3165 ls += bias;
3166 dot = ls;
2870 //q += bias; - recalculated anyway 3167 //q += bias; - recalculated anyway
2871 // check for "global" :s/foo/bar/g 3168 // check for "global" :s/foo/bar/g
2872 if (gflag == 'g') { 3169 if (gflag == 'g') {
@@ -2878,12 +3175,21 @@ static void colon(char *buf)
2878 } 3175 }
2879 q = next_line(ls); 3176 q = next_line(ls);
2880 } 3177 }
3178 if (subs == 0) {
3179 status_line_bold("No match");
3180 } else {
3181 dot_skip_over_ws();
3182# if ENABLE_FEATURE_VI_VERBOSE_STATUS
3183 if (subs > 1)
3184 status_line("%d substitutions on %d lines", subs, lines);
3185# endif
3186 }
2881# endif /* FEATURE_VI_SEARCH */ 3187# endif /* FEATURE_VI_SEARCH */
2882 } else if (strncmp(cmd, "version", i) == 0) { // show software version 3188 } else if (strncmp(cmd, "version", i) == 0) { // show software version
2883 status_line(BB_VER); 3189 status_line(BB_VER);
2884 } else if (strncmp(cmd, "write", i) == 0 // write text to file 3190 } else if (strncmp(cmd, "write", i) == 0 // write text to file
2885 || strncmp(cmd, "wq", i) == 0 3191 || strcmp(cmd, "wq") == 0
2886 || strncmp(cmd, "wn", i) == 0 3192 || strcmp(cmd, "wn") == 0
2887 || (cmd[0] == 'x' && !cmd[1]) 3193 || (cmd[0] == 'x' && !cmd[1])
2888 ) { 3194 ) {
2889 int size; 3195 int size;
@@ -2891,10 +3197,21 @@ static void colon(char *buf)
2891 3197
2892 // is there a file name to write to? 3198 // is there a file name to write to?
2893 if (args[0]) { 3199 if (args[0]) {
2894 fn = args; 3200 struct stat statbuf;
3201
3202 exp = expand_args(args);
3203 if (exp == NULL)
3204 goto ret;
3205 if (!useforce && (fn == NULL || strcmp(fn, exp) != 0) &&
3206 stat(exp, &statbuf) == 0) {
3207 status_line_bold("File exists (:w! overrides)");
3208 goto ret;
3209 }
3210 fn = exp;
3211 init_filename(fn);
2895 } 3212 }
2896# if ENABLE_FEATURE_VI_READONLY 3213# if ENABLE_FEATURE_VI_READONLY
2897 if (readonly_mode && !useforce) { 3214 else if (readonly_mode && !useforce && fn) {
2898 status_line_bold("'%s' is read only", fn); 3215 status_line_bold("'%s' is read only", fn);
2899 goto ret; 3216 goto ret;
2900 } 3217 }
@@ -2930,10 +3247,20 @@ static void colon(char *buf)
2930 modified_count = 0; 3247 modified_count = 0;
2931 last_modified_count = -1; 3248 last_modified_count = -1;
2932 } 3249 }
2933 if (cmd[0] == 'x' 3250 if (cmd[1] == 'n') {
2934 || cmd[1] == 'q' || cmd[1] == 'n' 3251 editing = 0;
2935 || cmd[1] == 'Q' || cmd[1] == 'N' 3252 } else if (cmd[0] == 'x' || cmd[1] == 'q') {
2936 ) { 3253 // are there other files to edit?
3254 int n = cmdline_filecnt - optind - 1;
3255 if (n > 0) {
3256 if (useforce) {
3257 // force end of argv list
3258 optind = cmdline_filecnt;
3259 } else {
3260 status_line_bold("%u more file(s) to edit", n);
3261 goto ret;
3262 }
3263 }
2937 editing = 0; 3264 editing = 0;
2938 } 3265 }
2939 } 3266 }
@@ -2944,7 +3271,7 @@ static void colon(char *buf)
2944 q = begin_line(dot); // assume .,. for the range 3271 q = begin_line(dot); // assume .,. for the range
2945 r = end_line(dot); 3272 r = end_line(dot);
2946 } 3273 }
2947 text_yank(q, r, YDreg); 3274 text_yank(q, r, YDreg, WHOLE);
2948 li = count_lines(q, r); 3275 li = count_lines(q, r);
2949 status_line("Yank %d lines (%d chars) into [%c]", 3276 status_line("Yank %d lines (%d chars) into [%c]",
2950 li, strlen(reg[YDreg]), what_reg()); 3277 li, strlen(reg[YDreg]), what_reg());
@@ -2954,6 +3281,9 @@ static void colon(char *buf)
2954 not_implemented(cmd); 3281 not_implemented(cmd);
2955 } 3282 }
2956 ret: 3283 ret:
3284# if ENABLE_FEATURE_VI_COLON_EXPAND
3285 free(exp);
3286# endif
2957 dot = bound_dot(dot); // make sure "dot" is valid 3287 dot = bound_dot(dot); // make sure "dot" is valid
2958 return; 3288 return;
2959# if ENABLE_FEATURE_VI_SEARCH 3289# if ENABLE_FEATURE_VI_SEARCH
@@ -3067,83 +3397,109 @@ static void int_handler(int sig)
3067 3397
3068static void do_cmd(int c); 3398static void do_cmd(int c);
3069 3399
3070static int find_range(char **start, char **stop, char c) 3400static int at_eof(const char *s)
3071{ 3401{
3072 char *save_dot, *p, *q, *t; 3402 // does 's' point to end of file, even with no terminating newline?
3073 int cnt, multiline = 0, forward; 3403 return ((s == end - 2 && s[1] == '\n') || s == end - 1);
3404}
3405
3406static int find_range(char **start, char **stop, int cmd)
3407{
3408 char *p, *q, *t;
3409 int buftype = -1;
3410 int c;
3074 3411
3075 save_dot = dot;
3076 p = q = dot; 3412 p = q = dot;
3077 3413
3078 // will a 'G' command move forwards or backwards? 3414#if ENABLE_FEATURE_VI_YANKMARK
3079 forward = cmdcnt == 0 || cmdcnt > count_lines(text, dot); 3415 if (cmd == 'Y') {
3416 c = 'y';
3417 } else
3418#endif
3419 {
3420 c = get_motion_char();
3421 }
3080 3422
3081 if (strchr("cdy><", c)) { 3423#if ENABLE_FEATURE_VI_YANKMARK
3424 if ((cmd == 'Y' || cmd == c) && strchr("cdy><", c)) {
3425#else
3426 if (cmd == c && strchr("cd><", c)) {
3427#endif
3082 // these cmds operate on whole lines 3428 // these cmds operate on whole lines
3083 p = q = begin_line(p); 3429 buftype = WHOLE;
3084 for (cnt = 1; cnt < cmdcnt; cnt++) { 3430 if (--cmdcnt > 0)
3085 q = next_line(q); 3431 do_cmd('j');
3086 } 3432 } else if (strchr("^%$0bBeEfFtThnN/?|{}\b\177", c)) {
3087 q = end_line(q); 3433 // Most operate on char positions within a line. Of those that
3088 } else if (strchr("^%$0bBeEfth\b\177", c)) { 3434 // don't '%' needs no special treatment, search commands are
3089 // These cmds operate on char positions 3435 // marked as MULTI and "{}" are handled below.
3436 buftype = strchr("nN/?", c) ? MULTI : PARTIAL;
3090 do_cmd(c); // execute movement cmd 3437 do_cmd(c); // execute movement cmd
3091 q = dot; 3438 if (p == dot) // no movement is an error
3439 buftype = -1;
3092 } else if (strchr("wW", c)) { 3440 } else if (strchr("wW", c)) {
3441 buftype = MULTI;
3093 do_cmd(c); // execute movement cmd 3442 do_cmd(c); // execute movement cmd
3094 // if we are at the next word's first char 3443 // step back one char, but not if we're at end of file,
3095 // step back one char 3444 // or if we are at EOF and search was for 'w' and we're at
3096 // but check the possibilities when it is true 3445 // the start of a 'W' word.
3097 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0])) 3446 if (dot > p && (!at_eof(dot) || (c == 'w' && ispunct(*dot))))
3098 || (ispunct(dot[-1]) && !ispunct(dot[0])) 3447 dot--;
3099 || (isalnum(dot[-1]) && !isalnum(dot[0])))) 3448 t = dot;
3100 dot--; // move back off of next word 3449 // don't include trailing WS as part of word
3101 if (dot > text && *dot == '\n') 3450 while (dot > p && isspace(*dot)) {
3102 dot--; // stay off NL 3451 if (*dot-- == '\n')
3103 q = dot; 3452 t = dot;
3104 } else if (strchr("H-k{", c) || (c == 'G' && !forward)) { 3453 }
3105 // these operate on multi-lines backwards 3454 // for non-change operations WS after NL is not part of word
3106 q = end_line(dot); // find NL 3455 if (cmd != 'c' && dot != t && *dot != '\n')
3107 do_cmd(c); // execute movement cmd 3456 dot = t;
3108 dot_begin(); 3457 } else if (strchr("GHL+-jk'\r\n", c)) {
3109 p = dot; 3458 // these operate on whole lines
3110 } else if (strchr("L+j}\r\n", c) || (c == 'G' && forward)) { 3459 buftype = WHOLE;
3111 // these operate on multi-lines forwards
3112 p = begin_line(dot);
3113 do_cmd(c); // execute movement cmd 3460 do_cmd(c); // execute movement cmd
3114 dot_end(); // find NL 3461 } else if (c == ' ' || c == 'l') {
3115 q = dot;
3116 } else /* if (c == ' ' || c == 'l') */ {
3117 // forward motion by character 3462 // forward motion by character
3118 int tmpcnt = (cmdcnt ?: 1); 3463 int tmpcnt = (cmdcnt ?: 1);
3464 buftype = PARTIAL;
3119 do_cmd(c); // execute movement cmd 3465 do_cmd(c); // execute movement cmd
3120 // exclude last char unless range isn't what we expected 3466 // exclude last char unless range isn't what we expected
3121 // this indicates we've hit EOL 3467 // this indicates we've hit EOL
3122 if (tmpcnt == dot - p) 3468 if (tmpcnt == dot - p)
3123 dot--; 3469 dot--;
3124 q = dot;
3125 } 3470 }
3471
3472 if (buftype == -1) {
3473 if (c != 27)
3474 indicate_error();
3475 return buftype;
3476 }
3477
3478 q = dot;
3126 if (q < p) { 3479 if (q < p) {
3127 t = q; 3480 t = q;
3128 q = p; 3481 q = p;
3129 p = t; 3482 p = t;
3130 } 3483 }
3131 3484
3132 // backward char movements don't include start position 3485 // movements which don't include end of range
3133 if (q > p && strchr("^0bBh\b\177", c)) q--; 3486 if (q > p) {
3134 3487 if (strchr("^0bBFThnN/?|\b\177", c)) {
3135 multiline = 0; 3488 q--;
3136 for (t = p; t <= q; t++) { 3489 } else if (strchr("{}", c)) {
3137 if (*t == '\n') { 3490 buftype = (p == begin_line(p) && (*q == '\n' || at_eof(q))) ?
3138 multiline = 1; 3491 WHOLE : MULTI;
3139 break; 3492 if (!at_eof(q)) {
3493 q--;
3494 if (q > p && p != begin_line(p))
3495 q--;
3496 }
3140 } 3497 }
3141 } 3498 }
3142 3499
3143 *start = p; 3500 *start = p;
3144 *stop = q; 3501 *stop = q;
3145 dot = save_dot; 3502 return buftype;
3146 return multiline;
3147} 3503}
3148 3504
3149//--------------------------------------------------------------------- 3505//---------------------------------------------------------------------
@@ -3174,11 +3530,19 @@ static void do_cmd(int c)
3174 int dir; 3530 int dir;
3175 int cnt, i, j; 3531 int cnt, i, j;
3176 int c1; 3532 int c1;
3533#if ENABLE_FEATURE_VI_YANKMARK
3534 char *orig_dot = dot;
3535#endif
3536#if ENABLE_FEATURE_VI_UNDO
3537 int allow_undo = ALLOW_UNDO;
3538 int undo_del = UNDO_DEL;
3539#endif
3177 3540
3178// c1 = c; // quiet the compiler 3541// c1 = c; // quiet the compiler
3179// cnt = yf = 0; // quiet the compiler 3542// cnt = yf = 0; // quiet the compiler
3180// p = q = save_dot = buf; // quiet the compiler 3543// p = q = save_dot = buf; // quiet the compiler
3181 memset(buf, '\0', sizeof(buf)); 3544 memset(buf, '\0', sizeof(buf));
3545 keep_index = FALSE;
3182 3546
3183 show_status_line(); 3547 show_status_line();
3184 3548
@@ -3208,7 +3572,7 @@ static void do_cmd(int c)
3208 } else { 3572 } else {
3209 if (1 <= c || Isprint(c)) { 3573 if (1 <= c || Isprint(c)) {
3210 if (c != 27) 3574 if (c != 27)
3211 dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char 3575 dot = yank_delete(dot, dot, PARTIAL, YANKDEL, ALLOW_UNDO); // delete char
3212 dot = char_insert(dot, c, ALLOW_UNDO_CHAIN); // insert new char 3576 dot = char_insert(dot, c, ALLOW_UNDO_CHAIN); // insert new char
3213 } 3577 }
3214 goto dc1; 3578 goto dc1;
@@ -3254,11 +3618,9 @@ static void do_cmd(int c)
3254 //case '*': // *- 3618 //case '*': // *-
3255 //case '=': // =- 3619 //case '=': // =-
3256 //case '@': // @- 3620 //case '@': // @-
3257 //case 'F': // F-
3258 //case 'K': // K- 3621 //case 'K': // K-
3259 //case 'Q': // Q- 3622 //case 'Q': // Q-
3260 //case 'S': // S- 3623 //case 'S': // S-
3261 //case 'T': // T-
3262 //case 'V': // V- 3624 //case 'V': // V-
3263 //case '[': // [- 3625 //case '[': // [-
3264 //case '\\': // \- 3626 //case '\\': // \-
@@ -3303,9 +3665,10 @@ static void do_cmd(int c)
3303 case KEYCODE_DOWN: // cursor key Down 3665 case KEYCODE_DOWN: // cursor key Down
3304 do { 3666 do {
3305 dot_next(); // go to next B-o-l 3667 dot_next(); // go to next B-o-l
3306 // try stay in same col
3307 dot = move_to_col(dot, ccol + offset);
3308 } while (--cmdcnt > 0); 3668 } while (--cmdcnt > 0);
3669 // try to stay in saved column
3670 dot = cindex == C_END ? end_line(dot) : move_to_col(dot, cindex);
3671 keep_index = TRUE;
3309 break; 3672 break;
3310 case 12: // ctrl-L force redraw whole screen 3673 case 12: // ctrl-L force redraw whole screen
3311 case 18: // ctrl-R force redraw 3674 case 18: // ctrl-R force redraw
@@ -3315,8 +3678,8 @@ static void do_cmd(int c)
3315 case '+': // +- goto next line 3678 case '+': // +- goto next line
3316 do { 3679 do {
3317 dot_next(); 3680 dot_next();
3318 dot_skip_over_ws();
3319 } while (--cmdcnt > 0); 3681 } while (--cmdcnt > 0);
3682 dot_skip_over_ws();
3320 break; 3683 break;
3321 case 21: // ctrl-U scroll up half screen 3684 case 21: // ctrl-U scroll up half screen
3322 dot_scroll((rows - 2) / 2, -1); 3685 dot_scroll((rows - 2) / 2, -1);
@@ -3363,6 +3726,9 @@ static void do_cmd(int c)
3363 dot = swap_context(dot); // swap current and previous context 3726 dot = swap_context(dot); // swap current and previous context
3364 dot_begin(); // go to B-o-l 3727 dot_begin(); // go to B-o-l
3365 dot_skip_over_ws(); 3728 dot_skip_over_ws();
3729#if ENABLE_FEATURE_VI_YANKMARK
3730 orig_dot = dot; // this doesn't update stored contexts
3731#endif
3366 } else { 3732 } else {
3367 indicate_error(); 3733 indicate_error();
3368 } 3734 }
@@ -3387,12 +3753,14 @@ static void do_cmd(int c)
3387 status_line_bold("Nothing in register %c", what_reg()); 3753 status_line_bold("Nothing in register %c", what_reg());
3388 break; 3754 break;
3389 } 3755 }
3756 cnt = 0;
3757 i = cmdcnt ?: 1;
3390 // are we putting whole lines or strings 3758 // are we putting whole lines or strings
3391 if (strchr(p, '\n') != NULL) { 3759 if (regtype[YDreg] == WHOLE) {
3392 if (c == 'P') { 3760 if (c == 'P') {
3393 dot_begin(); // putting lines- Put above 3761 dot_begin(); // putting lines- Put above
3394 } 3762 }
3395 if (c == 'p') { 3763 else /* if ( c == 'p') */ {
3396 // are we putting after very last line? 3764 // are we putting after very last line?
3397 if (end_line(dot) == (end - 1)) { 3765 if (end_line(dot) == (end - 1)) {
3398 dot = end; // force dot to end of text[] 3766 dot = end; // force dot to end of text[]
@@ -3403,8 +3771,21 @@ static void do_cmd(int c)
3403 } else { 3771 } else {
3404 if (c == 'p') 3772 if (c == 'p')
3405 dot_right(); // move to right, can move to NL 3773 dot_right(); // move to right, can move to NL
3774 // how far to move cursor if register doesn't have a NL
3775 if (strchr(p, '\n') == NULL)
3776 cnt = i * strlen(p) - 1;
3406 } 3777 }
3407 string_insert(dot, p, ALLOW_UNDO); // insert the string 3778 do {
3779 // dot is adjusted if text[] is reallocated so we don't have to
3780 string_insert(dot, p, allow_undo); // insert the string
3781# if ENABLE_FEATURE_VI_UNDO
3782 allow_undo = ALLOW_UNDO_CHAIN;
3783# endif
3784 } while (--cmdcnt > 0);
3785 dot += cnt;
3786# if ENABLE_FEATURE_VI_YANKMARK && ENABLE_FEATURE_VI_VERBOSE_STATUS
3787 yank_status("Put", p, i);
3788# endif
3408 end_cmd_q(); // stop adding to q 3789 end_cmd_q(); // stop adding to q
3409 break; 3790 break;
3410 case 'U': // U- Undo; replace current line with original version 3791 case 'U': // U- Undo; replace current line with original version
@@ -3415,6 +3796,9 @@ static void do_cmd(int c)
3415 p += string_insert(p, reg[Ureg], ALLOW_UNDO_CHAIN); // insert orig line 3796 p += string_insert(p, reg[Ureg], ALLOW_UNDO_CHAIN); // insert orig line
3416 dot = p; 3797 dot = p;
3417 dot_skip_over_ws(); 3798 dot_skip_over_ws();
3799# if ENABLE_FEATURE_VI_YANKMARK && ENABLE_FEATURE_VI_VERBOSE_STATUS
3800 yank_status("Undo", reg[Ureg], 1);
3801# endif
3418 } 3802 }
3419 break; 3803 break;
3420#endif /* FEATURE_VI_YANKMARK */ 3804#endif /* FEATURE_VI_YANKMARK */
@@ -3431,6 +3815,8 @@ static void do_cmd(int c)
3431 break; 3815 break;
3432 dot_next(); 3816 dot_next();
3433 } 3817 }
3818 cindex = C_END;
3819 keep_index = TRUE;
3434 break; 3820 break;
3435 case '%': // %- find matching char of pair () [] {} 3821 case '%': // %- find matching char of pair () [] {}
3436 for (q = dot; q < end && *q != '\n'; q++) { 3822 for (q = dot; q < end && *q != '\n'; q++) {
@@ -3449,129 +3835,109 @@ static void do_cmd(int c)
3449 indicate_error(); 3835 indicate_error();
3450 break; 3836 break;
3451 case 'f': // f- forward to a user specified char 3837 case 'f': // f- forward to a user specified char
3452 last_forward_char = get_one_char(); // get the search char 3838 case 'F': // F- backward to a user specified char
3453 // 3839 case 't': // t- move to char prior to next x
3454 // dont separate these two commands. 'f' depends on ';' 3840 case 'T': // T- move to char after previous x
3455 // 3841 last_search_char = get_one_char(); // get the search char
3456 //**** fall through to ... ';' 3842 last_search_cmd = c;
3457 case ';': // ;- look at rest of line for last forward char 3843 // fall through
3458 do { 3844 case ';': // ;- look at rest of line for last search char
3459 if (last_forward_char == 0) 3845 case ',': // ,- repeat latest search in opposite direction
3460 break; 3846 dot_to_char(c != ',' ? last_search_cmd : last_search_cmd ^ 0x20);
3461 q = dot + 1;
3462 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3463 q++;
3464 }
3465 if (*q == last_forward_char)
3466 dot = q;
3467 } while (--cmdcnt > 0);
3468 break;
3469 case ',': // repeat latest 'f' in opposite direction
3470 if (last_forward_char == 0)
3471 break;
3472 do {
3473 q = dot - 1;
3474 while (q >= text && *q != '\n' && *q != last_forward_char) {
3475 q--;
3476 }
3477 if (q >= text && *q == last_forward_char)
3478 dot = q;
3479 } while (--cmdcnt > 0);
3480 break; 3847 break;
3481
3482 case '-': // -- goto prev line 3848 case '-': // -- goto prev line
3483 do { 3849 do {
3484 dot_prev(); 3850 dot_prev();
3485 dot_skip_over_ws();
3486 } while (--cmdcnt > 0); 3851 } while (--cmdcnt > 0);
3852 dot_skip_over_ws();
3487 break; 3853 break;
3488#if ENABLE_FEATURE_VI_DOT_CMD 3854#if ENABLE_FEATURE_VI_DOT_CMD
3489 case '.': // .- repeat the last modifying command 3855 case '.': // .- repeat the last modifying command
3490 // Stuff the last_modifying_cmd back into stdin 3856 // Stuff the last_modifying_cmd back into stdin
3491 // and let it be re-executed. 3857 // and let it be re-executed.
3492 if (lmc_len != 0) { 3858 if (lmc_len != 0) {
3493 ioq = ioq_start = xstrndup(last_modifying_cmd, lmc_len); 3859 if (cmdcnt) // update saved count if current count is non-zero
3860 dotcnt = cmdcnt;
3861 last_modifying_cmd[lmc_len] = '\0';
3862 ioq = ioq_start = xasprintf("%u%s", dotcnt, last_modifying_cmd);
3494 } 3863 }
3495 break; 3864 break;
3496#endif 3865#endif
3497#if ENABLE_FEATURE_VI_SEARCH 3866#if ENABLE_FEATURE_VI_SEARCH
3498 case '?': // /- search for a pattern 3867 case 'N': // N- backward search for last pattern
3499 case '/': // /- search for a pattern 3868 dir = last_search_pattern[0] == '/' ? BACK : FORWARD;
3869 goto dc4; // now search for pattern
3870 break;
3871 case '?': // ?- backward search for a pattern
3872 case '/': // /- forward search for a pattern
3500 buf[0] = c; 3873 buf[0] = c;
3501 buf[1] = '\0'; 3874 buf[1] = '\0';
3502 q = get_input_line(buf); // get input line- use "status line" 3875 q = get_input_line(buf); // get input line- use "status line"
3503 if (q[0] && !q[1]) { 3876 if (!q[0]) // user changed mind and erased the "/"- do nothing
3877 break;
3878 if (!q[1]) { // if no pat re-use old pat
3504 if (last_search_pattern[0]) 3879 if (last_search_pattern[0])
3505 last_search_pattern[0] = c; 3880 last_search_pattern[0] = c;
3506 goto dc3; // if no pat re-use old pat 3881 } else { // strlen(q) > 1: new pat- save it and find
3507 }
3508 if (q[0]) { // strlen(q) > 1: new pat- save it and find
3509 // there is a new pat
3510 free(last_search_pattern); 3882 free(last_search_pattern);
3511 last_search_pattern = xstrdup(q); 3883 last_search_pattern = xstrdup(q);
3512 goto dc3; // now find the pattern
3513 }
3514 // user changed mind and erased the "/"- do nothing
3515 break;
3516 case 'N': // N- backward search for last pattern
3517 dir = BACK; // assume BACKWARD search
3518 p = dot - 1;
3519 if (last_search_pattern[0] == '?') {
3520 dir = FORWARD;
3521 p = dot + 1;
3522 } 3884 }
3523 goto dc4; // now search for pattern 3885 // fall through
3524 break;
3525 case 'n': // n- repeat search for last pattern 3886 case 'n': // n- repeat search for last pattern
3526 // search rest of text[] starting at next char 3887 // search rest of text[] starting at next char
3527 // if search fails return orignal "p" not the "p+1" address 3888 // if search fails "dot" is unchanged
3528 do { 3889 dir = last_search_pattern[0] == '/' ? FORWARD : BACK;
3529 const char *msg;
3530 dc3:
3531 dir = FORWARD; // assume FORWARD search
3532 p = dot + 1;
3533 if (last_search_pattern[0] == '?') {
3534 dir = BACK;
3535 p = dot - 1;
3536 }
3537 dc4: 3890 dc4:
3538 q = char_search(p, last_search_pattern + 1, (dir << 1) | FULL); 3891 if (last_search_pattern[1] == '\0') {
3892 status_line_bold("No previous search");
3893 break;
3894 }
3895 do {
3896 q = char_search(dot + dir, last_search_pattern + 1,
3897 (dir << 1) | FULL);
3539 if (q != NULL) { 3898 if (q != NULL) {
3540 dot = q; // good search, update "dot" 3899 dot = q; // good search, update "dot"
3541 msg = NULL;
3542 goto dc2;
3543 }
3544 // no pattern found between "dot" and "end"- continue at top
3545 p = text;
3546 if (dir == BACK) {
3547 p = end - 1;
3548 }
3549 q = char_search(p, last_search_pattern + 1, (dir << 1) | FULL);
3550 if (q != NULL) { // found something
3551 dot = q; // found new pattern- goto it
3552 msg = "search hit BOTTOM, continuing at TOP";
3553 if (dir == BACK) {
3554 msg = "search hit TOP, continuing at BOTTOM";
3555 }
3556 } else { 3900 } else {
3557 msg = "Pattern not found"; 3901 // no pattern found between "dot" and top/bottom of file
3902 // continue from other end of file
3903 const char *msg;
3904 q = char_search(dir == FORWARD ? text : end - 1,
3905 last_search_pattern + 1, (dir << 1) | FULL);
3906 if (q != NULL) { // found something
3907 dot = q; // found new pattern- goto it
3908 msg = "search hit %s, continuing at %s";
3909 } else { // pattern is nowhere in file
3910 cmdcnt = 0; // force exit from loop
3911 msg = "Pattern not found";
3912 }
3913 if (dir == FORWARD)
3914 status_line_bold(msg, "BOTTOM", "TOP");
3915 else
3916 status_line_bold(msg, "TOP", "BOTTOM");
3558 } 3917 }
3559 dc2:
3560 if (msg)
3561 status_line_bold("%s", msg);
3562 } while (--cmdcnt > 0); 3918 } while (--cmdcnt > 0);
3563 break; 3919 break;
3564 case '{': // {- move backward paragraph 3920 case '{': // {- move backward paragraph
3565 q = char_search(dot, "\n\n", ((unsigned)BACK << 1) | FULL);
3566 if (q != NULL) { // found blank line
3567 dot = next_line(q); // move to next blank line
3568 }
3569 break;
3570 case '}': // }- move forward paragraph 3921 case '}': // }- move forward paragraph
3571 q = char_search(dot, "\n\n", (FORWARD << 1) | FULL); 3922 dir = c == '}' ? FORWARD : BACK;
3572 if (q != NULL) { // found blank line 3923 do {
3573 dot = next_line(q); // move to next blank line 3924 int skip = TRUE; // initially skip consecutive empty lines
3574 } 3925 while (dir == FORWARD ? dot < end - 1 : dot > text) {
3926 if (*dot == '\n' && dot[dir] == '\n') {
3927 if (!skip) {
3928 if (dir == FORWARD)
3929 ++dot; // move to next blank line
3930 goto dc2;
3931 }
3932 }
3933 else {
3934 skip = FALSE;
3935 }
3936 dot += dir;
3937 }
3938 goto dc6; // end of file
3939 dc2: continue;
3940 } while (--cmdcnt > 0);
3575 break; 3941 break;
3576#endif /* FEATURE_VI_SEARCH */ 3942#endif /* FEATURE_VI_SEARCH */
3577 case '0': // 0- goto beginning of line 3943 case '0': // 0- goto beginning of line
@@ -3597,28 +3963,31 @@ static void do_cmd(int c)
3597 case '<': // <- Left shift something 3963 case '<': // <- Left shift something
3598 case '>': // >- Right shift something 3964 case '>': // >- Right shift something
3599 cnt = count_lines(text, dot); // remember what line we are on 3965 cnt = count_lines(text, dot); // remember what line we are on
3600 c1 = get_one_char(); // get the type of thing to delete 3966 if (find_range(&p, &q, c) == -1)
3601 find_range(&p, &q, c1); 3967 goto dc6;
3602 yank_delete(p, q, 1, YANKONLY, NO_UNDO); // save copy before change
3603 p = begin_line(p);
3604 q = end_line(q);
3605 i = count_lines(p, q); // # of lines we are shifting 3968 i = count_lines(p, q); // # of lines we are shifting
3606 for ( ; i > 0; i--, p = next_line(p)) { 3969 for (p = begin_line(p); i > 0; i--, p = next_line(p)) {
3607 if (c == '<') { 3970 if (c == '<') {
3608 // shift left- remove tab or 8 spaces 3971 // shift left- remove tab or tabstop spaces
3609 if (*p == '\t') { 3972 if (*p == '\t') {
3610 // shrink buffer 1 char 3973 // shrink buffer 1 char
3611 text_hole_delete(p, p, NO_UNDO); 3974 text_hole_delete(p, p, allow_undo);
3612 } else if (*p == ' ') { 3975 } else if (*p == ' ') {
3613 // we should be calculating columns, not just SPACE 3976 // we should be calculating columns, not just SPACE
3614 for (j = 0; *p == ' ' && j < tabstop; j++) { 3977 for (j = 0; *p == ' ' && j < tabstop; j++) {
3615 text_hole_delete(p, p, NO_UNDO); 3978 text_hole_delete(p, p, allow_undo);
3979#if ENABLE_FEATURE_VI_UNDO
3980 allow_undo = ALLOW_UNDO_CHAIN;
3981#endif
3616 } 3982 }
3617 } 3983 }
3618 } else if (c == '>') { 3984 } else /* if (c == '>') */ {
3619 // shift right -- add tab or 8 spaces 3985 // shift right -- add tab or tabstop spaces
3620 char_insert(p, '\t', ALLOW_UNDO); 3986 char_insert(p, '\t', allow_undo);
3621 } 3987 }
3988#if ENABLE_FEATURE_VI_UNDO
3989 allow_undo = ALLOW_UNDO_CHAIN;
3990#endif
3622 } 3991 }
3623 dot = find_line(cnt); // what line were we on 3992 dot = find_line(cnt); // what line were we on
3624 dot_skip_over_ws(); 3993 dot_skip_over_ws();
@@ -3652,7 +4021,7 @@ static void do_cmd(int c)
3652 save_dot = dot; 4021 save_dot = dot;
3653 dot = dollar_line(dot); // move to before NL 4022 dot = dollar_line(dot); // move to before NL
3654 // copy text into a register and delete 4023 // copy text into a register and delete
3655 dot = yank_delete(save_dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete to e-o-l 4024 dot = yank_delete(save_dot, dot, PARTIAL, YANKDEL, ALLOW_UNDO); // delete to e-o-l
3656 if (c == 'C') 4025 if (c == 'C')
3657 goto dc_i; // start inserting 4026 goto dc_i; // start inserting
3658#if ENABLE_FEATURE_VI_DOT_CMD 4027#if ENABLE_FEATURE_VI_DOT_CMD
@@ -3679,6 +4048,7 @@ static void do_cmd(int c)
3679 if (cmdcnt > 0) { 4048 if (cmdcnt > 0) {
3680 dot = find_line(cmdcnt); // what line is #cmdcnt 4049 dot = find_line(cmdcnt); // what line is #cmdcnt
3681 } 4050 }
4051 dot_begin();
3682 dot_skip_over_ws(); 4052 dot_skip_over_ws();
3683 break; 4053 break;
3684 case 'H': // H- goto top line on screen 4054 case 'H': // H- goto top line on screen
@@ -3735,20 +4105,21 @@ static void do_cmd(int c)
3735 dot = screenbegin; 4105 dot = screenbegin;
3736 for (cnt = 0; cnt < (rows-1) / 2; cnt++) 4106 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3737 dot = next_line(dot); 4107 dot = next_line(dot);
4108 dot_skip_over_ws();
3738 break; 4109 break;
3739 case 'O': // O- open a empty line above 4110 case 'O': // O- open an empty line above
3740 // 0i\n ESC -i 4111 dot_begin();
3741 p = begin_line(dot); 4112 set_openabove();
3742 if (p[-1] == '\n') { 4113 goto dc3;
4114 case 'o': // o- open an empty line below
4115 dot_end();
4116 dc3:
4117 dot = char_insert(dot, '\n', ALLOW_UNDO);
4118 if (c == 'O' && !autoindent) {
4119 // done in char_insert() for openabove+autoindent
3743 dot_prev(); 4120 dot_prev();
3744 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3745 dot_end();
3746 dot = char_insert(dot, '\n', ALLOW_UNDO);
3747 } else {
3748 dot_begin(); // 0
3749 dot = char_insert(dot, '\n', ALLOW_UNDO); // i\n ESC
3750 dot_prev(); // -
3751 } 4121 }
4122 clear_openabove();
3752 goto dc_i; 4123 goto dc_i;
3753 break; 4124 break;
3754 case 'R': // R- continuous Replace char 4125 case 'R': // R- continuous Replace char
@@ -3758,7 +4129,7 @@ static void do_cmd(int c)
3758 break; 4129 break;
3759 case KEYCODE_DELETE: 4130 case KEYCODE_DELETE:
3760 if (dot < end - 1) 4131 if (dot < end - 1)
3761 dot = yank_delete(dot, dot, 1, YANKDEL, ALLOW_UNDO); 4132 dot = yank_delete(dot, dot, PARTIAL, YANKDEL, ALLOW_UNDO);
3762 break; 4133 break;
3763 case 'X': // X- delete char before dot 4134 case 'X': // X- delete char before dot
3764 case 'x': // x- delete the current char 4135 case 'x': // x- delete the current char
@@ -3770,7 +4141,10 @@ static void do_cmd(int c)
3770 if (dot[dir] != '\n') { 4141 if (dot[dir] != '\n') {
3771 if (c == 'X') 4142 if (c == 'X')
3772 dot--; // delete prev char 4143 dot--; // delete prev char
3773 dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char 4144 dot = yank_delete(dot, dot, PARTIAL, YANKDEL, allow_undo); // delete char
4145#if ENABLE_FEATURE_VI_UNDO
4146 allow_undo = ALLOW_UNDO_CHAIN;
4147#endif
3774 } 4148 }
3775 } while (--cmdcnt > 0); 4149 } while (--cmdcnt > 0);
3776 end_cmd_q(); // stop adding to q 4150 end_cmd_q(); // stop adding to q
@@ -3785,7 +4159,7 @@ static void do_cmd(int c)
3785 break; 4159 break;
3786 } 4160 }
3787 if (modified_count) { 4161 if (modified_count) {
3788 if (ENABLE_FEATURE_VI_READONLY && readonly_mode) { 4162 if (ENABLE_FEATURE_VI_READONLY && readonly_mode && current_filename) {
3789 status_line_bold("'%s' is read only", current_filename); 4163 status_line_bold("'%s' is read only", current_filename);
3790 break; 4164 break;
3791 } 4165 }
@@ -3799,6 +4173,14 @@ static void do_cmd(int c)
3799 } else { 4173 } else {
3800 editing = 0; 4174 editing = 0;
3801 } 4175 }
4176 // are there other files to edit?
4177 j = cmdline_filecnt - optind - 1;
4178 if (editing == 0 && j > 0) {
4179 editing = 1;
4180 modified_count = 0;
4181 last_modified_count = -1;
4182 status_line_bold("%u more file(s) to edit", j);
4183 }
3802 break; 4184 break;
3803 case '^': // ^- move to first non-blank on line 4185 case '^': // ^- move to first non-blank on line
3804 dot_begin(); 4186 dot_begin();
@@ -3830,103 +4212,76 @@ static void do_cmd(int c)
3830 case 'Y': // Y- Yank a line 4212 case 'Y': // Y- Yank a line
3831#endif 4213#endif
3832 { 4214 {
3833 int yf, ml, whole = 0; 4215 int yf = YANKDEL; // assume either "c" or "d"
3834 yf = YANKDEL; // assume either "c" or "d" 4216 int buftype;
3835#if ENABLE_FEATURE_VI_YANKMARK 4217#if ENABLE_FEATURE_VI_YANKMARK
4218# if ENABLE_FEATURE_VI_VERBOSE_STATUS
4219 char *savereg = reg[YDreg];
4220# endif
3836 if (c == 'y' || c == 'Y') 4221 if (c == 'y' || c == 'Y')
3837 yf = YANKONLY; 4222 yf = YANKONLY;
3838#endif 4223#endif
3839 c1 = 'y';
3840 if (c != 'Y')
3841 c1 = get_one_char(); // get the type of thing to delete
3842 // determine range, and whether it spans lines 4224 // determine range, and whether it spans lines
3843 ml = find_range(&p, &q, c1); 4225 buftype = find_range(&p, &q, c);
3844 place_cursor(0, 0); 4226 if (buftype == -1) // invalid range
3845 if (c1 == 27) { // ESC- user changed mind and wants out 4227 goto dc6;
3846 c = c1 = 27; // Escape- do nothing 4228 if (buftype == WHOLE) {
3847 } else if (strchr("wW", c1)) { 4229 save_dot = p; // final cursor position is start of range
3848 ml = 0; // multi-line ranges aren't allowed for words 4230 p = begin_line(p);
3849 if (c == 'c') { 4231 q = end_line(q);
3850 // don't include trailing WS as part of word
3851 while (isspace(*q) && q > p) {
3852 q--;
3853 }
3854 }
3855 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word
3856 } else if (strchr("^0bBeEft%$ lh\b\177", c1)) {
3857 // partial line copy text into a register and delete
3858 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word
3859 } else if (strchr("cdykjGHL+-{}\r\n", c1)) {
3860 // whole line copy text into a register and delete
3861 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete lines
3862 whole = 1;
3863 } else {
3864 // could not recognize object
3865 c = c1 = 27; // error-
3866 ml = 0;
3867 indicate_error();
3868 } 4232 }
3869 if (ml && whole) { 4233 dot = yank_delete(p, q, buftype, yf, ALLOW_UNDO); // delete word
4234 if (buftype == WHOLE) {
3870 if (c == 'c') { 4235 if (c == 'c') {
3871 dot = char_insert(dot, '\n', ALLOW_UNDO_CHAIN); 4236 dot = char_insert(dot, '\n', ALLOW_UNDO_CHAIN);
3872 // on the last line of file don't move to prev line 4237 // on the last line of file don't move to prev line
3873 if (whole && dot != (end-1)) { 4238 if (dot != (end-1)) {
3874 dot_prev(); 4239 dot_prev();
3875 } 4240 }
3876 } else if (c == 'd') { 4241 } else {
3877 dot_begin(); 4242 dot = save_dot;
3878 dot_skip_over_ws();
3879 } 4243 }
3880 } 4244 }
3881 if (c1 != 27) { 4245 // if CHANGING, not deleting, start inserting after the delete
3882 // if CHANGING, not deleting, start inserting after the delete 4246 if (c == 'c') {
3883 if (c == 'c') { 4247 goto dc_i; // start inserting
3884 strcpy(buf, "Change");
3885 goto dc_i; // start inserting
3886 }
3887 if (c == 'd') {
3888 strcpy(buf, "Delete");
3889 }
3890#if ENABLE_FEATURE_VI_YANKMARK
3891 if (c == 'y' || c == 'Y') {
3892 strcpy(buf, "Yank");
3893 }
3894 p = reg[YDreg];
3895 q = p + strlen(p);
3896 for (cnt = 0; p <= q; p++) {
3897 if (*p == '\n')
3898 cnt++;
3899 }
3900 status_line("%s %u lines (%u chars) using [%c]",
3901 buf, cnt, (unsigned)strlen(reg[YDreg]), what_reg());
3902#endif
3903 end_cmd_q(); // stop adding to q
3904 } 4248 }
4249#if ENABLE_FEATURE_VI_YANKMARK && ENABLE_FEATURE_VI_VERBOSE_STATUS
4250 // only update status if a yank has actually happened
4251 if (reg[YDreg] != savereg)
4252 yank_status(c == 'd' ? "Delete" : "Yank", reg[YDreg], 1);
4253#endif
4254 dc6:
4255 end_cmd_q(); // stop adding to q
3905 break; 4256 break;
3906 } 4257 }
3907 case 'k': // k- goto prev line, same col 4258 case 'k': // k- goto prev line, same col
3908 case KEYCODE_UP: // cursor key Up 4259 case KEYCODE_UP: // cursor key Up
3909 do { 4260 do {
3910 dot_prev(); 4261 dot_prev();
3911 dot = move_to_col(dot, ccol + offset); // try stay in same col
3912 } while (--cmdcnt > 0); 4262 } while (--cmdcnt > 0);
4263 // try to stay in saved column
4264 dot = cindex == C_END ? end_line(dot) : move_to_col(dot, cindex);
4265 keep_index = TRUE;
3913 break; 4266 break;
3914 case 'r': // r- replace the current char with user input 4267 case 'r': // r- replace the current char with user input
3915 c1 = get_one_char(); // get the replacement char 4268 c1 = get_one_char(); // get the replacement char
3916 if (*dot != '\n') { 4269 if (c1 != 27) {
3917 dot = text_hole_delete(dot, dot, ALLOW_UNDO); 4270 if (end_line(dot) - dot < (cmdcnt ?: 1)) {
3918 dot = char_insert(dot, c1, ALLOW_UNDO_CHAIN); 4271 indicate_error();
4272 goto dc6;
4273 }
4274 do {
4275 dot = text_hole_delete(dot, dot, allow_undo);
4276#if ENABLE_FEATURE_VI_UNDO
4277 allow_undo = ALLOW_UNDO_CHAIN;
4278#endif
4279 dot = char_insert(dot, c1, allow_undo);
4280 } while (--cmdcnt > 0);
3919 dot_left(); 4281 dot_left();
3920 } 4282 }
3921 end_cmd_q(); // stop adding to q 4283 end_cmd_q(); // stop adding to q
3922 break; 4284 break;
3923 case 't': // t- move to char prior to next x
3924 last_forward_char = get_one_char();
3925 do_cmd(';');
3926 if (*dot == last_forward_char)
3927 dot_left();
3928 last_forward_char = 0;
3929 break;
3930 case 'w': // w- forward a word 4285 case 'w': // w- forward a word
3931 do { 4286 do {
3932 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM 4287 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
@@ -3957,14 +4312,11 @@ static void do_cmd(int c)
3957 case '~': // ~- flip the case of letters a-z -> A-Z 4312 case '~': // ~- flip the case of letters a-z -> A-Z
3958 do { 4313 do {
3959#if ENABLE_FEATURE_VI_UNDO 4314#if ENABLE_FEATURE_VI_UNDO
3960 if (islower(*dot)) { 4315 if (isalpha(*dot)) {
3961 undo_push(dot, 1, UNDO_DEL); 4316 undo_push(dot, 1, undo_del);
3962 *dot = toupper(*dot); 4317 *dot = islower(*dot) ? toupper(*dot) : tolower(*dot);
3963 undo_push(dot, 1, UNDO_INS_CHAIN);
3964 } else if (isupper(*dot)) {
3965 undo_push(dot, 1, UNDO_DEL);
3966 *dot = tolower(*dot);
3967 undo_push(dot, 1, UNDO_INS_CHAIN); 4318 undo_push(dot, 1, UNDO_INS_CHAIN);
4319 undo_del = UNDO_DEL_CHAIN;
3968 } 4320 }
3969#else 4321#else
3970 if (islower(*dot)) { 4322 if (islower(*dot)) {
@@ -4012,7 +4364,8 @@ static void do_cmd(int c)
4012 dot = bound_dot(dot); // make sure "dot" is valid 4364 dot = bound_dot(dot); // make sure "dot" is valid
4013 } 4365 }
4014#if ENABLE_FEATURE_VI_YANKMARK 4366#if ENABLE_FEATURE_VI_YANKMARK
4015 check_context(c); // update the current context 4367 if (dot != orig_dot)
4368 check_context(c); // update the current context
4016#endif 4369#endif
4017 4370
4018 if (!isdigit(c)) 4371 if (!isdigit(c))
@@ -4270,7 +4623,6 @@ static void edit_file(char *fn)
4270 mark[26] = mark[27] = text; // init "previous context" 4623 mark[26] = mark[27] = text; // init "previous context"
4271#endif 4624#endif
4272 4625
4273 last_forward_char = '\0';
4274#if ENABLE_FEATURE_VI_CRASHME 4626#if ENABLE_FEATURE_VI_CRASHME
4275 last_input_char = '\0'; 4627 last_input_char = '\0';
4276#endif 4628#endif
@@ -4297,7 +4649,6 @@ static void edit_file(char *fn)
4297#if ENABLE_FEATURE_VI_DOT_CMD 4649#if ENABLE_FEATURE_VI_DOT_CMD
4298 free(ioq_start); 4650 free(ioq_start);
4299 ioq_start = NULL; 4651 ioq_start = NULL;
4300 lmc_len = 0;
4301 adding2q = 0; 4652 adding2q = 0;
4302#endif 4653#endif
4303 4654
@@ -4345,7 +4696,7 @@ static void edit_file(char *fn)
4345 // save a copy of the current line- for the 'U" command 4696 // save a copy of the current line- for the 'U" command
4346 if (begin_line(dot) != cur_line) { 4697 if (begin_line(dot) != cur_line) {
4347 cur_line = begin_line(dot); 4698 cur_line = begin_line(dot);
4348 text_yank(begin_line(dot), end_line(dot), Ureg); 4699 text_yank(begin_line(dot), end_line(dot), Ureg, PARTIAL);
4349 } 4700 }
4350#endif 4701#endif
4351#if ENABLE_FEATURE_VI_DOT_CMD 4702#if ENABLE_FEATURE_VI_DOT_CMD
@@ -4408,10 +4759,10 @@ int vi_main(int argc, char **argv)
4408 } 4759 }
4409#endif 4760#endif
4410 4761
4411 // autoindent is not default in vim 7.3 4762 // 0: all of our options are disabled by default in vim
4412 vi_setops = /*VI_AUTOINDENT |*/ VI_SHOWMATCH | VI_IGNORECASE; 4763 //vi_setops = 0;
4413 // 1- process $HOME/.exrc file (not inplemented yet) 4764 // 1- process EXINIT variable from environment
4414 // 2- process EXINIT variable from environment 4765 // 2- if EXINIT is unset process $HOME/.exrc file (not inplemented yet)
4415 // 3- process command line args 4766 // 3- process command line args
4416#if ENABLE_FEATURE_VI_COLON 4767#if ENABLE_FEATURE_VI_COLON
4417 { 4768 {