diff options
Diffstat (limited to 'editors')
-rw-r--r-- | editors/vi.c | 1503 |
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[] |
247 | static const char modifying_cmds[] ALIGN1 = "aAcCdDiIJoOpPrRs""xX<>~"; | 262 | static 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 | ||
803 | static int prev_tabstop(int col) | ||
804 | { | ||
805 | return col - ((col % tabstop) ?: tabstop); | ||
806 | } | ||
807 | |||
808 | static 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 | |||
817 | static 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 ------------------------------ |
745 | static void screen_erase(void) | 828 | static 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 -------------------------- |
772 | static NOINLINE void sync_cursor(char *d, int *row, int *col) | 855 | static 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 | ||
1196 | static 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) |
1115 | static char *get_input_line(const char *prompt) | 1217 | static 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 |
1346 | static char *text_yank(char *p, char *q, int dest) // copy text into a register | 1448 | // copy text into a register |
1449 | static 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 | ||
1372 | static void check_context(char cmd) | 1479 | static 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 | ||
1504 | static 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 |
1408 | static void undo_push(char *, unsigned, unsigned char); | 1521 | static 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 |
1535 | static void undo_push(char *src, unsigned length, uint8_t u_type) | 1648 | static 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 | ||
1899 | static 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 | |||
1788 | static void dot_scroll(int cnt, int dir) | 1926 | static 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" | |||
1827 | static void start_new_cmd_q(char c) | 1965 | static 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 | } |
1839 | static void end_cmd_q(void) | 1973 | static 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 |
1856 | static char *yank_delete(char *start, char *stop, int dist, int yf, int undo) | 1989 | static 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 |
2034 | static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' | 2161 | static 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 | ||
2270 | static 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 | |||
2285 | static 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 |
2107 | static int init_text_buffer(char *fn) | 2306 | static 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 |
2315 | static char *get_one_address(char *p, int *addr) // get colon addr, if present | 2501 | static 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 | ||
2374 | static 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. | ||
2596 | static 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 |
2403 | static void setops(const char *args, const char *opname, int flg_no, | 2632 | static 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 | ||
2670 | static 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 | ||
3068 | static void do_cmd(int c); | 3398 | static void do_cmd(int c); |
3069 | 3399 | ||
3070 | static int find_range(char **start, char **stop, char c) | 3400 | static 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 | |||
3406 | static 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 | { |